Commit realizado el 12:13:52 08-04-2024

This commit is contained in:
Pagina Web Monito
2024-04-08 12:13:55 -04:00
commit 0c33094de9
7815 changed files with 1365694 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
<?php
abstract class ET_Builder_Module_Field_Base {
/**
* @param array $args
*
* @return array
*/
abstract public function get_fields( array $args = array() );
}

View File

@@ -0,0 +1,800 @@
<?php
/**
* Border Field.
*
* @package Divi
* @subpackage Builder
*/
/**
* Handles border field for modules.
*/
class ET_Builder_Module_Field_Border extends ET_Builder_Module_Field_Base {
/**
* @var ET_Core_Data_Utils
*/
protected static $_;
/**
* @var ET_Builder_Module_Helper_ResponsiveOptions
*
* @since 3.23
*/
public static $responsive = null;
protected static $_is_default = array();
/**
* List of no border reset.
*
* @since 3.23 Remove pricing tables from the list to allow border reset.
*
* @var array
*/
protected static $_no_border_reset = array(
'et_pb_accordion',
'et_pb_accordion_item',
'et_pb_pricing_table',
'et_pb_tabs',
'et_pb_toggle',
'et_pb_social_media_follow',
);
/**
* Option template helper.
*
* @var ET_Builder_Module_Helper_OptionTemplate
*/
public $template;
/**
* ET_Builder_Module_Field_Border constructor.
*/
public function __construct() {
$this->template = et_pb_option_template();
self::$_ = ET_Core_Data_Utils::instance();
$this->set_template();
}
/**
* Set option template for borders
*
* @since 3.28
*
* @return void
*/
public function set_template() {
$template = $this->template;
if ( $template->is_enabled() && ! $template->has( 'border' ) ) {
$template_placeholders = $template->placeholders(
array(
'suffix' => null,
'label_prefix' => null,
'tab_slug' => null,
'toggle_slug' => null,
'color_type' => null,
'depends_on' => null,
'depends_show_if_not' => null,
'depends_show_if' => null,
'use_radius' => null,
'sub_toggle' => null,
'defaults' => array(
'border_radii' => null,
'border_styles' => array(
'width' => null,
'color' => null,
'style' => null,
),
),
)
);
$template->add( 'border', $this->get_fields( $template_placeholders ) );
}
}
/**
* Get border fields.
*
* @since 3.28 Add option template support
* @since 3.23 Add support for responsive settings. Add allowed units for some range fields.
*
* @param array $args Border settings arguments.
* @param bool $return_template_id return template id
*
* @return array Border fields.
*/
public function get_fields( array $args = array(), $return_template_id = false ) {
$settings = shortcode_atts(
array(
'suffix' => '',
'label_prefix' => '',
'tab_slug' => 'advanced',
'toggle_slug' => 'border',
'color_type' => 'color-alpha',
'depends_on' => null,
'depends_show_if_not' => null,
'depends_show_if' => null,
'sub_toggle' => null,
'use_radius' => true,
'defaults' => array(
'border_radii' => 'on||||',
'border_styles' => array(
'width' => '0px',
'color' => '#333333',
'style' => 'solid',
),
),
),
$args
);
if ( $this->template->is_enabled() && $this->template->has( 'border' ) ) {
return $this->template->create( 'border', $settings, $return_template_id );
}
$additional_options = array();
$suffix = $settings['suffix'];
$defaults = $settings['defaults']['border_styles'];
$defaultUnit = 'px';
if ( $settings['use_radius'] ) {
$additional_options[ "border_radii{$suffix}" ] = array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Rounded Corners', 'et_builder' ) ),
'type' => 'border-radius',
'hover' => 'tabs',
'validate_input' => true,
'default' => $settings['defaults']['border_radii'],
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
'sub_toggle' => $settings['sub_toggle'],
'attr_suffix' => $suffix,
'option_category' => 'border',
'description' => esc_html__( 'Here you can control the corner radius of this element. Enable the link icon to control all four corners at once, or disable to define custom values for each.', 'et_builder' ),
'tooltip' => esc_html__( 'Sync values', 'et_builder' ),
'mobile_options' => true,
'sticky' => true,
'allowed_units' => array( '%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ),
);
} else {
$additional_options[ "border_radii{$suffix}" ] = array();
}
$additional_options[ "border_styles{$suffix}" ] = array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Border Styles', 'et_builder' ) ),
'description' => esc_html__( 'You can add borders to any element, customize their appearance and assign unique styles to each edge.', 'et_builder' ),
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
'sub_toggle' => $settings['sub_toggle'],
'type' => 'composite',
'attr_suffix' => $suffix,
'option_category' => 'border',
'composite_type' => 'tabbed',
'composite_structure' => array(
'border_all' => array(
'icon' => 'border-all',
'controls' => array(
"border_width_all{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Border Width', 'et_builder' ) ),
'description' => esc_html__( 'Increasing the width of the border will increase its size/thickness.', 'et_builder' ),
'type' => 'range',
'hover' => 'tabs',
'default' => $defaults['width'],
'default_unit' => $defaultUnit,
'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ),
'range_settings' => array(
'min' => 0,
'max' => 50,
'step' => 1,
'min_limit' => 0,
),
'context' => "border_styles{$suffix}",
'mobile_options' => true,
'sticky' => true,
),
"border_color_all{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Border Color', 'et_builder' ) ),
'description' => esc_html__( 'Pick a color to be used for the border.', 'et_builder' ),
'type' => $settings['color_type'],
'hover' => 'tabs',
'default' => $defaults['color'],
'context' => "border_styles{$suffix}",
'mobile_options' => true,
'sticky' => true,
),
"border_style_all{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Border Style', 'et_builder' ) ),
'description' => esc_html__( 'Borders support various different styles, each of which will change the shape of the border element.', 'et_builder' ),
'type' => 'select',
'options' => et_builder_get_border_styles(),
'default' => $defaults['style'],
'context' => "border_styles{$suffix}",
'hover' => 'tabs',
'mobile_options' => true,
'sticky' => true,
),
),
),
'border_top' => array(
'icon' => 'border-top',
'controls' => array(
"border_width_top{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Top Border Width', 'et_builder' ) ),
'description' => esc_html__( 'Increasing the width of the border will increase its size/thickness.', 'et_builder' ),
'type' => 'range',
'hover' => 'tabs',
'allow_empty' => true,
'default_from' => "border_all.controls.border_width_all{$suffix}",
'default_unit' => $defaultUnit,
'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ),
'range_settings' => array(
'min' => 0,
'max' => 50,
'step' => 1,
'min_limit' => 0,
),
'context' => "border_styles{$suffix}",
'mobile_options' => true,
'sticky' => true,
),
"border_color_top{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Top Border Color', 'et_builder' ) ),
'description' => esc_html__( 'Pick a color to be used for the border.', 'et_builder' ),
'type' => $settings['color_type'],
'hover' => 'tabs',
'default_from' => "border_all.controls.border_color_all{$suffix}",
'context' => "border_styles{$suffix}",
'mobile_options' => true,
'sticky' => true,
),
"border_style_top{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Top Border Style', 'et_builder' ) ),
'description' => esc_html__( 'Borders support various different styles, each of which will change the shape of the border element.', 'et_builder' ),
'type' => 'select',
'options' => et_builder_get_border_styles(),
'default_from' => "border_all.controls.border_style_all{$suffix}",
'context' => "border_styles{$suffix}",
'hover' => 'tabs',
'mobile_options' => true,
'sticky' => true,
),
),
),
'border_right' => array(
'icon' => 'border-right',
'controls' => array(
"border_width_right{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Right Border Width', 'et_builder' ) ),
'description' => esc_html__( 'Increasing the width of the border will increase its size/thickness.', 'et_builder' ),
'type' => 'range',
'hover' => 'tabs',
'allow_empty' => true,
'default_from' => "border_all.controls.border_width_all{$suffix}",
'default_unit' => $defaultUnit,
'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ),
'range_settings' => array(
'min' => 0,
'max' => 50,
'step' => 1,
'min_limit' => 0,
),
'context' => "border_styles{$suffix}",
'mobile_options' => true,
'sticky' => true,
),
"border_color_right{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Right Border Color', 'et_builder' ) ),
'description' => esc_html__( 'Pick a color to be used for the border.', 'et_builder' ),
'type' => $settings['color_type'],
'hover' => 'tabs',
'default_from' => "border_all.controls.border_color_all{$suffix}",
'context' => "border_styles{$suffix}",
'mobile_options' => true,
'sticky' => true,
),
"border_style_right{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Right Border Style', 'et_builder' ) ),
'description' => esc_html__( 'Borders support various different styles, each of which will change the shape of the border element.', 'et_builder' ),
'type' => 'select',
'options' => et_builder_get_border_styles(),
'default_from' => "border_all.controls.border_style_all{$suffix}",
'context' => "border_styles{$suffix}",
'hover' => 'tabs',
'mobile_options' => true,
'sticky' => true,
),
),
),
'border_bottom' => array(
'icon' => 'border-bottom',
'controls' => array(
"border_width_bottom{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Bottom Border Width', 'et_builder' ) ),
'description' => esc_html__( 'Increasing the width of the border will increase its size/thickness.', 'et_builder' ),
'type' => 'range',
'hover' => 'tabs',
'allow_empty' => true,
'default_from' => "border_all.controls.border_width_all{$suffix}",
'default_unit' => $defaultUnit,
'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ),
'range_settings' => array(
'min' => 0,
'max' => 50,
'step' => 1,
'min_limit' => 0,
),
'context' => "border_styles{$suffix}",
'mobile_options' => true,
'sticky' => true,
),
"border_color_bottom{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Bottom Border Color', 'et_builder' ) ),
'description' => esc_html__( 'Pick a color to be used for the border.', 'et_builder' ),
'type' => $settings['color_type'],
'hover' => 'tabs',
'default_from' => "border_all.controls.border_color_all{$suffix}",
'context' => "border_styles{$suffix}",
'mobile_options' => true,
'sticky' => true,
),
"border_style_bottom{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Bottom Border Style', 'et_builder' ) ),
'description' => esc_html__( 'Borders support various different styles, each of which will change the shape of the border element.', 'et_builder' ),
'type' => 'select',
'options' => et_builder_get_border_styles(),
'default_from' => "border_all.controls.border_style_all{$suffix}",
'context' => "border_styles{$suffix}",
'hover' => 'tabs',
'mobile_options' => true,
'sticky' => true,
),
),
),
'border_left' => array(
'icon' => 'border-left',
'controls' => array(
"border_width_left{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Left Border Width', 'et_builder' ) ),
'description' => esc_html__( 'Increasing the width of the border will increase its size/thickness.', 'et_builder' ),
'type' => 'range',
'hover' => 'tabs',
'allow_empty' => true,
'default_from' => "border_all.controls.border_width_all{$suffix}",
'default_unit' => $defaultUnit,
'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ),
'range_settings' => array(
'min' => 0,
'max' => 50,
'step' => 1,
'min_limit' => 0,
),
'context' => "border_styles{$suffix}",
'mobile_options' => true,
'sticky' => true,
),
"border_color_left{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Left Border Color', 'et_builder' ) ),
'description' => esc_html__( 'Pick a color to be used for the border.', 'et_builder' ),
'type' => $settings['color_type'],
'hover' => 'tabs',
'default_from' => "border_all.controls.border_color_all{$suffix}",
'context' => "border_styles{$suffix}",
'mobile_options' => true,
'sticky' => true,
),
"border_style_left{$suffix}" => array(
'label' => sprintf( '%1$s%2$s', '' !== $settings['label_prefix'] ? sprintf( '%1$s ', $settings['label_prefix'] ) : '', esc_html__( 'Left Border Style', 'et_builder' ) ),
'description' => esc_html__( 'Borders support various different styles, each of which will change the shape of the border element.', 'et_builder' ),
'type' => 'select',
'options' => et_builder_get_border_styles(),
'default_from' => "border_all.controls.border_style_all{$suffix}",
'context' => "border_styles{$suffix}",
'hover' => 'tabs',
'mobile_options' => true,
'sticky' => true,
),
),
),
),
);
// Override default_from. In some cases, we need to set different default from the parent
// modules. For example, in Pricing Tables -> Pricing Area borders, we only want to set
// default value for border bottom width, but keep the rest to inherit from border all.
if ( ! empty( $settings['defaults']['composite'] ) ) {
$composites = $additional_options[ "border_styles{$suffix}" ]['composite_structure'];
$new_composites = array();
foreach ( $settings['defaults']['composite'] as $border_position => $default_controls ) {
// Make sure the border position property exists on additional_options.
if ( ! isset( $composites[ $border_position ] ) ) {
continue;
}
// Make sure it has controls.
if ( ! isset( $composites[ $border_position ]['controls'] ) ) {
continue;
}
// Make sure target controls are not empty.
if ( ! empty( $default_controls ) ) {
foreach ( $default_controls as $control_key => $control_default ) {
// Make sure the target control exists on border_styles.
$control_suffix = $control_key . $suffix;
if ( isset( $composites[ $border_position ]['controls'][ $control_suffix ] ) ) {
// Unset default_from and set default attribute.
unset( $composites[ $border_position ]['controls'][ $control_suffix ]['default_from'] );
$composites[ $border_position ]['controls'][ $control_suffix ]['default'] = $control_default;
}
}
}
}
// Set composites structures back to additional options border styles.
$additional_options[ "border_styles{$suffix}" ]['composite_structure'] = $composites;
}
// Add options dependency
if ( ! is_null( $settings['depends_on'] ) ) {
foreach ( $additional_options as &$option ) {
$option['depends_on'] = $settings['depends_on'];
$option['depends_show_if'] = $settings['depends_show_if'];
$option['depends_show_if_not'] = $settings['depends_show_if_not'];
}
}
return $additional_options;
}
/**
* Get border radii CSS styles.
*
* @since 3.23 Add responsive setting support.
* @since 4.6.0 Add sticky style support
*
* @param array $atts All module attributes.
* @param array $advanced_fields All module advanced fields definition.
* @param string $suffix Border options group or toggle name.
* @param boolean|string $overflow Overflow status or type.
* @param boolean $is_hover Hover options status.
* @param string $device Current active device.
* @param boolean $is_sticky Sticky options status.
*
* @return string Generated border radii styles.
*/
public function get_radii_style( array $atts, array $advanced_fields, $suffix = '', $overflow = true, $is_hover = false, $device = 'desktop', $is_sticky = false ) {
$style = '';
$important = '';
// Backward compatibility. Use `border` settings as default if exists.
$legacy_border = self::$_->array_get( $advanced_fields, 'border', array() );
$borders_fields = self::$_->array_get(
$advanced_fields,
'borders',
array(
'default' => $legacy_border,
)
);
if ( isset( $borders_fields['css']['important'] ) ) {
if ( 'plugin_only' === $borders_fields['css']['important'] ) {
$important = et_builder_has_limitation( 'force_use_global_important' ) ? '!important' : '';
} else {
$important = '!important';
}
}
// Border Radius CSS
$is_desktop = 'desktop' === $device;
$value_suffix = '';
if ( $is_hover ) {
$value_suffix = et_pb_hover_options()->get_suffix();
}
if ( $is_sticky ) {
$value_suffix = et_pb_sticky_options()->get_suffix();
}
$value_suffix = ! $is_hover && ! $is_sticky && ! $is_desktop ? "_{$device}" : $value_suffix;
// Get border settings based on main border_radii field on option template.
// This used to refer to module's advanced_fields property but for performance reason
// it is now fetched from option template field's instead.
// Rebuilt field on option template is cached on property so it is safe to get it on demand.
$border_advanced_setting = self::$_->array_get( $advanced_fields, "border{$suffix}", array() );
$border_template_id = ET_Builder_Module_Fields_Factory::get( 'Border' )->get_fields( $border_advanced_setting, true );
$border_fields = $this->template->is_enabled() ? $this->template->rebuild_field_template( $border_template_id ) : array();
// Border radii settings
$settings = self::$_->array_get( $border_fields, "border_radii{$suffix}", array() );
$radii = isset( $atts[ "border_radii{$suffix}{$value_suffix}" ] ) ? $atts[ "border_radii{$suffix}{$value_suffix}" ] : false;
// Bail early if current device value doesn't exist.
if ( false === $radii || empty( $radii ) ) {
return '';
}
// Bail early if current device is tablet/phone and responsive is disabled.
if ( ! $is_desktop && ! et_pb_responsive_options()->is_responsive_enabled( $atts, "border_radii{$suffix}" ) ) {
return '';
}
// We need the default radius value.
if ( ! isset( $settings['default'] ) ) {
$settings['default'] = 'on||||';
}
// Make sure current radii value is different with default value. Default of desktop is
// default. Default of tablet is desktop and default of phone is tablet.
if ( isset( $settings['default'] ) && ! $is_desktop ) {
// Get previous device value.
$previous_suffix = 'phone' === $device ? '_tablet' : '';
$radii_previous = isset( $atts[ "border_radii{$suffix}{$previous_suffix}" ] ) ? $atts[ "border_radii{$suffix}{$previous_suffix}" ] : false;
if ( $radii_previous ) {
$settings['default'] = $radii_previous;
}
}
if ( isset( $settings['default'] ) && ( $settings['default'] !== $radii ) ) {
$radii = explode( '|', $radii );
if ( count( $radii ) === 5 ) {
$top_left_radius = empty( $radii[1] ) ? '0' : esc_html( $radii[1] );
$top_right_radius = empty( $radii[2] ) ? '0' : esc_html( $radii[2] );
$bottom_right_radius = empty( $radii[3] ) ? '0' : esc_html( $radii[3] );
$bottom_left_radius = empty( $radii[4] ) ? '0' : esc_html( $radii[4] );
$important = et_core_intentionally_unescaped( $important, 'fixed_string' );
$style = "border-radius: {$top_left_radius} {$top_right_radius} {$bottom_right_radius} {$bottom_left_radius}{$important};";
if ( true === $overflow || in_array( $overflow, array( 'overflow-x', 'overflow-y' ) ) ) {
// $overflow can be either a boolean or a string: 'overflow-x' / 'overflow-y'
// If it is a boolean the CSS property is set to 'overflow'
$overflow_property = in_array( $overflow, array( 'overflow-x', 'overflow-y' ), true ) ? $overflow : 'overflow';
$style .= "{$overflow_property}: hidden{$important};";
}
}
}
return $style;
}
/**
* Get border styles CSS styles.
*
* @since 3.23 Add responsive setting support.
* @since 4.6.0 Add sticky style support
*
* @param array $attrs All module attributes.
* @param array $advanced_fields All module advanced fields definition.
* @param string $suffix Border options group or toggle name.
* @param boolean $is_hover Hover options status.
* @param string $device Current active device.
* @param boolean $is_sticky Sticky options status.
*
* @return string Generated border styles.
*/
public function get_borders_style( array $attrs, array $advanced_fields, $suffix = '', $is_hover = false, $device = 'desktop', $is_sticky = false ) {
$style = '';
$important = '';
$hover = et_pb_hover_options();
$sticky = et_pb_sticky_options();
$is_desktop = 'desktop' === $device;
$device_suffix = ! $is_desktop ? "_{$device}" : '';
self::$_is_default = array();
if ( self::$_->array_get( $advanced_fields, "border{$suffix}.css.important", false ) ) {
if ( 'plugin_only' === self::$_->array_get( $advanced_fields, "border{$suffix}.css.important", '' ) ) {
$important = et_builder_has_limitation( 'force_use_global_important' ) ? '!important' : '';
} else {
$important = '!important';
}
}
// Get border settings based on main border_style field on option template.
// This used to refer to module's advanced_fields property but for performance reason
// it is now fetched from option template field's instead.
// Rebuilt field on option template is cached on property so it is safe to get it on demand.
$border_advanced_setting = self::$_->array_get( $advanced_fields, "border{$suffix}", array() );
$border_template_id = ET_Builder_Module_Fields_Factory::get( 'Border' )->get_fields( $border_advanced_setting, true );
$border_fields = $this->template->is_enabled() ? $this->template->rebuild_field_template( $border_template_id ) : array();
// Border Style settings
$settings = self::$_->array_get( $border_fields, "border_styles{$suffix}", array() );
if ( ! isset( $settings['composite_structure'] ) || ! is_array( $settings['composite_structure'] ) ) {
return $style;
}
$styles = array();
$properties = array( 'width', 'style', 'color' );
$border_edges = array( 'top', 'right', 'bottom', 'left' );
// Individual edge tabs get their default values from the all edges tab. If a value in
// the all edges tab has been changed from the default, that value will be used as the
// default for the individual edge tabs, otherwise the all edges default value is used.
$value_suffix = '';
if ( $is_hover ) {
$value_suffix = $hover->get_suffix();
}
if ( $is_sticky ) {
$value_suffix = $sticky->get_suffix();
}
foreach ( $border_edges as $edge ) {
$edge = "{$edge}";
foreach ( $properties as $property ) {
// Set key to get all edges and edge value based on current active device.
$all_edges_key = "border_{$property}_all{$suffix}";
$all_edges_key_device = "border_{$property}_all{$suffix}{$device_suffix}";
$edge_key = "border_{$property}_{$edge}{$suffix}";
$edge_key_device = "border_{$property}_{$edge}{$suffix}{$device_suffix}";
$is_all_edges_responsive = et_pb_responsive_options()->is_responsive_enabled( $attrs, $all_edges_key );
$is_edge_responsive = et_pb_responsive_options()->is_responsive_enabled( $attrs, $edge_key );
$all_edges_desktop_value = et_pb_responsive_options()->get_any_value( $attrs, $all_edges_key );
$edge_desktop_value = et_pb_responsive_options()->get_any_value( $attrs, $edge_key );
// Don't output styles for default values unless the default value is actually
// a custom value from the all edges tab.
$value = false;
if ( $is_hover ) {
$default_hover_value = $hover->is_enabled( $edge_key, $attrs ) ? $edge_desktop_value : false;
$value = $hover->get_value( $edge_key, $attrs, $default_hover_value );
} elseif ( $is_sticky ) {
$default_sticky_value = $sticky->is_enabled( $edge_key, $attrs ) ? $edge_desktop_value : false;
$value = $sticky->get_value( $edge_key, $attrs, $default_sticky_value );
} elseif ( ! $is_desktop ) {
$value = $is_edge_responsive ? et_pb_responsive_options()->get_any_value( $attrs, $edge_key_device, '', true ) : et_pb_responsive_options()->get_any_value( $attrs, $edge_key );
} elseif ( $is_desktop ) {
$value = $edge_desktop_value;
}
if ( ! $value ) {
// If specific edge value doesn't exist, get from all active device.
$value = false;
if ( $is_hover ) {
$default_hover_value = $hover->is_enabled( $all_edges_key, $attrs ) ? $all_edges_desktop_value : false;
$value = $hover->get_value( $all_edges_key, $attrs, $default_hover_value );
} elseif ( $is_sticky ) {
$default_sticky_value = $sticky->is_enabled( $all_edges_key, $attrs ) ? $all_edges_desktop_value : false;
$value = $sticky->get_value( $all_edges_key, $attrs, $default_sticky_value );
} elseif ( $is_desktop || ( ! $is_desktop && $is_all_edges_responsive ) ) {
$value = et_pb_responsive_options()->get_any_value( $attrs, $all_edges_key_device );
}
if ( ! $value ) {
self::$_is_default[] = "{$edge_key}{$value_suffix}";
self::$_is_default[] = "{$all_edges_key}{$value_suffix}";
continue;
}
}
// Don't output wrongly migrated border-color value
if ( 'color' === $property && 'off' === $value ) {
continue;
}
if ( ! isset( $styles[ $property ] ) ) {
$styles[ $property ] = array();
}
// Sanitize value
if ( 'width' === $property ) {
$value = et_builder_process_range_value( $value );
}
$styles[ $property ][ $edge ] = esc_html( $value );
}
}
foreach ( $styles as $prop => $edges ) {
$all_values = array_values( $edges );
$all_edges = 4 === count( $all_values );
if ( $all_edges && 1 === count( array_unique( $all_values ) ) ) {
// All edges have the same value, so let's combine them into a single prop.
$style .= "border-{$prop}:{$all_values[0]}{$important};";
} elseif ( $all_edges && $edges['top'] === $edges['bottom'] && $edges['left'] === $edges['right'] ) {
// Let's combine them into a single prop.
$style .= "border-{$prop}:{$edges['top']} {$edges['left']}{$important};";
} elseif ( $all_edges ) {
// You know the drill.
$style .= "border-{$prop}:{$edges['top']} {$edges['right']} {$edges['bottom']} {$edges['left']}{$important};";
} else {
// We're not going to mess with the other shorthand variants, so separate styles it is!
foreach ( $edges as $edge => $value ) {
$style .= "border-{$edge}-{$prop}:{$value}{$important};";
}
}
}
return $style;
}
/**
* Whether or not the provided module needs the border reset CSS class.
*
* @param string $module_slug
* @param array $attrs
*
* @return bool
*/
public function needs_border_reset_class( $module_slug, $attrs ) {
if ( in_array( $module_slug, self::$_no_border_reset ) ) {
return false;
}
foreach ( $attrs as $attr => $value ) {
if ( ! $value || 0 === strpos( $attr, 'border_radii' ) ) {
continue;
}
// don't use 2 === substr_count( $attr, '_' ) because in some cases border option may have 3 underscores ( in case we have several border options in module ).
// It's enough to make sure we have more than 1 underscores.
$is_new_border_attr = 0 === strpos( $attr, 'border_' ) && substr_count( $attr, '_' ) > 1;
if ( $is_new_border_attr && ! in_array( $attr, self::$_is_default ) ) {
return true;
}
}
return false;
}
/**
* Check if attribute has border radius values.
*
* @param array $attrs border attrs.
*
* @return bool
*/
public function has_any_border_attrs( $attrs ) {
foreach ( $attrs as $attr => $value ) {
// Dont neglet border radius here.
// Since border and border radius are handled by same function.
// We should also check for border radius here.
if ( ! $value ) {
continue;
}
// don't use 2 === substr_count( $attr, '_' ) because in some cases border option may have 3 underscores ( in case we have several border options in module ).
// It's enough to make sure we have more than 1 underscores.
$is_new_border_attr = 0 === strpos( $attr, 'border_' ) && ( 'border_radii' === $attr || substr_count( $attr, '_' ) > 1 );
if ( $is_new_border_attr && ! in_array( $attr, self::$_is_default, true ) ) {
return true;
}
}
return false;
}
/**
* Add border reset class using filter. Obsolete method and only applied to old 3rd party modules without `modules_classname()` method
*
* @param string $output
* @param string $module_slug
*
* @return string
*/
public function add_border_reset_class( $output, $module_slug ) {
if ( in_array( $module_slug, ET_Builder_Element::$uses_module_classname ) ) {
return $output;
}
remove_filter( "{$module_slug}_shortcode_output", array( $this, 'add_border_reset_class' ), 10 );
return preg_replace( "/class=\"(.*?{$module_slug}_\d+.*?)\"/", 'class="$1 et_pb_with_border"', $output, 1 );
}
}
return new ET_Builder_Module_Field_Border();

View File

@@ -0,0 +1,674 @@
<?php
class ET_Builder_Module_Field_BoxShadow extends ET_Builder_Module_Field_Base {
private static $classes = array();
/**
* ET_Builder_Module_Helper_OptionTemplate class
*
* @var ET_Builder_Module_Helper_OptionTemplate
*/
public $template;
/**
* @var ET_Builder_Module_Helper_ResponsiveOptions
*
* @since 3.23
*/
public static $responsive = null;
/**
* Constructor.
*/
public function __construct() {
$this->template = et_pb_option_template();
$this->set_template();
}
/**
* Set option template for Box Shadow
*
* @since 3.28
*
* @return void
*/
public function set_template() {
$template = $this->template;
if ( $template->is_enabled() && ! $template->has( 'box_shadow' ) ) {
$template->add(
'box_shadow',
$this->get_fields(
$template->placeholders(
array(
'suffix' => null,
'label' => null,
'option_category' => null,
'tab_slug' => null,
'toggle_slug' => null,
'sub_toggle' => null,
'depends_show_if_not' => null,
'depends_show_if' => null,
'depends_on' => null,
'default_on_fronts' => null,
'show_if' => null,
'show_if_not' => null,
)
)
)
);
}
}
/**
* Get box shadow fields.
*
* @since 3.23 Add support for responsive settings. Add allowed units for range fields.
*
* @param array $args Box shadow settings args.
* @return array Box shadow fields.
*/
public function get_fields( array $args = array() ) {
static $i18n;
// Cache translations.
if ( ! isset( $i18n ) ) {
$i18n['label'] = esc_html__( 'Box Shadow', 'et_builder' );
}
$arguments = shortcode_atts(
array(
'suffix' => '',
'label' => $i18n['label'],
'option_category' => '',
'tab_slug' => '',
'toggle_slug' => '',
'sub_toggle_slug' => null, // @deprecated Use {@see `sub_toggle`} instead. Keep it here as backward compatibility.
'sub_toggle' => null,
'depends_show_if_not' => null,
'depends_show_if' => null,
'depends_on' => null,
'default_on_fronts' => array(),
'show_if' => null,
'show_if_not' => null,
),
$args
);
// The `sub_toggle_slug` is deprecated in favor of `sub_toggle` which are used by
// other option groups. Keep it here for backward compatibiluty.
if ( ! empty( $arguments['sub_toggle_slug'] ) && empty( $arguments['sub_toggle'] ) ) {
$arguments['sub_toggle'] = $arguments['sub_toggle_slug'];
}
if ( $this->template->is_enabled() && $this->template->has( 'box_shadow' ) ) {
return $this->template->create( 'box_shadow', $arguments );
}
$prefix = 'box_shadow_';
$style = $prefix . 'style' . $arguments['suffix'];
$horizontal = $prefix . 'horizontal' . $arguments['suffix'];
$vertical = $prefix . 'vertical' . $arguments['suffix'];
$blur = $prefix . 'blur' . $arguments['suffix'];
$spread = $prefix . 'spread' . $arguments['suffix'];
$position = $prefix . 'position' . $arguments['suffix'];
$color = $prefix . 'color' . $arguments['suffix'];
$options = array();
$option = array(
'option_category' => $arguments['option_category'],
'tab_slug' => $arguments['tab_slug'],
'toggle_slug' => $arguments['toggle_slug'],
'sub_toggle' => $arguments['sub_toggle'],
'show_if_not' => array(
"{$style}" => 'none',
),
'default_on_child' => true,
);
$range = array_merge(
$option,
array(
'type' => 'range',
'range_settings' => array(
'min' => - 80,
'max' => 80,
'step' => 1,
),
'default' => 0,
'validate_unit' => true,
'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ),
'default_unit' => 'px',
'fixed_range' => true,
'hover' => 'tabs',
'mobile_options' => true,
'sticky' => true,
)
);
$presets = array();
foreach ( $this->get_presets() as $id => $preset ) {
if ( 'none' === $id ) {
$presets[] = array(
'value' => $id,
'icon' => $id,
'fields' => $this->fetch_preset( $preset, $arguments['suffix'] ),
);
} else {
$presets[] = array(
'value' => $id,
'content' => sprintf( '<span class="preset %1$s"></span>', esc_attr( $id ) ),
'fields' => $this->fetch_preset( $preset, $arguments['suffix'] ),
);
}
}
$options[ $style ] = array_merge(
$option,
array(
'label' => $arguments['label'],
'type' => 'select_box_shadow',
'default' => 'none',
'className' => 'box_shadow',
'presets' => $presets,
'affects' => array( $horizontal, $vertical, $blur, $spread, $color, $position ),
'copy_with' => array( $horizontal, $vertical, $blur, $spread, $color, $position ),
'depends_show_if' => $arguments['depends_show_if'],
'depends_show_if_not' => $arguments['depends_show_if_not'],
'depends_on' => $arguments['depends_on'],
'show_if' => $arguments['show_if'],
'show_if_not' => $arguments['show_if_not'],
'description' => esc_html__( 'Pick a box shadow style to enable box shadow for this element. Once enabled, you will be able to customize your box shadow style further. To disable custom box shadow style, choose the None option.', 'et_builder' ),
)
);
// Configure dependency for fields via show_if/show_if_not attribute
if ( null === $options[ $style ]['show_if'] ) {
unset( $options[ $style ]['show_if'] );
}
if ( null === $options[ $style ]['show_if_not'] ) {
unset( $options[ $style ]['show_if_not'] );
}
// Field dependency via depends_on, depends_show_if, and depends_show_if_not have been deprecated
// These remain here as backward compatibility for third party modules
if ( null === $options[ $style ]['depends_on'] ) {
unset( $options[ $style ]['depends_on'] );
}
if ( null === $options[ $style ]['depends_show_if'] ) {
unset( $options[ $style ]['depends_show_if'] );
}
if ( null === $options[ $style ]['depends_show_if_not'] ) {
unset( $options[ $style ]['depends_show_if_not'] );
}
if ( isset( $arguments['default_on_fronts']['style'] ) && false !== $arguments['default_on_fronts']['style'] ) {
$options[ $style ]['default_on_front'] = $arguments['default_on_fronts']['style'];
}
$options[ $horizontal ] = array_merge(
$range,
array(
'label' => esc_html__( 'Box Shadow Horizontal Position', 'et_builder' ),
'description' => esc_html__( 'Shadow\'s horizontal distance from the element. A negative value places the shadow to the left of the element.', 'et_builder' ),
)
);
$options[ $vertical ] = array_merge(
$range,
array(
'label' => esc_html__( 'Box Shadow Vertical Position', 'et_builder' ),
'description' => esc_html__( 'Shadow\'s vertical distance from the element. A negative value places the shadow above the element.', 'et_builder' ),
)
);
$options[ $blur ] = array_merge(
$range,
array(
'label' => esc_html__( 'Box Shadow Blur Strength', 'et_builder' ),
'range_settings' => array(
'min' => 0,
'max' => 80,
'step' => 1,
),
'description' => esc_html__( 'The higher the value, the bigger the blur, the shadow becomes wider and lighter.', 'et_builder' ),
)
);
$options[ $spread ] = array_merge(
$range,
array(
'label' => esc_html__( 'Box Shadow Spread Strength', 'et_builder' ),
'description' => esc_html__( 'Increasing the spread strength will increase the density of the box shadow. Higher density results in a more intense shadow.', 'et_builder' ),
)
);
$options[ $color ] = array_merge(
$option,
array(
'label' => esc_html__( 'Shadow Color', 'et_builder' ),
'type' => 'color-alpha',
'hover' => 'tabs',
'default' => 'rgba(0,0,0,0.3)',
'field_template' => 'color',
'mobile_options' => true,
'sticky' => true,
'description' => esc_html__( 'The color of the shadow.', 'et_builder' ),
)
);
if ( isset( $arguments['default_on_fronts']['color'] ) && false !== $arguments['default_on_fronts']['color'] ) {
$options[ $color ]['default_on_front'] = $arguments['default_on_fronts']['color'];
}
$options[ $position ] = array_merge(
$option,
array(
'label' => esc_html__( 'Box Shadow Position', 'et_builder' ),
'description' => esc_html__( 'Choose whether you would like the shadow to appear outside your module, lifting the module up from the page, or inside the module, setting the module downwards within the page.', 'et_builder' ),
'type' => 'select',
'default' => 'outer',
'options' => array(
'outer' => esc_html__( 'Outer Shadow', 'et_builder' ),
'inner' => esc_html__( 'Inner Shadow', 'et_builder' ),
),
'mobile_options' => true,
)
);
if ( isset( $arguments['default_on_fronts']['position'] ) && false !== $arguments['default_on_fronts']['position'] ) {
$options[ $position ]['default_on_front'] = $arguments['default_on_fronts']['position'];
}
$list = array(
'vertical' => $vertical,
'horizontal' => $horizontal,
'blur' => $blur,
'spread' => $spread,
'position' => $position,
);
foreach ( $list as $id => $field ) {
$values = array();
foreach ( array_keys( $this->get_presets() ) as $preset ) {
$values[ $preset ] = $this->get_preset_field( $preset, $id );
}
$options[ $field ]['default'] = array( $style, $values );
}
return $options;
}
/**
* Get box-shadow declaration style.
*
* @since 3.23 Add support for responsive settings.
*
* @param array $atts Module attributes.
* @param array $args Box-shadow arguments.
* @return string Box shadow CSS declaration.
*/
public function get_value( $atts, array $args = array() ) {
$args = shortcode_atts(
array(
'suffix' => '',
'important' => false,
'hover' => false,
'sticky' => false,
'device' => 'desktop',
),
$args
);
$suffix = $args['suffix'];
$important = $args['important'] ? '!important' : '';
$hover = $args['hover'];
$sticky = $args['sticky'];
$device = $args['device'];
$style = $this->get_key_value( "style$suffix", $atts );
if ( empty( $style ) || 'none' === $style ) {
return '';
}
// 1. Get preset styles as default.
$preset = $this->get_preset( $style );
// 2. Get current device properties value.
$atts = array_merge(
array(
"box_shadow_position{$suffix}" => $preset['position'],
"box_shadow_horizontal{$suffix}" => $preset['horizontal'],
"box_shadow_vertical{$suffix}" => $preset['vertical'],
"box_shadow_blur{$suffix}" => $preset['blur'],
"box_shadow_spread{$suffix}" => $preset['spread'],
"box_shadow_color{$suffix}" => 'rgba(0,0,0,0.3)',
),
array_filter( $atts, 'strlen' )
);
// All the values below sometime return null.
$position = $this->get_key_value( "position{$suffix}", $atts, false, $device, $sticky ) === 'inner' ? 'inset' : '';
$horizontal = $this->get_key_value( "horizontal{$suffix}", $atts, $hover, $device, $sticky );
$vertical = $this->get_key_value( "vertical{$suffix}", $atts, $hover, $device, $sticky );
$blur = $this->get_key_value( "blur{$suffix}", $atts, $hover, $device, $sticky );
$strength = $this->get_key_value( "spread{$suffix}", $atts, $hover, $device, $sticky );
$color = $this->get_key_value( "color{$suffix}", $atts, $hover, $device, $sticky );
// CSS declaration.
$value = sprintf(
'box-shadow: %1$s %2$s %3$s %4$s %5$s %6$s %7$s;',
$position,
$horizontal,
$vertical,
$blur,
$strength,
$color,
$important
);
// Do not provider hover style if it is the same as normal style
if ( $hover ) {
$new_args = $args;
$new_args['hover'] = false;
$normal = $this->get_value( $atts, $new_args );
if ( $normal === $value ) {
return '';
}
}
return $value;
}
public function get_presets() {
return array(
'none' => array(
'horizontal' => '',
'vertical' => '',
'blur' => '',
'spread' => '',
'position' => 'outer',
),
'preset1' => array(
'horizontal' => '0px',
'vertical' => '2px',
'blur' => '18px',
'spread' => '0px',
'position' => 'outer',
),
'preset2' => array(
'horizontal' => '6px',
'vertical' => '6px',
'blur' => '18px',
'spread' => '0px',
'position' => 'outer',
),
'preset3' => array(
'horizontal' => '0px',
'vertical' => '12px',
'blur' => '18px',
'spread' => '-6px',
'position' => 'outer',
),
'preset4' => array(
'horizontal' => '10px',
'vertical' => '10px',
'blur' => '0px',
'spread' => '0px',
'position' => 'outer',
),
'preset5' => array(
'horizontal' => '0px',
'vertical' => '6px',
'blur' => '0px',
'spread' => '10px',
'position' => 'outer',
),
'preset6' => array(
'horizontal' => '0px',
'vertical' => '0px',
'blur' => '18px',
'spread' => '0px',
'position' => 'inner',
),
'preset7' => array(
'horizontal' => '10px',
'vertical' => '10px',
'blur' => '0px',
'spread' => '0px',
'position' => 'inner',
),
);
}
public function get_preset( $name ) {
$presets = $this->get_presets();
return isset( $presets[ $name ] )
? $presets[ $name ]
: array(
'horizontal' => 0,
'vertical' => 0,
'blur' => 0,
'spread' => 0,
'position' => 'outer',
);
}
public function get_style( $selector, array $atts = array(), array $args = array() ) {
$value = $this->get_value( $atts, $args );
return array(
'selector' => $selector,
'declaration' => empty( $value ) ? null : $value,
);
}
public function has_overlay( $atts, $args ) {
$overlay = ET_Core_Data_Utils::instance()->array_get( $args, 'overlay', false );
$inset = $this->is_inset( $this->get_value( $atts, $args ) );
return ( $inset && 'inset' === $overlay ) || 'always' === 'overlay';
}
public function get_overlay_selector( $selector ) {
$selectors = array_map( 'trim', explode( ',', $selector ) );
$new_selector = array();
foreach ( $selectors as $selector ) {
$new_selector[] = $selector . '>.box-shadow-overlay, ' . $selector . '.et-box-shadow-no-overlay';
}
return implode( ',', $new_selector );
}
public function get_overlay_style( $function_name, $selector, $atts, array $args = array() ) {
$order_class_name = ET_Builder_Element::get_module_order_class( $function_name );
$reg_selector = str_replace( '%%order_class%%', ".{$order_class_name}", $selector );
$reg_selector = str_replace( '%order_class%', ".{$order_class_name}", $reg_selector );
// %%parent_class%% only works if child module's slug is `parent_slug` + _item suffix. If child module slug
// use different slug structure, %%parent_class%% should not be used
if ( false !== strpos( $reg_selector, '%%parent_class%%' ) ) {
$parent_class = str_replace( '_item', '', $function_name );
$reg_selector = str_replace( '%%parent_class%%', ".{$parent_class}", $reg_selector );
}
$selector = $this->get_overlay_selector( $selector );
$value = $this->get_value( $atts, $args );
if ( empty( $value ) ) {
return array(
'selector' => $selector,
'declaration' => null,
);
}
array_map(
array( get_class( $this ), 'register_element' ),
array_map( 'trim', explode( ',', $reg_selector ) )
);
return array(
'selector' => $selector,
'declaration' => $value,
);
}
public function is_inset( $style ) {
return strpos( $style, 'inset' ) !== false;
}
public static function register_element( $class ) {
self::$classes[] = $class;
}
public static function get_elements() {
return self::$classes;
}
protected function fetch_preset( array $preset, $suffix ) {
return array(
"box_shadow_horizontal{$suffix}" => $preset['horizontal'],
"box_shadow_vertical{$suffix}" => $preset['vertical'],
"box_shadow_blur{$suffix}" => $preset['blur'],
"box_shadow_spread{$suffix}" => $preset['spread'],
"box_shadow_position{$suffix}" => $preset['position'],
);
}
protected function get_preset_field( $name, $field ) {
$preset = $this->get_preset( $name );
return $preset[ $field ];
}
/**
* Get box shadow property value based on current active device.
*
* @since 3.23 Add responsive support. Check last edited value first for tablet/phone.
*
* @param string $key Box shadow property.
* @param array $atts All module attributes.
* @param boolean $hover Hover mode status.
* @param string $device Current device.
* @param boolean $sticky Sticky mode status.
*
* @return string Box shadow property value.
*/
protected function get_key_value( $key, $atts = array(), $hover = false, $device = 'desktop', $sticky = false ) {
$hover_options = et_pb_hover_options();
// Add device name as suffix.
$is_desktop = 'desktop' === $device;
$device_suffix = '';
if ( ! $hover && ! $is_desktop && ! $sticky ) {
$device_suffix = "_{$device}";
}
// Get current active device value.
$attr_value = et_pb_responsive_options()->get_any_value( $atts, "box_shadow_{$key}{$device_suffix}", '', true );
// Bail early if current mode is sticky, hover or desktop mode.
if ( $sticky ) {
return et_pb_sticky_options()->get_value( "box_shadow_{$key}", $atts, $attr_value );
} elseif ( $hover ) {
return $hover_options->get_value( "box_shadow_{$key}", $atts, $attr_value );
} elseif ( $is_desktop ) {
return $attr_value;
}
// Ensure responsive settings is enabled before return tablet/phone value.
$is_responsive = et_pb_responsive_options()->is_responsive_enabled( $atts, "box_shadow_{$key}" );
if ( ! $is_responsive ) {
// To avoid any issue when no box shadow defined on tablet and phone, we should return
// desktop value instead. By doing this, tablet and phone box shadow will be identical
// with desktop box shadow value.
return et_pb_responsive_options()->get_any_value( $atts, "box_shadow_{$key}" );
}
return $attr_value;
}
/**
* Check if box shadow is used.
*
* @since 4.10.0
* @param array $attrs All module attributes.
* @param string $key Box shadow property.
*/
public function is_used( $attrs, $key = '' ) {
foreach ( $attrs as $attr => $value ) {
if ( ! $value ) {
continue;
}
$has_attr = false !== strpos( $attr, 'box_shadow_style' );
if ( ! $has_attr ) {
continue;
}
return ! empty( $attr );
}
}
/**
* Check if module has inset.
*
* @since 4.10.0
* @param array $attrs All module attributes.
* @param array $advanced_options Advanced module options.
*/
public function has_inset( $attrs, $advanced_options, $_ ) {
$has_box_inset = false;
foreach ( $advanced_options as $option_name => $option_settings ) {
if ( true === $has_box_inset ) {
break;
}
// Enable module to explicitly disable box shadow fields (box shadow is automatically)
// added to all module by default.
if ( false === $option_settings ) {
continue;
}
// Prepare attribute for getting box shadow's css declaration.
$declaration_args = array(
'suffix' => 'default' === $option_name ? '' : "_{$option_name}",
'important' => $_->array_get( $option_settings, 'css.important', false ),
);
$overlay = $_->array_get( $option_settings, 'css.overlay', false );
$inset = $this->is_inset( $this->get_value( $attrs, $declaration_args ) );
$inset_hover = $this->is_inset(
$this->get_value(
$attrs,
array_merge( $declaration_args, array( 'hover' => true ) )
)
);
$has_video_bg = ! empty( $atts['background_video_mp4'] ) || ! empty( $atts['background_video_webm'] );
foreach ( et_pb_responsive_options()->get_modes() as $device ) {
// Add device argument.
$device_declaration_args = array_merge( $declaration_args, array( 'device' => $device ) );
if ( ( $inset && 'inset' === $overlay ) || 'always' === $overlay || $has_video_bg ) {
$has_box_inset = true;
break;
}
}
// Get box-shadow styles.
}
return $has_box_inset;
}
}
function _action_et_pb_box_shadow_overlay() {
wp_localize_script(
et_get_combined_script_handle(),
'et_pb_box_shadow_elements',
ET_Builder_Module_Field_BoxShadow::get_elements()
);
}
add_action( 'wp_footer', '_action_et_pb_box_shadow_overlay' );
return new ET_Builder_Module_Field_BoxShadow();

View File

@@ -0,0 +1,398 @@
<?php
/**
* Display Conditions logics lies below, Buckle up!
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
/**
* Load traits, No autoloader :sad_pepe:
*/
require_once __DIR__ . '/display-conditions/LoggedInStatus.php';
require_once __DIR__ . '/display-conditions/UserRole.php';
require_once __DIR__ . '/display-conditions/DateTime.php';
require_once __DIR__ . '/display-conditions/PostType.php';
require_once __DIR__ . '/display-conditions/Author.php';
require_once __DIR__ . '/display-conditions/Categories.php';
require_once __DIR__ . '/display-conditions/Tags.php';
require_once __DIR__ . '/display-conditions/DateArchive.php';
require_once __DIR__ . '/display-conditions/ProductPurchase.php';
require_once __DIR__ . '/display-conditions/CartContents.php';
require_once __DIR__ . '/display-conditions/SearchResults.php';
require_once __DIR__ . '/display-conditions/OperatingSystem.php';
require_once __DIR__ . '/display-conditions/Browser.php';
require_once __DIR__ . '/display-conditions/PageVisit.php';
require_once __DIR__ . '/display-conditions/DynamicPosts.php';
require_once __DIR__ . '/display-conditions/Cookie.php';
require_once __DIR__ . '/display-conditions/CategoryPage.php';
require_once __DIR__ . '/display-conditions/TagPage.php';
require_once __DIR__ . '/display-conditions/NumberOfViews.php';
require_once __DIR__ . '/display-conditions/CustomField.php';
require_once __DIR__ . '/display-conditions/UrlParameter.php';
require_once __DIR__ . '/display-conditions/ProductStock.php';
/**
* Import class dependencies
*/
use ET_Builder_Module_Field_Base;
/**
* Display Conditions class.
*
* @since 4.11.0
*/
class ET_Builder_Module_Field_DisplayConditions extends ET_Builder_Module_Field_Base {
/**
* Import traits dependencies.
* Keep the code clean and the logic separated, don't be ET_Builder_Element.
*/
use LoggedInStatusCondition;
use UserRoleCondition;
use DateTimeCondition;
use PostTypeCondition;
use AuthorCondition;
use CategoriesCondition;
use TagsCondition;
use DateArchiveCondition;
use ProductPurchaseCondition;
use CartContentsCondition;
use SearchResultsCondition;
use OperatingSystemCondition;
use BrowserCondition;
use PageVisitCondition;
use DynamicPostsCondition;
use CookieCondition;
use CategoryPageCondition;
use TagPageCondition;
use NumberOfViewsCondition;
use CustomFieldCondition;
use UrlParameterCondition;
use ProductStockCondition;
/**
* Custom current date.
* Useful for testing purposes where we don't want to depend on server's timestamp.
*
* @since 4.11.0
*
* @var string
*/
protected $_custom_current_date = '';
/**
* Retrieves fields for Display Conditions.
* Used in `ET_Builder_ELement` to set Display Conditions fields in Divi Builder.
*
* @since 4.11.0
*
* @param array $args Associative array for settings.
*
* @return array $fields Option settings.
*/
public function get_fields( array $args = array() ) {
$defaults = [
'prefix' => '',
'tab_slug' => 'custom_css',
'toggle_slug' => 'conditions',
'mobile_options' => false,
'default' => '',
];
$settings = array_merge( $defaults, $args );
return array_merge(
$this->get_field( $settings )
);
}
/**
* Retrieves field for Display Conditions.
*
* @since 4.11.0
*
* @param array $args Associative array for settings.
*
* @return array $options Option settings.
*/
public function get_field( $args ) {
static $i18n;
// Cache translations.
if ( ! $i18n ) {
$i18n = [
'Display Conditions' => esc_html__( 'Display Conditions', 'et_builder' ),
'description' => et_get_safe_localization( sprintf( __( 'Choose when to display this element based on a set of conditions. Multiple conditions can be added. Date & Time condition is based on your timezone settings in your <a href="%1$s" target="_blank" title="WordPress General Settings">WordPress General Settings</a>', 'et_builder' ), esc_url( admin_url( 'options-general.php' ) ) ) ),
];
}
$settings = array(
'label' => $i18n['Display Conditions'],
'type' => 'display_conditions',
'mobile_options' => $args['mobile_options'],
'default' => $args['default'],
'tab_slug' => $args['tab_slug'],
'toggle_slug' => $args['toggle_slug'],
'description' => $i18n['description'],
);
$options = array( 'display_conditions' => $settings );
return $options;
}
/**
* Checks all $display_conditions and returns a final boolean output.
*
* @since 4.11.0
*
* @param array $display_conditions Associative array containing conditions.
* @param boolean $only_return_status Whether to return all conditions full status (useful in VB tooltips).
*
* @return boolean Conditions final result.
*/
public function is_displayable( $display_conditions, $only_return_status = false ) {
// Bail out and just display the module if $display_conditions is not array.
if ( ! is_array( $display_conditions ) ) {
return true;
}
// Holds current condition evaluation.
$should_display = true;
$status = [];
// Reverses condition list, We start from the bottom of the list.
$display_conditions = array_reverse( $display_conditions );
// Holds all the conditions that have been processed, except the ones detected as conflicted.
$processed_conditions = array();
foreach ( $display_conditions as $arr_key => $condition ) {
$condition_id = isset( $condition['id'] ) ? $condition['id'] : '';
$condition_name = isset( $condition['condition'] ) ? $condition['condition'] : '';
$condition_settings = isset( $condition['conditionSettings'] ) ? $condition['conditionSettings'] : [];
$operator = isset( $condition['operator'] ) ? $condition['operator'] : 'OR';
$is_enable_condition_set = isset( $condition_settings['enableCondition'] ) ? true : false;
$is_disabled = $is_enable_condition_set && 'off' === $condition_settings['enableCondition'] ? true : false;
// Skip if condition is disabled.
if ( $is_disabled ) {
$status[] = [
'id' => $condition_id,
'is_conflicted' => false,
];
continue;
}
$is_conflict_detected = $this->_is_condition_conflicted( $condition, $processed_conditions, $operator );
$status[] = [
'id' => $condition_id,
'is_conflicted' => $is_conflict_detected,
];
if ( $is_conflict_detected ) {
continue;
} else {
$should_display = $this->is_condition_true( $condition_id, $condition_name, $condition_settings );
$processed_conditions[] = $condition;
}
// If operator is set to "OR/ANY" break as soon as one condition is true - returning a final true.
// If operator is set to "AND/ALL" break as soon as one condition is false - returning a final false.
if ( 'OR' === $operator && $should_display && ! $only_return_status ) {
break;
} elseif ( 'AND' === $operator && ! $should_display && ! $only_return_status ) {
break;
}
}
return ( $only_return_status ) ? $status : $should_display;
}
/**
* Checks a single condition and returns a boolean result.
*
* @since 4.11.0
*
* @param string $condition_id Condition ID.
* @param string $condition_name Condition name.
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
public function is_condition_true( $condition_id, $condition_name, $condition_settings ) {
switch ( $condition_name ) {
case 'loggedInStatus':
return $this->_process_logged_in_status_condition( $condition_settings );
case 'userRole':
return $this->_process_user_role_condition( $condition_settings );
case 'dateTime':
return $this->_process_date_time_condition( $condition_settings );
case 'postType':
return $this->_process_post_type_condition( $condition_settings );
case 'author':
return $this->_process_author_condition( $condition_settings );
case 'categories':
return $this->_process_categories_condition( $condition_settings );
case 'categoryPage':
return $this->_process_category_page_condition( $condition_settings );
case 'tags':
return $this->_process_tags_condition( $condition_settings );
case 'tagPage':
return $this->_process_tag_page_condition( $condition_settings );
case 'dateArchive':
return $this->_process_date_archive_condition( $condition_settings );
case 'productPurchase':
return $this->_process_product_purchase_condition( $condition_settings );
case 'cartContents':
return $this->_process_cart_contents_condition( $condition_settings );
case 'searchResults':
return $this->_process_search_results_condition( $condition_settings );
case 'operatingSystem':
return $this->_process_operating_system_condition( $condition_settings );
case 'browser':
return $this->_process_browser_condition( $condition_settings );
case 'pageVisit':
return $this->_process_page_visit_condition( $condition_settings );
case 'postVisit':
return $this->_process_page_visit_condition( $condition_settings );
case 'cookie':
return $this->_process_cookie_condition( $condition_settings );
case 'numberOfViews':
return $this->_process_number_of_views_condition( $condition_id, $condition_settings );
case 'customField':
return $this->_process_custom_field_condition( $condition_settings );
case 'urlParameter':
return $this->_process_url_parameter_condition( $condition_settings );
case 'productStock':
return $this->_process_product_stock_condition( $condition_settings );
default:
if ( isset( $condition_settings['dynamicPosts'] ) ) {
return $this->_process_dynamic_posts_condition( $condition_settings );
}
return true;
}
}
/**
* Checks the $condition against $processed_conditions to determine if the $condition is considered a conflict or not.
*
* When operator 'OR/Any' is selected and we have more than one condition of the same type the priority
* is with the latest condition (located lower in the list).
*
* When operator 'AND/All' is selected no condition is considered a conflict.
*
* @since 4.11.0
*
* @param array $condition Containing all settings of the condition.
* @param array $processed_conditions Containing all settings of previously processed conditions.
* @param array $operator Selected operator for the Display Conditions, Options: 'OR' or 'AND'.
*
* @return boolean Condition output.
*/
protected function _is_condition_conflicted( $condition, $processed_conditions, $operator ) {
if ( 'AND' === $operator ) {
return false;
}
$is_conflicted = false;
// Check condition against all previously processed conditions.
foreach ( $processed_conditions as $processed_condition ) {
// Only check same condition types against each other, Ex. UserRole against UserRole.
if ( $condition['condition'] !== $processed_condition['condition'] ) {
continue;
}
// Exception! "Date Time" Condition can have multiple positive conditions.
$is_datetime = 'dateTime' === $condition['condition'];
$is_prev_cond_datetime_and_negative = $is_datetime && 'isNotOnSpecificDate' === $processed_condition['conditionSettings']['dateTimeDisplay'];
$is_current_cond_datetime_and_negative = $is_datetime && 'isNotOnSpecificDate' === $condition['conditionSettings']['dateTimeDisplay'];
if ( $is_prev_cond_datetime_and_negative || $is_current_cond_datetime_and_negative ) {
$is_conflicted = true;
break;
} elseif ( $is_datetime ) {
$is_conflicted = false;
break;
}
// Exception! "Custom Field" Condition can have multiple conditions.
$is_custom_field = 'customField' === $condition['condition'];
if ( $is_custom_field ) {
$is_conflicted = false;
break;
}
// Exception! "URL Parameter" Condition can have multiple conditions.
$is_url_parameter = 'urlParameter' === $condition['condition'];
if ( $is_url_parameter ) {
$is_conflicted = false;
break;
}
/**
* When operator is set to "OR/ANY" and we have more than one condition, all other conditions
* will be set as conflicted, giving the priority to the latest condition in the list.
*/
if ( count( $processed_conditions ) > 0 ) {
$is_conflicted = true;
break;
}
}
return $is_conflicted;
}
/**
* Overrides current date with specified date.
* Useful for testing purposes where we don't want to depend on server's timestamp.
*
* @since 4.11.0
*
* @param DateTimeImmutable $date The datetime which will overrides current datetime.
*
* @return void
*/
public function override_current_date( $date ) {
$this->_custom_current_date = $date;
}
}
return new ET_Builder_Module_Field_DisplayConditions();

View File

@@ -0,0 +1,574 @@
<?php
/**
* Module Divider class.
*/
class ET_Builder_Module_Field_Divider extends ET_Builder_Module_Field_Base {
/**
* List of available dividers for the sections.
*
* @var array
*/
public $dividers = array();
/**
* Markup for the SVG
*
* @var string
*/
public $svg;
/**
* List of classes for using in styling.
*
* @var array
*/
public $classes = array( 'section_has_divider' );
/**
* @var ET_Core_Data_Utils
*/
public static $data_utils = null;
/**
* @var ET_Builder_Module_Helper_ResponsiveOptions
*
* @since 3.23
*/
public static $responsive = null;
/**
* Number of times the module has been rendered.
*
* @var int
*/
public $count;
/**
* Constructor for the class. This is done so that the divider options could be filtered
* by a child theme or plugin.
*/
public function __construct() {
$section_dividers = array(
'arrow-bottom' => '<path d="M640 139L0 0v140h1280V0L640 139z"/>',
'arrow-top' => '<path d="M640 140L1280 0H0z"/>',
'arrow2-bottom' => '<path d="M640 139L0 0v140h1280V0L640 139z" fill-opacity=".5"/><path d="M640 139L0 42v98h1280V42l-640 97z"/>',
'arrow2-top' => '<path d="M640 140L1280 0H0z" fill-opacity=".5"/><path d="M640 98l640-98H0z"/>',
'arrow3-bottom' => '<path d="M0 140l640-70 640 70V0L640 70 0 0v140z" fill-opacity=".5"/><path d="M0 140h1280L640 70 0 140z"/>',
'arrow3-top' => '<path d="M1280 0L640 70 0 0v140l640-70 640 70V0z" fill-opacity=".5"/><path d="M1280 0H0l640 70 640-70z"/>',
'asymmetric-bottom' => '<path d="M1280 0l-262.1 116.26a73.29 73.29 0 0 1-39.09 6L0 0v140h1280z"/>',
'asymmetric-top' => '<path d="M978.81 122.25L0 0h1280l-262.1 116.26a73.29 73.29 0 0 1-39.09 5.99z"/>',
'asymmetric2-bottom' => '<path d="M1280 0l-266 91.52a72.59 72.59 0 0 1-30.76 3.71L0 0v140h1280z" fill-opacity=".5"/><path d="M1280 0l-262.1 116.26a73.29 73.29 0 0 1-39.09 6L0 0v140h1280z"/>',
'asymmetric2-top' => '<path d="M978.81 122.25L0 0h1280l-262.1 116.26a73.29 73.29 0 0 1-39.09 5.99z" fill-opacity=".5"/><path d="M983.19 95.23L0 0h1280l-266 91.52a72.58 72.58 0 0 1-30.81 3.71z"/>',
'asymmetric3-bottom' => '<path d="M1093.48 131.85L173 94a76.85 76.85 0 0 1-36.79-11.46L0 0v140h1280V0l-131.81 111.68c-16.47 13.96-35.47 20.96-54.71 20.17z"/>',
'asymmetric3-top' => '<path d="M1280 0l-131.81 111.68c-16.47 14-35.47 21-54.71 20.17L173 94a76.85 76.85 0 0 1-36.79-11.46L0 0z"/>',
'asymmetric4-bottom' => '<path d="M1094.44 119L172.7 68.72a74.54 74.54 0 0 1-25.19-5.95L0 0v140h1280V0l-133.85 102c-15.84 12.09-33.7 17.95-51.71 17z" fill-opacity=".5"/><path d="M1093.48 131.85L173 94a76.85 76.85 0 0 1-36.79-11.46L0 0v140h1280V0l-131.81 111.68c-16.47 13.96-35.47 20.96-54.71 20.17z"/>',
'asymmetric4-top' => '<path d="M1093.48 131.85L173 94a76.85 76.85 0 0 1-36.79-11.46L0 0h1280l-131.81 111.68c-16.47 13.96-35.47 20.96-54.71 20.17z" fill-opacity=".5"/><path d="M1094.44 119L172.7 68.72a74.54 74.54 0 0 1-25.19-5.95L0 0h1280l-133.85 102c-15.84 12.09-33.7 17.95-51.71 17z"/>',
'clouds-bottom' => '<path d="M1280 63.1c-3.8 0-7.6.3-11.4.8-18.3-32.6-59.6-44.2-92.2-25.9-3.5 2-6.9 4.3-10 6.9-22.7-41.7-74.9-57.2-116.6-34.5-14.2 7.7-25.9 19.3-33.8 33.3-.2.3-.3.6-.5.8-12.2-1.4-23.7 5.9-27.7 17.5-11.9-6.1-25.9-6.3-37.9-.6-21.7-30.4-64-37.5-94.4-15.8-12.1 8.6-21 21-25.4 35.2-10.8-9.3-24.3-15-38.5-16.2-8.1-24.6-34.6-38-59.2-29.9-14.3 4.7-25.5 16-30 30.3-4.3-1.9-8.9-3.2-13.6-3.8-13.6-45.2-61.5-71.1-107-57.6A86.38 86.38 0 0 0 538.6 33c-8.7-3.6-18.7-1.8-25.4 4.8-23.1-24.8-61.9-26.2-86.7-3.1-7.1 6.6-12.5 14.8-15.9 24-26.7-10.1-56.9-.4-72.8 23.3-2.6-2.7-5.6-5.1-8.9-6.9-.4-.2-.8-.4-1.2-.7-.6-25.9-22-46.4-47.9-45.8-11.5.3-22.5 4.7-30.9 12.5-16.5-33.5-57-47.4-90.5-31-22 10.8-36.4 32.6-37.8 57.1-7-2.3-14.5-2.8-21.8-1.6-14-21.7-43.1-27.9-64.8-13.8-5.6 3.6-10.3 8.4-13.9 14C13.5 64 6.8 63.2 0 63.2-.1 63.2 0 86 0 86h1280V63.1z"/>',
'clouds-bottom2' => '<path d="M1280,63.1a81.42,81.42,0,0,0-11.41.81,67.71,67.71,0,0,0-102.21-19,86,86,0,0,0-150.47-1.2c-.16.28-.29.57-.45.85a26.07,26.07,0,0,0-27.65,17.54,43,43,0,0,0-37.93-.57A67.66,67.66,0,0,0,830.15,81a67.85,67.85,0,0,0-38.51-16.19,46.9,46.9,0,0,0-89.25.45,46.66,46.66,0,0,0-13.56-3.77A86,86,0,0,0,538.67,33.07a23.42,23.42,0,0,0-25.4,4.8A61.36,61.36,0,0,0,410.7,58.74a61.44,61.44,0,0,0-72.79,23.32A38.37,38.37,0,0,0,329,75.15c-.41-.23-.83-.45-1.25-.66a46.88,46.88,0,0,0-78.77-33.31A67.65,67.65,0,0,0,120.71,67.29a46.76,46.76,0,0,0-21.82-1.62,46.91,46.91,0,0,0-78.8.07A79.35,79.35,0,0,0,0,63.17C0,63.17,0,140,0,140H1280Z"/>',
'clouds-top' => '<path d="M1280 0H0v65.2c6.8 0 13.5.9 20.1 2.6 14-21.8 43.1-28 64.8-14 5.6 3.6 10.3 8.3 14 13.9 7.3-1.2 14.8-.6 21.8 1.6 2.1-37.3 34.1-65.8 71.4-63.7 24.3 1.4 46 15.7 56.8 37.6 19-17.6 48.6-16.5 66.3 2.4C323 54 327.4 65 327.7 76.5c.4.2.8.4 1.2.7 3.3 1.9 6.3 4.2 8.9 6.9 15.9-23.8 46.1-33.4 72.8-23.3 11.6-31.9 46.9-48.3 78.8-36.6 9.1 3.3 17.2 8.7 23.8 15.7 6.7-6.6 16.7-8.4 25.4-4.8 29.3-37.4 83.3-44 120.7-14.8 14 11 24.3 26.1 29.4 43.1 4.7.6 9.3 1.8 13.6 3.8 7.8-24.7 34.2-38.3 58.9-30.5 14.4 4.6 25.6 15.7 30.3 30 14.2 1.2 27.7 6.9 38.5 16.2 11.1-35.7 49-55.7 84.7-44.7 14.1 4.4 26.4 13.3 35 25.3 12-5.7 26.1-5.5 37.9.6 3.9-11.6 15.5-18.9 27.7-17.5.2-.3.3-.6.5-.9 23.3-41.4 75.8-56 117.2-32.6 14.1 7.9 25.6 19.7 33.3 33.8 28.8-23.8 71.5-19.8 95.3 9 2.6 3.1 4.9 6.5 6.9 10 3.8-.5 7.6-.8 11.4-.8L1280 0z"/>',
'clouds-top2' => '<path d="M1280,0H0S0,116.17,0,116.17a79.47,79.47,0,0,1,20.07,2.57,46.91,46.91,0,0,1,78.8-.07,46.76,46.76,0,0,1,21.82,1.62A67.67,67.67,0,0,1,248.93,94.17a46.88,46.88,0,0,1,78.77,33.31c.42.22.84.43,1.25.66a38.38,38.38,0,0,1,8.94,6.92,61.44,61.44,0,0,1,72.79-23.32A61.43,61.43,0,0,1,513.26,90.87a23.42,23.42,0,0,1,25.4-4.8,86,86,0,0,1,150.15,28.37,46.65,46.65,0,0,1,13.56,3.77,46.9,46.9,0,0,1,89.25-.45A67.85,67.85,0,0,1,830.13,134a67.7,67.7,0,0,1,119.73-19.38,43,43,0,0,1,37.93.57,26.07,26.07,0,0,1,27.65-17.54c.16-.28.29-.57.45-.85A86,86,0,0,1,1166.37,98a67.71,67.71,0,0,1,102.21,19,81.66,81.66,0,0,1,11.42-.81Z"/>',
'clouds2-bottom' => '<path d="M1280 66.1c-3.8 0-7.6.3-11.4.8-18.3-32.6-59.6-44.2-92.2-25.9-3.5 2-6.9 4.3-10 6.9-22.7-41.7-74.9-57.2-116.6-34.5-14.2 7.7-25.9 19.3-33.8 33.3-.2.3-.3.6-.5.8-12.2-1.4-23.7 5.9-27.7 17.5-11.9-6.1-25.9-6.3-37.9-.6-21.7-30.4-64-37.5-94.4-15.7-12.1 8.6-21 21-25.4 35.2-10.8-9.3-24.3-15-38.5-16.2-8.1-24.6-34.6-38-59.2-29.9-14.3 4.7-25.5 16-30 30.3-4.3-1.9-8.9-3.2-13.6-3.8-13.6-45.5-61.5-71.4-107-57.8a86.38 86.38 0 0 0-43.2 29.4c-8.7-3.6-18.7-1.8-25.4 4.8-23.1-24.8-61.9-26.2-86.7-3.1-7.1 6.6-12.5 14.8-15.9 24-26.7-10.1-56.9-.4-72.8 23.3-2.6-2.7-5.6-5.1-8.9-6.9-.4-.2-.8-.4-1.2-.7-.6-25.9-22-46.4-47.9-45.8-11.5.3-22.5 4.7-30.9 12.5-16.5-33.5-57.1-47.3-90.6-30.8-21.9 11-36.3 32.7-37.6 57.1-7-2.3-14.5-2.8-21.8-1.6C84.8 47 55.7 40.7 34 54.8c-5.6 3.6-10.3 8.4-13.9 14-6.6-1.7-13.3-2.6-20.1-2.6-.1 0 0 19.8 0 19.8h1280V66.1z" fill-opacity=".5"/><path d="M15.6 86H1280V48.5c-3.6 1.1-7.1 2.5-10.4 4.4-6.3 3.6-11.8 8.5-16 14.5-8.1-1.5-16.4-.9-24.2 1.7-3.2-39-37.3-68.1-76.4-64.9-24.8 2-46.8 16.9-57.9 39.3-19.9-18.5-51-17.3-69.4 2.6-8.2 8.8-12.8 20.3-13.1 32.3-.4.2-.9.4-1.3.7-3.5 1.9-6.6 4.4-9.4 7.2-16.6-24.9-48.2-35-76.2-24.4-12.2-33.4-49.1-50.6-82.5-38.4-9.5 3.5-18.1 9.1-25 16.5-7.1-6.9-17.5-8.8-26.6-5-30.4-39.3-87-46.3-126.2-15.8-14.8 11.5-25.6 27.4-31 45.4-4.9.6-9.7 1.9-14.2 3.9-8.2-25.9-35.8-40.2-61.7-32-15 4.8-26.9 16.5-31.8 31.5-14.9 1.3-29 7.2-40.3 17-11.5-37.4-51.2-58.4-88.7-46.8-14.8 4.6-27.7 13.9-36.7 26.5-12.6-6-27.3-5.7-39.7.6-4.1-12.2-16.2-19.8-29-18.4-.2-.3-.3-.6-.5-.9-24.4-43.3-79.4-58.6-122.7-34.2-13.3 7.5-24.4 18.2-32.4 31.2C99.8 18.5 50 28.5 25.4 65.4c-4.3 6.4-7.5 13.3-9.8 20.6z"/>',
'clouds2-bottom2' => '<path d="M1269.61,52.83a48.82,48.82,0,0,0-16,14.48A48.6,48.6,0,0,0,1229.45,69a70.88,70.88,0,0,0-134.21-25.66,49.11,49.11,0,0,0-82.51,34.9c-.44.23-.88.45-1.31.69a40.18,40.18,0,0,0-9.36,7.24,64.35,64.35,0,0,0-76.25-24.43A64.34,64.34,0,0,0,818.36,39.85a24.53,24.53,0,0,0-26.61-5A90,90,0,0,0,634.48,64.55a48.89,48.89,0,0,0-14.21,3.95A49.12,49.12,0,0,0,526.79,68a71.07,71.07,0,0,0-40.34,17A70.91,70.91,0,0,0,361,64.68a45.07,45.07,0,0,0-39.73.6,27.31,27.31,0,0,0-29-18.37c-.16-.29-.31-.59-.47-.89a90.06,90.06,0,0,0-155.12-3A80.23,80.23,0,0,0,12.64,99.75a80.1,80.1,0,0,0-12.64,2V140H1280V48.48A49.22,49.22,0,0,0,1269.61,52.83Z" fill-opacity=".5"/><path d="M1280,66.1a81.63,81.63,0,0,0-11.42.81,67.71,67.71,0,0,0-102.21-19,86,86,0,0,0-150.47-1.2c-.16.28-.29.57-.45.85a26.07,26.07,0,0,0-27.65,17.54,43,43,0,0,0-37.93-.57A67.66,67.66,0,0,0,830.13,84a67.85,67.85,0,0,0-38.51-16.19,46.9,46.9,0,0,0-89.25.45,46.66,46.66,0,0,0-13.56-3.77A86,86,0,0,0,538.66,36.07a23.42,23.42,0,0,0-25.4,4.8A61.36,61.36,0,0,0,410.68,61.74a61.44,61.44,0,0,0-72.79,23.32A38.37,38.37,0,0,0,329,78.15c-.41-.23-.83-.45-1.25-.66a46.88,46.88,0,0,0-78.77-33.31A67.65,67.65,0,0,0,120.69,70.29a46.76,46.76,0,0,0-21.82-1.62,46.91,46.91,0,0,0-78.8.07A79.46,79.46,0,0,0,0,66.17C-.07,66.17,0,140,0,140H1280Z"/>',
'clouds2-top' => '<path d="M833.9 27.5c-5.8 3.2-11 7.3-15.5 12.2-7.1-6.9-17.5-8.8-26.6-5-30.6-39.2-87.3-46.1-126.5-15.5-1.4 1.1-2.8 2.2-4.1 3.4C674.4 33.4 684 48 688.8 64.3c4.7.6 9.3 1.8 13.6 3.8 7.8-24.7 34.2-38.3 58.9-30.5 14.4 4.6 25.6 15.7 30.3 30 14.2 1.2 27.7 6.9 38.5 16.2C840.6 49.6 876 29.5 910.8 38c-20.4-20.3-51.8-24.6-76.9-10.5zM384 43.9c-9 5-16.7 11.9-22.7 20.3 15.4-7.8 33.3-8.7 49.4-2.6 3.7-10.1 9.9-19.1 18.1-26-15.4-2.3-31.2.6-44.8 8.3zm560.2 13.6c2 2.2 3.9 4.5 5.7 6.9 5.6-2.6 11.6-4 17.8-4.1-7.6-2.4-15.6-3.3-23.5-2.8zM178.7 7c29-4.2 57.3 10.8 70.3 37 8.9-8.3 20.7-12.8 32.9-12.5C256.4 1.8 214.7-8.1 178.7 7zm146.5 56.3c1.5 4.5 2.4 9.2 2.5 14 .4.2.8.4 1.2.7 3.3 1.9 6.3 4.2 8.9 6.9 5.8-8.7 13.7-15.7 22.9-20.5-11.1-5.2-23.9-5.6-35.5-1.1zM33.5 54.9c21.6-14.4 50.7-8.5 65 13 .1.2.2.3.3.5 7.3-1.2 14.8-.6 21.8 1.6.6-10.3 3.5-20.4 8.6-29.4.3-.6.7-1.2 1.1-1.8-32.1-17.2-71.9-10.6-96.8 16.1zm1228.9 2.7c2.3 2.9 4.4 5.9 6.2 9.1 3.8-.5 7.6-.8 11.4-.8V48.3c-6.4 1.8-12.4 5-17.6 9.3zM1127.3 11c1.9.9 3.7 1.8 5.6 2.8 14.2 7.9 25.8 19.7 33.5 34 13.9-11.4 31.7-16.9 49.6-15.3-20.5-27.7-57.8-36.8-88.7-21.5z" fill-opacity=".5"/><path d="M0 0v66c6.8 0 13.5.9 20.1 2.6 3.5-5.4 8.1-10.1 13.4-13.6 24.9-26.8 64.7-33.4 96.8-16 10.5-17.4 28.2-29.1 48.3-32 36.1-15.1 77.7-5.2 103.2 24.5 19.7.4 37.1 13.1 43.4 31.8 11.5-4.5 24.4-4.2 35.6 1.1l.4-.2c15.4-21.4 41.5-32.4 67.6-28.6 25-21 62.1-18.8 84.4 5.1 6.7-6.6 16.7-8.4 25.4-4.8 29.2-37.4 83.3-44.1 120.7-14.8l1.8 1.5c37.3-32.9 94.3-29.3 127.2 8 1.2 1.3 2.3 2.7 3.4 4.1 9.1-3.8 19.5-1.9 26.6 5 24.3-26 65-27.3 91-3.1.5.5 1 .9 1.5 1.4 12.8 3.1 24.4 9.9 33.4 19.5 7.9-.5 15.9.4 23.5 2.8 7-.1 13.9 1.5 20.1 4.7 3.9-11.6 15.5-18.9 27.7-17.5.2-.3.3-.6.5-.9 22.1-39.2 70.7-54.7 111.4-35.6 30.8-15.3 68.2-6.2 88.6 21.5 18.3 1.7 35 10.8 46.5 25.1 5.2-4.3 11.1-7.4 17.6-9.3V0H0z"/>',
'clouds2-top2' => '<path d="M833.9,77.67a64.2,64.2,0,0,0-15.53,12.18,24.53,24.53,0,0,0-26.61-5,90.1,90.1,0,0,0-130.57-12.1,85.54,85.54,0,0,1,27.62,41.73,46.66,46.66,0,0,1,13.56,3.77,46.9,46.9,0,0,1,89.25-.45A67.84,67.84,0,0,1,830.13,134,67.61,67.61,0,0,1,910.8,88.2,64.38,64.38,0,0,0,833.9,77.67Z M384,94.11a70.48,70.48,0,0,0-22.73,20.27,61.44,61.44,0,0,1,49.42-2.63,61.19,61.19,0,0,1,18.15-26A70.64,70.64,0,0,0,384,94.11Z M944.2,107.71a67.34,67.34,0,0,1,5.66,6.87,43.3,43.3,0,0,1,17.8-4.1A64.53,64.53,0,0,0,944.2,107.71Z M178.67,57.16a67.63,67.63,0,0,1,70.26,37,47.06,47.06,0,0,1,32.92-12.5A90.18,90.18,0,0,0,178.67,57.16Z M325.24,113.52a46.93,46.93,0,0,1,2.46,14c.42.22.84.43,1.25.66a38.38,38.38,0,0,1,8.94,6.92,61,61,0,0,1,22.94-20.48A45.09,45.09,0,0,0,325.24,113.52Z M33.49,105.13a46.91,46.91,0,0,1,65.38,13.55,46.75,46.75,0,0,1,21.82,1.62,67.13,67.13,0,0,1,8.58-29.39c.34-.6.7-1.19,1.06-1.78a80.19,80.19,0,0,0-96.84,16Z M1262.42,107.77a67.35,67.35,0,0,1,6.16,9.15,81.66,81.66,0,0,1,11.42-.81V98.48a48.83,48.83,0,0,0-17.58,9.29Z M1127.33,61.18c1.88.88,3.74,1.81,5.58,2.84A85.42,85.42,0,0,1,1166.37,98a68,68,0,0,1,49.55-15.27A70.94,70.94,0,0,0,1127.33,61.18Z" fill-opacity=".5"/><path d="M361,114.68l.23-.3-.43.22Z M0,0V120.87c0-3,0-4.69,0-4.69a79.35,79.35,0,0,1,20.06,2.57,46.56,46.56,0,0,1,13.42-13.62,80.19,80.19,0,0,1,96.84-16,67.52,67.52,0,0,1,48.33-32A90.18,90.18,0,0,1,281.84,81.67a46.82,46.82,0,0,1,43.4,31.85,45.09,45.09,0,0,1,35.59,1.07l.43-.22a70.84,70.84,0,0,1,67.57-28.6,61.47,61.47,0,0,1,84.42,5.09,23.42,23.42,0,0,1,25.4-4.8A86,86,0,0,1,661.19,72.72a90.1,90.1,0,0,1,130.57,12.1,24.53,24.53,0,0,1,26.61,5A64.37,64.37,0,0,1,910.8,88.2a67.45,67.45,0,0,1,33.4,19.51,64.53,64.53,0,0,1,23.45,2.77,42.8,42.8,0,0,1,20.14,4.67,26.07,26.07,0,0,1,27.65-17.54c.16-.28.29-.57.45-.85a86,86,0,0,1,111.44-35.58,70.94,70.94,0,0,1,88.59,21.52,67.79,67.79,0,0,1,46.5,25.07A48.83,48.83,0,0,1,1280,98.48V0Z"/>',
'curve-bottom' => '<path d="M1280 140V0S993.46 140 640 139 0 0 0 0v140z"/>',
'curve-top' => '<path d="M640 140C286.54 140 0 0 0 0h1280S993.46 140 640 140z"/>',
'curve2-bottom' => '<path d="M725.29 101.2C325.22 122.48 0 0 0 0v140h1280V0s-154.64 79.92-554.71 101.2z" fill-opacity=".3"/><path d="M556.45 119.74C953.41 140 1280 14 1280 14v126H0V0s159.5 99.48 556.45 119.74z" fill-opacity=".5"/><path d="M640 140c353.46 0 640-140 640-139v140H0V0s286.54 140 640 140z"/>',
'curve2-top' => '<path d="M0 0v.48C18.62 9.38 297.81 140 639.5 140 993.24 140 1280 0 1280 0z" fill-opacity=".3"/><path d="M0 .6c14 8.28 176.54 99.8 555.45 119.14C952.41 140 1280 0 1280 0H0z" fill-opacity=".5"/><path d="M726.29 101.2C1126.36 79.92 1281 0 1281 0H1c.05 0 325.25 122.48 725.29 101.2z"/>',
'graph-bottom' => '<path d="M0 122.138l60.614 9.965 95.644-4.2 86.363-18.654 78.684 13.079L411.442 99.4l94.453 10.303L582.821 93.8l82.664 18.728 76.961-11.39L816.11 71.4l97.601 9.849L997.383 50.4l66.285 14.694 70.793-24.494h79.863L1280 0v140H0z"/>',
'graph-top' => '<path d="M156.258 127.903l86.363-18.654 78.684 13.079L411.441 99.4l94.454 10.303L582.82 93.8l82.664 18.728 76.961-11.39L816.109 71.4l97.602 9.849L997.383 50.4l66.285 14.694 70.793-24.494h79.863L1280 0H0v122.138l60.613 9.965z"/>',
'graph2-bottom' => '<path d="M0 127.899l60.613 4.878 95.645-6.211 86.363-16.074 78.684 9.883 90.136-21.594 94.454 7.574 77.925-17.66 91.664 20.798 76.961-12.649 63.664-21.422 97.602 7.07 83.672-29.617 66.285 11.678 70.793-23.334 74.863-4.641L1280 0v140H0z" fill-opacity=".5"/><path d="M0 126.71l60.613 7.415L156.257 131l86.364-13.879 78.683 9.731 90.137-17.059 94.453 7.666 76.926-11.833 82.664 13.935 76.961-8.475 73.664-22.126 97.601 7.328 83.672-22.952 66.285 10.933 70.794-18.224h79.862L1280 35.838V140H0z"/>',
'graph2-top' => '<path d="M1214.323 66.051h-79.863l-70.793 18.224-66.285-10.933-83.672 22.953-97.601-7.328-73.664 22.125-76.961 8.475-82.664-13.934-76.926 11.832-94.453-7.666-90.137 17.059-78.684-9.731-86.363 13.879-95.644 3.125L0 126.717V0h1280l-.001 35.844z" fill-opacity=".5"/><path d="M0 0h1280v.006l-70.676 36.578-74.863 4.641-70.793 23.334-66.285-11.678-83.672 29.618-97.602-7.07-63.664 21.421-76.961 12.649-91.664-20.798-77.926 17.66-94.453-7.574-90.137 21.595-78.683-9.884-86.363 16.074-95.645 6.211L0 127.905z"/>',
'graph3-bottom' => '<path d="M0 0l64.8 38.69 91.2-3.18 95.45 34.84 120.04.24 71.5 33.35 90.08-3.91 106.91 37.62 102.38-37.17 85.55 10.65 88.11-7.19 75.95-38.66 73.21 5.31 66.78-22.1 77-.42 71-48.07v140H0V0z"/>',
'graph3-top' => '<path d="M156 35.51l95.46 34.84 120.04.24 71.5 33.35 90.09-3.91L640 137.65l102.39-37.17 85.55 10.65 88.11-7.19L992 65.28l73.21 5.31 66.79-22.1 77-.42L1280 0H0l64.8 38.69 91.2-3.18z"/>',
'graph4-bottom' => '<path d="M0 0l64.8 30.95 91.2-2.54 95.46 27.87 120.04.2L443 83.15l90.09-3.12L640 110.12l102.39-29.73 85.55 8.51 88.11-5.75L992 52.22l73.21 4.26L1132 38.79l77-.33L1280 0v140H0V0z" fill-opacity=".5"/><path d="M0 0l64.8 38.69 91.2-3.18 95.46 34.84 120.04.24 71.5 33.35 90.09-3.91L640 137.65l102.39-37.17 85.55 10.65 88.11-7.19L992 65.28l73.21 5.31 66.79-22.1 77-.41L1280 0v140H0V0z"/>',
'graph4-top' => '<path d="M156 35.41l95.46 34.73 120.04.25 71.5 33.24 90.09-3.89L640 137.25l102.39-37.06 85.55 10.61 88.11-7.17L992 65.08l73.21 5.31L1132 48.35l77-.42L1280 0H0l64.8 38.57 91.2-3.16z" fill-opacity=".5"/><path d="M156 28.32l95.46 27.79 120.04.2L443 82.9l90.09-3.11L640 109.8l102.39-29.65 85.55 8.49 88.11-5.74L992 52.07l73.21 4.24L1132 38.68l77-.34L1280 0H0l64.8 30.86 91.2-2.54z"/>',
'mountains-bottom' => '<path d="M0 70.35l320-49.24 640 98.49 320-49.25V140H0V70.35z"/>',
'mountains-top' => '<path d="M1280 69.65l-320 49.24L320 20.4 0 69.65V0h1280v69.65z"/>',
'mountains2-bottom' => '<path d="M0 47.44L170 0l626.48 94.89L1110 87.11l170-39.67V140H0V47.44z" fill-opacity=".5"/><path d="M0 90.72l140-28.28 315.52 24.14L796.48 65.8 1140 104.89l140-14.17V140H0V90.72z"/>',
'mountains2-top' => '<path d="M0 90.72l140-28.28 315.52 24.14L796.48 65.8 1140 104.89l140-14.17V0H0v90.72z" fill-opacity=".5"/><path d="M0 0v47.44L170 0l626.48 94.89L1110 87.11l170-39.67V0H0z"/>',
'ramp-bottom' => '<path d="M0 140h1280C573.08 140 0 0 0 0z"/>',
'ramp-top' => '<path d="M0 0s573.08 140 1280 140V0z"/>',
'ramp2-bottom' => '<path d="M0 140h1280C573.08 140 0 0 0 0z" fill-opacity=".3"/><path d="M0 140h1280C573.08 140 0 30 0 30z" fill-opacity=".5"/><path d="M0 140h1280C573.08 140 0 60 0 60z"/>',
'ramp2-top' => '<path d="M0 0v60s573.09 80 1280 80V0z" fill-opacity=".3"/><path d="M0 0v30s573.09 110 1280 110V0z" fill-opacity=".5"/><path d="M0 0s573.09 140 1280 140V0z"/>',
'slant-bottom' => '<path d="M0 0v140h1280L0 0z"/>',
'slant-top' => '<path d="M1280 140V0H0l1280 140z"/>',
'slant2-bottom' => '<path d="M0 0v140h1280L0 0z" fill-opacity=".5"/><path d="M0 42v98h1280L0 42z"/>',
'slant2-top' => '<path d="M1280 140V0H0l1280 140z" fill-opacity=".5"/><path d="M1280 98V0H0l1280 98z"/>',
'triangle-bottom' => '<path d="M80 0L0 140h160z"/>',
'triangle-bottom2' => '<polygon points="640 0 560 140 720 140 640 0"/>',
'triangle-top' => '<path d="M720 140L640 0l-80 140H0V0h1280v140H720z"/>',
'triangle-top2' => '<path d="M720 140L640 0l-80 140H0V0h1280v140H720z"/>',
'wave-bottom' => '<path d="M320 28c320 0 320 84 640 84 160 0 240-21 320-42v70H0V70c80-21 160-42 320-42z"/>',
'wave-top' => '<path d="M320 28C160 28 80 49 0 70V0h1280v70c-80 21-160 42-320 42-320 0-320-84-640-84z"/>',
'wave2-bottom' => '<path d="M1280 3.4C1050.59 18 1019.4 84.89 734.42 84.89c-320 0-320-84.3-640-84.3C59.4.59 28.2 1.6 0 3.4V140h1280z" fill-opacity=".3"/><path d="M0 24.31c43.46-5.69 94.56-9.25 158.42-9.25 320 0 320 89.24 640 89.24 256.13 0 307.28-57.16 481.58-80V140H0z" fill-opacity=".5"/><path d="M1280 51.76c-201 12.49-242.43 53.4-513.58 53.4-320 0-320-57-640-57-48.85.01-90.21 1.35-126.42 3.6V140h1280z"/>',
'wave2-top' => '<path d="M0 51.76c36.21-2.25 77.57-3.58 126.42-3.58 320 0 320 57 640 57 271.15 0 312.58-40.91 513.58-53.4V0H0z" fill-opacity=".3"/><path d="M0 24.31c43.46-5.69 94.56-9.25 158.42-9.25 320 0 320 89.24 640 89.24 256.13 0 307.28-57.16 481.58-80V0H0z" fill-opacity=".5"/><path d="M0 0v3.4C28.2 1.6 59.4.59 94.42.59c320 0 320 84.3 640 84.3 285 0 316.17-66.85 545.58-81.49V0z"/>',
'waves-bottom' => '<path d="M1280 86c-19.9-17.21-40.08-39.69-79.89-39.69-57.49 0-56.93 46.59-115 46.59-53.61 0-59.76-39.62-115.6-39.62C923.7 53.27 924.26 87 853.89 87c-89.35 0-78.74-87-188.2-87C554 0 543.95 121.8 423.32 121.8c-100.52 0-117.84-54.88-191.56-54.88-77.06 0-100 48.57-151.75 48.57-40 0-60-12.21-80-29.51v54H1280z"/>',
'waves-top' => '<path d="M0 0v100c20 17.3 40 29.51 80 29.51 51.79 0 74.69-48.57 151.75-48.57 73.72 0 91 54.88 191.56 54.88C543.95 135.8 554 14 665.69 14c109.46 0 98.85 87 188.2 87 70.37 0 69.81-33.73 115.6-33.73 55.85 0 62 39.62 115.6 39.62 58.08 0 57.52-46.59 115-46.59 39.8 0 60 22.48 79.89 39.69V0z"/>',
'waves2-bottom' => '<path d="M853.893,86.998c-38.859,0-58.811-16.455-77.956-35.051c18.295-10.536,40.891-18.276,73.378-18.276 c38.685,0,64.132,12.564,85.489,28.347C916.192,72.012,900.8,86.998,853.893,86.998z M526.265,80.945 c-6.517-0.562-13.599-0.879-21.41-0.879c-70.799,0-91.337,27.229-134.433,35.662c14.901,3.72,32.118,6.07,52.898,6.07 C470.171,121.797,500.34,103.421,526.265,80.945z" fill-opacity=".3"/><path d="M663.458,109.671c-67.137,0-80.345-23.824-137.193-28.726C567.086,45.555,597.381,0,665.691,0 c61.857,0,85.369,27.782,110.246,51.947C736.888,74.434,717.459,109.671,663.458,109.671z M217.68,94.163 c55.971,0,62.526,24.026,126.337,24.026c9.858,0,18.508-0.916,26.404-2.461c-57.186-14.278-80.177-48.808-138.659-48.808 c-77.063,0-99.96,48.569-151.751,48.569c-40.006,0-60.008-12.206-80.011-29.506v16.806c20.003,10.891,40.005,21.782,80.011,21.782 C160.014,124.57,158.608,94.163,217.68,94.163z M1200.112,46.292c-57.493,0-56.935,46.595-115.015,46.595 c-53.612,0-59.755-39.618-115.602-39.618c-15.267,0-25.381,3.751-34.69,8.749c36.096,26.675,60.503,62.552,117.342,62.552 c69.249,0,75.951-43.559,147.964-43.559c39.804,0,59.986,10.943,79.888,21.777V85.982 C1260.097,68.771,1239.916,46.292,1200.112,46.292z" fill-opacity=".5"/><path d="M1052.147,124.57c-56.84,0-81.247-35.876-117.342-62.552c-18.613,9.994-34.005,24.98-80.912,24.98 c-38.859,0-58.811-16.455-77.956-35.051c-39.05,22.487-58.479,57.724-112.48,57.724c-67.137,0-80.345-23.824-137.193-28.726 c-25.925,22.475-56.093,40.852-102.946,40.852c-20.779,0-37.996-2.349-52.898-6.07c-7.895,1.545-16.546,2.461-26.404,2.461 c-63.811,0-70.366-24.026-126.337-24.026c-59.072,0-57.665,30.407-137.669,30.407c-40.006,0-60.008-10.891-80.011-21.782V140h1280 v-37.212c-19.903-10.835-40.084-21.777-79.888-21.777C1128.098,81.011,1121.397,124.57,1052.147,124.57z"/>',
'waves2-top' => '<path d="M504.854,80.066c7.812,0,14.893,0.318,21.41,0.879 c-25.925,22.475-56.093,40.852-102.946,40.852c-20.779,0-37.996-2.349-52.898-6.07C413.517,107.295,434.056,80.066,504.854,80.066z M775.938,51.947c19.145,18.596,39.097,35.051,77.956,35.051c46.907,0,62.299-14.986,80.912-24.98 c-21.357-15.783-46.804-28.348-85.489-28.348C816.829,33.671,794.233,41.411,775.938,51.947z" fill-opacity=".3"/><path d="M1200.112,46.292c39.804,0,59.986,22.479,79.888,39.69v16.805 c-19.903-10.835-40.084-21.777-79.888-21.777c-72.014,0-78.715,43.559-147.964,43.559c-56.84,0-81.247-35.876-117.342-62.552 c9.309-4.998,19.423-8.749,34.69-8.749c55.846,0,61.99,39.617,115.602,39.617C1143.177,92.887,1142.618,46.292,1200.112,46.292z M80.011,115.488c-40.006,0-60.008-12.206-80.011-29.506v16.806c20.003,10.891,40.005,21.782,80.011,21.782 c80.004,0,78.597-30.407,137.669-30.407c55.971,0,62.526,24.026,126.337,24.026c9.858,0,18.509-0.916,26.404-2.461 c-57.186-14.278-80.177-48.808-138.66-48.808C154.698,66.919,131.801,115.488,80.011,115.488z M526.265,80.945 c56.848,4.902,70.056,28.726,137.193,28.726c54.001,0,73.43-35.237,112.48-57.724C751.06,27.782,727.548,0,665.691,0 C597.381,0,567.086,45.555,526.265,80.945z" fill-opacity=".5"/><path d="M0,0v85.982c20.003,17.3,40.005,29.506,80.011,29.506c51.791,0,74.688-48.569,151.751-48.569 c58.482,0,81.473,34.531,138.66,48.808c43.096-8.432,63.634-35.662,134.433-35.662c7.812,0,14.893,0.318,21.41,0.879 C567.086,45.555,597.381,0,665.691,0c61.856,0,85.369,27.782,110.246,51.947c18.295-10.536,40.891-18.276,73.378-18.276 c38.685,0,64.132,12.564,85.489,28.348c9.309-4.998,19.423-8.749,34.69-8.749c55.846,0,61.99,39.617,115.602,39.617 c58.08,0,57.521-46.595,115.015-46.595c39.804,0,59.986,22.479,79.888,39.69V0H0z"/>',
);
/**
* Filters the section divider paths.
*
* @param array $dividers Array list of available dividers.
*/
$this->dividers = apply_filters( 'et_section_dividers', $section_dividers );
if ( null === self::$data_utils ) {
self::$data_utils = ET_Core_Data_Utils::instance();
}
}
/**
* Retrieves fields for divider settings.
*
* @since 3.23 Add responsive settings on Divider Style. Add allowed units on some range fields.
*
* @param array $args Associative array for settings.
* @return array Option settings.
*/
public function get_fields( array $args = array() ) {
// Create an array so we don't get an error.
$additional_options = array();
// Create the options by first creating the structure.
$structure = array();
foreach ( array( 'top', 'bottom' ) as $placement ) :
$structure[ "{$placement}_divider" ] = array(
'controls' => array(
"{$placement}_divider_style" => array(
'label' => esc_html__( 'Divider Style', 'et_builder' ),
'description' => esc_html__( 'Select the divider shape that you would like to use. Shapes are represented visually within the list.', 'et_builder' ),
'type' => 'divider',
'options' => array(
'none' => et_builder_i18n( 'None' ),
'slant' => esc_html__( 'Slant', 'et_builder' ),
'slant2' => esc_html__( 'Slant 2', 'et_builder' ),
'arrow' => esc_html__( 'Arrow', 'et_builder' ),
'arrow2' => esc_html__( 'Arrow 2', 'et_builder' ),
'arrow3' => esc_html__( 'Arrow 3', 'et_builder' ),
'ramp' => esc_html__( 'Ramp', 'et_builder' ),
'ramp2' => esc_html__( 'Ramp 2', 'et_builder' ),
'curve' => esc_html__( 'Curve', 'et_builder' ),
'curve2' => esc_html__( 'Curve 2', 'et_builder' ),
'mountains' => esc_html__( 'Mountains', 'et_builder' ),
'mountains2' => esc_html__( 'Mountains 2', 'et_builder' ),
'wave' => esc_html__( 'Wave', 'et_builder' ),
'wave2' => esc_html__( 'Wave 2', 'et_builder' ),
'waves' => esc_html__( 'Waves', 'et_builder' ),
'waves2' => esc_html__( 'Waves 2', 'et_builder' ),
'asymmetric' => esc_html__( 'Asymmetric', 'et_builder' ),
'asymmetric2' => esc_html__( 'Asymmetric 2', 'et_builder' ),
'asymmetric3' => esc_html__( 'Asymmetric 3', 'et_builder' ),
'asymmetric4' => esc_html__( 'Asymmetric 4', 'et_builder' ),
'graph' => esc_html__( 'Graph', 'et_builder' ),
'graph2' => esc_html__( 'Graph 2', 'et_builder' ),
'graph3' => esc_html__( 'Graph 3', 'et_builder' ),
'graph4' => esc_html__( 'Graph 4', 'et_builder' ),
'triangle' => esc_html__( 'Triangle', 'et_builder' ),
'clouds' => esc_html__( 'Clouds', 'et_builder' ),
'clouds2' => esc_html__( 'Clouds 2', 'et_builder' ),
),
'default' => 'none',
'flip' => '',
'mobile_options' => true,
),
"{$placement}_divider_color" => array(
'label' => esc_html__( 'Divider Color', 'et_builder' ),
'description' => esc_html__( 'Pick a color to use for the section divider. By default, it will assume the color of the section above or below this section to ensure a smooth transition.', 'et_builder' ),
'type' => 'color-alpha',
'default' => '',
'show_if_not' => array(
"{$placement}_divider_style" => 'none',
),
'mobile_options' => true,
),
"{$placement}_divider_height" => array(
'label' => esc_html__( 'Divider Height', 'et_builder' ),
'description' => esc_html__( 'Increase or decrease the height of the shape divider.', 'et_builder' ),
'type' => 'range',
'range_settings' => array(
'min' => 0,
'max' => 500,
'step' => 1,
),
'default' => '100px',
'hover' => 'tabs',
'allowed_units' => array( '%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ),
'default_unit' => 'px',
'show_if_not' => array(
"{$placement}_divider_style" => 'none',
),
'mobile_options' => true,
'sticky' => true,
),
"{$placement}_divider_repeat" => array(
'label' => esc_html__( 'Divider Horizontal Repeat', 'et_builder' ),
'description' => esc_html__( 'Choose how many times the shape divider should repeat. Setting to 1x will remove all repetition.', 'et_builder' ),
'type' => 'range',
'range_settings' => array(
'min' => 1,
'max' => 20,
'step' => 1,
'min_limit' => 1, // Changed to 1 from 0 since it's basically the same result for both values.
),
'default' => '1', // Dont use the fixed_unit in default value ( i.e. 1x, just use 1 ), or else input will return undefined.
'fixed_unit' => 'x',
'show_if_not' => array(
"{$placement}_divider_style" => array( 'none', 'clouds', 'clouds2', 'triangle' ),
),
'mobile_options' => true,
),
"{$placement}_divider_flip" => array(
'label' => esc_html__( 'Divider Flip', 'et_builder' ),
'description' => esc_html__( 'Flip the divider horizontally or vertically to change the shape and its direction.', 'et_builder' ),
'type' => 'multiple_buttons',
'options' => array(
'horizontal' => array(
'title' => esc_html__( 'Horizontal', 'et_builder' ),
'icon' => 'flip-horizontally',
),
'vertical' => array(
'title' => esc_html__( 'Vertical', 'et_builder' ),
'icon' => 'flip-vertically',
),
),
'toggleable' => true,
'multi_selection' => true,
'default' => '',
'show_if_not' => array(
"{$placement}_divider_style" => 'none',
),
'mobile_options' => true,
),
"{$placement}_divider_arrangement" => array(
'label' => esc_html__( 'Divider Arrangement', 'et_builder' ),
'description' => esc_html__( 'Dividers can be placed either above or below section content. If placed above section content, then modules and rows within the section will be hidden behind the divider when they overlap.', 'et_builder' ),
'type' => 'select',
'options' => array(
'above_content' => esc_html__( 'On Top Of Section Content', 'et_builder' ),
'below_content' => esc_html__( 'Underneath Section Content', 'et_builder' ),
),
'default' => 'below_content',
'show_if_not' => array(
"{$placement}_divider_style" => 'none',
'fullwidth' => 'on',
),
'mobile_options' => true,
),
),
);
// Automatically append responsive field
foreach ( $structure[ "{$placement}_divider" ]['controls'] as $field_name => $field ) {
if ( isset( $field['mobile_options'] ) && $field['mobile_options'] ) {
$responsive_field_default = isset( $field['default'] ) ? $field['default'] : '';
// Tablet field
$structure[ "{$placement}_divider" ]['controls'][ "{$field_name}_tablet" ] = array(
'type' => 'hidden',
'default' => $responsive_field_default,
);
// Phone field
$structure[ "{$placement}_divider" ]['controls'][ "{$field_name}_phone" ] = array(
'type' => 'hidden',
'default' => $responsive_field_default,
);
// Last edited field
$structure[ "{$placement}_divider" ]['controls'][ "{$field_name}_last_edited" ] = array(
'type' => 'hidden',
'default' => 'off|desktop',
);
}
}
endforeach; // End foreach().
// Set our labels.
$structure['bottom_divider']['label'] = et_builder_i18n( 'Bottom' );
$structure['top_divider']['label'] = et_builder_i18n( 'Top' );
$additional_options['divider_settings'] = array(
'label' => esc_html__( 'Dividers', 'et_builder' ),
'description' => esc_html__( 'Section dividers allow you to add creative shape transitions between different sections on your page.', 'et_builder' ),
'tab_slug' => $args['tab_slug'],
'toggle_slug' => $args['toggle_slug'],
'attr_suffix' => '',
'type' => 'composite',
'option_category' => 'layout',
'composite_type' => 'default',
'composite_structure' => $structure,
);
return $additional_options;
}
/**
* Process Section Divider
*
* Adds a CSS class to the section, determines orientaion of the SVG, encodes an SVG to use as data
* for the background-image property.
*
* @since 3.23 Pass values parameter to support responsive settings.
* @since 4.6.0 Add sticky style support.
*
* @param string $placement Whether it is the top or bottom divider.
* @param array $atts Associative array of shortcode and their
* respective values.
* @param string $breakpoint ''|tablet|phone
* @param array $values Existing responsive values.
*/
public function process_svg( $placement, $atts, $breakpoint = '', $values = array() ) {
// add a class to the section.
$this->classes[] = sprintf( 'et_pb_%s_divider', esc_attr( $placement ) );
// set some defaults.
$previous_section = ! empty( $atts['prev_background_color'] ) ? $atts['prev_background_color'] : '#ffffff';
$next_section = ! empty( $atts['next_background_color'] ) ? $atts['next_background_color'] : '#ffffff';
// set a default based on whether it is the top or bottom divider.
$default_color = ( 'top' === $placement ) ? $previous_section : $next_section;
$color = ! empty( $atts[ "{$placement}_divider_color" ] ) ? $atts[ "{$placement}_divider_color" ] : $default_color;
$height = ! empty( $atts[ "{$placement}_divider_height" ] ) ? $atts[ "{$placement}_divider_height" ] : '100px';
$height_hover = et_pb_hover_options()->get_value( "{$placement}_divider_height", $atts, false );
$repeat = ! empty( $atts[ "{$placement}_divider_repeat" ] ) ? floatval( $atts[ "{$placement}_divider_repeat" ] ) : 1;
$flip = ( '' !== $atts[ "{$placement}_divider_flip" ] ) ? explode( '|', $atts[ "{$placement}_divider_flip" ] ) : array();
$arrangement = ! empty( $atts[ "{$placement}_divider_arrangement" ] ) ? $atts[ "{$placement}_divider_arrangement" ] : 'below_content';
$divider_style = et_pb_responsive_options()->get_any_value( $atts, "{$placement}_divider_style", '', true, $breakpoint );
$style = $divider_style . "-{$placement}";
$fullwidth = $atts['fullwidth'];
// Apply adjustment for responsive styling
if ( '' !== $breakpoint ) {
// Get all responsive unique value.
$values = et_pb_responsive_options()->get_any_responsive_values(
$atts,
array(
"{$placement}_divider_color" => '',
"{$placement}_divider_height" => '',
"{$placement}_divider_repeat" => '',
"{$placement}_divider_flip" => '',
"{$placement}_divider_arrangement" => '',
),
true,
$breakpoint
);
// Replace all default values.
$color = ! empty( $values[ "{$placement}_divider_color" ] ) ? $values[ "{$placement}_divider_color" ] : $color;
$height = ! empty( $values[ "{$placement}_divider_height" ] ) ? $values[ "{$placement}_divider_height" ] : $height;
$repeat = ! empty( $values[ "{$placement}_divider_repeat" ] ) ? floatval( $values[ "{$placement}_divider_repeat" ] ) : $repeat;
$flip = ! empty( $values[ "{$placement}_divider_flip" ] ) ? explode( '|', $values[ "{$placement}_divider_flip" ] ) : $flip;
$arrangement = ! empty( $values[ "{$placement}_divider_arrangement" ] ) ? $values[ "{$placement}_divider_arrangement" ] : $arrangement;
if ( ! empty( $values[ "{$placement}_divider_flip" ] ) && 'none' === $values[ "{$placement}_divider_flip" ] ) {
$flip = array();
}
}
// Make sure that we don't divide by zero.
if ( ! $repeat ) {
$repeat = 1;
}
// let's make sure we flip the fight ones, yeah?
// use the opposite SVG
if ( in_array( 'vertical', $flip ) ) {
switch ( $placement ) {
case 'top':
$style = $divider_style . '-bottom';
break;
case 'bottom':
$style = $divider_style . '-top';
break;
}
}
// The SVG markup for the background.
switch ( $style ) {
case 'clouds-top':
case 'clouds2-top':
// we can use the viewBox to move down the image since it has a height of 86px
$svg_markup = '<svg width="100%%" height="%1$s" viewBox="0 0 1280 86" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg"><g fill="%2$s">%3$s</g></svg>';
break;
case 'clouds-bottom':
case 'clouds2-bottom':
// we can use the viewBox to move up the image since it has a height of 86px
$svg_markup = '<svg width="100%%" viewBox="0 0 1280 86" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg"><g fill="%2$s">%3$s</g></svg>';
break;
case 'triangle-top':
$svg_markup = '<svg width="100%%" height="100px" viewBox="0 0 1280 100" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg"><g fill="%2$s">%3$s</g></svg>';
break;
case 'triangle-bottom':
$svg_markup = '<svg width="160px" height="140px" viewBox="0 0 160 140" xmlns="http://www.w3.org/2000/svg"><g fill="%2$s">%3$s</g></svg>';
break;
default:
$svg_markup = '<svg width="100%%" height="%1$s" viewBox="0 0 1280 140" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg"><g fill="%2$s">%3$s</g></svg>';
break;
}
$divider_style = isset( $this->dividers[ $style ] ) ? $this->dividers[ $style ] : '';
$svg = sprintf( $svg_markup, $height, $color, $divider_style );
// encode the SVG so we can use it as data for background-image.
$this->svg = base64_encode( $svg ); // phpcs:ignore
// Build up our declaration.
// bg-image
$declaration['background-image'] = sprintf( 'url( data:image/svg+xml;base64,%s )', $this->svg );
// bg-size. the percent is how many times to repeat the image.
if ( 0 === strpos( $style, 'clouds' ) ) {
$declaration['background-size'] = 'cover';
switch ( $placement ) {
case 'top':
$declaration['background-position'] = ( 'top' === $placement || 'vertical' === $flip ) ? 'center top' : 'center bottom';
break;
case 'bottom':
$declaration['background-position'] = ( 'top' === $placement || 'vertical' !== $flip ) ? 'center top' : 'center bottom';
break;
}
} elseif ( 0 === strpos( $style, 'triangle' ) ) {
$declaration['background-size'] = 'cover';
$declaration['background-position-x'] = 'center';
} else {
// Adjusts for when percentages are being used.
if ( 0 < strpos( $height, '%' ) ) {
$declaration['background-size'] = sprintf( '%1$s%% 100%%', floatval( 100 / $repeat ) );
} else {
$declaration['background-size'] = sprintf( '%1$s%% %2$s', floatval( 100 / $repeat ), $height );
}
}
// position
$declaration[ $placement ] = 0;
// height
$declaration['height'] = $height;
// z-index - determined by arrangement.
$declaration['z-index'] = ( 'on' === $fullwidth || 'above_content' === $arrangement ) ? 10 : 1;
$flip_styles = array(
in_array( 'horizontal', $flip, true ) ? '-1' : '1',
in_array( 'vertical', $flip, true ) ? '-1' : '1',
);
$declaration['transform'] = 'scale(' . implode( ', ', $flip_styles ) . ')';
// finally create our CSS declaration.
$css = '';
foreach ( $declaration as $rule => $value ) {
$css .= esc_html( "{$rule}:{$value};" );
}
// prepare our selector.
$selector = sprintf( '%%order_class%%.section_has_divider.et_pb_%1$s_divider .et_pb_%1$s_inside_divider', esc_attr( $placement ) );
// The styling of our section divider.
$styling = array(
'selector' => $selector,
'declaration' => $css,
);
// Apply media query if needed
if ( in_array( $breakpoint, array( 'tablet', 'phone' ) ) ) {
$query_map = array(
'tablet' => 'max_width_980',
'phone' => 'max_width_767',
);
$styling['media_query'] = ET_Builder_Element::get_media_query( $query_map[ $breakpoint ] );
}
ET_Builder_Element::set_style( 'et_pb_section', $styling );
// if we are on the first section and is the top divider.
if ( 0 === $this->count && 'top' === $placement && '' === $breakpoint ) {
// we will use a transparent bg.
ET_Builder_Element::set_style(
'et_pb_section',
array(
'selector' => $selector,
'declaration' => 'background-color: transparent;',
)
);
}
// Print Hover / Sticky height.
$modes = array( 'hover', 'sticky' );
// sprintf() removes `%` while add_hover* and add_sticky* only recognize %%order_class%%.
// thus append mode selector before $placement is re-added to the selector.
$height_selector_base = '%%order_class%%.section_has_divider.et_pb_%1$s_divider .et_pb_%1$s_inside_divider';
foreach ( $modes as $mode ) {
switch ( $mode ) {
case 'hover':
$helper = et_pb_hover_options();
$height_mode_selector = $helper->add_hover_to_order_class( $height_selector_base );
break;
case 'sticky':
$helper = et_pb_sticky_options();
$height_mode_selector = $helper->add_sticky_to_order_class( $height_selector_base, $helper->is_sticky_module( $atts ) );
break;
}
$height_mode = $helper->get_value( "{$placement}_divider_height", $atts, false );
if ( false === $height_mode ) {
continue;
}
$css = '';
$height = $height_mode;
$selector = sprintf(
$height_mode_selector,
esc_attr( $placement )
);
$declaration = array(
'height' => $height,
);
// Adjusts for when percentages are being used.
if ( 0 < strpos( $height, '%' ) ) {
$declaration['background-size'] = sprintf( '%1$s%% 100%%', floatval( 100 / $repeat ) );
} else {
$declaration['background-size'] = sprintf( '%1$s%% %2$s', floatval( 100 / $repeat ), $height );
}
foreach ( $declaration as $rule => $value ) {
$css .= esc_html( "{$rule}:{$value};" );
}
ET_Builder_Element::set_style(
'et_pb_section',
array(
'selector' => "$selector",
'declaration' => $css,
)
);
}
}
/**
* Returns a placeholder for the section only if it is set to be inside of the section.
*
* @param string $placement Whether it is the top or bottom
* @return string HTML container
*/
public function get_svg( $placement ) {
// we return a div to use for the divider
return sprintf( '<div class="et_pb_%s_inside_divider et-no-transition"></div>', esc_attr( $placement ) );
}
}
return new ET_Builder_Module_Field_Divider();

View File

@@ -0,0 +1,23 @@
<?php
class ET_Builder_Module_Fields_Factory {
protected static $fields = array();
/**
* @param $fields_type
*
* @return ET_Builder_Module_Field_Base
*/
public static function get( $fields_type ) {
if ( ! isset( self::$fields[ $fields_type ] ) ) {
$file = implode( DIRECTORY_SEPARATOR, array( ET_BUILDER_DIR, 'module', 'field', "$fields_type.php" ) );
$instance = file_exists( $file ) ? require_once $file : null;
self::$fields[ $fields_type ] = $instance instanceof ET_Builder_Module_Field_Base ? $instance : null;
}
return self::$fields[ $fields_type ];
}
}

View File

@@ -0,0 +1,191 @@
<?php
class ET_Builder_Module_Field_Height extends ET_Builder_Module_Field_Base {
/**
* Translations.
*
* @var array
*/
protected $i18n = array();
public function get_defaults() {
return array(
'prefix' => '',
'use_min_height' => true,
'use_height' => true,
'use_max_height' => true,
);
}
public function get_fields( array $args = array() ) {
$settings = array_merge( $this->get_defaults(), $args );
return array_merge(
$this->get_min_height( $settings ),
$this->get_height( $settings ),
$this->get_max_height( $settings )
);
}
private function get_min_height( $settings ) {
if ( ! $settings['use_min_height'] ) {
return array();
}
$helper = et_pb_min_height_options( $settings['prefix'] );
$i18n =& $this->i18n;
if ( ! isset( $i18n['minheight'] ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
$i18n['minheight'] = array(
'label' => __( 'Min Height', 'et_builder' ),
'description' => __( 'When a minimum height is set, the element will always have a height of at least the amount defined. This supersedes smaller static height values. Unlike height, minimum height does not result in content overflow and will allow the height of your element to remain dynamic.', 'et_builder' ),
);
// phpcs:enable
}
return array_merge(
array(
$helper->get_field() => array_merge(
array(
'label' => $i18n['minheight']['label'],
'description' => $i18n['minheight']['description'],
'default' => 'auto',
'default_tablet' => 'auto',
'default_phone' => 'auto',
'allowed_values' => et_builder_get_acceptable_css_string_values( 'min-height' ),
'range_settings' => array(
'min' => 100,
'max' => 1000,
'step' => 1,
),
),
$this->get_base_field()
),
),
$this->responsive_fields( $helper->get_field() )
);
}
private function get_height( $settings ) {
if ( ! $settings['use_height'] ) {
return array();
}
$helper = et_pb_height_options( $settings['prefix'] );
$i18n =& $this->i18n;
if ( ! isset( $i18n['height'] ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
$i18n['height'] = array(
'label' => __( 'Height', 'et_builder' ),
'description' => __( 'This sets a static height value for your element. Once set, the height of the element will no longer be determined by its inner content. Content that exceeds the static height of the element will overflow the element wrapper.', 'et_builder' ),
);
// phpcs:enable
}
return array_merge(
array(
$helper->get_field() => array_merge(
array(
'label' => $i18n['height']['label'],
'description' => $i18n['height']['description'],
'default' => 'auto',
'default_tablet' => 'auto',
'default_phone' => 'auto',
'allowed_values' => et_builder_get_acceptable_css_string_values( 'height' ),
'range_settings' => array(
'min' => 100,
'max' => 1000,
'step' => 1,
),
),
$this->get_base_field()
),
),
$this->responsive_fields( $helper->get_field() )
);
}
private function get_max_height( $settings ) {
if ( ! $settings['use_max_height'] ) {
return array();
}
$helper = et_pb_max_height_options( $settings['prefix'] );
$i18n =& $this->i18n;
if ( ! isset( $i18n['maxheight'] ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
$i18n['maxheight'] = array(
'label' => __( 'Max Height', 'et_builder' ),
'description' => __( 'Setting a maximum height will prevent your element from ever surpassing the defined height value. As your module content increases and surpasses the maximum height, it will overflow the element wrapper.', 'et_builder' ),
);
// phpcs:enable
}
return array_merge(
array(
$helper->get_field() => array_merge(
array(
'label' => $i18n['maxheight']['label'],
'description' => $i18n['maxheight']['description'],
'default' => 'none',
'default_tablet' => 'none',
'default_phone' => 'none',
'allowed_values' => et_builder_get_acceptable_css_string_values( 'max-height' ),
'range_settings' => array(
'min' => 100,
'max' => 1000,
'step' => 1,
),
),
$this->get_base_field()
),
),
$this->responsive_fields( $helper->get_field() )
);
}
private function get_base_field() {
return array(
'type' => 'range',
'hover' => 'tabs',
'default_on_child' => true,
'mobile_options' => true,
'sticky' => true,
'validate_unit' => true,
'unitless' => false,
'default_unit' => 'px',
'allow_empty' => true,
'tab_slug' => 'advanced',
'toggle_slug' => 'width',
);
}
private function responsive_fields( $field ) {
return array(
"{$field}_tablet" => array(
'type' => 'skip',
'tab_slug' => 'advanced',
'toggle_slug' => 'width',
),
"{$field}_phone" => array(
'type' => 'skip',
'tab_slug' => 'advanced',
'toggle_slug' => 'width',
),
"{$field}_last_edited" => array(
'type' => 'skip',
'tab_slug' => 'advanced',
'toggle_slug' => 'width',
),
);
}
}
return new ET_Builder_Module_Field_Height();

View File

@@ -0,0 +1,558 @@
<?php
/**
* Module Margin and Padding class.
*
* This is not how main module margin and padding is generated. Mostly used by other custom options
* group such as Button and Field Input. Doesn't support selective padding yet.
*
* Copy of ET_Builder_Element::process_advanced_custom_margin_options().
*
* @since 3.23
* @since 4.6.0 Add sticky style support
*/
class ET_Builder_Module_Field_MarginPadding extends ET_Builder_Module_Field_Base {
/**
* True when Divi plugin is active.
*
* @since 3.23
*
* @var bool
*/
public $is_plugin_active = false;
/**
* Margin padding properties.
*
* @since 3.23
*
* @var array
*/
public $properties;
/**
* Margin padding config.
*
* @since 3.23
*
* @var array
*/
public $config;
/**
* Constructor.
*/
public function __construct() {
$this->is_plugin_active = et_is_builder_plugin_active();
$this->properties = array(
'custom_padding',
'custom_padding_tablet',
'custom_padding_phone',
'custom_padding_last_edited',
'custom_margin',
'custom_margin_tablet',
'custom_margin_phone',
'custom_margin_last_edited',
'padding_1_last_edited',
'padding_2_last_edited',
'padding_3_last_edited',
'padding_4_last_edited',
);
$this->config = array(
'label' => '',
'prefix' => '',
'tab_slug' => 'advanced',
'toggle_slug' => 'margin_padding',
'sub_toggle' => null,
'option_category' => 'layout',
'depends_show_if' => '',
'depends_show_if_not' => '',
'use_padding' => true,
'use_margin' => true,
'mobile_options' => true,
'sticky' => true,
'hover' => 'tabs',
'custom_padding' => '',
'custom_margin' => '',
'depends_show_if' => 'on',
'priority' => 90,
);
}
/**
* Returns prefixed field names.
*
* @since 3.23
*
* @param string $prefix Prefix.
*
* @return array
*/
public function get_prefixed_field_names( $prefix ) {
$prefix = $prefix ? "{$prefix}_" : '';
return array(
"{$prefix}custom_padding",
"{$prefix}custom_padding_tablet",
"{$prefix}custom_padding_phone",
"{$prefix}custom_padding_last_edited",
"{$prefix}custom_margin",
"{$prefix}custom_margin_tablet",
"{$prefix}custom_margin_phone",
"{$prefix}custom_margin_last_edited",
"{$prefix}padding_1_last_edited",
"{$prefix}padding_2_last_edited",
"{$prefix}padding_3_last_edited",
"{$prefix}padding_4_last_edited",
);
}
/**
* Add selector prefix if needed.
*
* @since 3.23
*
* Custom margin & padding selector for button element. This is custom selector exist on
* ET_Builder_Element. We should do the samething to override hardcoded padding generated
* by button element.
*
* @see ET_Builder_Element::process_advanced_button_options
*/
public function get_prefixed_selector( $css_element, $type = '', $is_divi_builder_plugin = false ) {
// See ET_Builder_Element->process_advanced_button_options() on generating $css_element_processed
// for non Divi Builder Plugin. Explicitly add '.et_pb_section' to the selector so selector
// splitting during prefixing does not incorrectly add third party classes before #et-boc.
if ( 'button' === $type && ! $is_divi_builder_plugin ) {
$css_element = "body #page-container .et_pb_section {$css_element}";
}
return $css_element;
}
/**
* Returns fields definition.
*
* @since 3.23
*
* @param array $args Field configuration.
*
* @return array
*/
public function get_fields( array $args = array() ) {
$fields = array();
$config = wp_parse_args( $args, $this->config );
// Config details.
$tab_slug = $config['tab_slug'];
$toggle_slug = $config['toggle_slug'];
$sub_toggle = $config['sub_toggle'];
list(
$custom_padding,
$custom_padding_tablet,
$custom_padding_phone,
$custom_padding_last_edited,
$custom_margin,
$custom_margin_tablet,
$custom_margin_phone,
$custom_margin_last_edited,
$padding_1_last_edited,
$padding_2_last_edited,
$padding_3_last_edited,
$padding_4_last_edited,
) = $this->get_prefixed_field_names( $config['prefix'] );
// Custom margin.
if ( $config['use_margin'] ) {
$fields[ $custom_margin ] = array(
'label' => sprintf( esc_html__( '%1$s Margin', 'et_builder' ), $config['label'] ),
'description' => esc_html__( 'Margin adds extra space to the outside of the element, increasing the distance between the element and other items on the page.', 'et_builder' ),
'type' => 'custom_margin',
'option_category' => $config['option_category'],
'mobile_options' => $config['mobile_options'],
'hover' => $config['hover'],
'sticky' => $config['sticky'],
'depends_show_if' => $config['depends_show_if'],
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'sub_toggle' => $sub_toggle,
'priority' => $config['priority'],
);
$fields[ $custom_margin_tablet ] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
);
$fields[ $custom_margin_phone ] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
// Make it possible to override/add options.
if ( ! empty( $config['custom_margin'] ) ) {
$fields[ $custom_margin ] = array_merge( $fields[ $custom_margin ], $config['custom_margin'] );
}
$fields[ $custom_margin_last_edited ] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$fields[ $padding_1_last_edited ] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$fields[ $padding_2_last_edited ] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$fields[ $padding_3_last_edited ] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$fields[ $padding_4_last_edited ] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
}
// Custom padding.
if ( $config['use_padding'] ) {
$fields[ $custom_padding ] = array(
'label' => sprintf( esc_html__( '%1$s Padding', 'et_builder' ), $config['label'] ),
'description' => esc_html__( 'Padding adds extra space to the inside of the element, increasing the distance between the edge of the element and its inner contents.', 'et_builder' ),
'type' => 'custom_padding',
'option_category' => $config['option_category'],
'mobile_options' => $config['mobile_options'],
'hover' => $config['hover'],
'sticky' => $config['sticky'],
'depends_show_if' => $config['depends_show_if'],
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'sub_toggle' => $sub_toggle,
'priority' => $config['priority'],
);
if ( isset( $config['depends_on'] ) && '' !== $config['depends_on'] ) {
$fields[ $custom_padding ]['depends_on'] = $config['depends_on'];
}
$fields[ $custom_padding_tablet ] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$fields[ $custom_padding_phone ] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
// Make it possible to override/add options.
if ( ! empty( $config['custom_padding'] ) ) {
$fields[ $custom_padding ] = array_merge( $fields[ $custom_padding ], $config['custom_padding'] );
}
$fields[ $custom_padding_last_edited ] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
}
return $fields;
}
/**
* Adds CSS rule.
*
* @since 3.23
* @since 4.6.0 Add sticky style support
*
* @see ET_Builder_Element->process_advanced_custom_margin_options()
*
* @param ET_Builder_Element $module Module object.
* @param string $prefix Label.
* @param array $options Field settings.
* @param string $function_name Shortcode function.
* @param string $type Margin padding type.
*/
public function update_styles( $module, $prefix, $options, $function_name, $type ) {
$utils = ET_Core_Data_Utils::instance();
$all_values = $module->props;
$hover = et_pb_hover_options();
$sticky = et_pb_sticky_options();
$responsive = ET_Builder_Module_Helper_ResponsiveOptions::instance();
$advanced_fields = $module->advanced_fields;
$css = isset( $this->advanced_fields['margin_padding']['css'] ) ? $this->advanced_fields['margin_padding']['css'] : array();
// Conditional status.
$is_divi_builder_plugin = et_is_builder_plugin_active();
$is_important_set = isset( $options['css']['important'] );
$is_global_important = $is_important_set && 'all' === $options['css']['important'];
$is_use_padding = $utils->array_get( $options, 'use_padding', true );
$is_use_margin = $utils->array_get( $options, 'use_margin', true );
$is_sticky_module = $sticky->is_sticky_module( $all_values );
// Selectors.
$main_selector = ! empty( $options['css']['main'] ) ? $options['css']['main'] : $module->main_css_element;
$limited_selector = ! empty( $options['css']['limited_main'] ) ? $options['css']['limited_main'] : '';
$default_selector = $is_divi_builder_plugin && ! empty( $limited_selector ) ? $limited_selector : $main_selector;
// Get important CSS list.
$important_options = array();
if ( $is_important_set && is_array( $options['css']['important'] ) ) {
$important_options = $options['css']['important'];
}
// A. Padding.
if ( $is_use_padding ) {
// Padding Selectors.
$padding_selector = ! empty( $options['css']['padding'] ) ? $options['css']['padding'] : $default_selector;
$padding_selector_processed = $this->get_prefixed_selector( $padding_selector, $type, $is_divi_builder_plugin );
if ( $is_divi_builder_plugin && ! empty( $limited_selector ) ) {
$padding_selector_processed = $padding_selector;
}
// A.1. Responsive Padding.
$is_padding_responsive = $responsive->is_responsive_enabled( $all_values, "{$prefix}_custom_padding" );
$padding_desktop = $responsive->get_any_value( $all_values, "{$prefix}_custom_padding" );
$padding_tablet = $is_padding_responsive ? $responsive->get_any_value( $all_values, "{$prefix}_custom_padding_tablet" ) : '';
$padding_phone = $is_padding_responsive ? $responsive->get_any_value( $all_values, "{$prefix}_custom_padding_phone" ) : '';
$important = in_array( 'custom_padding', $important_options ) || $is_global_important ? true : false;
$padding_styles = array(
'desktop' => '' !== $padding_desktop ? rtrim( et_builder_get_element_style_css( $padding_desktop, 'padding', $important ) ) : '',
'tablet' => '' !== $padding_tablet ? rtrim( et_builder_get_element_style_css( $padding_tablet, 'padding', $important ) ) : '',
'phone' => '' !== $padding_phone ? rtrim( et_builder_get_element_style_css( $padding_phone, 'padding', $important ) ) : '',
);
$responsive->declare_responsive_css( $padding_styles, $padding_selector_processed, $function_name, $module->get_style_priority() );
// A.2. Hover Padding.
$custom_padding_hover = $hover->get_value( "{$prefix}_custom_padding", $all_values, '' );
if ( '' !== $custom_padding_hover && et_builder_is_hover_enabled( "{$prefix}_custom_padding", $all_values ) ) {
$padding_hover_selector = $utils->array_get( $options, 'css.hover', $hover->add_hover_to_selectors( $padding_selector ) );
$padding_hover_selector_processed = $this->get_prefixed_selector( $padding_hover_selector, $type, $is_divi_builder_plugin );
if ( $is_divi_builder_plugin && ! empty( $limited_selector ) ) {
$padding_hover_selector_processed = $padding_hover_selector;
}
$el_style = array(
'selector' => $padding_hover_selector_processed,
'declaration' => rtrim( et_builder_get_element_style_css( $custom_padding_hover, 'padding', true ) ),
'priority' => 20,
);
ET_Builder_Element::set_style( $function_name, $el_style );
}
// A.3. Sticky Padding.
$custom_padding_sticky = $sticky->get_value( "{$prefix}_custom_padding", $all_values, '' );
if ( '' !== $custom_padding_sticky && $sticky->is_enabled( "{$prefix}_custom_padding", $all_values ) ) {
$padding_sticky_selector = $sticky->add_sticky_to_order_class(
$padding_selector,
$is_sticky_module
);
$padding_sticky_selector_processed = $this->get_prefixed_selector(
$padding_sticky_selector,
$type,
$is_divi_builder_plugin
);
if ( $is_divi_builder_plugin && ! empty( $limited_selector ) ) {
$padding_sticky_selector_processed = $padding_sticky_selector;
}
ET_Builder_Element::set_style(
$function_name,
array(
'selector' => $padding_sticky_selector_processed,
'declaration' => rtrim(
et_builder_get_element_style_css(
$custom_padding_sticky,
'padding',
true
)
),
'priority' => 20,
)
);
}
}
// B. Margin.
if ( $is_use_margin ) {
// Margin Selectors.
$margin_selector = ! empty( $options['css']['margin'] ) ? $options['css']['margin'] : $default_selector;
$margin_selector_processed = $this->get_prefixed_selector( $margin_selector, $type, $is_divi_builder_plugin );
if ( $is_divi_builder_plugin && ! empty( $limited_selector ) ) {
$margin_selector_processed = $margin_selector;
}
// A.1. Responsive margin.
$is_margin_responsive = $responsive->is_responsive_enabled( $all_values, "{$prefix}_custom_margin" );
$margin_desktop = $responsive->get_any_value( $all_values, "{$prefix}_custom_margin" );
$margin_tablet = $is_margin_responsive ? $responsive->get_any_value( $all_values, "{$prefix}_custom_margin_tablet" ) : '';
$margin_phone = $is_margin_responsive ? $responsive->get_any_value( $all_values, "{$prefix}_custom_margin_phone" ) : '';
$important = in_array( 'custom_margin', $important_options ) || $is_global_important ? true : false;
$margin_styles = array(
'desktop' => '' !== $margin_desktop ? rtrim( et_builder_get_element_style_css( $margin_desktop, 'margin', $important ) ) : '',
'tablet' => '' !== $margin_tablet ? rtrim( et_builder_get_element_style_css( $margin_tablet, 'margin', $important ) ) : '',
'phone' => '' !== $margin_phone ? rtrim( et_builder_get_element_style_css( $margin_phone, 'margin', $important ) ) : '',
);
$responsive->declare_responsive_css( $margin_styles, $margin_selector_processed, $function_name, $module->get_style_priority() );
// A.2. Hover margin.
$custom_margin_hover = $hover->get_value( "{$prefix}_custom_margin", $all_values, '' );
if ( '' !== $custom_margin_hover && et_builder_is_hover_enabled( "{$prefix}_custom_margin", $all_values ) ) {
$margin_hover_selector = $utils->array_get( $options, 'css.hover', $hover->add_hover_to_selectors( $margin_selector ) );
$margin_hover_selector_processed = $this->get_prefixed_selector( $margin_hover_selector, $type, $is_divi_builder_plugin );
if ( $is_divi_builder_plugin && ! empty( $limited_selector ) ) {
$margin_hover_selector_processed = $margin_hover_selector;
}
$el_style = array(
'selector' => $margin_hover_selector_processed,
'declaration' => rtrim( et_builder_get_element_style_css( $custom_margin_hover, 'margin', true ) ),
'priority' => 20,
);
ET_Builder_Element::set_style( $function_name, $el_style );
}
// A.3. Hover margin.
$custom_margin_sticky = $sticky->get_value( "{$prefix}_custom_margin", $all_values, '' );
if ( '' !== $custom_margin_sticky && $sticky->is_enabled( "{$prefix}_custom_margin", $all_values ) ) {
$margin_sticky_selector = $sticky->add_sticky_to_order_class(
$margin_selector,
$is_sticky_module
);
$margin_sticky_selector_processed = $this->get_prefixed_selector(
$margin_sticky_selector,
$type,
$is_divi_builder_plugin
);
if ( $is_divi_builder_plugin && ! empty( $limited_selector ) ) {
$margin_sticky_selector_processed = $margin_sticky_selector;
}
ET_Builder_Element::set_style(
$function_name,
array(
'selector' => $margin_sticky_selector_processed,
'declaration' => rtrim(
et_builder_get_element_style_css(
$custom_margin_sticky,
'margin',
true
)
),
'priority' => 20,
)
);
}
}
}
/**
* Process Margin & Padding options and adds CSS rules.
*
* @since 3.23
*
* @param ET_Builder_Element $module Module object.
* @param string $function_name Shortcode function.
*/
public function process_advanced_css( $module, $function_name ) {
$utils = ET_Core_Data_Utils::instance();
$all_values = $module->props;
$advanced_fields = $module->advanced_fields;
// Disable if module doesn't set advanced_fields property and has no VB support.
if ( ! $module->has_vb_support() && ! $module->has_advanced_fields ) {
return;
}
$allowed_advanced_fields = array( 'form_field', 'button', 'image_icon' );
foreach ( $allowed_advanced_fields as $advanced_field ) {
if ( empty( $advanced_fields[ $advanced_field ] ) ) {
continue;
}
foreach ( $advanced_fields[ $advanced_field ] as $label => $form_field ) {
$margin_key = "{$label}_custom_margin";
$padding_key = "{$label}_custom_padding";
$multi_view = et_pb_multi_view_options( $module );
$has_margin = '' !== $utils->array_get( $all_values, $margin_key, '' );
$has_padding = '' !== $utils->array_get( $all_values, $padding_key, '' );
$has_margin_hover = $multi_view->hover_is_enabled( $margin_key );
$has_padding_hover = $multi_view->hover_is_enabled( $padding_key );
$has_padding_sticky = ! empty( et_pb_sticky_options()->get_value( "{$label}_custom_padding", $all_values, '' ) ) && et_pb_sticky_options()->is_enabled( "{$label}_custom_padding", $all_values );
$has_margin_sticky = ! empty( et_pb_sticky_options()->get_value( "{$label}_custom_margin", $all_values, '' ) ) && et_pb_sticky_options()->is_enabled( "{$label}_custom_margin", $all_values );
if ( $has_margin || $has_padding || $has_margin_hover || $has_padding_hover || $has_padding_sticky || $has_margin_sticky ) {
$settings = $utils->array_get( $form_field, 'margin_padding', array() );
// Ensure main selector exists.
$form_field_margin_padding_css = $utils->array_get( $settings, 'css.main', '' );
if ( empty( $form_field_margin_padding_css ) ) {
$utils->array_set( $settings, 'css.main', $utils->array_get( $form_field, 'css.main', '' ) );
}
$this->update_styles( $module, $label, $settings, $function_name, $advanced_field );
}
}
}
}
/**
* Process Margin & Padding options and adds CSS rules.
*
* @since 4.10.0
* @param array $attrs Module attributes.
*/
public function is_used( $attrs ) {
foreach ( $attrs as $attr => $value ) {
if ( ! $value ) {
continue;
}
$is_attr = false !== strpos( $attr, 'custom_margin' )
|| false !== strpos( $attr, 'custom_padding' );
if ( $is_attr ) {
return true;
}
}
return false;
}
}
return new ET_Builder_Module_Field_MarginPadding();

View File

@@ -0,0 +1,214 @@
<?php
class ET_Builder_Module_Field_MaxWidth extends ET_Builder_Module_Field_Base {
/**
* Translations.
*
* @var array
*/
protected $i18n = array();
public function get_defaults() {
return array(
'prefix' => '',
'use_width' => true,
'use_max_width' => true,
'use_module_alignment' => true,
);
}
public function get_fields( array $args = array() ) {
$settings = array_merge( $this->get_defaults(), $args );
return array_merge(
$this->width_fields( $settings ),
$this->max_width_fields( $settings ),
$this->alignment_fields( $settings )
);
}
private function width_fields( $settings ) {
if ( ! $settings['use_width'] ) {
return array();
}
$i18n =& $this->i18n;
if ( ! isset( $i18n['width'] ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
$i18n['width'] = array(
'label' => __( 'Width', 'et_builder' ),
'description' => __( 'By default, elements will extend the full width of their parent element. If you would like to set a custom static width, you can do so using this option.', 'et_builder' ),
);
// phpcs:enable
}
$alignment = new ET_Builder_Module_Helper_Alignment( $settings['prefix'] );
$width = new ET_Builder_Module_Helper_Width( $settings['prefix'] );
$field_name = $width->get_field();
$field = array_merge(
array(
$field_name => array_merge(
array(
'label' => $i18n['width']['label'],
'description' => $i18n['width']['description'],
'default' => 'auto',
'default_tablet' => 'auto',
'allowed_values' => et_builder_get_acceptable_css_string_values( 'width' ),
),
$this->get_base_field()
),
),
$this->responsive_fields( $field_name )
);
if ( $settings['use_module_alignment'] ) {
$field[ $field_name ]['responsive_affects'] = array( $alignment->get_field() );
}
return $field;
}
private function max_width_fields( $settings ) {
if ( ! $settings['use_max_width'] ) {
return array();
}
$i18n =& $this->i18n;
if ( ! isset( $i18n['maxwidth'] ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
$i18n['maxwidth'] = array(
'label' => __( 'Max Width', 'et_builder' ),
'description' => __( 'Setting a maximum width will prevent your element from ever surpassing the defined width value. Maximum width can be used in combination with the standard width setting. Maximum width supersedes the normal width value.', 'et_builder' ),
);
// phpcs:enable
}
$alignment = new ET_Builder_Module_Helper_Alignment( $settings['prefix'] );
$max_width = new ET_Builder_Module_Helper_Max_Width( $settings['prefix'] );
$field_name = $max_width->get_field();
$field = array_merge(
array(
$field_name => array_merge(
array(
'label' => $i18n['maxwidth']['label'],
'description' => $i18n['maxwidth']['description'],
'default' => 'none',
'default_tablet' => 'none',
'allowed_values' => et_builder_get_acceptable_css_string_values( 'max-width' ),
),
$this->get_base_field()
),
),
$this->responsive_fields( $field_name )
);
if ( $settings['use_module_alignment'] ) {
$field[ $field_name ]['responsive_affects'] = array( $alignment->get_field() );
}
return $field;
}
private function alignment_fields( $settings ) {
if ( ! $settings['use_module_alignment'] ) {
return array();
}
$i18n =& $this->i18n;
if ( ! isset( $i18n['alignment'] ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
$i18n['alignment'] = array(
'label' => esc_html__( 'Module Alignment', 'et_builder' ),
'description' => esc_html__( 'Align the module to the left, right or center.', 'et_builder' ),
);
// phpcs:enable
}
$width = new ET_Builder_Module_Helper_Width( $settings['prefix'] );
$max_width = new ET_Builder_Module_Helper_Max_Width( $settings['prefix'] );
$alignment = new ET_Builder_Module_Helper_Alignment( $settings['prefix'] );
$field_name = $alignment->get_field();
$depends = array();
$depends_responsive = array();
$field = array(
'label' => $i18n['alignment']['label'],
'description' => $i18n['alignment']['description'],
'type' => 'align',
'option_category' => 'layout',
'options' => et_builder_get_text_orientation_options( array( 'justified' ) ),
'tab_slug' => 'advanced',
'toggle_slug' => 'width',
'mobile_options' => true,
'show_if_not' => array(
'positioning' => array( 'absolute', 'fixed' ),
),
);
if ( $settings['use_width'] ) {
array_push( $depends, $width->get_field() );
array_push( $depends_responsive, $width->get_field() );
}
if ( $settings['use_max_width'] ) {
array_push( $depends, $max_width->get_field() );
array_push( $depends_responsive, $max_width->get_field() );
}
if ( $settings['use_width'] || $settings['use_max_width'] ) {
$field['depends_show_if_not'] = array( '', '100%', 'auto', 'none' );
}
if ( ! empty( $depends ) ) {
$field['depends_on'] = $depends;
$field['depends_on_responsive'] = $depends_responsive;
}
return array( $field_name => $field );
}
private function get_base_field() {
return array(
'type' => 'range',
'hover' => 'tabs',
'default_on_child' => true,
'mobile_options' => true,
'sticky' => true,
'validate_unit' => true,
'default_unit' => '%',
'allow_empty' => true,
'tab_slug' => 'advanced',
'toggle_slug' => 'width',
'range_settings' => array(
'min' => 0,
'max' => 100,
'step' => 1,
),
);
}
private function responsive_fields( $field ) {
return array(
"{$field}_tablet" => array(
'type' => 'skip',
'tab_slug' => 'advanced',
'toggle_slug' => 'width',
),
"{$field}_phone" => array(
'type' => 'skip',
'tab_slug' => 'advanced',
'toggle_slug' => 'width',
),
"{$field}_last_edited" => array(
'type' => 'skip',
'tab_slug' => 'advanced',
'toggle_slug' => 'width',
),
);
}
}
return new ET_Builder_Module_Field_MaxWidth();

View File

@@ -0,0 +1,85 @@
<?php
class ET_Builder_Module_Field_Overflow extends ET_Builder_Module_Field_Base {
public function get_defaults() {
return array(
'prefix' => '',
'tab_slug' => 'custom_css',
'toggle_slug' => 'visibility',
'hover' => 'tabs',
'mobile_options' => true,
'sticky' => true,
'default' => ET_Builder_Module_Helper_Overflow::OVERFLOW_DEFAULT,
);
}
public function get_fields( array $args = array() ) {
$settings = array_merge( $this->get_defaults(), $args );
return array_merge(
$this->get_field( 'x', $settings ),
$this->get_field( 'y', $settings )
);
}
protected function get_field( $axis, $args ) {
static $i18n;
if ( ! $i18n ) {
$i18n = array(
'default' => __( 'Default', 'et_builder' ),
'visible' => __( 'Visible', 'et_builder' ),
'scroll' => __( 'Scroll', 'et_builder' ),
'hidden' => __( 'Hidden', 'et_builder' ),
'auto' => __( 'Auto', 'et_builder' ),
'description' => __( 'Here you can control element overflow on the %s axis. If set to scroll, content that overflows static widths or heights will trigger a browser scrollbar. If set to hidden, content overflow will be clipped.', 'et_builder' ),
'horizontal' => __( 'Horizontal Overflow', 'et_builder' ),
'vertical' => __( 'Vertical Overflow', 'et_builder' ),
);
}
$overflow = et_pb_overflow();
$OVERFLOW_DEFAULT = ET_Builder_Module_Helper_Overflow::OVERFLOW_DEFAULT;
$OVERFLOW_VISIBLE = ET_Builder_Module_Helper_Overflow::OVERFLOW_VISIBLE;
$OVERFLOW_SCROLL = ET_Builder_Module_Helper_Overflow::OVERFLOW_SCROLL;
$OVERFLOW_HIDDEN = ET_Builder_Module_Helper_Overflow::OVERFLOW_HIDDEN;
$OVERFLOW_AUTO = ET_Builder_Module_Helper_Overflow::OVERFLOW_AUTO;
switch ( $axis ) {
case 'x':
$field = $overflow->get_field_x( $args['prefix'] );
$label = $i18n['horizontal'];
break;
default:
$field = $overflow->get_field_y( $args['prefix'] );
$label = $i18n['vertical'];
break;
}
$settings = array(
'label' => $label,
'type' => 'select',
'hover' => $args['hover'],
'mobile_options' => $args['mobile_options'],
'sticky' => $args['sticky'],
'default' => $args['default'],
'tab_slug' => $args['tab_slug'],
'toggle_slug' => $args['toggle_slug'],
'options' => array(
$OVERFLOW_DEFAULT => $i18n['default'],
$OVERFLOW_VISIBLE => $i18n['visible'],
$OVERFLOW_SCROLL => $i18n['scroll'],
$OVERFLOW_HIDDEN => $i18n['hidden'],
$OVERFLOW_AUTO => $i18n['auto'],
),
'description' => sprintf( $i18n['description'], strtoupper( $axis ) ),
);
$options = array( $field => $settings );
return $options;
}
}
return new ET_Builder_Module_Field_Overflow();

View File

@@ -0,0 +1,722 @@
<?php
class ET_Builder_Module_Field_Position extends ET_Builder_Module_Field_Base {
const TAB_SLUG = 'custom_css';
const TOGGLE_SLUG = 'position_fields';
/**
* @var ET_Builder_Element
*/
private $module;
public function get_fields( array $args = array() ) {
$responsive_options = array();
$additional_options = array();
$skip = array(
'type' => 'skip',
'tab_slug' => self::TAB_SLUG,
'toggle_slug' => self::TOGGLE_SLUG,
);
static $i18n;
if ( ! isset( $i18n ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
$i18n = array(
'positioning' => array(
'description' => esc_html__( 'Here you can choose the element\'s position type. Absolutlely positioned elements will float inside their parent elements. Fixed positioned elements will float within the browser viewport. Relatively positioned elements sit statically in their parent container, but can still be offset without disrupting surrounding elements.', 'et_builder' ),
'options' => array(
'relative' => esc_html__( 'Relative', 'et_builder' ),
'absolute' => esc_html__( 'Absolute', 'et_builder' ),
'fixed' => esc_html__( 'Fixed', 'et_builder' ),
),
),
'origin' => array(
'label' => esc_html__( 'Location', 'et_builder' ),
'description' => esc_html__( 'Here you can adjust the element\'s starting location within its parent container. You can further adjust the element\'s position using the offset controls.', 'et_builder' ),
),
'offset' => array(
'label' => esc_html__( 'Offset Origin ', 'et_builder' ),
'description' => esc_html__( 'Here you can choose from which corner this element is offset from. The vertical and horizontal offset adjustments will be affected based on the element\'s offset origin.', 'et_builder' ),
),
'vertical' => array(
'label' => esc_html__( 'Vertical Offset', 'et_builder' ),
'description' => esc_html__( 'Here you can adjust the element\'s position upwards or downwards from its starting location, which may differ based on its offset origin.', 'et_builder' ),
),
'horizontal' => array(
'label' => esc_html__( 'Horizontal Offset', 'et_builder' ),
'description' => esc_html__( 'Here you can adjust the element\'s position left or right from its starting location, which may differ based on its offset origin.', 'et_builder' ),
),
'zindex' => array(
'label' => esc_html__( 'Z Index', 'et_builder' ),
'description' => esc_html__( 'Here you can control element position on the z axis. Elements with higher z-index values will sit atop elements with lower z-index values.', 'et_builder' ),
),
);
// phpcs:enable
}
if ( ! $args['hide_position_fields'] ) {
$corner_options = array(
'top_left' => et_builder_i18n( 'Top Left' ),
'top_right' => et_builder_i18n( 'Top Right' ),
'bottom_left' => et_builder_i18n( 'Bottom Left' ),
'bottom_right' => et_builder_i18n( 'Bottom Right' ),
);
$center_options = array(
'center_left' => et_builder_i18n( 'Center Left' ),
'center_center' => et_builder_i18n( 'Center Center' ),
'center_right' => et_builder_i18n( 'Center Right' ),
'top_center' => et_builder_i18n( 'Top Center' ),
'bottom_center' => et_builder_i18n( 'Bottom Center' ),
);
$additional_options['positioning'] = array(
'label' => et_builder_i18n( 'Position' ),
'description' => $i18n['positioning']['description'],
'type' => 'select',
'options' => array(
'none' => et_builder_i18n( 'Default' ),
'relative' => $i18n['positioning']['options']['relative'],
'absolute' => $i18n['positioning']['options']['absolute'],
'fixed' => $i18n['positioning']['options']['fixed'],
),
'option_category' => 'layout',
'default' => $args['defaults']['positioning'],
'default_on_child' => true,
'tab_slug' => self::TAB_SLUG,
'toggle_slug' => self::TOGGLE_SLUG,
'mobile_options' => true,
'sticky' => true,
'hover' => 'tabs',
'bb_support' => false,
'linked_responsive' => array( 'position_origin_a', 'position_origin_f', 'position_origin_r' ),
);
// Position origin/location options
$origin_option = array(
'label' => $i18n['origin']['label'],
'description' => $i18n['origin']['description'],
'type' => 'position',
'options' => $corner_options + $center_options,
'option_category' => 'layout',
'default' => $args['defaults']['position_origin'],
'default_on_child' => true,
'tab_slug' => self::TAB_SLUG,
'toggle_slug' => self::TOGGLE_SLUG,
'mobile_options' => true,
'sticky' => true,
'hover' => 'tabs',
'bb_support' => false,
);
// For absolute position
$additional_options['position_origin_a'] = $origin_option;
$additional_options['position_origin_a']['linked_responsive'] = array( 'positioning', 'position_origin_f', 'position_origin_r' );
// For fixed position
$additional_options['position_origin_f'] = $origin_option;
$additional_options['position_origin_f']['linked_responsive'] = array( 'positioning', 'position_origin_a', 'position_origin_r' );
// For relative position
$additional_options['position_origin_r'] = $origin_option;
$additional_options['position_origin_r']['label'] = $i18n['offset']['label'];
$additional_options['position_origin_r']['description'] = $i18n['offset']['description'];
$additional_options['position_origin_r']['options'] = $corner_options;
$additional_options['position_origin_r']['linked_responsive'] = array( 'positioning', 'position_origin_f', 'position_origin_a' );
// Offset options
$offset_option = array(
'type' => 'range',
'range_settings' => array(
'min' => -1000,
'max' => 1000,
'step' => 1,
),
'option_category' => 'layout',
'default_unit' => 'px',
'default_on_child' => true,
'tab_slug' => self::TAB_SLUG,
'toggle_slug' => self::TOGGLE_SLUG,
'responsive' => true,
'mobile_options' => true,
'sticky' => true,
'hover' => 'tabs',
);
$additional_options['vertical_offset'] = $offset_option;
$additional_options['vertical_offset']['default'] = $args['defaults']['vertical_offset'];
$additional_options['vertical_offset']['label'] = $i18n['vertical']['label'];
$additional_options['vertical_offset']['description'] = $i18n['vertical']['description'];
$additional_options['horizontal_offset'] = $offset_option;
$additional_options['horizontal_offset']['default'] = $args['defaults']['horizontal_offset'];
$additional_options['horizontal_offset']['label'] = $i18n['horizontal']['label'];
$additional_options['horizontal_offset']['description'] = $i18n['horizontal']['description'];
$responsive_options += array(
'vertical_offset',
'horizontal_offset',
'position_origin_a',
'position_origin_f',
'position_origin_r',
);
}
if ( ! $args['hide_z_index_fields'] ) {
$additional_options['z_index'] = array(
'label' => $i18n['zindex']['label'],
'description' => $i18n['zindex']['description'],
'type' => 'range',
'range_settings' => array(
'min' => -500,
'max' => 500,
'step' => 1,
),
'option_category' => 'layout',
'default' => $args['defaults']['z_index'],
'default_on_child' => true,
'tab_slug' => self::TAB_SLUG,
'toggle_slug' => self::TOGGLE_SLUG,
'allowed_values' => et_builder_get_acceptable_css_string_values( 'z-index' ),
'unitless' => true,
'hover' => 'tabs',
'sticky' => true,
'responsive' => true,
'mobile_options' => true,
);
$responsive_options += array(
'z_index',
);
}
foreach ( $responsive_options as $option ) {
$additional_options[ "{$option}_tablet" ] = $skip;
$additional_options[ "{$option}_phone" ] = $skip;
$additional_options[ "{$option}_last_edited" ] = $skip;
}
return $additional_options;
}
// Processing functions
/**
* @param object $module Current module to be processed
*/
public function set_module( $module ) {
$this->module = $module;
}
/**
* Interpreter of ET_Builder_Element::get_media_query
*
* @param string $view
*
* @return array
*/
public function get_media_query( $view ) {
$media_query = array();
if ( 'tablet' === $view ) {
$media_query = array(
'media_query' => ET_Builder_Element::get_media_query( 'max_width_980' ),
);
} elseif ( 'phone' === $view ) {
$media_query = array(
'media_query' => ET_Builder_Element::get_media_query( 'max_width_767' ),
);
}
return $media_query;
}
/**
* @param array $attrs
* @param string $name
* @param string $desktopDefault
* @param string $view
*
* @return mixed
*/
private function get_default( $attrs, $name, $desktopDefault = '', $view = 'desktop' ) {
$utils = ET_Core_Data_Utils::instance();
$responsive = ET_Builder_Module_Helper_ResponsiveOptions::instance();
$suffix = in_array( $view, array( 'tablet', 'phone' ) ) ? "_$view" : '';
if ( 'hover' === $view ) {
return $utils->array_get( $attrs, $name, $desktopDefault );
}
return $responsive->get_default_value( $attrs, "$name$suffix", $desktopDefault );
}
/**
* @param array $attrs
* @param string $name
* @param string $default_value
* @param string $view
* @param bool $force_return
*
* @return mixed
*/
private function get_value( $attrs, $name, $default_value = '', $view = 'desktop', $force_return = false ) {
// Sticky style.
if ( 'sticky' === $view ) {
$sticky = et_pb_sticky_options();
return $sticky->get_value( $name, $attrs, $default_value );
}
$utils = ET_Core_Data_Utils::instance();
$responsive = ET_Builder_Module_Helper_ResponsiveOptions::instance();
$hover = et_pb_hover_options();
$is_hover = 'hover' === $view;
$field_device = $is_hover ? 'desktop' : $view;
$field_name = $is_hover ? $hover->get_hover_field( $name ) : $name;
$field_default = $is_hover ? $utils->array_get( $attrs, $name, $default_value ) : $default_value;
return $responsive->get_any_value( $attrs, $field_name, $field_default, $force_return, $field_device );
}
/**
* Get exposed module settings for assisting layout block preview rendering
*
* @since 4.3.2
*
* @param string $function_name
*
* @return null|array
*/
public function get_layout_block_settings( $function_name ) {
$position_fields = et_()->array_get( $this->module->advanced_fields, self::TOGGLE_SLUG, array() );
$has_position_fields = is_array( $position_fields );
// Bail if current module has no position fields
if ( ! $has_position_fields ) {
return;
}
$props = $this->module->props;
$responsive = et_pb_responsive_options();
$hover = et_pb_hover_options();
// Position values
$position = $responsive->is_responsive_enabled( $props, 'positioning' ) ?
$responsive->get_property_values( $props, 'positioning', '', true ) :
array(
'desktop' => $responsive->get_desktop_value( 'positioning', $props ),
);
if ( $hover->is_enabled( 'positioning', $props ) ) {
$position['hover'] = $hover->get_value( 'positioning', $props );
}
// Bail if current module is not fixed positioning on any breakpoint/mode
if ( ! in_array( 'fixed', $position ) ) {
return;
}
// Position fixed origin values
$position_fixed_origin = $responsive->is_responsive_enabled( $props, 'position_origin_f' ) ?
$responsive->get_property_values( $props, 'position_origin_f', '', true ) :
array(
'desktop' => $responsive->get_desktop_value( 'position_origin_f', $props ),
);
if ( $hover->is_enabled( 'position_origin_f', $props ) ) {
$position_fixed_origin['hover'] = $hover->get_value( 'position_origin_f', $props );
}
// Vertical offset
$vertical_offset = $responsive->is_responsive_enabled( $props, 'vertical_offset' ) ?
$responsive->get_property_values( $props, 'vertical_offset', '', true ) :
array(
'desktop' => $responsive->get_desktop_value( 'vertical_offset', $props ),
);
if ( $hover->is_enabled( 'vertical_offset', $props ) ) {
$vertical_offset['hover'] = $hover->get_value( 'vertical_offset', $props );
// Offset rendering relies on origin position. Thus if position origin has
// no hover value, set desktop as hover value to trigger adjustment rendering
if ( ! isset( $position_fixed_origin['hover'] ) ) {
$position_fixed_origin['hover'] = $position_fixed_origin['desktop'];
}
}
// Horizontal offset
$horizontal_offset = $responsive->is_responsive_enabled( $props, 'horizontal_offset' ) ?
$responsive->get_property_values( $props, 'horizontal_offset', '', true ) :
array(
'desktop' => $responsive->get_desktop_value( 'horizontal_offset', $props ),
);
if ( $hover->is_enabled( 'horizontal_offset', $props ) ) {
$horizontal_offset['hover'] = $hover->get_value( 'horizontal_offset', $props );
// Offset rendering relies on origin position. Thus if position origin has
// no hover value, set desktop as hover value to trigger adjustment rendering
if ( ! isset( $position_fixed_origin['hover'] ) ) {
$position_fixed_origin['hover'] = $position_fixed_origin['desktop'];
}
}
// Return current module's position and position origin settings
return array(
'position' => $position,
'position_fixed_origin' => $position_fixed_origin,
'vertical_offset' => $vertical_offset,
'horizontal_offset' => $horizontal_offset,
);
}
/**
* @param string $function_name
*
* @since 4.6.0 Add sticky style support.
*/
public function process( $function_name ) {
$utils = ET_Core_Data_Utils::instance();
$hover = et_pb_hover_options();
$sticky = et_pb_sticky_options();
$responsive = ET_Builder_Module_Helper_ResponsiveOptions::instance();
$position_config = $utils->array_get( $this->module->advanced_fields, self::TOGGLE_SLUG, array() );
$z_index_config = $utils->array_get( $this->module->advanced_fields, 'z_index', array() );
$props = $this->module->props;
$this->module->set_position_locations( array() );
if ( ! is_array( $z_index_config ) && ! is_array( $position_config ) ) {
return;
}
$has_z_index = false;
$has_position = false;
// z_index processing
if ( is_array( $z_index_config ) ) {
$z_index_selector = $utils->array_get( $z_index_config, 'css.main', '%%order_class%%' );
$z_index_default = $utils->array_get( $z_index_config, 'default', '' );
$z_important = $utils->array_get( $z_index_config, 'important', false ) !== false ? ' !important' : '';
$views = array( 'desktop' );
if ( $hover->is_enabled( 'z_index', $props ) ) {
array_push( $views, 'hover' );
}
if ( $responsive->is_responsive_enabled( $props, 'z_index' ) ) {
array_push( $views, 'tablet', 'phone' );
}
// If the module is sticky or inside a sticky module, we need to add z-index for sticky state
// with an `!important` flag to override sticky's default inline z-index: 10000 value when module enters sticky state.
if ( $sticky->is_sticky_module( $props ) || $sticky->is_inside_sticky_module() ) {
array_push( $views, 'sticky' );
}
foreach ( $views as $type ) {
$value = $this->get_value( $props, 'z_index', $z_index_default, $type, false );
if ( 'sticky' === $type ) {
$desktop_value = $this->get_value( $props, 'z_index', $z_index_default, 'desktop', false );
$value = $sticky->get_value( 'z_index', $props, $desktop_value );
$z_important = ' !important';
}
if ( '' !== $value ) {
$type_selector = $z_index_selector;
if ( 'hover' === $type ) {
$type_selector = $hover->add_hover_to_selectors( $z_index_selector );
}
if ( 'sticky' === $type ) {
$type_selector = $sticky->add_sticky_to_selectors( $z_index_selector, $sticky->is_sticky_module( $props ) );
}
$el_style = array(
'selector' => $type_selector,
'declaration' => "z-index: $value$z_important;",
'priority' => $this->module->get_style_priority(),
) + $this->get_media_query( $type );
ET_Builder_Element::set_style( $function_name, $el_style );
$has_z_index = true;
}
}
}
if ( is_array( $position_config ) ) {
$position_selector = $utils->array_get( $position_config, 'css.main', '%%order_class%%' );
$position_default = $utils->array_get( $position_config, 'default', 'none' );
$position_important = $utils->array_get( $position_config, 'important', false ) !== false ? ' !important' : '';
$desktop_origin_value = 'top_left';
$views = array( 'desktop' );
if ( $hover->is_enabled( 'positioning', $props ) ) {
array_push( $views, 'hover' );
}
if ( $responsive->is_responsive_enabled( $props, 'positioning' ) ) {
array_push( $views, 'tablet', 'phone' );
}
if ( $sticky->is_inside_sticky_module() && $sticky->is_enabled( 'positioning', $props ) ) {
array_push( $views, 'sticky' );
}
$position_origins = array();
foreach ( $views as $type ) {
$value = $this->get_value( $props, 'positioning', $position_default, $type, true );
$default_value = $this->get_default( $props, 'positioning', $position_default, $type );
$important = in_array( $value, array( 'fixed', 'absolute' ) ) || ( 'desktop' != $type ) ? ' !important' : $position_important;
$position_value = $value;
$is_parallax_on = 'on' === $this->get_value( $props, 'parallax' ) ? true : false;
$is_divider_set = isset( $props['top_divider_style'] ) || isset( $props['bottom_divider_style'] );
// When parallax or divider is enabled on the element and the position value
// is set to none skip because it should always be relative.
if ( 'none' === $value && ( $is_parallax_on || $is_divider_set ) ) {
continue;
}
if ( 'none' === $value ) {
// none is interpreted as static in FE.
$position_value = 'static';
$important = ' !important';
$position_origins[ $type ] = 'none';
} else {
$suffix = sprintf( '_%s', substr( $value, 0, 1 ) );
$position_origins[ $type ] = $this->get_value( $props, "position_origin$suffix", 'top_left', $type, true );
}
if ( $default_value === $value ) {
$position_origins[ $type ] .= '_is_default';
}
if ( strpos( $position_origins[ $type ], '_is_default' ) === false ) {
$type_selector = 'hover' === $type ? "{$position_selector}:hover" : $position_selector;
$el_style = array(
'selector' => $type_selector,
'declaration' => "position: $position_value$important;",
'priority' => $this->module->get_style_priority(),
) + $this->get_media_query( $type );
ET_Builder_Element::set_style( $function_name, $el_style );
$has_position = true;
}
}
$resp_status = array(
'horizontal' => $responsive->get_responsive_status( $utils->array_get( $props, 'horizontal_offset_last_edited', false ) ),
'vertical' => $responsive->get_responsive_status( $utils->array_get( $props, 'vertical_offset_last_edited', false ) ),
);
$hover_status = array(
'horizontal' => $hover->is_enabled( 'horizontal_offset', $props ),
'vertical' => $hover->is_enabled( 'vertical_offset', $props ),
);
$sticky_status = array(
'horizontal' => $sticky->is_inside_sticky_module() ? $sticky->is_enabled( 'horizontal_offset', $props ) : false,
'vertical' => $sticky->is_inside_sticky_module() ? $sticky->is_enabled( 'vertical_offset', $props ) : false,
);
if ( $resp_status['horizontal'] || $resp_status['vertical'] ) {
if ( ! isset( $position_origins['tablet'] ) ) {
$position_origins['tablet'] = $position_origins['desktop'];
}
if ( ! isset( $position_origins['phone'] ) ) {
$position_origins['phone'] = $position_origins['desktop'];
}
}
if ( ( $hover_status['horizontal'] || $hover_status['vertical'] ) && ! isset( $position_origins['hover'] ) ) {
$position_origins['hover'] = $position_origins['desktop'];
}
if ( ( $sticky_status['horizontal'] || $sticky_status['vertical'] ) && ! isset( $position_origins['sticky'] ) ) {
$position_origins['sticky'] = $position_origins['desktop'];
}
$this->module->set_position_locations( $position_origins );
foreach ( $position_origins as $type => $origin ) {
switch ( $type ) {
case 'hover':
$type_selector = $hover->add_hover_to_selectors( $position_selector );
break;
case 'sticky':
$type_selector = $sticky->add_sticky_to_selectors( $position_selector, false );
break;
default:
$type_selector = $position_selector;
break;
}
$active_origin = $origin;
$is_default_position = false;
$default_strpos = strpos( $origin, '_is_default' );
if ( $default_strpos !== false ) {
$is_default_position = true;
$active_origin = substr( $origin, 0, $default_strpos );
}
if ( 'none' === $active_origin ) {
if ( ! $is_default_position ) {
$el_style = array(
'selector' => $type_selector,
'declaration' => 'top:0px; right:auto; bottom:auto; left:0px;',
'priority' => $this->module->get_style_priority(),
) + $this->get_media_query( $type );
ET_Builder_Element::set_style( $function_name, $el_style );
}
continue;
}
$offsets = array( 'vertical', 'horizontal' );
foreach ( $offsets as $offsetSlug ) {
// phpcs:disable ET.Sniffs.ValidVariableName.InterpolatedVariableNotSnakeCase -- Existing codebase.
$field_slug = "{$offsetSlug}_offset";
$is_hover = 'hover' === $type && $hover_status[ $offsetSlug ];
$is_sticky = 'sticky' === $type;
$is_responsive = in_array( $type, array( 'tablet', 'phone' ) ) && $resp_status[ $offsetSlug ];
$offset_view = $is_hover || $is_sticky || $is_responsive ? $type : 'desktop';
$value = esc_attr( $this->get_value( $props, $field_slug, '0px', $offset_view, true ) );
if (
in_array( $offset_view, array( 'desktop', 'sticky' ), true )
&& $is_default_position
&& 'top_left' === $active_origin
&& '0px' === $value
) {
continue;
}
$origin_array = explode( '_', $active_origin );
$property = 'left';
$inverse_property = 'right';
if ( 'horizontal' === $offsetSlug ) {
if ( 'center' === $origin_array[1] ) {
$value = '50%';
} elseif ( 'right' === $origin_array[1] ) {
$property = 'right';
$inverse_property = 'left';
}
} else {
$property = 'top';
$inverse_property = 'bottom';
if ( 'center' === $origin_array[0] ) {
$value = '50%';
} elseif ( 'bottom' === $origin_array[0] ) {
$property = 'bottom';
$inverse_property = 'top';
}
}
// phpcs:enable ET.Sniffs.ValidVariableName.InterpolatedVariableNotSnakeCase -- Existing codebase.
// add the adminbar height offset to avoid overflow of fixed elements.
$active_position = $this->get_value( $props, 'positioning', $position_default, $type, true );
$has_negative_position = strpos( $props['custom_css_main_element'], 'top: -' ) || strpos( $props['custom_css_main_element'], 'top:-' );
if ( 'top' === $property ) {
$admin_bar_declaration = "$property: $value";
if ( 'fixed' === $active_position ) {
$admin_bar_height = 'phone' === $type ? '46px' : '32px';
$admin_bar_declaration = "$property: calc($value + $admin_bar_height);";
}
if ( ! $has_negative_position && ( 'desktop' !== $type || 'fixed' === $active_position ) ) {
$el_style = array(
'selector' => "body.logged-in.admin-bar $type_selector",
'declaration' => $admin_bar_declaration,
'priority' => $this->module->get_style_priority(),
) + $this->get_media_query( $type );
ET_Builder_Element::set_style( $function_name, $el_style );
}
}
if ( 'top' === $inverse_property && ( 'desktop' !== $type || 'fixed' === $active_position ) ) {
$el_style = array(
'selector' => "body.logged-in.admin-bar $type_selector",
'declaration' => "$inverse_property: auto",
'priority' => $this->module->get_style_priority(),
) + $this->get_media_query( $type );
ET_Builder_Element::set_style( $function_name, $el_style );
}
$el_style = array(
'selector' => $type_selector,
'declaration' => "$property: $value;",
'priority' => $this->module->get_style_priority(),
) + $this->get_media_query( $type );
ET_Builder_Element::set_style( $function_name, $el_style );
$el_style = array(
'selector' => $type_selector,
'declaration' => "$inverse_property: auto;",
'priority' => $this->module->get_style_priority(),
) + $this->get_media_query( $type );
ET_Builder_Element::set_style( $function_name, $el_style );
}
}
}
if ( $has_z_index && ( ! is_array( $position_config ) || ! $has_position ) ) {
// Backwards compatibility. Before this feature if z-index was set, position got defaulted as relative
$el_style = array(
'selector' => '%%order_class%%',
'declaration' => 'position: relative;',
'priority' => $this->module->get_style_priority(),
);
ET_Builder_Element::set_style( $function_name, $el_style );
}
}
/**
* Determine if Position Options are used.
*
* @since 4.10.0
*
* @param array $attrs Module attributes/props.
*
* @return bool
*/
public function is_used( $attrs ) {
foreach ( $attrs as $attr => $value ) {
if ( ! $value ) {
continue;
}
$is_attr = false !== strpos( $attr, 'z_index' )
|| false !== strpos( $attr, 'positioning' )
|| false !== strpos( $attr, 'position_origin' )
|| 'vertical_offset' === $attr
|| 'horizontal_offset' === $attr;
// Ignore default value.
if ( 'positioning' === $attr && 'relative' === $value ) {
continue;
}
// Ignore default value.
if ( ( 'position_origin_a' === $attr || 'position_origin_f' === $attr || 'position_origin_r' === $attr ) && 'top_left' === $value ) {
continue;
}
// Ignore default value.
if ( 'z_index' === $attr && 'auto' === $value ) {
continue;
}
if ( $is_attr ) {
return true;
}
}
return false;
}
}
return new ET_Builder_Module_Field_Position();

View File

@@ -0,0 +1,191 @@
<?php
class ET_Builder_Module_Field_Scroll extends ET_Builder_Module_Field_Base {
/**
* Translations.
*
* @var array
*/
protected $i18n = array();
public function get_defaults() {
$i18n =& $this->i18n;
if ( ! isset( $i18n['defaults'] ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
$i18n['defaults'] = array(
'label' => esc_html__( 'Scroll Transform Effects', 'et_builder' ),
'description' => esc_html__( 'Using Scrolling Effects, you can transform elements on your page as you scroll. The animation\'s transition is based on the user\'s scrolling behavior. Once the element enters the browser viewport (top), the animation begins, and it once it leaves the viewport (bottom), the animation ends.', 'et_builder' ),
);
// phpcs:enable
}
return array(
'prefix' => '',
'label' => $i18n['defaults']['label'],
'description' => $i18n['defaults']['description'],
'tab_slug' => 'custom_css',
'toggle_slug' => 'scroll_effects',
'options' => array(),
);
}
public function get_fields( array $args = array() ) {
$settings = array_merge( $this->get_defaults(), $args );
$prefix = $settings['prefix'];
$grid_support = $settings['grid_support'];
$name = et_builder_add_prefix( $prefix, 'scroll_effects' );
$grid_motion_toggle = array();
$i18n =& $this->i18n;
if ( ! isset( $i18n['grid'] ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
$i18n['grid'] = array(
'label' => esc_html__( 'Apply Motion Effects To Child Elements', 'et_builder' ),
'description' => esc_html__( 'This applies motion effects to individual elements within the module rather than the module as a whole. For example, to each image within a Gallery, rather than the Gallery container.', 'et_builder' ),
);
$i18n['trigger'] = array(
'label' => esc_html__( 'Motion Effect Trigger', 'et_builder' ),
'description' => esc_html__( 'Here you can choose when motion effects are triggered: When the top of the element enters into view, when the middle of the element enters into view, or when the bottom of the element enters into view.', 'et_builder' ),
'options' => array(
'middle' => esc_html__( 'Middle of Element', 'et_builder' ),
'top' => esc_html__( 'Top of Element', 'et_builder' ),
'bottom' => esc_html__( 'Bottom of Element', 'et_builder' ),
),
);
// phpcs:enable
}
if ( 'yes' === $grid_support ) {
$grid_motion_toggle = array(
'enable_grid_motion' => array(
'label' => $i18n['grid']['label'],
'description' => $i18n['grid']['description'],
'type' => 'yes_no_button',
'option_category' => 'configuration',
'options' => array(
'off' => et_builder_i18n( 'No' ),
'on' => et_builder_i18n( 'Yes' ),
),
'default' => 'off',
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
'bb_support' => false,
),
);
}
$motion_triggers = array(
'motion_trigger_start' => array(
'label' => $i18n['trigger']['label'],
'description' => $i18n['trigger']['description'],
'options' => array(
'middle' => $i18n['trigger']['options']['middle'],
'top' => $i18n['trigger']['options']['top'],
'bottom' => $i18n['trigger']['options']['bottom'],
),
'type' => 'select',
'option_category' => 'configuration',
'default' => 'middle',
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
'bb_support' => false,
),
);
return array_merge(
$grid_motion_toggle,
array(
$name => array(
'label' => $settings['label'],
'description' => $settings['description'],
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
'attr_suffix' => '',
'type' => 'composite',
'option_category' => 'layout',
'composite_type' => 'default',
'composite_structure' => $this->get_options( $settings['options'], $settings['tab_slug'], $settings['toggle_slug'], $prefix ),
'bb_support' => false,
),
),
$motion_triggers
);
}
private function get_options( array $options_settings, $tab, $toggle, $prefix = '' ) {
$options = array();
$i18n =& $this->i18n;
if ( ! isset( $i18n['options'] ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
$i18n['options'] = array(
'enable' => __( 'Enable %s', 'et_builder' ),
'set' => __( 'Set %s', 'et_builder' ),
'startTitle' => esc_html__( 'Viewport Bottom', 'et_builder' ),
'endTitle' => esc_html__( 'Viewport Top', 'et_builder' ),
'startValueTitle' => esc_html__( 'Starting', 'et_builder' ),
'middleValueTitle' => esc_html__( 'Mid', 'et_builder' ),
'endValueTitle' => esc_html__( 'Ending', 'et_builder' ),
);
// phpcs:enable
}
foreach ( $options_settings as $name => $settings ) {
$option_name = et_builder_add_prefix( $prefix, $name );
$icon = et_()->array_get( $settings, 'icon', '' );
$label = et_()->array_get( $settings, 'label', '' );
$description = et_()->array_get( $settings, 'description' );
$default = et_()->array_get( $settings, 'default' );
$resolver = et_()->array_get( $settings, 'resolver' );
$options[ $option_name ] = array(
'icon' => $icon,
'tooltip' => $label,
'controls' => array(
"{$option_name}_enable" => array(
'label' => et_esc_html_once( $i18n['options']['enable'], $label ),
'description' => et_esc_html_once( $description ),
'type' => 'yes_no_button',
'option_category' => 'configuration',
'options' => array(
'off' => et_builder_i18n( 'No' ),
'on' => et_builder_i18n( 'Yes' ),
),
'default' => 'off',
'tab_slug' => $tab,
'toggle_slug' => $toggle,
'main_tab_setting' => 'on',
),
$option_name => array(
'label' => et_esc_html_once( $i18n['options']['set'], $label ),
'description' => et_esc_html_once( $description ),
'type' => 'motion',
'default' => $default,
'tab_slug' => $tab,
'toggle_slug' => $toggle,
'mobile_options' => true,
'show_if' => array(
"{$option_name}_enable" => 'on',
),
'resolver' => $resolver,
'i10n' => array(
'startTitle' => $i18n['options']['startTitle'],
'endTitle' => $i18n['options']['endTitle'],
'startValueTitle' => et_()->array_get( $settings, 'startValueTitle', $i18n['options']['startValueTitle'] ),
'middleValueTitle' => et_()->array_get( $settings, 'middleValueTitle', $i18n['options']['middleValueTitle'] ),
'endValueTitle' => et_()->array_get( $settings, 'endValueTitle', $i18n['options']['endValueTitle'] ),
),
),
),
);
}
return $options;
}
}
return new ET_Builder_Module_Field_Scroll();

View File

@@ -0,0 +1,285 @@
<?php
/**
* Field definition for sticky elements feature.
*
* @package Divi
* @sub-package Builder
* @since 4.6.0
*/
/**
* Sticky field class.
*
* @since 4.6.0
*/
class ET_Builder_Module_Field_Sticky extends ET_Builder_Module_Field_Base {
/**
* Default sticky element field attributes (prefix, tab, and toggle).
*
* @var array Default sticky element field attributes.
*/
protected $defaults = array(
'prefix' => 'sticky',
'tab_slug' => 'custom_css',
'toggle_slug' => 'scroll_effects',
);
/**
* Retrieves default settings for sticky fields.
*
* @since 4.6.0
*
* @return array $settings Default settings.
*/
public function get_defaults() {
return $this->defaults;
}
/**
* Retrieves default value of specific field settings
*
* @since 4.6.0
*
* @param string $name Default name.
* @param string $default Default's default value.
*
* @return mixed
*/
public function get_default( $name = '', $default = '' ) {
return et_()->array_get( $this->defaults, $name, $default );
}
/**
* Retrieves fields for sticky settings.
*
* @since 4.6.0
*
* @param array $args Associative array for settings.
*
* @return array $fields Option settings.
*/
public function get_fields( array $args = array() ) {
static $i18n;
// Cache translations.
if ( ! isset( $i18n ) ) {
$i18n = array(
'position' => array(
'label' => esc_html__( 'Sticky Position', 'et_builder' ),
'description' => esc_html__( 'Choose to have this element remain a fixed distance from the top or bottom edge of the browser window as the user scrolls', 'et_builder' ),
'options' => array(
'do_not_sticky' => esc_html__( 'Do Not Stick', 'et_builder' ),
'sticky_to_top' => esc_html__( 'Stick to Top', 'et_builder' ),
'stick_to_bottom' => esc_html__( 'Stick to Bottom', 'et_builder' ),
'stick_to_top_bottom' => esc_html__( 'Stick to Top and Bottom', 'et_builder' ),
),
),
'offset_top' => array(
'label' => esc_html__( 'Sticky Top Offset', 'et_builder' ),
'description' => esc_html__( 'Define the vertical offset distance from the top edge of the browser window', 'et_builder' ),
),
'offset_bottom' => array(
'label' => esc_html__( 'Sticky Bottom Offset', 'et_builder' ),
'description' => esc_html__( 'Define the vertical offset distance from the bottom edge of the browser window', 'et_builder' ),
),
'limit_top' => array(
'label' => esc_html__( 'Top Sticky Limit', 'et_builder' ),
'description' => esc_html__( 'If defined, this element will stick to the top of this container, overriding its stickiness edge of the browser', 'et_builder' ),
),
'limit_bottom' => array(
'label' => esc_html__( 'Bottom Sticky Limit', 'et_builder' ),
'description' => esc_html__( 'If defined, this element will stick to the bottom of this container, overriding its stickiness to the edge of the browser', 'et_builder' ),
),
'offset_surrounding' => array(
'label' => esc_html__( 'Offset From Surrounding Sticky Elements', 'et_builder' ),
'description' => esc_html__( 'Apply “Sticky Offset” to the nearest Sticky Element above or below this element', 'et_builder' ),
),
'transition' => array(
'label' => esc_html__( 'Transition Default and Sticky Styles', 'et_builder' ),
'description' => esc_html__( 'Enabled animated transitions between default and sticky styles when the element becomes “stuck”', 'et_builder' ),
),
);
}
$settings = array_merge( $this->get_defaults(), $args );
$prefix = $settings['prefix'];
return array(
"{$prefix}_position" => array(
'label' => $i18n['position']['label'],
'description' => $i18n['position']['description'],
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
'type' => 'select',
'options' => array(
'none' => $i18n['position']['options']['do_not_sticky'],
'top' => $i18n['position']['options']['sticky_to_top'],
'bottom' => $i18n['position']['options']['stick_to_bottom'],
'top_bottom' => $i18n['position']['options']['stick_to_top_bottom'],
),
'default' => 'none',
'option_category' => 'layout',
'responsive' => true,
'mobile_options' => true,
'bb_support' => false,
),
"{$prefix}_offset_top" => array(
'label' => $i18n['offset_top']['label'],
'description' => $i18n['offset_top']['description'],
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
'type' => 'range',
'range_settings' => array(
'min' => 0,
'max' => 400,
'step' => 1,
),
'default' => '0px',
'show_if_not' => array(
"{$prefix}_position" => array( 'none', 'bottom' ),
),
'responsive' => true,
'mobile_options' => true,
'bb_support' => false,
),
"{$prefix}_offset_bottom" => array(
'label' => $i18n['offset_bottom']['label'],
'description' => $i18n['offset_bottom']['description'],
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
'type' => 'range',
'range_settings' => array(
'min' => 0,
'max' => 400,
'step' => 1,
),
'default' => '0px',
'show_if_not' => array(
"{$prefix}_position" => array( 'none', 'top' ),
),
'responsive' => true,
'mobile_options' => true,
'bb_support' => false,
),
"{$prefix}_limit_top" => array(
'label' => $i18n['limit_top']['label'],
'description' => $i18n['limit_top']['description'],
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
'type' => 'select',
'options' => $this->get_limit_options( $settings['module_slug'] ),
'default' => 'none',
'depends_show_if_not' => array( 'top', 'none' ),
'depends_on' => array(
"{$prefix}_position",
),
'responsive' => true,
'mobile_options' => true,
'bb_support' => false,
),
"{$prefix}_limit_bottom" => array(
'label' => $i18n['limit_bottom']['label'],
'description' => $i18n['limit_bottom']['description'],
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
'type' => 'select',
'options' => $this->get_limit_options( $settings['module_slug'] ),
'default' => 'none',
'depends_show_if_not' => array( 'bottom', 'none' ),
'depends_on' => array(
"{$prefix}_position",
),
'responsive' => true,
'mobile_options' => true,
'bb_support' => false,
),
"{$prefix}_offset_surrounding" => array(
'label' => $i18n['offset_surrounding']['label'],
'description' => $i18n['offset_surrounding']['description'],
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
'type' => 'yes_no_button',
'options' => array(
'on' => et_builder_i18n( 'Yes' ),
'off' => et_builder_i18n( 'No' ),
),
'default' => 'on',
'show_if_not' => array(
"{$prefix}_position" => 'none',
),
'responsive' => true,
'mobile_options' => true,
'bb_support' => false,
),
"{$prefix}_transition" => array(
'label' => $i18n['transition']['label'],
'description' => $i18n['transition']['description'],
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
'type' => 'yes_no_button',
'options' => array(
'on' => et_builder_i18n( 'Yes' ),
'off' => et_builder_i18n( 'No' ),
),
'default' => 'on',
'show_if_not' => array(
"{$prefix}_position" => 'none',
),
'responsive' => true,
'mobile_options' => true,
'bb_support' => false,
),
);
}
/**
* Get limit position options based on Element Type.
*
* @since 4.6.0
*
* @param string $module_slug Module Slug.
*
* @return array $options Limit options.
*/
private function get_limit_options( $module_slug ) {
static $i18n;
// Cache translations.
if ( ! isset( $i18n ) ) {
$i18n = array(
'none' => esc_html__( 'None', 'et_builder' ),
'body' => esc_html__( 'Body Area', 'et_builder' ),
'section' => esc_html__( 'Section', 'et_builder' ),
'row' => esc_html__( 'Row', 'et_builder' ),
'column' => esc_html__( 'Column', 'et_builder' ),
);
}
$options = array(
'none' => $i18n['none'],
'body' => $i18n['body'],
'section' => $i18n['section'],
'row' => $i18n['row'],
'column' => $i18n['column'],
);
if ( 'et_pb_column' === $module_slug ) {
unset( $options['column'] );
}
if ( 'et_pb_row' === $module_slug ) {
unset( $options['column'] );
unset( $options['row'] );
}
if ( 'et_pb_section' === $module_slug ) {
unset( $options['column'] );
unset( $options['row'] );
unset( $options['section'] );
}
return $options;
}
}
return new ET_Builder_Module_Field_Sticky();

View File

@@ -0,0 +1,777 @@
<?php
class ET_Builder_Module_Field_TextShadow extends ET_Builder_Module_Field_Base {
/**
* True when Divi plugin is active.
*
* @var bool
*/
public $is_plugin_active = false;
/**
* Text shadow properties.
*
* @var array
*/
public $properties;
protected $template;
/**
* Constructor.
*/
public function __construct() {
$this->template = et_pb_option_template();
$this->is_plugin_active = et_is_builder_plugin_active();
$this->properties = array(
'horizontal_length',
'vertical_length',
'blur_strength',
'color',
);
$this->set_template();
}//end __construct()
/**
* Returns prefixed field names.
*
* @param string $prefix Prefix.
*
* @return array
*/
public function get_prefixed_field_names( $prefix ) {
$prefix = $prefix ? "{$prefix}_" : '';
return array(
"{$prefix}text_shadow_style",
"{$prefix}text_shadow_horizontal_length",
"{$prefix}text_shadow_vertical_length",
"{$prefix}text_shadow_blur_strength",
"{$prefix}text_shadow_color",
);
}//end get_prefixed_field_names()
/**
* Returns Text Shadow presets.
*
* @param string $prefix Prefix.
*
* @return array
*/
public function get_presets( $prefix, $suffix = '' ) {
list(
$text_shadow_style,
$text_shadow_horizontal_length,
$text_shadow_vertical_length,
$text_shadow_blur_strength,
$text_shadow_color
) = $this->get_prefixed_field_names( $prefix );
return array(
array(
'icon' => 'none',
'value' => 'none',
),
array(
'value' => 'preset1',
'content' => array(
'content' => 'aA',
'class' => 'preset preset1',
),
'fields' => array(
$text_shadow_horizontal_length => '0em',
$text_shadow_vertical_length => '0.1em',
$text_shadow_blur_strength => '0.1em',
),
),
array(
'value' => 'preset2',
'content' => array(
'content' => 'aA',
'class' => 'preset preset2',
),
'fields' => array(
$text_shadow_horizontal_length => '0.08em',
$text_shadow_vertical_length => '0.08em',
$text_shadow_blur_strength => '0.08em',
),
),
array(
'value' => 'preset3',
'content' => array(
'content' => 'aA',
'class' => 'preset preset3',
),
'fields' => array(
$text_shadow_horizontal_length => '0em',
$text_shadow_vertical_length => '0em',
$text_shadow_blur_strength => '0.3em',
),
),
array(
'value' => 'preset4',
'content' => array(
'content' => 'aA',
'class' => 'preset preset4',
),
'fields' => array(
$text_shadow_horizontal_length => '0em',
$text_shadow_vertical_length => '0.08em',
$text_shadow_blur_strength => '0em',
),
),
array(
'value' => 'preset5',
'content' => array(
'content' => 'aA',
'class' => 'preset preset5',
),
'fields' => array(
$text_shadow_horizontal_length => '0.08em',
$text_shadow_vertical_length => '0.08em',
$text_shadow_blur_strength => '0em',
),
),
);
}//end get_presets()
/**
* Returns conditional defaults array.
*
* @param string $prefix Prefix.
* @param string $depend Field whose value controls which default should be used.
* @param string $field Field for which we're generating the defaults array.
* @param string $default Default value to be used when a Preset doesn't include a value for $field.
*
* @return array
*/
public function get_defaults( $prefix, $depend, $field, $default ) {
$presets = $this->get_presets( $prefix );
$defaults = array();
foreach ( $presets as $preset ) {
$value = $preset['value'];
$defaults[ $value ] = isset( $preset['fields'][ $field ] ) ? $preset['fields'][ $field ] : $default;
}
return array(
$depend,
$defaults,
);
}//end get_defaults()
/**
* Set option template for Text Shadow
*
* @since 3.28
*
* @return void
*/
public function set_template() {
$template = $this->template;
if ( $template->is_enabled() && ! $template->has( 'text_shadow' ) ) {
$template_placeholder = $template->placeholders(
array(
'label' => null,
'prefix' => null,
'tab_slug' => null,
'toggle_slug' => null,
'sub_toggle' => null,
'option_category' => null,
'depends_show_if' => null,
'depends_show_if_not' => null,
'show_if' => null,
'show_if_not' => null,
)
);
$template->add( 'text_shadow', $this->get_fields( $template_placeholder ) );
}
}
/**
* Returns fields definition.
*
* @since 3.23 Add mobile_options attributes for all fields to support responsive settings, except
* text_shadow_style. Add allowed units for some fields with range type.
*
* @param array $args Field configuration.
*
* @return array
*/
public function get_fields( array $args = array() ) {
$config = shortcode_atts(
array(
'label' => '',
'prefix' => '',
'tab_slug' => 'advanced',
'toggle_slug' => 'text',
'sub_toggle' => false,
'option_category' => 'configuration',
'depends_show_if' => '',
'depends_show_if_not' => '',
'show_if' => '',
'show_if_not' => '',
),
$args
);
if ( $this->template->is_enabled() && $this->template->has( 'text_shadow' ) ) {
return $this->template->create( 'text_shadow', $config );
}
$prefix = $config['prefix'];
list(
$text_shadow_style,
$text_shadow_horizontal_length,
$text_shadow_vertical_length,
$text_shadow_blur_strength,
$text_shadow_color
) = $this->get_prefixed_field_names( $prefix );
$tab_slug = $config['tab_slug'];
$toggle_slug = $config['toggle_slug'];
$sub_toggle = $config['sub_toggle'];
$option_category = $config['option_category'];
// Some option categories (like font) have custom logic that involves changing default values and we don't want that to interfere with conditional defaults. This might change in future so, for now, I'm just overriding the value while leaving the possibility to remove this line afterwards and provide custom option_category via $config.
$option_category = 'configuration';
$label = $config['label'];
if ( $label ) {
$labels = array(
// translators: text shadow group label
sprintf( esc_html__( '%1$s Text Shadow', 'et_builder' ), $label ),
// translators: text shadow group label
sprintf( esc_html__( '%1$s Text Shadow Horizontal Length', 'et_builder' ), $label ),
// translators: text shadow group label
sprintf( esc_html__( '%1$s Text Shadow Vertical Length', 'et_builder' ), $label ),
// translators: text shadow group label
sprintf( esc_html__( '%1$s Text Shadow Blur Strength', 'et_builder' ), $label ),
// translators: text shadow group label
sprintf( esc_html__( '%1$s Text Shadow Color', 'et_builder' ), $label ),
);
} else {
$labels = array(
esc_html__( 'Text Shadow', 'et_builder' ),
esc_html__( 'Text Shadow Horizontal Length', 'et_builder' ),
esc_html__( 'Text Shadow Vertical Length', 'et_builder' ),
esc_html__( 'Text Shadow Blur Strength', 'et_builder' ),
esc_html__( 'Text Shadow Color', 'et_builder' ),
);
}
$fields = array(
$text_shadow_style => array(
'label' => $labels[0],
'description' => esc_html__( 'Pick a text shadow style to enable text shadow for this element. Once enabled, you will be able to customize your text shadow style further. To disable custom text shadow style, choose the None option.', 'et_builder' ),
'type' => 'presets_shadow',
'option_category' => $option_category,
'default' => 'none',
'default_on_child' => true,
'presets' => $this->get_presets( $prefix ),
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'sync_affects' => array(
$text_shadow_horizontal_length,
$text_shadow_vertical_length,
$text_shadow_blur_strength,
$text_shadow_color,
),
'affects' => array(
$text_shadow_horizontal_length,
$text_shadow_vertical_length,
$text_shadow_blur_strength,
$text_shadow_color,
),
'copy_with' => array(
$text_shadow_horizontal_length,
$text_shadow_vertical_length,
$text_shadow_blur_strength,
$text_shadow_color,
),
),
$text_shadow_horizontal_length => array(
'label' => $labels[1],
'description' => esc_html__( 'Shadow\'s horizontal distance from the text. A negative value places the shadow to the left of the text.', 'et_builder' ),
'type' => 'range',
'hover' => 'tabs',
'option_category' => $option_category,
'range_settings' => array(
'min' => -2,
'max' => 2,
'step' => 0.01,
),
'default' => $this->get_defaults( $prefix, $text_shadow_style, $text_shadow_horizontal_length, '0em' ),
'default_on_child' => true,
'hide_sync' => true,
'validate_unit' => true,
'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ),
'default_unit' => 'em',
'fixed_range' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if_not' => 'none',
'mobile_options' => true,
'sticky' => true,
),
$text_shadow_vertical_length => array(
'label' => $labels[2],
'description' => esc_html__( 'Shadow\'s vertical distance from the text. A negative value places the shadow above the text.', 'et_builder' ),
'type' => 'range',
'hover' => 'tabs',
'option_category' => $option_category,
'range_settings' => array(
'min' => -2,
'max' => 2,
'step' => 0.01,
),
'default' => $this->get_defaults( $prefix, $text_shadow_style, $text_shadow_vertical_length, '0em' ),
'default_on_child' => true,
'hide_sync' => true,
'validate_unit' => true,
'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ),
'default_unit' => 'em',
'fixed_range' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if_not' => 'none',
'mobile_options' => true,
'sticky' => true,
),
$text_shadow_blur_strength => array(
'label' => $labels[3],
'description' => esc_html__( 'The higher the value, the bigger the blur, the shadow becomes wider and lighter.', 'et_builder' ),
'type' => 'range',
'hover' => 'tabs',
'option_category' => $option_category,
'range_settings' => array(
'min' => 0,
'max' => 2,
'step' => 0.01,
),
'default' => $this->get_defaults( $prefix, $text_shadow_style, $text_shadow_blur_strength, '0em' ),
'default_on_child' => true,
'hide_sync' => true,
'validate_unit' => true,
'allowed_units' => array( 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw' ),
'default_unit' => 'em',
'fixed_range' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if_not' => 'none',
'mobile_options' => true,
'sticky' => true,
),
$text_shadow_color => array(
'label' => $labels[4],
'description' => esc_html__( 'The color of the shadow.', 'et_builder' ),
'type' => 'color-alpha',
'hover' => 'tabs',
'option_category' => $option_category,
'default' => 'rgba(0,0,0,0.4)',
'default_on_child' => true,
'hide_sync' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if_not' => 'none',
'mobile_options' => true,
'sticky' => true,
),
);
// Only add sub_toggle to fields if defined
if ( false !== $sub_toggle ) {
$fields[ $text_shadow_style ]['sub_toggle'] = $sub_toggle;
$fields[ $text_shadow_vertical_length ]['sub_toggle'] = $sub_toggle;
$fields[ $text_shadow_horizontal_length ]['sub_toggle'] = $sub_toggle;
$fields[ $text_shadow_blur_strength ]['sub_toggle'] = $sub_toggle;
$fields[ $text_shadow_color ]['sub_toggle'] = $sub_toggle;
}
// add conditional settings if defined
if ( '' !== $config['show_if'] ) {
$fields[ $text_shadow_style ]['show_if'] = $config['show_if'];
}
if ( '' !== $config['show_if_not'] ) {
$fields[ $text_shadow_style ]['show_if_not'] = $config['show_if_not'];
}
if ( '' !== $config['depends_show_if'] ) {
$fields[ $text_shadow_style ]['depends_show_if'] = $config['depends_show_if'];
}
if ( '' !== $config['depends_show_if_not'] ) {
$fields[ $text_shadow_style ]['depends_show_if_not'] = $config['depends_show_if_not'];
}
return $fields;
}//end get_fields()
/**
* Returns whether a declaration should be added !important or not.
*
* @param array $options Field definition.
* @param string $key Property name.
*
* @return bool
*/
public function get_important( $options, $key = false ) {
if ( ! isset( $options['css']['important'] ) ) {
// nothing to do, bye
return false;
}
$important = $options['css']['important'];
if ( 'all' === $important || ( $this->is_plugin_active && 'plugin_only' === $important ) ) {
return true;
}
if ( is_array( $important ) ) {
if ( $this->is_plugin_active && in_array( 'plugin_all', $important ) ) {
return true;
}
if ( false !== $key && in_array( $key, $important ) ) {
return true;
}
}
return false;
}//end get_important()
/**
* Returns the text-shadow declaration
*
* @since 3.23 Add responsive settings support to get the correct tablet and phone values.
*
* @param string $label Prefix.
* @param bool $important Whether to add !important or not.
* @param array $all_values All shortcode values.
* @param bool $is_hover Hover status.
* @param string $device Current active device.
*
* @return string
*/
public function get_declaration( $label, $important, $all_values, $is_hover = false, $device = 'desktop' ) {
$prefix = $label ? "{$label}_" : '';
$hover = et_pb_hover_options();
$sticky = et_pb_sticky_options();
$utils = ET_Core_Data_Utils::instance();
$responsive = ET_Builder_Module_Helper_ResponsiveOptions::instance();
$is_desktop = 'desktop' === $device;
$is_sticky = $sticky->get_suffix() === $device;
$suffix = '';
// Responsive styles. Ensure to render when at least one of the fields activate responsive
// settings to avoid unnecessary CSS styles rendered.
$is_any_shadow_responsive = false;
if ( ! $is_desktop && ! $is_hover && ! $is_sticky ) {
$is_any_shadow_responsive = $responsive->is_any_responsive_enabled(
$all_values,
array(
"{$prefix}text_shadow_horizontal_length",
"{$prefix}text_shadow_vertical_length",
"{$prefix}text_shadow_blur_strength",
"{$prefix}text_shadow_color",
)
);
// Bail early
if ( ! $is_any_shadow_responsive ) {
return '';
}
$suffix = "_{$device}";
}
$text_shadow = array();
foreach ( $this->properties as $property ) {
// As default, we will return desktop value.
$prop = "{$prefix}text_shadow_{$property}";
$value = et_()->array_get( $all_values, $prop, '' );
if ( $is_any_shadow_responsive ) {
// If current device is mobile (responsive settings is enabled already checked above),
// return any value exist.
$value = $responsive->is_responsive_enabled( $all_values, $prop ) ? $responsive->get_any_value( $all_values, "{$prop}{$suffix}", '', true ) : $value;
} elseif ( $is_hover ) {
$value = $hover->get_value( $prop, $all_values, $value );
} elseif ( $is_sticky ) {
$value = $sticky->get_value( $prop, $all_values, $value );
}
$text_shadow[] = $value;
}
return sprintf(
'text-shadow: %s%s;',
et_core_esc_previously( join( ' ', array_filter( $text_shadow ) ) ),
$important ? '!important' : ''
);
}//end get_declaration()
/**
* Adds CSS rule.
*
* @since 3.23 Add responsive settings support to render tablet and phone styles.
*
* @param ET_Builder_Element $module Module object.
* @param string $label Label.
* @param array $font Field definition.
* @param string $function_name Shortcode function.
* @param bool $is_hover Hover status.
* @param string $device Current active device.
*
* @return void
*/
public function update_styles( $module, $label, $font, $function_name, $is_hover = false, $device = 'desktop' ) {
$all_values = $module->props;
$main_element_selector = $module->main_css_element;
$device = '' === $device ? 'desktop' : $device;
$hover = et_pb_hover_options();
$sticky = et_pb_sticky_options();
$is_sticky = $device === $sticky->get_suffix();
// Use a different selector for plugin
$css_element = $this->is_plugin_active && isset( $font['css']['limited_main'] ) ? 'css.limited_main' : 'css.main';
// Use 'text_shadow' selector if defined, fallback to $css_element or default selector
$selector = et_()->array_get(
$font,
'css.text_shadow',
et_()->array_get( $font, $css_element, $main_element_selector )
);
$responsive_selector = $selector;
if ( $is_hover ) {
if ( is_array( $selector ) ) {
$selector = array_map( array( $this, 'add_hover_to_selectors' ), $selector );
} else {
$selector = $hover->add_hover_to_selectors( $selector );
}
$selector = et_()->array_get( $font, 'css.text_shadow_hover', et_()->array_get( $font, 'css.hover', $selector ) );
}
if ( $is_sticky ) {
$has_wrapper = et_()->array_get( $module->wrapper_settings, 'order_class_wrapper', false );
$is_sticky_module_without_wrapper = $has_wrapper ? false : $sticky->is_sticky_module( $all_values );
$selector = $sticky->add_sticky_to_selectors( $selector, $is_sticky_module_without_wrapper, is_string( $selector ) );
$selector = et_()->array_get( $font, 'css.text_shadow_sticky', et_()->array_get( $font, 'css.sticky', $selector ) );
}
// Get the text-shadow declaration (horizontal vertical blur color).
$declaration = $this->get_declaration(
$label,
$this->get_important( $font, 'text-shadow' ),
$all_values,
$is_hover,
$device
);
// Do not provide hover or sticky style if it is the same as normal style.
if ( $is_hover || $is_sticky ) {
$normal = $this->get_declaration(
$label,
$this->get_important( $font, 'text-shadow' ),
$all_values,
false
);
if ( $declaration === $normal ) {
return;
}
}
// Media query.
$media_query = array();
if ( 'desktop' !== $device && ! $is_hover && ! $is_sticky ) {
$breakpoint = 'tablet' === $device ? 'max_width_980' : 'max_width_767';
$media_query = array( 'media_query' => ET_Builder_Element::get_media_query( $breakpoint ) );
}
if ( is_array( $selector ) ) {
foreach ( $selector as $single_selector ) {
ET_Builder_Element::set_style(
$function_name,
array_merge(
array(
'selector' => $single_selector,
'declaration' => $declaration,
'priority' => $module->get_style_priority(),
),
$media_query
)
);
}
} else {
ET_Builder_Element::set_style(
$function_name,
array_merge(
array(
'selector' => $selector,
'declaration' => $declaration,
'priority' => $module->get_style_priority(),
),
$media_query
)
);
}
}//end update_styles()
/**
* Added to fix array_map can't access static class of Hover Options.
*
* @since 3.23
*
* @param string $selector Current selector.
*
* @return string Updated selector with hover suffix.
*/
private function add_hover_to_selectors( $selector ) {
return et_pb_hover_options()->add_hover_to_selectors( $selector );
}
/**
* Process Text Shadow options and adds CSS rules.
*
* @since 4.6.0 Add sticky style support
*
* @param ET_Builder_Element $module Module object.
* @param string $function_name Shortcode function.
*
* @return void
*/
public function process_advanced_css( $module, $function_name ) {
$all_values = $module->props;
$advanced_fields = $module->advanced_fields;
$hover = et_pb_hover_options();
$sticky = et_pb_sticky_options();
// Disable if module doesn't set advanced_fields property and has no VB support
if ( ! $module->has_vb_support() && ! $module->has_advanced_fields ) {
return;
}
$suffixes = array( '', 'tablet', 'phone', $hover->get_suffix(), $sticky->get_suffix() );
foreach ( $suffixes as $suffix ) {
$is_hover = $hover->get_suffix() === $suffix;
$is_sticky = $sticky->get_suffix() === $suffix;
// Check for text shadow settings in font-options
if ( ! empty( $advanced_fields['fonts'] ) ) {
// We have a 'fonts' section, fetch its values
foreach ( $advanced_fields['fonts'] as $label => $font ) {
// label can be header / body / toggle / etc
$shadow_style = "{$label}_text_shadow_style";
if ( 'none' !== et_()->array_get( $all_values, $shadow_style, 'none' ) ) {
// We have a preset selected which isn't none, need to add text-shadow style
$this->update_styles( $module, $label, $font, $function_name, $is_hover, $suffix );
}
}
}
// Check for text shadow settings in Advanced/Text toggle
if ( isset( $advanced_fields['text'] ) && 'none' !== et_()->array_get( $all_values, 'text_shadow_style', 'none' ) ) {
// We have a preset selected which isn't none, need to add text-shadow style
$text = $advanced_fields['text'];
$this->update_styles( $module, '', $text, $function_name, $is_hover, $suffix );
}
// Check for text shadow settings in Advanced/Fields toggle
if ( isset( $advanced_fields['fields'] ) && 'none' !== et_()->array_get( $all_values, 'fields_text_shadow_style', 'none' ) ) {
// We have a preset selected which isn't none, need to add text-shadow style
$fields = $advanced_fields['fields'];
$this->update_styles( $module, 'fields', $fields, $function_name, $is_hover, $suffix );
}
// Check for text shadow settings in Advanced/Button toggle
if ( ! empty( $advanced_fields['button'] ) ) {
// We have a 'button' section, fetch its values
foreach ( $advanced_fields['button'] as $label => $button ) {
// label can be header / body / toggle / etc
$shadow_style = "{$label}_text_shadow_style";
if ( 'none' !== et_()->array_get( $all_values, $shadow_style, 'none' ) ) {
// We have a preset selected which isn't none, need to add text-shadow style
// Build a selector to only target the button
$css_element = et_()->array_get( $button, 'css.main', "{$module->main_css_element} .et_pb_button" );
// Make sure it has highest priority
et_()->array_set( $button, 'css.text_shadow', $css_element );
if ( ! isset( $button['css.hover'] ) ) {
et_()->array_set( $button, 'css.hover', $hover->add_hover_to_selectors( $css_element ) );
}
$this->update_styles( $module, $label, $button, $function_name, $is_hover, $suffix );
}
}
}
// Check for text shadow settings in Advanced/Fields Input toggle
if ( ! empty( $advanced_fields['form_field'] ) ) {
// There are possibilities to have more than one field inputs.
foreach ( $advanced_fields['form_field'] as $label => $form_field ) {
// Ensure the text shadow style is selected before updating the styles.
if ( 'none' !== et_()->array_get( $all_values, $label . '_text_shadow_style', 'none' ) ) {
// Build a selector to only target the field input.
$main_selector = isset( $form_field['css']['main'] ) ? $form_field['css']['main'] : "{$module->main_css_element} .input";
$text_shadow_selector = isset( $form_field['css']['text_shadow'] ) ? $form_field['css']['text_shadow'] : $main_selector;
$text_shadow_hover_selector = isset( $form_field['css']['text_shadow_hover'] ) ? $form_field['css']['text_shadow_hover'] : $hover->add_hover_to_selectors( $text_shadow_selector );
// Make sure it has highest priority.
$form_field['css']['text_shadow'] = $text_shadow_selector;
$form_field['css']['text_shadow_hover'] = $text_shadow_hover_selector;
// Check and override important status.
if ( ! empty( $form_field['css']['important'] ) ) {
$form_field_important = $form_field['css']['important'];
if ( ! empty( $form_field_important['font'] ) ) {
$form_field['css']['important'] = $form_field_important['font'];
}
if ( ! empty( $form_field_important['text_shadow'] ) ) {
$form_field['css']['important'] = $form_field_important['text_shadow'];
}
}
$this->update_styles( $module, $label, $form_field, $function_name, $is_hover, $suffix );
}
}
}
}
}//end process_advanced_css()
/**
* Determine if Text Shadow is used.
*
* @since 4.10.0
*
* @param array $attrs Module attributes/props.
*
* @return bool
*/
public function is_used( $attrs ) {
foreach ( $attrs as $attr => $value ) {
if ( ! $value ) {
continue;
}
$is_attr = false !== strpos( $attr, 'text_shadow_' );
if ( $is_attr ) {
return true;
}
}
return false;
}
}
return new ET_Builder_Module_Field_TextShadow();

View File

@@ -0,0 +1,654 @@
<?php
class ET_Builder_Module_Field_Transform extends ET_Builder_Module_Field_Base {
private $processing_props = array();
public $defaults = array(
'scale' => '100%',
'translate' => '0px',
'rotate' => '0deg',
'skew' => '0deg',
'origin' => '50%',
);
public $allTransforms = array(
'scaleX',
'scaleY',
'translateX',
'translateY',
'rotateX',
'rotateY',
'rotateZ',
'skewX',
'skewY',
'originX',
'originY',
);
public function get_fields( array $args = array() ) {
static $i18n;
if ( ! isset( $i18n ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
$i18n = array(
'scale' => array(
'label' => esc_html__( 'Transform Scale', 'et_builder' ),
),
'translate' => array(
'label' => esc_html__( 'Transform Translate', 'et_builder' ),
),
'rotate' => array(
'label' => esc_html__( 'Transform Rotate', 'et_builder' ),
),
'skew' => array(
'label' => esc_html__( 'Transform Skew', 'et_builder' ),
),
'origin' => array(
'label' => esc_html__( 'Transform Origin', 'et_builder' ),
),
'styles' => array(
'label' => esc_html__( 'Transform', 'et_builder' ),
'description' => esc_html__( 'Using the transform controls, you can performance visual adjustments to any element using a combination of Scale, Translation, Rotation and Skew settings. This allows you to create advanced design effects without the need of a separate graphic design program.', 'et_builder' ),
),
);
// phpcs:enable
}
$settings_args = array(
'option_category' => 'layout',
'tab_slug' => 'advanced',
'toggle_slug' => 'transform',
'depends_on' => null,
'depends_show_if' => null,
'defaults' => $this->defaults,
);
$settings = wp_parse_args( $settings_args, $args );
$additional_options = array();
$defaults = $settings['defaults'];
$tabs = array(
'scale' => array(
'icon' => 'resize',
'controls' => array(
'transform_scale' => array(
'type' => 'transform',
'label' => $i18n['scale']['label'],
'default' => "{$defaults['scale']}|{$defaults['scale']}",
'default_unit' => '%',
'range_settings' => array(
'min' => -100,
'max' => 300,
'step' => 1,
),
'context' => 'transform_styles',
'mobile_options' => true,
'sticky' => true,
),
),
),
'translate' => array(
'icon' => 'move',
'controls' => array(
'transform_translate' => array(
'type' => 'transform',
'label' => $i18n['translate']['label'],
'default' => "{$defaults['translate']}|{$defaults['translate']}",
'default_unit' => 'px',
'range_settings' => array(
'min' => -300,
'max' => 300,
'step' => 1,
),
'context' => 'transform_styles',
'mobile_options' => true,
'sticky' => true,
),
),
),
'rotate' => array(
'icon' => 'rotate',
'controls' => array(
'transform_rotate' => array(
'type' => 'transform',
'label' => $i18n['rotate']['label'],
'default' => "{$defaults['rotate']}|{$defaults['rotate']}|{$defaults['rotate']}",
'default_unit' => 'deg',
'range_settings' => array(
'min' => 0,
'max' => 360,
'step' => 1,
),
'context' => 'transform_styles',
'mobile_options' => true,
'sticky' => true,
),
),
),
'skew' => array(
'icon' => 'skew',
'controls' => array(
'transform_skew' => array(
'type' => 'transform',
'label' => $i18n['skew']['label'],
'default' => "{$defaults['skew']}|{$defaults['skew']}",
'default_unit' => 'deg',
'range_settings' => array(
'min' => -180,
'max' => 180,
'min_limit' => -180,
'max_limit' => 180,
'step' => 1,
),
'context' => 'transform_styles',
'mobile_options' => true,
'sticky' => true,
),
),
),
'origin' => array(
'icon' => 'transform-origin',
'controls' => array(
'transform_origin' => array(
'type' => 'transform',
'label' => $i18n['origin']['label'],
'default' => "{$defaults['origin']}|{$defaults['origin']}",
'default_unit' => '%',
'range_settings' => array(
'min' => -50,
'max' => 150,
'step' => 1,
),
'context' => 'transform_styles',
'mobile_options' => true,
'sticky' => true,
),
),
),
);
$additional_options['transform_styles'] = array(
'label' => $i18n['styles']['label'],
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
'type' => 'composite',
'attr_suffix' => '',
'composite_type' => 'transforms',
'hover' => 'tabs',
'mobile_options' => true,
'sticky' => true,
'responsive' => true,
'bb_support' => false,
'description' => $i18n['styles']['description'],
'composite_structure' => $tabs,
);
// Register responsive options
$skip = array(
'type' => 'skip',
'tab_slug' => $settings['tab_slug'],
'toggle_slug' => $settings['toggle_slug'],
);
$linked_skip = $skip + array( 'default' => 'on' );
foreach ( $additional_options['transform_styles']['composite_structure'] as $tab_name => $tab ) {
foreach ( $tab['controls'] as $field_name => $field_options ) {
$controls = $additional_options['transform_styles']['composite_structure'][ $tab_name ]['controls'];
$controls[ "{$field_name}_tablet" ] = $skip;
$controls[ "{$field_name}_phone" ] = $skip;
$controls[ "{$field_name}_last_edited" ] = $skip;
if ( in_array( $field_name, array( 'transform_scale', 'transform_translate', 'transform_skew' ) ) ) {
$controls[ "{$field_name}_linked" ] = $linked_skip;
$controls[ "{$field_name}_linked_tablet" ] = $linked_skip;
$controls[ "{$field_name}_linked_phone" ] = $linked_skip;
$controls[ "{$field_name}_linked__hover" ] = $linked_skip;
$controls[ "{$field_name}_linked__sticky" ] = $linked_skip;
}
$additional_options['transform_styles']['composite_structure'][ $tab_name ]['controls'] = $controls;
}
}
$additional_options['transform_styles_last_edited'] = $skip;
return $additional_options;
}
// Processing functions
public function percent_to_unit( $percent = 0 ) {
if ( strpos( $percent, '%' ) === false ) {
return $percent;
}
$value = (float) trim( str_replace( '%', '', $percent ) );
return $value / 100;
}
public function set_props( $props ) {
$this->processing_props = $props;
}
public function get_setting( $value, $default ) {
if ( ! empty( $this->processing_props[ $value ] ) ) {
return $this->processing_props[ $value ];
} else {
return $default;
}
}
public function get_option( $typeAxis, $type = 'desktop' ) {
$setting = "transform_$typeAxis[0]";
$interpreter = array(
'X' => 0,
'Y' => 1,
'Z' => 2,
);
$index = $interpreter[ $typeAxis[1] ];
$default_value = false;
$option_value = $this->get_setting( $setting, false );
if ( 'hover' === $type ) {
$default_value = $this->get_setting( $setting, false );
$option_value = $this->get_setting( $setting . '__hover', false );
} elseif ( 'sticky' === $type ) {
$default_value = $this->get_setting( $setting, false );
$option_value = $this->get_setting( $setting . '__sticky', false );
} elseif ( 'tablet' === $type ) {
$default_value = $this->get_setting( $setting, false );
$option_value = $this->get_setting( $setting . '_tablet', false );
} elseif ( 'phone' === $type ) {
$default_value = $this->get_setting( $setting . '_tablet', false );
$option_value = $this->get_setting( $setting . '_phone', false );
if ( false === $default_value ) {
$default_value = $this->get_setting( $setting, false );
}
}
if ( false === $option_value ) {
if ( false !== $default_value ) {
$option_value = $default_value;
}
}
if ( false === $option_value ) {
return '';
}
$value_array = explode( '|', $option_value );
$value = $value_array[ $index ];
if ( 'scale' === $typeAxis[0] ) {
return $this->percent_to_unit( $value );
}
return $value;
}
public function get_elements( $type ) {
if ( empty( $this->processing_props ) ) {
wp_die( new WP_Error( '666', 'Run set_props first' ) );
}
$transformElements = array();
$originArray = array();
foreach ( $this->allTransforms as $option ) {
$typeAxis = array();
$typeAxis[0] = substr( $option, 0, -1 );
$typeAxis[1] = substr( $option, -1 );
$value = esc_attr( $this->get_option( $typeAxis, $type ) );
if ( ! empty( $value ) ) {
if ( 'origin' === $typeAxis[0] ) {
if ( 'originY' === $option && empty( $originArray ) ) {
// default value of originX
array_push( $originArray, '50%' );
}
array_push( $originArray, $value );
} else {
$transformElements[ $option ] = $value;
}
}
}
return array(
'transform' => $transformElements,
'origin' => $originArray,
);
}
public function getTransformDeclaration( $transformElements, $view = 'desktop' ) {
$declaration = array();
unset( $transformElements['originX'], $transformElements['originY'] );
// Perspective is included on when combining with some animations
if ( ! empty( $transformElements['perspective'] ) ) {
array_push( $declaration, sprintf( 'perspective(%s)', $transformElements['perspective'] ) );
}
// Transforms must maintain this order to blend correctly with animation rules
foreach ( $this->allTransforms as $option ) {
if ( ! empty( $transformElements[ $option ] ) ) {
array_push( $declaration, sprintf( '%s(%s)', $option, $transformElements[ $option ] ) );
}
}
if ( ! empty( $declaration ) ) {
if ( $this->processing_props['transforms_important'] || 'hover' === $view || 'sticky' === $view ) {
array_push( $declaration, '!important' );
}
return sprintf( 'transform: %s;', implode( ' ', $declaration ) );
}
return '';
}
/**
* @param $animationType
* @param $elements
* @param $function_name
* @param $device
*
* @return array
*/
public function transformedAnimation( $animationType, $elements, $function_name, $device ) {
if ( 'hover' === $device || 'sticky' === $device ) {
return array();
}
// phpcs:disable ET.Sniffs.ValidVariableName.VariableNotSnakeCase, ET.Sniffs.ValidVariableName.InterpolatedVariableNotSnakeCase -- Existing codebase
$utils = ET_Core_Data_Utils::instance();
$startElements = $elements['transform'];
$responsive = ET_Builder_Module_Helper_ResponsiveOptions::instance();
$direction = $responsive->get_any_value( $this->processing_props, 'animation_direction', 'center', true, $device );
$animation_intensity = $utils->array_get( $this->processing_props, "animation_intensity_$animationType", 50 );
$module_class = ET_Builder_Element::get_module_order_class( $function_name );
$animationName = "et_pb_{$animationType}_{$direction}_$module_class";
$newKeyframe = "@keyframes $animationName";
$newAnimationSelector = ".$module_class.et_animated.transformAnim";
$newAnimationRules = "animation-name: $animationName;";
$newKeyframeRules = '';
$transformDeclaration = $this->getTransformDeclaration( $elements['transform'] );
$originDeclaration = sprintf( 'transform-origin:%s;', implode( ' ', $elements['origin'] ) );
$intensity = ! is_numeric( str_replace( '%', '', $animation_intensity ) )
? 50
: (int) str_replace( '%', '', $animation_intensity );
// slide animation direction center is the same animation as zoom center
if ( 'slide' === $animationType && 'center' === $direction ) {
$animationType = 'zoom';
}
// animation transform gets combined with transform settings as described on et-builder-custom-output.jsx processTransform method
switch ( $animationType ) {
case 'zoom':
$scale = ( 100 - $intensity ) * 0.01;
$startElements['scaleX'] = $scale * $utils->array_get( $elements['transform'], 'scaleX', 1 );
$startElements['scaleY'] = $scale * $utils->array_get( $elements['transform'], 'scaleY', 1 );
$startDeclaration = $this->getTransformDeclaration( $startElements );
// replace origin declaration to preserve animation direction setting only if transform origin is not set
if ( empty( $elements['origin'] ) ) {
$originDeclaration = "transform-origin: $direction;";
} else {
$originDeclaration = sprintf( 'transform-origin: %s;', implode( ' ', $elements['origin'] ) );
}
$newKeyframeRules = "0%{ $startDeclaration }";
$newKeyframeRules .= "100%{opacity:1;$transformDeclaration}";
$newAnimationRules .= $originDeclaration;
break;
case 'slide':
$translateY = $utils->array_get( $elements['transform'], 'translateY', '0%' );
$translateX = $utils->array_get( $elements['transform'], 'translateX', '0%' );
switch ( $direction ) {
case 'top':
$startElements['translateY'] = sprintf( 'calc(%s%% + %s)', $intensity * -2, $translateY );
$startElements['translateX'] = $translateX;
break;
case 'bottom':
$startElements['translateY'] = sprintf( 'calc(%s%% + %s)', $intensity * 2, $translateY );
$startElements['translateX'] = $translateX;
break;
case 'left':
$startElements['translateX'] = sprintf( 'calc(%s%% + %s)', $intensity * -2, $translateX );
$startElements['translateY'] = $translateY;
break;
case 'right':
$startElements['translateX'] = sprintf( 'calc(%s%% + %s)', $intensity * 2, $translateX );
$startElements['translateY'] = $translateY;
break;
}
$startDeclaration = $this->getTransformDeclaration( $startElements );
$newKeyframeRules = "0%{ $startDeclaration }";
$newKeyframeRules .= "100%{opacity:1;$transformDeclaration}";
break;
case 'bounce':
$translateX = $utils->array_get( $elements['transform'], 'translateX', '0px' );
$translateY = $utils->array_get( $elements['transform'], 'translateY', '0px' );
switch ( $direction ) {
case 'center':
$scaleX = (float) $utils->array_get( $elements['transform'], 'scaleX', 1 );
$scaleY = (float) $utils->array_get( $elements['transform'], 'scaleY', 1 );
$newKeyframeRules = 'from, 20%, 40%, 60%, 80%, to {animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);}';
$startElements['scaleX'] = 0.3 * $scaleX;
$startElements['scaleY'] = 0.3 * $scaleY;
$newKeyframeRules .= sprintf( '0%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['scaleX'] = 1.1 * $scaleX;
$startElements['scaleY'] = 1.1 * $scaleY;
$newKeyframeRules .= sprintf( '20%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['scaleX'] = 0.9 * $scaleX;
$startElements['scaleY'] = 0.9 * $scaleY;
$newKeyframeRules .= sprintf( '40%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['scaleX'] = 1.03 * $scaleX;
$startElements['scaleY'] = 1.03 * $scaleY;
$newKeyframeRules .= sprintf( '60%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['scaleX'] = 0.97 * $scaleX;
$startElements['scaleY'] = 0.97 * $scaleY;
$newKeyframeRules .= sprintf( '80%%{%s}', $this->getTransformDeclaration( $startElements ) );
$newKeyframeRules .= "100%{opacity: 1;$transformDeclaration}";
break;
case 'top':
$newKeyframeRules = 'from, 60%, 75%, 90%, to {animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);}';
$startElements['translateY'] = "calc(-200px + $translateY)";
$newKeyframeRules .= sprintf( '0%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['translateY'] = "calc(25px + $translateY)";
$newKeyframeRules .= sprintf( '60%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['translateY'] = "calc(-10px + $translateY)";
$newKeyframeRules .= sprintf( '75%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['translateY'] = "calc(5px + $translateY)";
$newKeyframeRules .= sprintf( '90%%{%s}', $this->getTransformDeclaration( $startElements ) );
$newKeyframeRules .= "100%{opacity: 1;$transformDeclaration}";
break;
case 'bottom':
$newKeyframeRules = 'from, 60%, 75%, 90%, to {animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);}';
$startElements['translateY'] = "calc(200px + $translateY)";
$newKeyframeRules .= sprintf( '0%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['translateY'] = "calc(-25px + $translateY)";
$newKeyframeRules .= sprintf( '60%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['translateY'] = "calc(10px + $translateY)";
$newKeyframeRules .= sprintf( '75%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['translateY'] = "calc(-5px + $translateY)";
$newKeyframeRules .= sprintf( '90%%{%s}', $this->getTransformDeclaration( $startElements ) );
$newKeyframeRules .= "100%{opacity: 1;$transformDeclaration}";
break;
case 'left':
$newKeyframeRules = 'from, 60%, 75%, 90%, to {animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);}';
$startElements['translateX'] = "calc(-200px + $translateX)";
$newKeyframeRules .= sprintf( '0%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['translateX'] = "calc(25px + $translateX)";
$newKeyframeRules .= sprintf( '60%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['translateX'] = "calc(-10px + $translateX)";
$newKeyframeRules .= sprintf( '75%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['translateX'] = "calc(5px + $translateX)";
$newKeyframeRules .= sprintf( '90%%{%s}', $this->getTransformDeclaration( $startElements ) );
$newKeyframeRules .= "100%{opacity: 1;$transformDeclaration}";
break;
case 'right':
$newKeyframeRules = 'from, 60%, 75%, 90%, to {animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);}';
$startElements['translateX'] = "calc(200px + $translateX)";
$newKeyframeRules .= sprintf( '0%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['translateX'] = "calc(-25px + $translateX)";
$newKeyframeRules .= sprintf( '60%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['translateX'] = "calc(10px + $translateX)";
$newKeyframeRules .= sprintf( '75%%{%s}', $this->getTransformDeclaration( $startElements ) );
$startElements['translateX'] = "calc(-5px + $translateX)";
$newKeyframeRules .= sprintf( '90%%{%s}', $this->getTransformDeclaration( $startElements ) );
$newKeyframeRules .= "100%{opacity: 1;$transformDeclaration}";
break;
}
break;
case 'flip':
$intensityAngle = ceil( ( 90 / 100 ) * $intensity );
$startElements['perspective'] = '2000px';
$rotateX = (float) str_replace( 'deg', '', $utils->array_get( $elements['transform'], 'rotateX', '0' ) );
$rotateY = (float) str_replace( 'deg', '', $utils->array_get( $elements['transform'], 'rotateY', '0' ) );
switch ( $direction ) {
default:
case 'top':
$intensityAngle += $rotateX;
$startElement['rotateX'] = "{$intensityAngle}deg";
break;
case 'bottom':
$intensityAngle *= -1;
$intensityAngle += $rotateX;
$startElement['rotateX'] = "{$intensityAngle}deg";
break;
case 'left':
$intensityAngle *= -1;
$intensityAngle += $rotateY;
$startElement['rotateY'] = "{$intensityAngle}deg";
break;
case 'right':
$intensityAngle += $rotateY;
$startElement['rotateY'] = "{$intensityAngle}deg";
break;
}
$startDeclaration = $this->getTransformDeclaration( $startElement );
$newKeyframeRules = "0%{ $startDeclaration }";
$newKeyframeRules .= "100%{opacity:1;$transformDeclaration}";
break;
case 'fold':
$intensityAngle = ceil( ( 90 / 100 ) * $intensity );
$startElements['perspective'] = '2000px';
$rotateX = (float) str_replace( 'deg', '', $utils->array_get( $elements['transform'], 'rotateX', '0' ) );
$rotateY = (float) str_replace( 'deg', '', $utils->array_get( $elements['transform'], 'rotateY', '0' ) );
switch ( $direction ) {
case 'top':
$intensityAngle *= -1;
$intensityAngle += $rotateX;
$startElements['rotateX'] = "{$intensityAngle}deg";
break;
case 'bottom':
$intensityAngle += $rotateX;
$startElements['rotateX'] = "{$intensityAngle}deg";
break;
case 'left':
$intensityAngle += $rotateY;
$startElements['rotateY'] = "{$intensityAngle}deg";
break;
default:
case 'right':
$intensityAngle *= -1;
$intensityAngle += $rotateY;
$startElements['rotateY'] = "{$intensityAngle}deg";
break;
}
$startDeclaration = $this->getTransformDeclaration( $startElements );
$newKeyframeRules = "0%{{$startDeclaration}}";
$newKeyframeRules .= "100%{opacity:1;{$transformDeclaration}}";
// replace origin declaration to preserve animation direction setting only if transform origin is not set
if ( empty( $elements['origin'] ) ) {
$originDeclaration = "transform-origin: $direction;";
} else {
$originDeclaration = sprintf( 'transform-origin: %s;', implode( ' ', $elements['origin'] ) );
}
$newAnimationRules .= $originDeclaration;
break;
case 'roll':
$rotateZ = (float) str_replace( 'deg', '', $utils->array_get( $elements['transform'], 'rotateZ', '0' ) );
$intensityAngle = ceil( ( 360 / 100 ) * $intensity );
if ( 'bottom' === $direction || 'right' === $direction ) {
$startElements['rotateZ'] = sprintf( '%sdeg', ( $intensityAngle * -1 ) + $rotateZ );
} else {
$startElements['rotateZ'] = sprintf( '%sdeg', $intensityAngle + $rotateZ );
}
$startDeclaration = $this->getTransformDeclaration( $startElements );
$newKeyframeRules = "0%{ $startDeclaration }";
$newKeyframeRules .= "100%{opacity:1;$transformDeclaration}";
// replace origin declaration to preserve animation direction setting only if transform origin is not set
if ( empty( $elements['origin'] ) ) {
$originDeclaration = "transform-origin: $direction;";
} else {
$originDeclaration = sprintf( 'transform-origin: %s;', implode( ' ', $elements['origin'] ) );
}
$newAnimationRules .= $originDeclaration;
break;
}
if ( ! empty( $newKeyframeRules ) ) {
return array(
'keyframe' => array(
'selector' => $newKeyframe,
'declaration' => $newKeyframeRules,
),
'animationRules' => array(
'selector' => $newAnimationSelector,
'declaration' => $newAnimationRules,
),
'declaration' => $transformDeclaration . $originDeclaration,
);
}
// phpcs:enable ET.Sniffs.ValidVariableName.VariableNotSnakeCase, ET.Sniffs.ValidVariableName.InterpolatedVariableNotSnakeCase
return array();
}
/**
* Check if we need to process transform.
* Here we also check positions since some of it
* requires transform css.
*
* @param array $attrs Module attributes.
* @param array $positions Position locations.
*
* @return bool
*/
public function is_used( $attrs, $positions ) {
// Check for transform attrs first.
foreach ( $attrs as $attr => $value ) {
if ( ! $value ) {
continue;
}
$is_attr = 0 === strpos( $attr, 'transform_' );
if ( $is_attr ) {
return true;
}
}
// Then check the current positions.
foreach ( $positions as $pos => $value ) {
$default_strpos = strpos( $value, '_is_default' );
if ( false === $default_strpos ) {
return true;
}
}
return false;
}
}
return new ET_Builder_Module_Field_Transform();

View File

@@ -0,0 +1,21 @@
<?php
/**
* Parser of additional composite type attributes
*/
class ET_Builder_Module_Field_Attribute_Composite_Parser {
/**
* @param string $type type of composite attribute
* @param array $structure attribute structure, depends on type
*
* @return array Additional attributes for merging with rest of module attributes
*/
public static function parse( $type, $structure ) {
switch ( $type ) {
case 'tabbed':
default:
require_once ET_BUILDER_DIR . 'module/field/attribute/composite/type/Tabbed.php';
return ET_Builder_Module_Field_Attribute_Composite_Type_Tabbed::parse( $structure );
}
}
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* Class for parsing shortcode attributes for tabbed controls.
* The tabbed composite structure as follows
* 'composite_structure' => array(
* //tab
* 'border_all' => array(
* //tab icon
* 'icon' => 'border-all',
* //list of standard controls which will be placed within the tab
* 'controls' => array(
* 'border_width_all' => array(
* 'label' => esc_html__( 'Border Width', 'et_builder' ),
* 'type' => 'range',
* 'default' => '0',
* 'range_settings' => array(
* 'min' => 0,
* 'max' => 50,
* 'step' => 1,
* ),
* ),
* 'border_color_all' => array(
* 'label' => esc_html__( 'Border Color', 'et_builder' ),
* 'type' => $color_type,
* 'default' => '#333333',
* ),
* ),
* ),
* )
*/
class ET_Builder_Module_Field_Attribute_Composite_Type_Tabbed {
/**
* @var ET_Core_Data_Utils
*/
protected static $_;
public static function parse( $structure ) {
$result = array();
if ( ! is_array( $structure ) ) {
return $result;
}
if ( is_null( self::$_ ) ) {
self::$_ = ET_Core_Data_Utils::instance();
}
foreach ( $structure as $tab ) {
if ( ! isset( $tab['controls'] ) || ! is_array( $tab['controls'] ) ) {
continue;
}
foreach ( $tab['controls'] as $control => $control_settings ) {
// We don't want to set defaults right now as doing so makes it virtually impossible
// to generate accurate border css styles later on.
$result[ $control ] = '';
}
}
return $result;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Author Condition's logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Author Condition Trait
*/
trait AuthorCondition {
/**
* Processes "Author" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all condition settings.
*
* @return boolean Condition output.
*/
protected function _process_author_condition( $condition_settings ) {
// Only check for Posts.
if ( ! is_singular() ) {
return false;
}
$legacy_display_rule = isset( $condition_settings['authorDisplay'] ) ? $condition_settings['authorDisplay'] : 'is';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$authors_raw = isset( $condition_settings['authors'] ) ? $condition_settings['authors'] : [];
$authors_ids = array_map(
function( $item ) {
return $item['value'];
},
$authors_raw
);
$is_on_shop_page = class_exists( 'WooCommerce' ) && is_shop();
$queried_object_id = $is_on_shop_page ? wc_get_page_id( 'shop' ) : get_queried_object_id();
$current_post_author_id = get_post_field( 'post_author', (int) $queried_object_id );
$should_display = array_intersect( $authors_ids, (array) $current_post_author_id ) ? true : false;
return ( 'is' === $display_rule ) ? $should_display : ! $should_display;
}
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* Browser condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Browser Condition Trait.
*/
trait BrowserCondition {
/**
* Processes "Operating System" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_browser_condition( $condition_settings ) {
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['browserDisplay'] ) ? $condition_settings['browserDisplay'] : 'is';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$browsers_raw = isset( $condition_settings['browsers'] ) ? $condition_settings['browsers'] : '';
$browsers = explode( '|', $browsers_raw );
// phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- User Agent is not stored or displayed therefore XSS safe.
$useragent = ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) ? $_SERVER['HTTP_USER_AGENT'] : '';
$is_old_edge = preg_match( '/edge\//i', $useragent );
$is_checking_for_chrome = array_search( 'chrome', $browsers, true ) !== false;
$current_browser = $this->_get_browser( $useragent );
// Exception: When checking "Chrome" condition we should treat New Edge as Chrome.
if ( 'edge' === $current_browser && ! $is_old_edge && $is_checking_for_chrome ) {
$current_browser = 'chrome';
}
$should_display = array_intersect( $browsers, (array) $current_browser ) ? true : false;
return ( 'is' === $display_rule ) ? $should_display : ! $should_display;
}
/**
* Returns the Browser name based on user agent.
*
* @since 4.11.0
*
* @param string $useragent The useragent of the berowser.
*
* @return string Detected browser.
*/
protected function _get_browser( $useragent ) {
$browser = 'unknown';
$browser_array = array(
'/safari/i' => 'safari',
'/chrome|CriOS/i' => 'chrome',
'/firefox|FxiOS/i' => 'firefox',
'/msie|Trident/i' => 'ie',
'/edg/i' => 'edge',
'/opr|Opera|Presto/i' => 'opera',
'/maxthon/i' => 'maxthon',
'/ucbrowser/i' => 'ucbrowser',
);
foreach ( $browser_array as $regex => $value ) {
if ( preg_match( $regex, $useragent ) ) {
$browser = $value;
}
}
return $browser;
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Cart Contents condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Cart Contents Condition Trait.
*/
trait CartContentsCondition {
/**
* Processes "Cart Contents" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_cart_contents_condition( $condition_settings ) {
if ( ! class_exists( 'WooCommerce' ) ) {
return false;
}
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['cartContentsDisplay'] ) ? $condition_settings['cartContentsDisplay'] : 'hasProducts';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$products_raw = isset( $condition_settings['products'] ) ? $condition_settings['products'] : [];
$products_ids = array_map(
function( $item ) {
return isset( $item['value'] ) ? $item['value'] : '';
},
$products_raw
);
$is_cart_empty = WC()->cart->is_empty();
switch ( $display_rule ) {
case 'hasProducts':
return ! $is_cart_empty;
case 'isEmpty':
return $is_cart_empty;
case 'hasSpecificProduct':
return $this->_has_specific_product_in_cart( $products_ids );
case 'doesNotHaveSpecificProduct':
return ! $this->_has_specific_product_in_cart( $products_ids );
default:
return false;
}
}
/**
* Checks presence of specified products in the Cart.
*
* @param Array $products_ids Array of products IDs to check against the Cart's products.
* @return boolean Indicating the presence of specified products in the Cart.
*/
protected function _has_specific_product_in_cart( $products_ids ) {
$has_specific_product = false;
if ( ! WC()->cart->is_empty() ) {
foreach ( WC()->cart->get_cart() as $cart_item ) {
$cart_item_ids = [ $cart_item['product_id'], $cart_item['variation_id'] ];
if ( array_intersect( $products_ids, $cart_item_ids ) ) {
$has_specific_product = true;
break;
}
}
}
return $has_specific_product;
}
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* Categories condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Categories Condition Trait.
*/
trait CategoriesCondition {
/**
* Processes "Categories" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all condition settings.
*
* @return boolean Condition output.
*/
protected function _process_categories_condition( $condition_settings ) {
// Only check for Posts.
if ( ! is_singular() ) {
return false;
}
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['categoriesDisplay'] ) ? $condition_settings['categoriesDisplay'] : 'is';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$categories_raw = isset( $condition_settings['categories'] ) ? $condition_settings['categories'] : [];
$categories = array_map(
function( $item ) {
return (object) [
'id' => $item['value'],
'taxonomy_slug' => $item['groupSlug'],
];
},
$categories_raw
);
$current_queried_id = get_queried_object_id();
$has_post_specified_term = false;
$tax_slugs_of_catch_all_items = [];
$is_any_catch_all_selected = false;
$has_post_specified_taxonomy = false;
// Logic evaluation.
foreach ( $categories_raw as $item ) {
if ( true === $item['isCatchAll'] ) {
$tax_slugs_of_catch_all_items[] = $item['groupSlug'];
$is_any_catch_all_selected = true;
}
}
foreach ( $categories as $cat ) {
if ( has_term( $cat->id, $cat->taxonomy_slug, $current_queried_id ) ) {
$has_post_specified_term = true;
break;
}
}
$is_displayable = $has_post_specified_term ? true : false;
if ( ! $is_displayable && $is_any_catch_all_selected ) {
foreach ( $tax_slugs_of_catch_all_items as $tax_slug ) {
$has_post_specified_taxonomy = has_term( '', $tax_slug, $current_queried_id );
if ( $has_post_specified_taxonomy ) {
break;
}
}
$is_displayable = $has_post_specified_taxonomy ? true : false;
}
// Evaluation output.
return ( 'is' === $display_rule ) ? $is_displayable : ! $is_displayable;
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* Category Page condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Category Page Condition Trait.
*/
trait CategoryPageCondition {
/**
* Processes "Category Page" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_category_page_condition( $condition_settings ) {
// Only check for Archive pages.
if ( ! is_archive() ) {
return false;
}
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['categoryPageDisplay'] ) ? $condition_settings['categoryPageDisplay'] : 'is';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$categories_raw = isset( $condition_settings['categories'] ) ? $condition_settings['categories'] : [];
$queried_object = get_queried_object();
$is_queried_object_valid = $queried_object instanceof \WP_Term && property_exists( $queried_object, 'taxonomy' );
if ( ! $is_queried_object_valid ) {
return false;
}
$queried_taxonomy = $queried_object->taxonomy;
$categories_ids = array_map(
function( $item ) {
return $item['value'];
},
$categories_raw
);
$tax_slugs_of_catch_all_items = [];
$is_any_catch_all_selected = false;
foreach ( $categories_raw as $item ) {
if ( true === $item['isCatchAll'] ) {
$tax_slugs_of_catch_all_items[] = $item['groupSlug'];
$is_any_catch_all_selected = true;
}
}
// Logic evaluation.
$current_category_id = get_queried_object_id();
$is_displayable = array_intersect( $categories_ids, (array) $current_category_id ) ? true : false;
if ( ! $is_displayable && $is_any_catch_all_selected ) {
$is_displayable = array_intersect( $tax_slugs_of_catch_all_items, (array) $queried_taxonomy ) ? true : false;
}
// Evaluation output.
return ( 'is' === $display_rule ) ? $is_displayable : ! $is_displayable;
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* Cookie condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Cookie Condition Trait.
*/
trait CookieCondition {
/**
* Processes "Cookie" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_cookie_condition( $condition_settings ) {
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['cookieDisplay'] ) ? $condition_settings['cookieDisplay'] : 'cookieExists';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$cookie_name = isset( $condition_settings['cookieName'] ) ? $condition_settings['cookieName'] : '';
$cookie_value = isset( $condition_settings['cookieValue'] ) ? $condition_settings['cookieValue'] : '';
$is_cookie_set = ( isset( $_COOKIE[ $cookie_name ] ) ) ? true : false;
$is_cookie_value_equals = ( isset( $_COOKIE[ $cookie_name ] ) ) ? $cookie_value === $_COOKIE[ $cookie_name ] : false;
switch ( $display_rule ) {
case 'cookieExists':
return $is_cookie_set;
case 'cookieDoesNotExist':
return ! $is_cookie_set;
case 'cookieValueEquals':
return $is_cookie_value_equals;
case 'cookieValueDoesNotEqual':
return ! $is_cookie_value_equals;
default:
return false;
}
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Custom Field's logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Custom Field Condition Trait
*/
trait CustomFieldCondition {
/**
* Evaluates "Custom Field" condition.
*
* @since 4.14.3
*
* @param array $condition_settings The Condition settings containing:
* 'selectConditionalMetaField' => base64_data.
*
* @return boolean Returns `true` if the condition evaluation is true, `false` otherwise.
*/
protected function _process_custom_field_condition( $condition_settings ) {
// Only check for Posts.
if ( ! is_singular() ) {
return false;
}
// Only check if queried object id is valid.
$queried_object_id = get_queried_object_id();
if ( ! \WP_Post::get_instance( $queried_object_id ) ) {
return false;
}
// Decodes the base64 data and runs validations.
$meta_field_settings_base64 = isset( $condition_settings['selectConditionalMetaField'] ) ? $condition_settings['selectConditionalMetaField'] : '';
$meta_field_settings_json = base64_decode( $meta_field_settings_base64 ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- The returned data is an array and necessary validation checks are performed.
$meta_field_settings = false !== $meta_field_settings_json ? json_decode( $meta_field_settings_json, true ) : [];
// Gets meta fields settings.
$display_rule = isset( $meta_field_settings['displayRule'] ) ? $meta_field_settings['displayRule'] : 'is';
$selected_field_name = isset( $meta_field_settings['selectedFieldName'] ) ? $meta_field_settings['selectedFieldName'] : 'manualCustomFieldName';
$custom_field_name = isset( $meta_field_settings['customFieldName'] ) ? $meta_field_settings['customFieldName'] : '';
$selected_field_value = isset( $meta_field_settings['selectedFieldValue'] ) ? $meta_field_settings['selectedFieldValue'] : 'manualCustomFieldValue';
$custom_field_value = isset( $meta_field_settings['customFieldValue'] ) ? $meta_field_settings['customFieldValue'] : '';
$field_name = 'manualCustomFieldName' === $selected_field_name ? $custom_field_name : $selected_field_name;
$has_custom_field_value = 'manualCustomFieldValue' === $selected_field_value ? true : false;
// Checks whether the specified custom fields actually exist.
$has_field_name_metadata = metadata_exists( 'post', $queried_object_id, $field_name );
$has_selected_field_value_metadata = $has_custom_field_value ? true : metadata_exists( 'post', $queried_object_id, $selected_field_value );
// Bailout if specified custom fields don't exist.
if ( ! $has_field_name_metadata || ! $has_selected_field_value_metadata ) {
return false;
}
$field_name_meta = (string) get_post_meta( $queried_object_id, $field_name, true );
$field_value_meta = $has_custom_field_value ? (string) $custom_field_value : (string) get_post_meta( $queried_object_id, $selected_field_value, true );
// The PHP 7.4 and below will throw warning if we pass empty string on the 2nd arg
// of `strpos`. We have to avoid this issue and make sure the `Contains` condition
// threat $field_value_meta empty value as invalid condition (no match).
$contains = false;
if ( ! empty( $field_value_meta ) ) {
$contains = strpos( $field_name_meta, $field_value_meta );
}
$output = [
'is' => $field_name_meta === $field_value_meta,
'isNot' => $field_name_meta !== $field_value_meta,
'contains' => false !== $contains,
'doesNotContain' => false === $contains,
'isAnyValue' => strlen( $field_name_meta ) > 0,
'hasNoValue' => strlen( $field_name_meta ) === 0,
'isGreaterThan' => (float) $field_name_meta > (float) $field_value_meta,
'isLessThan' => (float) $field_name_meta < (float) $field_value_meta,
];
return isset( $output[ $display_rule ] ) ? $output[ $display_rule ] : false;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Date Archive condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
use DateTimeImmutable;
/**
* Date Archive Condition Trait.
*/
trait DateArchiveCondition {
/**
* Processes "Date Archive" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_date_archive_condition( $condition_settings ) {
if ( ! is_date() ) {
return false;
}
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['dateArchiveDisplay'] ) ? $condition_settings['dateArchiveDisplay'] : 'isAfter';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$date = isset( $condition_settings['dateArchive'] ) ? $condition_settings['dateArchive'] : '';
$year = get_query_var( 'year' );
$monthnum = get_query_var( 'monthnum' ) === 0 ? 1 : get_query_var( 'monthnum' );
$day = get_query_var( 'day' ) === 0 ? 1 : get_query_var( 'day' );
$archive_date = sprintf( '%s-%s-%s', $year, $monthnum, $day );
$target_date = new DateTimeImmutable( $date, wp_timezone() );
$current_arhive_date = new DateTimeImmutable( $archive_date, wp_timezone() );
switch ( $display_rule ) {
case 'isAfter':
return ( $current_arhive_date > $target_date );
case 'isBefore':
return ( $current_arhive_date < $target_date );
default:
return ( $current_arhive_date > $target_date );
}
}
}

View File

@@ -0,0 +1,256 @@
<?php
/**
* Date Time condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
use DateTimeImmutable;
/**
* Date Time Condition Trait.
*/
trait DateTimeCondition {
/**
* Processes "Date & Time" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_date_time_condition( $condition_settings ) {
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['dateTimeDisplay'] ) ? $condition_settings['dateTimeDisplay'] : 'isAfter';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$date = isset( $condition_settings['date'] ) ? $condition_settings['date'] : '';
$time = isset( $condition_settings['time'] ) ? $condition_settings['time'] : '';
$all_day = isset( $condition_settings['allDay'] ) ? $condition_settings['allDay'] : '';
$from_time = isset( $condition_settings['fromTime'] ) ? $condition_settings['fromTime'] : '';
$until_time = isset( $condition_settings['untilTime'] ) ? $condition_settings['untilTime'] : '';
$weekdays = isset( $condition_settings['weekdays'] ) ? array_filter( explode( '|', $condition_settings['weekdays'] ) ) : array();
$repeat_frequency = isset( $condition_settings['repeatFrequency'] ) ? $condition_settings['repeatFrequency'] : '';
$repeat_frequency_specific_days = isset( $condition_settings['repeatFrequencySpecificDays'] ) ? $condition_settings['repeatFrequencySpecificDays'] : '';
$date_from_time = $date . ' ' . $from_time;
$date_until_time = $date . ' ' . $until_time;
$date_time = $date . ' ' . $time;
$target_date = new DateTimeImmutable( $date, wp_timezone() );
$target_datetime = new DateTimeImmutable( $date_time, wp_timezone() );
$target_from_datetime = new DateTimeImmutable( $date_from_time, wp_timezone() );
$target_until_datetime = new DateTimeImmutable( $date_until_time, wp_timezone() );
$current_datetime = ! empty( $this->_custom_current_date ) ? $this->_custom_current_date : current_datetime();
$current_datetime_from = $current_datetime->modify( $from_time );
$current_datetime_until = $current_datetime->modify( $until_time );
switch ( $display_rule ) {
case 'isAfter':
return ( $current_datetime > $target_datetime );
case 'isBefore':
return ( $current_datetime < $target_datetime );
case 'isOnSpecificDate':
$has_reached_target_datetime = $current_datetime >= $target_date;
$has_time_until_tomorrow = $current_datetime < $target_date->modify( 'tomorrow' );
if ( 'off' === $all_day ) {
$has_reached_target_datetime = $current_datetime >= $target_from_datetime;
$has_time_until_tomorrow = $current_datetime < $target_until_datetime;
}
$is_on_specific_date = ( $has_reached_target_datetime && $has_time_until_tomorrow );
$is_repeated = $this->_is_datetime_condition_repeated( $condition_settings, $is_on_specific_date, $current_datetime, $target_datetime );
return ( $is_on_specific_date || $is_repeated );
case 'isNotOnSpecificDate':
$has_reached_target_datetime = $current_datetime >= $target_date;
$has_time_until_tomorrow = $current_datetime < $target_date->modify( 'tomorrow' );
if ( 'off' === $all_day ) {
$has_reached_target_datetime = $current_datetime >= $target_from_datetime;
$has_time_until_tomorrow = $current_datetime < $target_until_datetime;
}
return ! ( $has_reached_target_datetime && $has_time_until_tomorrow );
case 'isOnSpecificDays':
$current_day = strtolower( $current_datetime->format( 'l' ) );
$is_on_selected_day = array_intersect( (array) $current_day, $weekdays ) ? true : false;
$has_reached_target_datetime = true;
$has_time_until_tomorrow = true;
if ( 'off' === $all_day ) {
$has_reached_target_datetime = $current_datetime >= $current_datetime_from;
$has_time_until_tomorrow = $current_datetime < $current_datetime_until;
}
$is_repeated = $this->_is_datetime_condition_repeated( $condition_settings, $is_on_selected_day, $current_datetime, $target_datetime );
$is_on_specific_days = $is_on_selected_day && $has_reached_target_datetime && $has_time_until_tomorrow;
return ( 'weekly' === $repeat_frequency_specific_days ) ? $is_on_specific_days : $is_repeated;
case 'isFirstDayOfMonth':
$is_first_day_of_month = $current_datetime->format( 'd' ) === '01';
$has_reached_target_datetime = true;
$has_time_until_tomorrow = true;
if ( 'off' === $all_day ) {
$has_reached_target_datetime = $current_datetime >= $current_datetime_from;
$has_time_until_tomorrow = $current_datetime < $current_datetime_until;
}
return ( $is_first_day_of_month && $has_reached_target_datetime && $has_time_until_tomorrow );
case 'isLastDayOfMonth':
$last_day_of_month = new DateTimeImmutable( 'last day of this month', wp_timezone() );
$is_last_day_of_month = $current_datetime->format( 'd' ) === $last_day_of_month->format( 'd' );
$has_reached_target_datetime = true;
$has_time_until_tomorrow = true;
if ( 'off' === $all_day ) {
$has_reached_target_datetime = $current_datetime >= $current_datetime_from;
$has_time_until_tomorrow = $current_datetime < $current_datetime_until;
}
return ( $is_last_day_of_month && $has_reached_target_datetime && $has_time_until_tomorrow );
default:
return ( $current_datetime >= $target_datetime );
}
}
/**
* Checks whether a condition should be repeated or not.
*
* @since 4.11.0
*
* @param array $condition_settings Contains all settings of the condition.
* @param boolean $is_on_specific_date Specifies if "Is On Specific Date" condition has already
* reached that specific date or not.
* Useful to avoid repetition checking if condition is already
* true and also for "Every Other" repeat frequency.
* @param DateTimeImmutable $current_datetime The current date and time to use.
* @param DateTimeImmutable $target_datetime To detect Monthly/Annually repetition and "After Number of times".
*
* @return boolean Condition repetition result.
*/
protected function _is_datetime_condition_repeated( $condition_settings, $is_on_specific_date, $current_datetime, $target_datetime ) {
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['dateTimeDisplay'] ) ? $condition_settings['dateTimeDisplay'] : 'isAfter';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$repeat = isset( $condition_settings['repeat'] ) ? $condition_settings['repeat'] : '';
$repeat_frequency = isset( $condition_settings['repeatFrequency'] ) ? $condition_settings['repeatFrequency'] : '';
$repeat_frequency_specific_days = isset( $condition_settings['repeatFrequencySpecificDays'] ) ? $condition_settings['repeatFrequencySpecificDays'] : '';
$repeat_end = isset( $condition_settings['repeatEnd'] ) ? $condition_settings['repeatEnd'] : '';
$repeat_until = isset( $condition_settings['repeatUntilDate'] ) ? $condition_settings['repeatUntilDate'] : '';
$repeat_times = isset( $condition_settings['repeatTimes'] ) ? $condition_settings['repeatTimes'] : '';
$all_day = isset( $condition_settings['allDay'] ) ? $condition_settings['allDay'] : '';
$from_time = isset( $condition_settings['fromTime'] ) ? $condition_settings['fromTime'] : '';
$until_time = isset( $condition_settings['untilTime'] ) ? $condition_settings['untilTime'] : '';
$is_repeated = false;
$is_on_specific_days = 'isOnSpecificDays' === $display_rule;
if ( $is_on_specific_days || ( 'on' === $repeat && ! $is_on_specific_date ) ) {
if ( $is_on_specific_days ) {
$is_day_repeated = $this->_is_day_repeated( $repeat_frequency_specific_days, $is_on_specific_date, $current_datetime, $target_datetime );
} else {
$is_day_repeated = $this->_is_day_repeated( $repeat_frequency, $is_on_specific_date, $current_datetime, $target_datetime );
}
$is_repeat_valid = false;
switch ( $repeat_end ) {
case 'untilDate':
$is_repeat_valid = $current_datetime <= new DateTimeImmutable( $repeat_until, wp_timezone() );
break;
case 'afterNumberOfTimes':
$target_date_after_number_of_times = $target_datetime->modify( '+' . $repeat_times . ' month' );
if ( 'annually' === $repeat_frequency ) {
$target_date_after_number_of_times = $target_datetime->modify( '+' . $repeat_times . ' year' );
}
if ( 'off' === $all_day ) {
$target_date_after_number_of_times = $target_date_after_number_of_times->modify( $until_time );
}
$is_repeat_valid = $current_datetime <= $target_date_after_number_of_times;
break;
case 'never':
$is_repeat_valid = true;
break;
default:
$is_repeat_valid = true;
break;
}
// We assume "All Day" switch is "On".
$has_reached_from_time = $is_day_repeated;
$has_reached_until_time = $current_datetime < $current_datetime->modify( 'tomorrow' );
// Calculate from time/until time if "All Day" switch is "Off".
if ( 'off' === $all_day ) {
$has_reached_from_time = $current_datetime >= $current_datetime->modify( $from_time );
$has_reached_until_time = $current_datetime < $current_datetime->modify( $until_time );
}
$is_repeated = $is_day_repeated && $has_reached_from_time && $has_reached_until_time && $is_repeat_valid;
}
return $is_repeated;
}
/**
* Checks whether a day is repeated or not.
*
* @since 4.11.0
*
* @param string $repeat_frequency Frequency of repeat Ex. monthly, annually, everyOther...
* @param boolean $is_on_specific_date Useful for "Every Other" repeat frequency.
* @param DateTimeImmutable $current_datetime The current date and time to use.
* @param DateTimeImmutable $target_datetime Checks monthly/annually repetition against this Date and Time.
*
* @return boolean Day repetition result.
*/
protected function _is_day_repeated( $repeat_frequency, $is_on_specific_date, $current_datetime, $target_datetime ) {
switch ( $repeat_frequency ) {
case 'monthly':
return ( $current_datetime->format( 'd' ) === $target_datetime->format( 'd' ) );
case 'annually':
return ( $current_datetime->format( 'm d' ) === $target_datetime->format( 'm d' ) );
case 'everyOther':
return ! $is_on_specific_date;
case 'firstInstanceOfMonth':
return ( $current_datetime->format( 'Y-m-d' ) === $current_datetime->modify( 'first ' . $current_datetime->format( 'l' ) . ' of this month' )->format( 'Y-m-d' ) );
case 'lastInstanceOfMonth':
return ( $current_datetime->format( 'Y-m-d' ) === $current_datetime->modify( 'last ' . $current_datetime->format( 'l' ) . ' of this month' )->format( 'Y-m-d' ) );
default:
return false;
}
}
/**
* Checks date and time for possible conflicts.
*
* @since 4.11.0
*
* @param string $current_display_rule Value of currently processing condition's display rule Ex. is,isNot...
* @param string $prev_display_rule Value of previously processed condition's display rule Ex. is,isNot...
* @param array $conflicting_display_rule_vals Array of values containing the conflicting display rules as defined in $this->conflicts.
*
* @return boolean Conflict evaluation result.
*/
protected function _is_date_time_conflicted( $current_display_rule, $prev_display_rule, $conflicting_display_rule_vals ) {
$is_current_display_rule_conflicted = $this->_in_array_conflict( $current_display_rule, $conflicting_display_rule_vals );
$is_prev_display_rule_conflicted = $this->_in_array_conflict( $prev_display_rule, $conflicting_display_rule_vals );
$is_from_same_group = $is_current_display_rule_conflicted['index'] === $is_prev_display_rule_conflicted['index'];
if ( $is_current_display_rule_conflicted['value'] && $is_prev_display_rule_conflicted['value'] && ! $is_from_same_group ) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Dynamic Posts Condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Dynamic Posts Condition Trait.
*/
trait DynamicPostsCondition {
/**
* Processes "Dynamic Posts" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_dynamic_posts_condition( $condition_settings ) {
// Only check for Posts.
if ( ! is_singular() ) {
return false;
}
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['dynamicPostsDisplay'] ) ? $condition_settings['dynamicPostsDisplay'] : 'is';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$dynamic_posts_raw = isset( $condition_settings['dynamicPosts'] ) ? $condition_settings['dynamicPosts'] : [];
$dynamic_posts_ids = array_map(
function( $item ) {
return isset( $item['value'] ) ? $item['value'] : '';
},
$dynamic_posts_raw
);
$is_on_shop_page = class_exists( 'WooCommerce' ) && is_shop();
$current_page_id = $is_on_shop_page ? wc_get_page_id( 'shop' ) : get_queried_object_id();
$should_display = array_intersect( $dynamic_posts_ids, (array) $current_page_id ) ? true : false;
return ( 'is' === $display_rule ) ? $should_display : ! $should_display;
}
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* Logged In Status Condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Logged In Status Condition Trait.
*/
trait LoggedInStatusCondition {
/**
* Processes "Logged In Status" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_logged_in_status_condition( $condition_settings ) {
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['loggedInStatus'] ) ? $condition_settings['loggedInStatus'] : 'loggedIn';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$should_display = ( is_user_logged_in() ) ? true : false;
return ( 'loggedIn' === $display_rule ) ? $should_display : ! $should_display;
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* Number of Views condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Number of Views Condition Trait.
*/
trait NumberOfViewsCondition {
/**
* Processes "Number of Views" condition.
*
* @since 4.11.0
*
* @param array $condition_id Condition ID.
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_number_of_views_condition( $condition_id, $condition_settings ) {
if ( ! isset( $_COOKIE['divi_module_views'] ) ) {
return true;
}
// Get condition's settings.
$number_of_views = isset( $condition_settings['numberOfViews'] ) ? $condition_settings['numberOfViews'] : '0';
$cookie_array = [];
$visit_count = 0;
$current_datetime = current_datetime();
$cookie_array = json_decode( base64_decode( $_COOKIE['divi_module_views'] ), true ); // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput, WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Cookie is not stored or displayed therefore XSS safe, The returned data is an array and necessary validation checks are performed.
if ( ! is_array( $cookie_array ) ) {
return true;
}
// Logic evaluation.
$col = array_column( $cookie_array, 'id' );
$is_condition_set_in_cookie = array_search( $condition_id, $col, true ) !== false;
if ( ! $is_condition_set_in_cookie ) {
// Display module if condition is not set in Cookie yet.
return true;
}
$is_reset_after_duration_on = 'on' === $condition_settings['resetAfterDuration'] ? true : false;
if ( $is_reset_after_duration_on ) {
$first_visit_timestamp = $cookie_array[ $condition_id ]['first_visit_timestamp'];
$display_again_after = $condition_settings['displayAgainAfter'] . ' ' . $condition_settings['displayAgainAfterUnit'];
$first_visit_datetime = $current_datetime->setTimestamp( $first_visit_timestamp );
$display_again_datetime = $first_visit_datetime->modify( $display_again_after );
if ( $current_datetime > $display_again_datetime ) {
return true;
}
}
$visit_count = $cookie_array[ $condition_id ]['visit_count'];
if ( (int) $visit_count >= (int) $number_of_views ) {
$is_displayable = false;
} else {
$is_displayable = true;
}
// Evaluation output.
return $is_displayable;
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* Operating System Condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Operating System Condition Trait.
*/
trait OperatingSystemCondition {
/**
* Processes "Operating System" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_operating_system_condition( $condition_settings ) {
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['operatingSystemDisplay'] ) ? $condition_settings['operatingSystemDisplay'] : 'is';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$operating_systems_raw = isset( $condition_settings['operatingSystems'] ) ? $condition_settings['operatingSystems'] : '';
$operating_systems = explode( '|', $operating_systems_raw );
$current_os = $this->_get_os();
$should_display = array_intersect( $operating_systems, (array) $current_os ) ? true : false;
return ( 'is' === $display_rule ) ? $should_display : ! $should_display;
}
/**
* Returns the Operating System name based on user agent.
*
* @since 4.11.0
*
* @return string
*/
protected function _get_os() {
$os_platform = 'unknown';
// phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- User Agent is not stored or displayed therefore XSS safe.
$user_agent = $_SERVER['HTTP_USER_AGENT'];
$os_array = array(
'/windows nt/i' => 'windows',
'/macintosh|mac os x/i' => 'macos',
'/linux/i' => 'linux',
'/android/i' => 'android',
'/iphone/i' => 'iphone',
'/ipad/i' => 'ipad',
'/ipod/i' => 'ipod',
'/appletv/i' => 'appletv',
'/playstation/i' => 'playstation',
'/xbox/i' => 'xbox',
'/nintendo/i' => 'nintendo',
'/webos|hpwOS/i' => 'webos',
);
foreach ( $os_array as $regex => $value ) {
if ( preg_match( $regex, $user_agent ) ) {
$os_platform = $value;
}
}
return $os_platform;
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* Page Visit Condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Page Visit Condition Trait.
*/
trait PageVisitCondition {
/**
* Processes "Page Visit" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_page_visit_condition( $condition_settings ) {
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['pageVisitDisplay'] ) ? $condition_settings['pageVisitDisplay'] : 'hasVisitedSpecificPage';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$pages_raw = isset( $condition_settings['pages'] ) ? $condition_settings['pages'] : [];
$pages_ids = array_map(
function( $item ) {
return isset( $item['value'] ) ? (int) $item['value'] : '';
},
$pages_raw
);
$has_visited_specific_page = false;
$cookie = [];
if ( isset( $_COOKIE['divi_post_visit'] ) ) {
// phpcs:ignore ET.Sniffs.ValidatedSanitizedInput, WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Cookie is not stored or displayed therefore XSS safe, base64_decode returned data is an array and necessary validation checks are performed.
$cookie = json_decode( base64_decode( $_COOKIE['divi_post_visit'] ), true );
}
if ( $cookie && is_array( $cookie ) ) {
$col = array_column( $cookie, 'id' );
$has_visited_specific_page = array_intersect( $pages_ids, $col ) ? true : false;
}
$should_display = $has_visited_specific_page;
return ( 'hasVisitedSpecificPage' === $display_rule ) ? $should_display : ! $should_display;
}
}

View File

@@ -0,0 +1,52 @@
<?php
/**
* Page Type Condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Post Type Condition Trait.
*/
trait PostTypeCondition {
/**
* Processes "Post Types" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_post_type_condition( $condition_settings ) {
// Only check for Posts.
if ( ! is_singular() ) {
return false;
}
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['postTypeDisplay'] ) ? $condition_settings['postTypeDisplay'] : 'is';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$post_types_raw = isset( $condition_settings['postTypes'] ) ? $condition_settings['postTypes'] : [];
$post_types_values = array_map(
function( $item ) {
return $item['value'];
},
$post_types_raw
);
$is_on_shop_page = class_exists( 'WooCommerce' ) && is_shop();
$current_queried_id = $is_on_shop_page ? wc_get_page_id( 'shop' ) : get_queried_object_id();
$post_type = get_post_type( $current_queried_id );
$should_display = array_intersect( $post_types_values, (array) $post_type ) ? true : false;
return ( 'is' === $display_rule ) ? $should_display : ! $should_display;
}
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* Product Purchase Condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Product Purchase Condition Trait.
*/
trait ProductPurchaseCondition {
/**
* Processes "Product Purchase" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_product_purchase_condition( $condition_settings ) {
if ( ! class_exists( 'WooCommerce' ) || ! is_user_logged_in() ) {
return false;
}
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['productPurchaseDisplay'] ) ? $condition_settings['productPurchaseDisplay'] : 'hasBoughtProduct';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$products_raw = isset( $condition_settings['products'] ) ? $condition_settings['products'] : [];
$current_user = wp_get_current_user();
$products_ids = array_map(
function( $item ) {
return isset( $item['value'] ) ? $item['value'] : '';
},
$products_raw
);
switch ( $display_rule ) {
case 'hasBoughtProduct':
$has_bought_product = $this->_has_user_bought_any_product( $current_user->ID );
return $has_bought_product;
case 'hasNotBoughtProduct':
$has_bought_product = $this->_has_user_bought_any_product( $current_user->ID );
return ! $has_bought_product;
case 'hasBoughtSpecificProduct':
return $this->_has_user_bought_specific_product( $current_user, $products_ids );
case 'hasNotBoughtSpecificProduct':
return ! $this->_has_user_bought_specific_product( $current_user, $products_ids );
default:
return false;
}
}
/**
* Checks whether `$current_user` has bought any specified products.
*
* @param WP_User $current_user Current user object.
* @param array $products_ids List of specefied product IDs.
*
* @return boolean Returns true if `$current_user` has bought any specified products, False otherwise.
*/
protected function _has_user_bought_specific_product( $current_user, $products_ids ) {
$has_bought_specific_product = false;
foreach ( $products_ids as $product_id ) {
$has_bought_specific_product = wc_customer_bought_product( $current_user->user_email, $current_user->ID, $product_id );
if ( $has_bought_specific_product ) {
break;
}
}
return $has_bought_specific_product;
}
/**
* Checks if `$user_id` has bought any product in WooCommerce.
*
* @param integer $user_id WordPress User ID.
*
* @return boolean Returns true if `$user_id` has any paid order, False otherwise.
*/
protected function _has_user_bought_any_product( $user_id = 0 ) {
if ( ! class_exists( 'WooCommerce' ) || ! $user_id || ! is_numeric( $user_id ) ) {
return false;
}
$paid_statuses = wc_get_is_paid_statuses();
$orders = wc_get_orders(
[
'limit' => 1,
'status' => $paid_statuses,
'customer_id' => (int) $user_id,
'return' => 'ids',
]
);
return count( $orders ) > 0 ? true : false;
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* Product Stock Condition logic swiftly crafted.
*
* @since 4.14.3
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Product Stock Condition Trait.
*/
trait ProductStockCondition {
/**
* Processes "Product Stock" condition.
*
* @since 4.14.3
*
* @param array $condition_settings The Condition settings containing:
* 'displayRule' => string,
* 'products' => array.
*
* @return boolean Returns `true` if the condition evaluation is true, `false` otherwise.
*/
protected function _process_product_stock_condition( $condition_settings ) {
if ( ! class_exists( 'WooCommerce' ) ) {
return false;
}
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : 'isInStock';
$products_raw = isset( $condition_settings['products'] ) ? $condition_settings['products'] : [];
$products_ids = array_map(
function( $item ) {
return isset( $item['value'] ) ? (int) $item['value'] : '';
},
$products_raw
);
$products_ids = array_filter( $products_ids );
$products = wc_get_products(
[
'limit' => -1,
'include' => $products_ids,
'stock_status' => 'instock',
'return' => 'ids',
]
);
$output = [
'isInStock' => count( $products ) > 0,
'isOutOfStock' => count( $products ) === 0,
];
return isset( $output[ $display_rule ] ) ? $output[ $display_rule ] : false;
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* Search Results Condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Search Results Condition Trait.
*/
trait SearchResultsCondition {
/**
* Processes "Search Results" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_search_results_condition( $condition_settings ) {
// Only check for Search.
if ( ! is_search() ) {
return false;
}
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['searchResultsDisplay'] ) ? $condition_settings['searchResultsDisplay'] : 'specificSearchQueries';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$specific_search_queries_raw = isset( $condition_settings['specificSearchQueries'] ) ? $condition_settings['specificSearchQueries'] : '';
$excluded_search_queries_raw = isset( $condition_settings['excludedSearchQueries'] ) ? $condition_settings['excludedSearchQueries'] : '';
$specific_search_queries = explode( ',', $specific_search_queries_raw );
$excluded_search_queries = explode( ',', $excluded_search_queries_raw );
switch ( $display_rule ) {
case 'specificSearchQueries':
return $this->_is_specific_search_query( $specific_search_queries );
case 'excludedSearchQueries':
return ! $this->_is_specific_search_query( $excluded_search_queries );
default:
return false;
}
}
/**
* "is specirfic serach query" Condition logic.
*
* @param array $specific_search_queries Array of search queries.
* @return boolean Indicating whether "is specirfic serach query" Condition is true or false.
*/
protected function _is_specific_search_query( $specific_search_queries ) {
$is_specific_search_query = false;
foreach ( $specific_search_queries as $search_query ) {
$is_specific_search_query = get_search_query() === $search_query;
if ( $is_specific_search_query ) {
break;
}
}
return $is_specific_search_query;
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* Tag Page condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Tag Page Condition Trait.
*/
trait TagPageCondition {
/**
* Processes "Tag Page" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_tag_page_condition( $condition_settings ) {
// Only check for Archive pages.
if ( ! is_archive() ) {
return false;
}
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['tagPageDisplay'] ) ? $condition_settings['tagPageDisplay'] : 'is';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$tags_raw = isset( $condition_settings['tags'] ) ? $condition_settings['tags'] : [];
$queried_object = get_queried_object();
$is_queried_object_valid = $queried_object instanceof \WP_Term && property_exists( $queried_object, 'taxonomy' );
if ( ! $is_queried_object_valid ) {
return false;
}
$queried_taxonomy = $queried_object->taxonomy;
$tags_raw_ids = array_map(
function( $item ) {
return $item['value'];
},
$tags_raw
);
$tax_slugs_of_catch_all_items = [];
$is_any_catch_all_selected = false;
foreach ( $tags_raw as $item ) {
if ( true === $item['isCatchAll'] ) {
$tax_slugs_of_catch_all_items[] = $item['groupSlug'];
$is_any_catch_all_selected = true;
}
}
// Logic evaluation.
$current_tag_id = get_queried_object_id();
$is_displayable = array_intersect( $tags_raw_ids, (array) $current_tag_id ) ? true : false;
if ( ! $is_displayable && $is_any_catch_all_selected ) {
$is_displayable = array_intersect( $tax_slugs_of_catch_all_items, (array) $queried_taxonomy ) ? true : false;
}
// Evaluation output.
return ( 'is' === $display_rule ) ? $is_displayable : ! $is_displayable;
}
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* Tags Condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* Tags Condition Trait.
*/
trait TagsCondition {
/**
* Processes "Tags" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_tags_condition( $condition_settings ) {
// Only check for Posts.
if ( ! is_singular() ) {
return false;
}
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['tagsDisplay'] ) ? $condition_settings['tagsDisplay'] : 'is';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$tags_raw = isset( $condition_settings['tags'] ) ? $condition_settings['tags'] : [];
$tags = array_map(
function( $item ) {
return (object) [
'id' => $item['value'],
'taxonomy_slug' => $item['groupSlug'],
];
},
$tags_raw
);
$current_queried_id = get_queried_object_id();
$has_post_specified_term = false;
$tax_slugs_of_catch_all_items = [];
$is_any_catch_all_selected = false;
$has_post_specified_taxonomy = false;
// Logic evaluation.
foreach ( $tags_raw as $item ) {
if ( true === $item['isCatchAll'] ) {
$tax_slugs_of_catch_all_items[] = $item['groupSlug'];
$is_any_catch_all_selected = true;
}
}
foreach ( $tags as $tag ) {
if ( has_term( $tag->id, $tag->taxonomy_slug, $current_queried_id ) ) {
$has_post_specified_term = true;
break;
}
}
$is_displayable = $has_post_specified_term ? true : false;
if ( ! $is_displayable && $is_any_catch_all_selected ) {
foreach ( $tax_slugs_of_catch_all_items as $tax_slug ) {
$has_post_specified_taxonomy = has_term( '', $tax_slug, $current_queried_id );
if ( $has_post_specified_taxonomy ) {
break;
}
}
$is_displayable = $has_post_specified_taxonomy ? true : false;
}
// Evaluation output.
return ( 'is' === $display_rule ) ? $is_displayable : ! $is_displayable;
}
}

View File

@@ -0,0 +1,100 @@
<?php
/**
* URL Parameter Condition logic swiftly crafted.
*
* @since 4.14.3
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* URL Parameter Condition Trait.
*/
trait UrlParameterCondition {
/**
* Processes "URL Parameter" condition.
*
* @since 4.14.3
*
* @param array $condition_settings The Condition settings containing:
* 'displayRule' => string,
* 'selectUrlParameter' => string,
* 'urlParameterName' => string,
* 'urlParameterValue' => string.
*
* @return boolean Returns `true` if the condition evaluation is true, `false` otherwise.
*/
protected function _process_url_parameter_condition( $condition_settings ) {
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : 'equals';
$select_url_parameter = isset( $condition_settings['selectUrlParameter'] ) ? $condition_settings['selectUrlParameter'] : 'specificUrlParameter';
$url_parameter_name = isset( $condition_settings['urlParameterName'] ) ? (string) $condition_settings['urlParameterName'] : '';
$url_parameter_value = isset( $condition_settings['urlParameterValue'] ) ? (string) $condition_settings['urlParameterValue'] : '';
$get_url_parameter = isset( $_GET[ $url_parameter_name ] ) ? sanitize_text_field( $_GET[ $url_parameter_name ] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No need to use nonce as there is no form processing.
$is_url_parameter_set = isset( $_GET[ $url_parameter_name ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No need to use nonce as there is no form processing.
if ( 'anyUrlParameter' === $select_url_parameter ) {
$parameter_values = $this->_get_all_parameter_values();
$output = [
'equals' => count( $parameter_values ) > 0 && array_intersect( $parameter_values, (array) $url_parameter_value ),
'exist' => count( $parameter_values ) > 0,
'doesNotExist' => count( $parameter_values ) === 0,
'doesNotEqual' => count( $parameter_values ) > 0 && ! array_intersect( $parameter_values, (array) $url_parameter_value ),
'contains' => count( $parameter_values ) > 0 && $this->_array_contains_string( $parameter_values, $url_parameter_value ),
'doesNotContain' => count( $parameter_values ) > 0 && ! $this->_array_contains_string( $parameter_values, $url_parameter_value ),
];
} else {
$output = [
'equals' => $is_url_parameter_set && $get_url_parameter === $url_parameter_value,
'exist' => $is_url_parameter_set,
'doesNotExist' => ! $is_url_parameter_set,
'doesNotEqual' => $is_url_parameter_set && $get_url_parameter !== $url_parameter_value,
'contains' => $is_url_parameter_set && strpos( $get_url_parameter, $url_parameter_value ) !== false,
'doesNotContain' => $is_url_parameter_set && strpos( $get_url_parameter, $url_parameter_value ) === false,
];
}
return isset( $output[ $display_rule ] ) ? $output[ $display_rule ] : false;
}
/**
* Returns all parameter values.
*
* @since 4.14.3
*
* @return array
*/
protected function _get_all_parameter_values() {
return array_map(
function( $value ) {
return $value;
},
$_GET // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No need to use nonce.
);
}
/**
* Checks if `$haystack` items contain `$needle` in their values.
*
* @since 4.14.3
*
* @param array $haystack The array to search in.
* @param string $needle The string needle to search for.
*
* @return boolean
*/
protected function _array_contains_string( $haystack, $needle ) {
$filtered_array = array_filter(
$haystack,
function( $value ) use ( $needle ) {
return strpos( $value, $needle ) !== false;
}
);
return count( $filtered_array ) > 0 ? true : false;
}
}

View File

@@ -0,0 +1,76 @@
<?php
/**
* User Role Condition logic swiftly crafted.
*
* @since 4.11.0
*
* @package Divi
* @sub-package Builder
*/
namespace Module\Field\DisplayConditions;
/**
* User Role Condition Trait.
*/
trait UserRoleCondition {
/**
* Processes "User Role" condition.
*
* @since 4.11.0
*
* @param array $condition_settings Containing all settings of the condition.
*
* @return boolean Condition output.
*/
protected function _process_user_role_condition( $condition_settings ) {
// Checks for additional display rule for compatibility with Conditional Display older versions which didn't use `displayRule` key.
$legacy_display_rule = isset( $condition_settings['userRoleDisplay'] ) ? $condition_settings['userRoleDisplay'] : 'is';
$display_rule = isset( $condition_settings['displayRule'] ) ? $condition_settings['displayRule'] : $legacy_display_rule;
$roles_raw = isset( $condition_settings['userRoles'] ) ? $condition_settings['userRoles'] : [];
$ids_raw = isset( $condition_settings['userIds'] ) ? $condition_settings['userIds'] : '';
$roles = array_map(
function( $item ) {
return $item['value'];
},
$roles_raw
);
$ids = isset( $ids_raw ) ? array_map( 'trim', array_filter( explode( ',', $ids_raw ) ) ) : array();
$user = wp_get_current_user();
$should_display_based_on_roles = array_intersect( $roles, (array) $user->roles ) ? true : false;
$should_display_based_on_ids = array_intersect( $ids, (array) $user->ID ) ? true : false;
$should_display = ( $should_display_based_on_roles || $should_display_based_on_ids );
return ( 'is' === $display_rule ) ? $should_display : ! $should_display;
}
/**
* Checks user role for possible conflicts.
*
* @since 4.11.0
*
* @param string $current_value Currently processing condition's conflicting value.
* @param string $prev_value Previously processed condition's conflicting value.
* @param array $conflicting_value Array of values containing the conflicting values as defined in $this->conflicts.
* @param string $current_display_rule Currently processing condition's display rule Ex. is,isNot...
* @param string $prev_display_rule Previously processed condition's display rule Ex. is,isNot...
* @param array $conflicting_display_rule_vals Array of values containing the conflicting display rules as defined in $this->conflicts.
*
* @return boolean Conflict evaluation result.
*/
protected function _is_user_role_conflicted( $current_value, $prev_value, $conflicting_value, $current_display_rule, $prev_display_rule, $conflicting_display_rule_vals ) {
$current_value = explode( '|', $current_value );
$prev_value = explode( '|', $prev_value );
$is_current_value_conflicted = ! empty( array_intersect( $current_value, $conflicting_value ) );
$is_prev_value_conflicted = ! empty( array_intersect( $prev_value, $conflicting_value ) );
$is_current_display_rule_conflicted = in_array( $current_display_rule, $conflicting_display_rule_vals, true );
$is_prev_display_rule_conflicted = in_array( $prev_display_rule, $conflicting_display_rule_vals, true );
if ( $is_current_value_conflicted && $is_prev_value_conflicted && $is_current_display_rule_conflicted && $is_prev_display_rule_conflicted ) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,31 @@
<?php
/**
* Class ET_Builder_Module_Field_Template_Base
* Base class for field renderers in BB
*/
abstract class ET_Builder_Module_Field_Template_Base {
/**
* @param $field
* @param $render_helper
*
* @return template
*/
abstract public function render( $field, $render_helper );
protected function _render_icon( $icon_name ) {
return '<div class="et-pb-icon">
<svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><%= et_builder_template_options.options_icons[ "' . esc_attr( $icon_name ) . '" ] %></svg>
</div>';
}
protected function _wrap_field_name( $name ) {
// Don't add 'et_pb_' prefix to the "Admin Label" field
if ( 'admin_label' === $name ) {
return $name;
}
return 'et_pb_' . $name;
}
}

View File

@@ -0,0 +1,76 @@
<?php
require_once ET_BUILDER_DIR . 'module/field/template/Base.php';
class ET_Builder_Module_Field_Template_Tabbed extends ET_Builder_Module_Field_Template_Base {
/**
* @param $field array Field settings
* @param $render_helper ET_Builder_Element
*
* @return string Control rendered html
*/
public function render( $field, $render_helper ) {
ob_start();
if ( ! empty( $field['composite_structure'] ) && is_array( $field['composite_structure'] ) ) {
?>
<div class="et-pb-composite-tabbed-wrapper">
<div id="<?php echo esc_attr( $this->_wrap_field_name( $field['name'] ) ); ?>" class="<?php echo esc_attr( $this->_get_control_class() ); ?>" data-attr-suffix="<?php echo esc_attr( $field['attr_suffix'] ); ?>">
<div class="et-pb-outside-preview-container">
<?php echo et_core_esc_previously( $this->_render_outside_preview() ); ?>
</div>
<ul class="et-pb-settings-tabs">
<?php foreach ( $field['composite_structure'] as $tab => $structure ) : ?>
<li class="et-pb-settings-tab">
<a href="#" class="et-pb-settings-tab-title" data-tab="<?php echo esc_attr( $tab ); ?>">
<?php
if ( isset( $structure['label'] ) && ! empty( $structure['label'] ) ) {
echo esc_html( $structure['label'] );
}
// render the icon if there is one defined.
if ( isset( $structure['icon'] ) && ! empty( $structure['icon'] ) ) {
echo et_core_esc_previously( $this->_render_icon( esc_html( $structure['icon'] ) ) );
}
?>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php foreach ( $field['composite_structure'] as $tab => $structure ) : ?>
<div class="et-pb-settings-tab-content" data-tab="<?php echo esc_attr( $tab ); ?>">
<div class="et-pb-tab-preview-container" data-tab="<?php echo esc_attr( $tab ); ?>">
<?php echo et_core_esc_previously( $this->_render_tab_preview( $tab ) ); ?>
</div>
<?php if ( ! empty( $structure['controls'] ) && is_array( $structure['controls'] ) ) : ?>
<?php foreach ( $structure['controls'] as $name => $control ) : ?>
<?php $control['name'] = $name; ?>
<?php $control['tab_slug'] = $field['tab_slug']; ?>
<?php $hidden = 'hidden' === $control['type'] ? ' et_pb_hidden' : ''; ?>
<div class="et-pb-composite-option et-pb-composite-tabbed-option et-pb-option<?php echo et_core_intentionally_unescaped( $hidden, 'fixed_string' ); ?>" data-control-index="<?php echo esc_attr( $name ); ?>" data-option_name="<?php echo esc_attr( $name ); ?>">
<?php echo et_core_esc_previously( $render_helper->wrap_settings_option_label( $control ) ); ?>
<?php echo et_core_esc_previously( $render_helper->wrap_settings_option_field( $control, $name ) ); ?>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</div>
<span class="et-pb-reset-setting et-pb-reset-skip et-pb-composite-tabbed-reset-setting"></span>
<?php
}
return ob_get_clean();
}
protected function _render_outside_preview() {
return '';
}
protected function _render_tab_preview( $tab ) {
return '';
}
protected function _get_control_class() {
return 'et-pb-composite-tabbed';
}
}

View File

@@ -0,0 +1,73 @@
<?php
require_once ET_BUILDER_DIR . 'module/field/template/Base.php';
class ET_Builder_Module_Field_Template_Border_Radius extends ET_Builder_Module_Field_Template_Base {
public function render( $field, $render_helper ) {
ob_start();
?>
<div class="et-pb-border-radius-wrap">
<div class="et-pb-border-radius-wrap-column">
<div class="et-pb-border-radius-top-left">
<input class="et-pb-border-radius-option-input"
type="text"
value=""
data-corner="top-left"
/>
</div>
<div class="et-pb-border-radius-bottom-left">
<input class="et-pb-border-radius-option-input"
type="text"
value=""
data-corner="bottom-left"
/>
</div>
</div>
<div class="et-pb-border-radius-wrap-column">
<div class="et-pb-border-radius-preview">
<div class="et-pb-border-radius-wrap-link-button">
<a href="#" class="">
<?php echo et_core_esc_previously( $this->_render_icon( 'border-link' ) ); ?>
</a>
</div>
</div>
</div>
<div class="et-pb-border-radius-wrap-column">
<div class="et-pb-border-radius-top-right">
<input class="et-pb-border-radius-option-input"
type="text"
value=""
data-corner="top-right"
/>
</div>
<div class="et-pb-border-radius-bottom-right">
<input class="et-pb-border-radius-option-input"
type="text"
value=""
data-corner="bottom-right"
/>
</div>
</div>
</div>
<span class="et-pb-reset-setting"></span>
<?php
$name = $render_helper->get_field_name( $field );
// Avoid underscore's _.template() being treated as PHP tags when PHP server's asp_tags is enabled
$value_open_tag = '<%-';
$value_close_tag = '%>';
?>
<?php $attr_name = $render_helper->get_type() === 'child' ? 'data.' . esc_attr( $name ) : esc_attr( $name ); ?>
<?php $default = isset( $field['default'] ) ? $field['default'] : 'on||||'; ?>
<input id="<?php echo esc_attr( $name ); ?>"
type="hidden"
name="<?php echo esc_attr( $name ); ?>"
data-default="<?php echo esc_attr( $default ); ?>"
value="<?php echo et_core_intentionally_unescaped( $value_open_tag, 'fixed_string' ); ?> typeof( <?php echo esc_attr( $attr_name ); ?> ) !== 'undefined' ? <?php echo esc_attr( $attr_name ) . '.replace(/%91/g, "[").replace(/%93/g, "]").replace(/%22/g, "\"")'; ?> : '' <?php echo et_core_intentionally_unescaped( $value_close_tag, 'fixed_string' ); ?>"
class="et-pb-main-setting"
/>
<?php
return ob_get_clean();
}
}

View File

@@ -0,0 +1,20 @@
<?php
require_once ET_BUILDER_DIR . 'module/field/template/Tabbed.php';
class ET_Builder_Module_Field_Template_Border_Styles extends ET_Builder_Module_Field_Template_Tabbed {
protected function _get_control_class() {
return 'et-pb-composite-tabbed-border-style';
}
protected function _render_tab_preview( $tab ) {
ob_start();
?>
<div class="et-pb-tab-preview-container-column">
<div class="et-pb-tab-preview-container-preview"></div>
</div>
<?php
return ob_get_clean();
}
}