* @copyright Copyright (c) 2022, Phi Phan
*/
namespace BoldBlocks;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;
if ( ! class_exists( CustomStyle::class ) ) :
/**
* The controller class for style.
*/
class CustomStyle extends CoreComponent {
/**
* The style handle for feature styles
*
* @var array
*/
private $feature_style_handle = 'cbb-dynamic-style';
/**
* Store the context of styles
*
* @var array
*/
private $style_contexts = [
'loops' => [],
'loop' => '',
'post' => 0,
'counter' => 0,
'content' => false,
'content_counter' => 0,
];
/**
* Store the ID of query loop with carousel or grid layout
*
* @var array
*/
private $query_ids = [];
/**
* The cached styles
*
* @var array
*/
private $styles = [];
/**
* The refined breakpoints
*
* @var array
*/
private $breakpoints = [];
/**
* The list of non-cbb blocks that support dynamic style
*
* @var array
*/
public $non_cbb_blocks = [ 'core/template-part' ];
/**
* Run main hooks
*
* @return void
*/
public function run() {
// Register setting fields.
add_action( 'init', [ $this, 'register_setting_fields' ] );
// Register style handle.
add_action( 'init', [ $this, 'register_feature_style' ] );
// Render block style.
add_filter( 'render_block', [ $this, 'render_block_style' ], 10, 3 );
// Render link to post first.
add_filter( 'render_block', [ $this, 'build_block_link_to_post' ], 20, 3 );
// Build dynamic block overlay.
add_filter( 'render_block', [ $this, 'build_block_overlay' ], 20, 3 );
// Build dynamic block background.
add_filter( 'render_block', [ $this, 'build_block_background' ], 30, 3 );
// Build animations.
add_filter( 'render_block', [ $this, 'build_block_animations' ], 30, 3 );
// Render accordions.
add_filter( 'render_block_data', [ $this, 'render_accordion_block_data' ] );
// Render accordion item blocks.
add_filter( 'render_block', [ $this, 'render_accordion_item_block' ], 10, 3 );
// Render grid style for the query loop block.
add_filter( 'render_block_core/query', [ $this, 'render_query_loop_grid_style' ], 10, 3 );
// Render carousel settings for the query loop block.
add_filter( 'render_block_core/query', [ $this, 'render_query_loop_carousel_layout' ], 10, 3 );
// Enqueue block styles.
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_responsive_styles' ] );
// Enqueue breakpoints.
add_action( 'init', [ $this, 'enqueue_reponsive_breakpoints' ] );
// Update loop id.
add_filter( 'render_block_context', [ $this, 'update_loop_id' ], 10, 2 );
// Reset loop id.
add_filter( 'render_block', [ $this, 'reset_loop_id' ], 10, 2 );
// Render preload style for carousels.
add_filter( 'cbb_get_block_style', [ $this, 'get_carousel_preload_style' ], 10, 2 );
// Support dynamic style for none-cbb blocks.
add_filter( 'cbb_support_block_style', [ $this, 'support_block_style' ], 10, 2 );
// Set a block layout for non-cbb blocks.
add_filter( 'cbb_get_block_layout', [ $this, 'get_block_layout' ], 10, 2 );
// Render sticky data for the core/template-part block.
add_filter( 'render_block_core/template-part', [ $this, 'render_template_part_sticky' ], 10, 3 );
// Render settings for carousels.
add_filter( 'render_block', [ $this, 'render_carousel_settings' ], 30, 3 );
// Migrate CBB class for old blocks.
add_filter( 'render_block', [ $this, 'migrate_cbb_class' ], 30, 3 );
// Render steps block data.
add_filter( 'render_block_data', [ $this, 'render_steps_block_data' ], 10, 3 );
// Render steps blocks.
add_filter( 'render_block', [ $this, 'render_steps_attributes' ], 10, 2 );
// Sanitize post data before saving to database.
add_filter( 'wp_insert_post_data', [ $this, 'sanitize_post_data' ] );
}
/**
* Register custom setting fields
*
* @return void
*/
public function register_setting_fields() {
// phpcs:ignore PluginCheck.CodeAnalysis.SettingSanitization.register_settingDynamic
register_setting(
'cbb',
'cbb_breakpoints',
[
'type' => 'array',
'show_in_rest' => [
'name' => 'CBBBreakpoints',
'schema' => [
'items' => [
'type' => 'object',
'properties' => [
'breakpoint' => [
'type' => 'number',
],
'prefix' => [
'type' => 'string',
],
],
'additionalProperties' => [
'type' => 'string',
],
],
],
],
'default' => [
[
'breakpoint' => 576,
'prefix' => 'sm',
],
[
'breakpoint' => 768,
'prefix' => 'md',
],
[
'breakpoint' => 1024,
'prefix' => 'lg',
],
],
]
);
}
/**
* Enqueue responsive breakpoints
*
* @return void
*/
public function enqueue_reponsive_breakpoints() {
// Custom blocks handle.
$custom_blocks_handler = $this->the_plugin_instance->get_component( CustomBlocks::class );
// Get breakpoint setting.
$breakpoints = $this->get_breakpoints();
// Get breakpoint scripts.
$breakpoint_script = 'var CBBBreakpoints=' . wp_json_encode( $breakpoints );
// Load JS breakpoints.
wp_add_inline_script( $custom_blocks_handler->custom_blocks_handle, $breakpoint_script, 'before' );
wp_add_inline_script( $custom_blocks_handler->carousel_blocks_frontend_handle, $breakpoint_script, 'before' );
}
/**
* Register a handle for enqueue block styles.
*
* @return void
*/
public function register_feature_style() {
// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
wp_register_style( $this->feature_style_handle, '' );
}
/**
* Enqueue all feature styles
*
* @return void
*/
public function enqueue_responsive_styles() {
if ( wp_is_block_theme() ) {
wp_enqueue_style( $this->feature_style_handle );
}
wp_add_inline_style( $this->feature_style_handle, $this->get_responsive_styles() );
}
/**
* Build responsive styles
*
* @return string
*/
private function get_responsive_styles() {
$style = '';
$breakpoints = $this->get_breakpoints();
$md_breakpoint = absint( $breakpoints['md']['breakpoint'] ?? 768 );
$md_breakpoint_max = $md_breakpoint - 1;
$lg_breakpoint = absint( $breakpoints['lg']['breakpoint'] ?? 1024 );
$lg_breakpoint_max = $lg_breakpoint - 1;
$md_start = "@media(min-width:{$md_breakpoint}px){";
$lg_start = "@media(min-width:{$lg_breakpoint}px){";
$end = '}';
// Padding.
$padding_style = '.sm-cbb-padding-top{padding-top:var(--cbb--padding-top) !important;}.sm-cbb-padding-right{padding-right:var(--cbb--padding-right) !important;}.sm-cbb-padding-bottom{padding-bottom:var(--cbb--padding-bottom) !important;}.sm-cbb-padding-left{padding-left:var(--cbb--padding-left) !important;}';
$padding_style .= "{$md_start}.md-cbb-padding-top{padding-top:var(--cbb--padding-top) !important;}.md-cbb-padding-right{padding-right:var(--cbb--padding-right) !important;}.md-cbb-padding-bottom{padding-bottom:var(--cbb--padding-bottom) !important;}.md-cbb-padding-left{padding-left:var(--cbb--padding-left) !important;}{$end}";
$padding_style .= "{$lg_start}.lg-cbb-padding-top{padding-top:var(--cbb--padding-top) !important;}.lg-cbb-padding-right{padding-right:var(--cbb--padding-right) !important;}.lg-cbb-padding-bottom{padding-bottom:var(--cbb--padding-bottom) !important;}.lg-cbb-padding-left{padding-left:var(--cbb--padding-left) !important;}{$end}";
$style .= $padding_style;
// Margin.
$margin_style = '.sm-cbb-margin-top{margin-top:var(--cbb--margin-top) !important;}.sm-cbb-margin-right{margin-right:var(--cbb--margin-right) !important;}.sm-cbb-margin-bottom{margin-bottom:var(--cbb--margin-bottom) !important;}.sm-cbb-margin-left{margin-left:var(--cbb--margin-left) !important;}';
$margin_style .= "{$md_start}.md-cbb-margin-top{margin-top:var(--cbb--margin-top) !important;}.md-cbb-margin-right{margin-right:var(--cbb--margin-right) !important;}.md-cbb-margin-bottom{margin-bottom:var(--cbb--margin-bottom) !important;}.md-cbb-margin-left{margin-left:var(--cbb--margin-left) !important;}{$end}";
$margin_style .= "{$lg_start}.lg-cbb-margin-top{margin-top:var(--cbb--margin-top) !important;}.lg-cbb-margin-right{margin-right:var(--cbb--margin-right) !important;}.lg-cbb-margin-bottom{margin-bottom:var(--cbb--margin-bottom) !important;}.lg-cbb-margin-left{margin-left:var(--cbb--margin-left) !important;}{$end}";
$style .= $margin_style;
// Block gap.
$block_gap_style = '.sm-cbb-block-gap > *{margin-block-start:0;margin-block-end:0;}.sm-cbb-block-gap > * + *{margin-block-start:var(--cbb--block-gap) !important;margin-block-end:0;}';
$block_gap_style .= "{$md_start}.md-cbb-block-gap > *{margin-block-start:0;margin-block-end:0;}.md-cbb-block-gap > * + *{margin-block-start:var(--cbb--block-gap) !important;margin-block-end:0;}{$end}";
$block_gap_style .= "{$lg_start}.lg-cbb-block-gap > *{margin-block-start:0;margin-block-end:0;}.lg-cbb-block-gap > * + *{margin-block-start:var(--cbb--block-gap) !important;margin-block-end:0;}{$end}";
$style .= $block_gap_style;
// Border.
$border_style = '.sm-cbb-border-top{border-top:var(--cbb--border-top) !important;}.sm-cbb-border-right{border-right:var(--cbb--border-right) !important;}.sm-cbb-border-bottom{border-bottom:var(--cbb--border-bottom) !important;}.sm-cbb-border-left{border-left:var(--cbb--border-left) !important;}';
$border_style .= "{$md_start}.md-cbb-border-top{border-top:var(--cbb--border-top) !important;}.md-cbb-border-right{border-right:var(--cbb--border-right) !important;}.md-cbb-border-bottom{border-bottom:var(--cbb--border-bottom) !important;}.md-cbb-border-left{border-left:var(--cbb--border-left) !important;}{$end}";
$border_style .= "{$lg_start}.lg-cbb-border-top{border-top:var(--cbb--border-top) !important;}.lg-cbb-border-right{border-right:var(--cbb--border-right) !important;}.lg-cbb-border-bottom{border-bottom:var(--cbb--border-bottom) !important;}.lg-cbb-border-left{border-left:var(--cbb--border-left) !important;}{$end}";
$style .= $border_style;
// Border radius.
$radius_style = '.sm-cbb-border-radius{overflow:hidden;border-radius: var(--cbb--border-radius) !important;}';
$radius_style .= "{$md_start}.md-cbb-border-radius{overflow:hidden;border-radius: var(--cbb--border-radius) !important;}{$end}";
$radius_style .= "{$lg_start}.lg-cbb-border-radius{overflow:hidden;border-radius: var(--cbb--border-radius) !important;}{$end}";
$style .= $radius_style;
// Width.
$width_style = '.sm-cbb-width{width:var(--cbb--width) !important;}';
$width_style .= "{$md_start}.md-cbb-width{width:var(--cbb--width) !important;}{$end}";
$width_style .= "{$lg_start}.lg-cbb-width{width:var(--cbb--width) !important;}{$end}";
$style .= $width_style;
// Height.
$height_style = '.sm-cbb-height{height:var(--cbb--height) !important;}';
$height_style .= "{$md_start}.md-cbb-height{height:var(--cbb--height) !important;}{$end}";
$height_style .= "{$lg_start}.lg-cbb-height{height:var(--cbb--height) !important;}{$end}";
$style .= $height_style;
// Aspect ratio.
$aspect_ratio_style = '.sm-cbb-aspect-ratio{aspect-ratio:var(--cbb--aspect-ratio);}';
$aspect_ratio_style .= "{$md_start}.md-cbb-aspect-ratio{aspect-ratio:var(--cbb--aspect-ratio);}{$end}";
$aspect_ratio_style .= "{$lg_start}.lg-cbb-aspect-ratio{aspect-ratio:var(--cbb--aspect-ratio);}{$end}";
$style .= $aspect_ratio_style;
// Text alignment.
$text_align_style = '.sm-cbb-text-align{text-align:var(--cbb--text-align);}';
$text_align_style .= "{$md_start}.md-cbb-text-align{text-align:var(--cbb--text-align);}{$end}";
$text_align_style .= "{$lg_start}.lg-cbb-text-align{text-align:var(--cbb--text-align);}{$end}";
$style .= $text_align_style;
// Vertical alignment.
$v_align_style = '.sm-cbb-v-align.cbb-layout-grid{display:grid;align-content:var(--cbb--v-align);}.sm-cbb-v-align.cbb-layout-grid>*{width:100%;}.sm-cbb-v-align.cbb-align-items{align-items:var(--cbb--v-align);}.sm-cbb-v-align.cbb-align-self{align-self:var(--cbb--v-align);}';
$v_align_style .= "{$md_start}.md-cbb-v-align.cbb-layout-grid{display:grid;align-content:var(--cbb--v-align);}.md-cbb-v-align.cbb-layout-grid>*{width:100%;}.md-cbb-v-align.cbb-align-items{align-items:var(--cbb--v-align);}.md-cbb-v-align.cbb-align-self{align-self:var(--cbb--v-align);}{$end}";
$v_align_style .= "{$lg_start}.lg-cbb-v-align.cbb-layout-grid{display:grid;align-content:var(--cbb--v-align);}.lg-cbb-v-align.cbb-layout-grid>*{width:100%;}.lg-cbb-v-align.cbb-align-items{align-items:var(--cbb--v-align);}.lg-cbb-v-align.cbb-align-self{align-self:var(--cbb--v-align);}{$end}";
$style .= $v_align_style;
// Justify alignment.
$h_align_style = '.sm-cbb-h-align.cbb-layout-grid{display:grid;justify-content:var(--cbb--h-align);}.sm-cbb-h-align.cbb-layout-grid>*{width:auto;}.sm-cbb-h-align.cbb-justify-items{justify-items:var(--cbb--h-align);}.sm-cbb-h-align.cbb-justify-self{justify-self:var(--cbb--h-align);}';
$h_align_style .= "{$md_start}.md-cbb-h-align.cbb-layout-grid{display:grid;justify-content:var(--cbb--h-align);}.md-cbb-h-align.cbb-layout-grid>*{width:auto;}.md-cbb-h-align.cbb-justify-items{justify-items:var(--cbb--h-align);}.md-cbb-h-align.cbb-justify-self{justify-self:var(--cbb--h-align);}{$end}";
$h_align_style .= "{$lg_start}.lg-cbb-h-align.cbb-layout-grid{display:grid;justify-content:var(--cbb--h-align);}.lg-cbb-h-align.cbb-layout-grid>*{width:auto;}.lg-cbb-h-align.cbb-justify-items{justify-items:var(--cbb--h-align);}.lg-cbb-h-align.cbb-justify-self{justify-self:var(--cbb--h-align);}{$end}";
$style .= $h_align_style;
// Transform.
$transform_style = '.sm-cbb-transform{transform:var(--cbb--transform);transform-origin:var(--cbb--transform-origin)}';
$transform_style .= "{$md_start}.md-cbb-transform{transform:var(--cbb--transform);transform-origin:var(--cbb--transform-origin)}{$end}";
$transform_style .= "{$lg_start}.lg-cbb-transform{transform:var(--cbb--transform);transform-origin:var(--cbb--transform-origin)}{$end}";
$style .= $transform_style;
// Visibility.
$visibility_style = "@media(max-width:{$md_breakpoint_max}px){.cbb-hidden-sm{display:none !important;}}";
$visibility_style .= "@media(min-width:{$md_breakpoint}px) and (max-width:{$lg_breakpoint_max}px){.cbb-hidden-md{display:none !important;}}";
$visibility_style .= "@media(min-width:{$lg_breakpoint}px){.cbb-hidden-lg{display:none !important;}}";
$style .= $visibility_style;
// Grid: Columns, rows, auto-rows, & gap.
$grid_style = '.sm-cbb-grid-columns > *,.sm-cbb-grid-columns > * + *{margin:0}.sm-cbb-grid-columns{grid-template-columns:var(--cbb--grid-columns);}.sm-cbb-grid-rows{grid-template-rows:var(--cbb--grid-rows);}.sm-cbb-grid-auto-rows{grid-auto-rows:var(--cbb--grid-auto-rows);}.sm-cbb-grid-gap-column{column-gap:var(--cbb--grid-gap-column);}.sm-cbb-grid-gap-row{row-gap:var(--cbb--grid-gap-row);}';
$grid_style .= "{$md_start}.md-cbb-grid-columns > * {margin:0}.md-cbb-grid-columns{grid-template-columns:var(--cbb--grid-columns);}.md-cbb-grid-rows{grid-template-rows:var(--cbb--grid-rows);}.md-cbb-grid-auto-rows{grid-auto-rows:var(--cbb--grid-auto-rows);}.md-cbb-grid-gap-column{column-gap:var(--cbb--grid-gap-column);}.md-cbb-grid-gap-row{row-gap:var(--cbb--grid-gap-row);}{$end}";
$grid_style .= "{$lg_start}.lg-cbb-grid-columns > * {margin:0}.lg-cbb-grid-columns{grid-template-columns:var(--cbb--grid-columns);}.lg-cbb-grid-rows{grid-template-rows:var(--cbb--grid-rows);}.lg-cbb-grid-auto-rows{grid-auto-rows:var(--cbb--grid-auto-rows);}.lg-cbb-grid-gap-column{column-gap:var(--cbb--grid-gap-column);}.lg-cbb-grid-gap-row{row-gap:var(--cbb--grid-gap-row);}{$end}";
$style .= $grid_style;
// Grid item: columnSpan & rowSpan.
$grid_item_style = '.sm-cbb-grid-item-column{grid-column:var(--cbb--grid-item-column);}.sm-cbb-grid-item-row{grid-row:var(--cbb--grid-item-row);}.sm-cbb-grid-item-order{order:var(--cbb--grid-item-order);}';
$grid_item_style .= "{$md_start}.md-cbb-grid-item-column{grid-column:var(--cbb--grid-item-column);}.md-cbb-grid-item-row{grid-row:var(--cbb--grid-item-row);}.md-cbb-grid-item-order{order:var(--cbb--grid-item-order);}{$end}";
$grid_item_style .= "{$lg_start}.lg-cbb-grid-item-column{grid-column:var(--cbb--grid-item-column);}.lg-cbb-grid-item-row{grid-row:var(--cbb--grid-item-row);}.lg-cbb-grid-item-order{order:var(--cbb--grid-item-order);}{$end}";
$style .= $grid_item_style;
// Accordion gap.
$accordion_gap_style = '.sm-cbb-accordion-gap{display:flex;flex-direction:column;gap:var(--cbb--accordion-gap);}.sm-cbb-a-has-border > .is-accordion-item{border-top:var(--cbb--ab-top);}.sm-cbb-a-no-border > .is-accordion-item{border-top:0;}';
$accordion_gap_style .= "{$md_start}.md-cbb-accordion-gap{display:flex;flex-direction:column;gap:var(--cbb--accordion-gap);}.md-cbb-a-has-border > .is-accordion-item{border-top:var(--cbb--ab-top);}.md-cbb-a-no-border > .is-accordion-item{border-top:0;}{$end}";
$accordion_gap_style .= "{$lg_start}.lg-cbb-accordion-gap{display:flex;flex-direction:column;gap:var(--cbb--accordion-gap);}.lg-cbb-a-has-border > .is-accordion-item{border-top:var(--cbb--ab-top);}.lg-cbb-a-no-border > .is-accordion-item{border-top:0;}{$end}";
$style .= $accordion_gap_style;
// Accordion padding.
// We don't need padding style, because it has a default value.
// Sticky offset.
$sticky_offset_style = '.is-stick-to-top.sm-cbb-sticky-offset{top: var(--cbb--sticky-offset);}.is-stick-to-bottom.sm-cbb-sticky-offset{bottom: var(--cbb--sticky-offset);}';
$sticky_offset_style .= "{$md_start}.is-stick-to-top.md-cbb-sticky-offset{top: var(--cbb--sticky-offset);}.is-stick-to-bottom.md-cbb-sticky-offset{bottom: var(--cbb--sticky-offset);}{$end}";
$sticky_offset_style .= "{$lg_start}.is-stick-to-top.lg-cbb-sticky-offset{top: var(--cbb--sticky-offset);}.is-stick-to-bottom.lg-cbb-sticky-offset{bottom: var(--cbb--sticky-offset);}{$end}";
$style .= $sticky_offset_style;
// Offcanvas.
$offcanvas_style = "{$md_start}.is-offcanvas.placement-start,.is-offcanvas.placement-end{width:var(--bb--modal-width--md,25rem);}.is-offcanvas.placement-top,.is-offcanvas.placement-bottom{height:var(--bb--modal-height--md,30vh);}{$end}";
$offcanvas_style .= "{$lg_start}.is-offcanvas.placement-start,.is-offcanvas.placement-end{width:var(--bb--modal-width--lg,25rem);}.is-offcanvas.placement-top,.is-offcanvas.placement-bottom{height:var(--bb--modal-height--lg,30vh);}{$end}";
$style .= $offcanvas_style;
// Popover.
$popover_style = "{$md_start}.is-popover{width:var(--bb--modal-width--md,auto);height:var(--bb--modal-height--md,auto);}{$end}";
$popover_style .= "{$lg_start}.is-popover{width:var(--bb--modal-width--lg,auto);height:var(--bb--modal-height--lg,auto);}{$end}";
$style .= $popover_style;
// Modal.
$modal_style = "{$md_start}.is-modal > .bb-modal-dialog{width:var(--bb--modal-width--md,32rem);}.is-modal > .bb-modal-dialog[style*=\"--bb--modal-height--md:\"]{height:var(--bb--modal-height--md);}.modal--custom-position{align-items:var(--bb--modal-v-align--md);justify-content:var(--bb--modal-h-align--md);}{$end}";
$modal_style .= "{$lg_start}.is-modal > .bb-modal-dialog{width:var(--bb--modal-width--md,50rem);}.is-modal > .bb-modal-dialog[style*=\"--bb--modal-height--lg:\"]{height:var(--bb--modal-height--lg);}.modal--custom-position{align-items:var(--bb--modal-v-align--lg);justify-content:var(--bb--modal-h-align--lg);}{$end}";
$style .= $modal_style;
// Mobile background.
$background_style = '.cbb-mobile-image > .bb\:block-background--img{display:none !important;}.cbb-mobile-image > .is-mobile-image {display:block !important;}';
$background_style .= "@media(min-width:{$md_breakpoint}px){.cbb-mobile-image > .bb\:block-background--img{display:block !important;}.cbb-mobile-image > .is-mobile-image {display:none !important;}}";
$background_style .= "@media(max-width:{$md_breakpoint_max}px){.cbb-mobile-background{background-image: var(--cbb-mobile-background) !important;}}";
$style .= $background_style;
return $style;
}
/**
* Render style for all supported blocks
*
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
public function render_block_style( $block_content, $block, $block_instance ) {
// Ignore admin side.
if ( is_admin() ) {
return $block_content;
}
// Bail if the block has no style.
if ( ! $this->has_style( $block, $block_instance ) ) {
return $block_content;
}
// Buil selector.
$selector = $this->generate_selector( $block['blockName'], $block_instance );
// Get custom style.
$is_in_cache = isset( $this->styles[ $selector ] );
if ( $is_in_cache ) {
$block_styles = $this->styles[ $selector ];
} else {
$block_styles = $this->get_block_style( $block, $selector, $block_instance );
// Add to the cache.
$this->styles[ $selector ] = $block_styles;
}
if ( empty( $block_styles['style'] ?? '' ) ) {
if ( isset( $block_styles['classes'] ) && count( $block_styles['classes'] ) > 1 ) {
// There is no style, but we have class-features like visibility.
$block_content = $this->add_class_to_block( $block_content, implode( ' ', $block_styles['classes'] ) );
}
return $block_content;
}
if ( ! wp_is_block_theme() ) {
// Enqueue style.
wp_enqueue_style( $this->feature_style_handle );
}
if ( ! $is_in_cache ) {
wp_add_inline_style( $this->feature_style_handle, $block_styles['style'] );
}
// Add selector to block wrapper element.
$block_content = $this->add_class_to_block( $block_content, implode( ' ', $block_styles['classes'] ) );
return $block_content;
}
/**
* Get block custom style
*
* @param array $block
* @param string $selector
* @param WP_Block $block_instance
* @return string
*/
private function get_block_style( $block, $selector, $block_instance ) {
$style = '';
$class_selector = '.' . $selector;
$classes = [ $selector ];
$style_array = [];
$responsive_style_array = [];
// Get responsive settings.
$breakpoints = $this->get_breakpoints();
// Get the settings.
$settings = $block['attrs']['boldblocks'];
// Features.
$features = [
'padding' => [
'func_build_responsive_style' => [ $this, 'build_padding_style' ],
'group' => 'spacing',
],
'margin' => [
'func_build_responsive_style' => [ $this, 'build_margin_style' ],
'group' => 'spacing',
],
'blockGap' => [
'func_build_responsive_style' => [ $this, 'build_block_gap_style' ],
'group' => 'spacing',
],
'border' => [ 'func_build_responsive_style' => [ $this, 'build_border_style' ] ],
'borderRadius' => [ 'func_build_responsive_style' => [ $this, 'build_border_radius_style' ] ],
'width' => [ 'func_build_responsive_style' => [ $this, 'build_width_style' ] ],
'height' => [ 'func_build_responsive_style' => [ $this, 'build_height_style' ] ],
'aspectRatio' => [ 'func_build_responsive_style' => [ $this, 'build_aspect_ratio_style' ] ],
'textAlignment' => [ 'func_build_responsive_style' => [ $this, 'build_text_align_style' ] ],
'verticalAlignment' => [
'func_build_responsive_style' => [ $this, 'build_vertical_align_style' ],
'func_build_dependent_style' => [ $this, 'build_vertical_align_dependent_style' ],
],
'justifyAlignment' => [
'func_build_responsive_style' => [ $this, 'build_justify_align_style' ],
'func_build_dependent_style' => [ $this, 'build_justify_align_dependent_style' ],
],
'transform' => [
'func_build_responsive_style' => [ $this, 'build_transform_style' ],
'func_build_dependent_style' => [ $this, 'build_transform_dependent_style' ],
],
'hidden' => [
'func_build_style' => [ $this, 'build_hidden_style' ],
'group' => 'visibility',
],
'columns' => [
'func_build_responsive_style' => [ $this, 'build_columns_style' ],
'group' => 'grid',
'layout_type' => 'grid',
],
'rows' => [
'func_build_responsive_style' => [ $this, 'build_rows_style' ],
'group' => 'grid',
'layout_type' => 'grid',
],
'autoRows' => [
'func_build_responsive_style' => [ $this, 'build_auto_rows_style' ],
'group' => 'grid',
'layout_type' => 'grid',
],
'gap' => [
'func_build_responsive_style' => [ $this, 'build_gap_style' ],
'group' => 'grid',
'layout_type' => 'grid',
],
'columnSpan' => [
'func_build_responsive_style' => [ $this, 'build_column_span_style' ],
'group' => 'gridItem',
'layout_type' => 'gridItem',
],
'rowSpan' => [
'func_build_responsive_style' => [ $this, 'build_row_span_style' ],
'group' => 'gridItem',
'layout_type' => 'gridItem',
],
'accordionGap' => [
'func_build_responsive_style' => [ $this, 'build_accordion_gap_style' ],
'func_build_dependent_style' => [ $this, 'build_accordion_gap_dependent_style' ],
'group' => 'accordion',
'setting_name' => 'gap',
'layout_type' => 'accordion',
],
'accordionPadding' => [
'func_build_responsive_style' => [ $this, 'build_accordion_padding_style' ],
'group' => 'accordion',
'setting_name' => 'padding',
'layout_type' => 'accordion',
],
'accordionBorder' => [
'func_build_style' => [ $this, 'build_accordion_border_style' ],
'group' => 'accordion',
'setting_name' => 'border',
'layout_type' => 'accordion',
],
'accordionRadius' => [
'func_build_style' => [ $this, 'build_accordion_radius_style' ],
'group' => 'accordion',
'setting_name' => 'radius',
'layout_type' => 'accordion',
],
'stickyOffset' => [
'func_build_responsive_style' => [ $this, 'build_sticky_offset_style' ],
'group' => 'sticky',
'setting_name' => 'offset',
],
];
$block_layout = apply_filters( 'cbb_get_block_layout', $block_instance->block_type->supports['layoutType'] ?? '', $block );
foreach ( $features as $feature => $feature_args ) {
if ( isset( $feature_args['layout_type'] ) && $block_layout !== $feature_args['layout_type'] ) {
continue;
}
if ( ! in_array( $feature, [ 'border', 'hidden' ], true ) && 'accordionItem' === $block_layout ) {
continue;
}
if ( ! in_array( $feature, [ 'border', 'hidden', 'accordionGap', 'accordionPadding', 'accordionBorder', 'accordionRadius' ], true ) && 'accordion' === $block_layout ) {
continue;
}
if ( 'stickyOffset' === $feature ) {
// Not supported block layouts.
if ( ! in_array( $block_layout, [ 'standalone', 'gridItem' ], true ) ) {
continue;
}
// Not enable sticky or enabling scrolling up.
if ( ! ( $settings['sticky']['enable'] ?? false ) || ( $settings['sticky']['isStickOnScrollingUp'] ?? false ) ) {
continue;
}
}
$group_name = $feature_args['group'] ?? null;
$setting_name = $feature_args['setting_name'] ?? $feature;
if ( $this->is_valid_value( $group_name ) ) {
$setting_value = $settings[ $group_name ][ $setting_name ] ?? null;
} else {
$setting_value = $settings[ $setting_name ] ?? null;
}
if ( empty( $setting_value ) ) {
continue;
}
$args = array_merge(
[
'settings' => $settings,
'setting_value' => $setting_value,
'feature' => $feature,
'selector' => $class_selector,
'breakpoints' => $breakpoints,
'block' => $block,
'block_layout' => $block_layout,
],
$feature_args
);
if ( \is_callable( $args['func_build_style'] ?? '' ) ) {
$this->build_style( array_merge( $args, [ 'value' => $setting_value ] ), $style_array, $classes );
}
if ( \is_callable( $args['func_build_responsive_style'] ?? '' ) && $this->is_valid_responsive_value( $args['setting_value'] ) ) {
$this->build_responsive_style( $args, $responsive_style_array, $classes );
}
}
if ( $style_array ) {
foreach ( $style_array as $item_style ) {
$style .= $item_style;
}
}
if ( $responsive_style_array ) {
foreach ( $responsive_style_array as $responsive_style ) {
$style .= $responsive_style;
}
}
$custom_css = $this->get_custom_css_style( $block );
if ( $custom_css ) {
// Get & refine custom style.
$custom_style = $this->refine_custom_value( $custom_css['value'] ?? '', [ 'selector' => $class_selector ], 'CSS' );
if ( $custom_style ) {
$style .= $custom_style;
}
}
// Allow altering the style.
$style = apply_filters(
'cbb_get_block_style',
$style,
[
'settings' => $settings,
'selector' => $class_selector,
'breakpoints' => $breakpoints,
'block' => $block,
'block_layout' => $block_layout,
]
);
return [
'style' => $style,
'classes' => $classes,
];
}
/**
* Build custom style
*
* @param array $args
* @param array &$style_array
* @param array &$classes
* @return string
*/
private function build_style( $args, &$style_array, &$classes ) {
$return_value = false;
$func_build_style = $args['func_build_style'] ?? '';
if ( ! \is_callable( $func_build_style ) ) {
return $return_value;
}
$setting_value = $args['setting_value'] ?? [];
if ( ! $this->is_valid_value( $setting_value ) ) {
return $return_value;
}
// Build style.
return $func_build_style( $args, $style_array, $classes );
}
/**
* Build style for padding
*
* @param array $args
* @return string
*/
private function build_padding_style( $args ) {
$style_array = [];
$value = $args['value'];
if ( is_array( $value ) ) {
if ( isset( $value['top'] ) && is_string( $value['top'] ) ) {
$top_style = $this->get_spacing_value( $value['top'] );
if ( $this->is_valid_value( $top_style ) ) {
$style_array['--cbb--padding-top'] = $top_style;
}
}
if ( isset( $value['right'] ) && is_string( $value['right'] ) ) {
$right_style = $this->get_spacing_value( $value['right'] );
if ( $this->is_valid_value( $right_style ) ) {
$style_array['--cbb--padding-right'] = $right_style;
}
}
if ( isset( $value['bottom'] ) && is_string( $value['bottom'] ) ) {
$bottom_style = $this->get_spacing_value( $value['bottom'] );
if ( $this->is_valid_value( $bottom_style ) ) {
$style_array['--cbb--padding-bottom'] = $bottom_style;
}
}
if ( isset( $value['left'] ) && is_string( $value['left'] ) ) {
$left_style = $this->get_spacing_value( $value['left'] );
if ( $this->is_valid_value( $left_style ) ) {
$style_array['--cbb--padding-left'] = $left_style;
}
}
}
return $style_array;
}
/**
* Build style for margin
*
* @param array $args
* @return string
*/
private function build_margin_style( $args ) {
$style_array = [];
$value = $args['value'];
if ( is_array( $value ) ) {
if ( isset( $value['top'] ) && is_string( $value['top'] ) ) {
$top_style = $this->get_spacing_value( $value['top'] );
if ( $this->is_valid_value( $top_style ) ) {
$style_array['--cbb--margin-top'] = $top_style;
}
}
if ( isset( $value['right'] ) && is_string( $value['right'] ) ) {
$right_style = $this->get_spacing_value( $value['right'] );
if ( $this->is_valid_value( $right_style ) ) {
$style_array['--cbb--margin-right'] = $right_style;
}
}
if ( isset( $value['bottom'] ) && is_string( $value['bottom'] ) ) {
$bottom_style = $this->get_spacing_value( $value['bottom'] );
if ( $this->is_valid_value( $bottom_style ) ) {
$style_array['--cbb--margin-bottom'] = $bottom_style;
}
}
if ( isset( $value['left'] ) && is_string( $value['left'] ) ) {
$left_style = $this->get_spacing_value( $value['left'] );
if ( $this->is_valid_value( $left_style ) ) {
$style_array['--cbb--margin-left'] = $left_style;
}
}
}
return $style_array;
}
/**
* Build style for gap
*
* @param array $args
* @return string
*/
private function build_block_gap_style( $args ) {
$style_array = [];
$value = $args['value'];
if ( is_array( $value ) && ( $value['top'] ?? false ) ) {
$gap_style = $this->get_spacing_value( $value['top'] );
if ( $this->is_valid_value( $gap_style ) ) {
$style_array['--cbb--block-gap'] = $gap_style;
}
}
return $style_array;
}
/**
* Build style for border
*
* @param array $args
* @return string
*/
private function build_border_style( $args ) {
$style_array = [];
$value = $args['value'];
if ( is_array( $value ) ) {
if ( ( $value['top'] ?? false ) || ( $value['right'] ?? false ) || ( $value['bottom'] ?? false ) || ( $value['left'] ?? false ) ) {
$top = $value['top'] ?? null;
$right = $value['right'] ?? null;
$bottom = $value['bottom'] ?? null;
$left = $value['left'] ?? null;
} else {
$top = $value;
$right = $value;
$bottom = $value;
$left = $value;
}
if ( $top ) {
$top_style = $this->build_border_side_css_value( $top, 'top' );
if ( $top_style ) {
$style_array['--cbb--border-top'] = $top_style;
}
}
if ( $right ) {
$right_style = $this->build_border_side_css_value( $right, 'right' );
if ( $right_style ) {
$style_array['--cbb--border-right'] = $right_style;
}
}
if ( $bottom ) {
$bottom_style = $this->build_border_side_css_value( $bottom, 'bottom' );
if ( $bottom_style ) {
$style_array['--cbb--border-bottom'] = $bottom_style;
}
}
if ( $left ) {
$left_style = $this->build_border_side_css_value( $left, 'left' );
if ( $left_style ) {
$style_array['--cbb--border-left'] = $left_style;
}
}
}
return $style_array;
}
/**
* Build the css variable for border by side
*
* @param array $value
* @param string $side
* @return string
*/
private function build_border_side_css_value( $value, $side ) {
$style_array = [];
if ( is_array( $value ) ) {
if ( $this->is_valid_value( $value['width'] ?? null ) && is_string( $value['width'] ) ) {
$style_array[] = $value['width'];
}
if ( $this->is_valid_value( $value['style'] ?? null ) && is_string( $value['style'] ) ) {
$style_array[] = $value['style'];
}
if ( $this->is_valid_value( $value['color'] ?? null ) ) {
$color = $this->get_css_color_value( $value['color'] );
if ( $color ) {
$style_array[] = $color;
}
}
}
return implode( ' ', $style_array );
}
/**
* Build style for radius
*
* @param array $args
* @return string
*/
private function build_border_radius_style( $args ) {
$style_array = [];
$value = $args['value'];
if ( is_array( $value ) ) {
$css_value = '';
$top_left = $value['top-left'] ?? 0;
$top_right = $value['top-right'] ?? 0;
$bottom_right = $value['bottom-right'] ?? 0;
$bottom_left = $value['bottom-left'] ?? 0;
if ( ! empty( $top_left ) || ! empty( $top_right ) || ! empty( $bottom_right ) || ! empty( $bottom_left ) ) {
$css_value = "{$top_left} {$top_right} {$bottom_right} {$bottom_left}";
}
if ( $args['settings']['enableEllipticalRadius'] ?? false ) {
$top_left = $value['top-left-vertical'] ?? 0;
$top_right = $value['top-right-vertical'] ?? 0;
$bottom_right = $value['bottom-right-vertical'] ?? 0;
$bottom_left = $value['bottom-left-vertical'] ?? 0;
if ( ! empty( $top_left ) || ! empty( $top_right ) || ! empty( $bottom_right ) || ! empty( $bottom_left ) ) {
$css_value = "{$css_value} / {$top_left} {$top_right} {$bottom_right} {$bottom_left}";
}
}
if ( $css_value ) {
$style_array['--cbb--border-radius'] = $css_value;
}
}
return $style_array;
}
/**
* Build style for width
*
* @param array $args
* @return string
*/
private function build_width_style( $args ) {
$style_array = [];
$value = $args['value'];
if ( is_array( $value ) && ( $value['value'] ?? false ) ) {
$width_style = $value['value'];
if ( $this->is_valid_value( $width_style ) ) {
$style_array['--cbb--width'] = $width_style;
}
}
return $style_array;
}
/**
* Build style for height
*
* @param array $args
* @return string
*/
private function build_height_style( $args ) {
$style_array = [];
$value = $args['value'];
if ( is_array( $value ) && ( $value['value'] ?? false ) ) {
$style = $value['value'];
if ( $this->is_valid_value( $style ) ) {
$style_array['--cbb--height'] = $style;
}
}
return $style_array;
}
/**
* Build style for aspect ratio
*
* @param array $args
* @return string
*/
private function build_aspect_ratio_style( $args ) {
$style_array = [];
$value = $args['value'];
if ( is_array( $value ) && ( $value['value'] ?? false ) ) {
$style = $value['value'];
if ( $this->is_valid_value( $style ) ) {
$style_array['--cbb--aspect-ratio'] = $style;
}
}
return $style_array;
}
/**
* Build style for text alignment
*
* @param array $args
* @return string
*/
private function build_text_align_style( $args ) {
$style_array = [];
$value = $args['value'];
if ( $this->is_valid_value( $value ) ) {
$style_array['--cbb--text-align'] = $value;
}
return $style_array;
}
/**
* Build style for vertical alignment
*
* @param array $args
* @return string
*/
private function build_vertical_align_style( $args ) {
$style_array = [];
$value = $args['value'] ?? null;
if ( $this->is_valid_value( $value ) ) {
if ( 'top' === $value ) {
$value = 'start';
} elseif ( 'bottom' === $value ) {
$value = 'end';
}
$style_array['--cbb--v-align'] = $value;
}
return $style_array;
}
/**
* Build dependent style for vertical alignment
*
* @param array $args
* @param array &$classes
* @return string
*/
private function build_vertical_align_dependent_style( $args, &$classes ) {
$style = '';
$value = $args['setting_value'] ?? null;
if ( $this->is_valid_value( $value ) ) {
$block_layout = $args['block_layout'] ?? '';
if ( 'grid' === $block_layout ) {
$class = 'cbb-align-items';
} elseif ( 'gridItem' === $block_layout ) {
$class = 'cbb-align-self';
} else {
$class = 'cbb-layout-grid';
}
if ( ! in_array( $class, $classes, true ) ) {
$classes[] = $class;
}
}
return $style;
}
/**
* Build style for justify alignment
*
* @param array $args
* @return string
*/
private function build_justify_align_style( $args ) {
$style_array = [];
$value = $args['value'] ?? null;
if ( $this->is_valid_value( $value ) ) {
$block_layout = $args['block_layout'] ?? '';
if ( in_array( $block_layout, [ 'standalone', 'carouselItem', 'vstackItem' ], true ) ) {
if ( 'left' === $value ) {
$value = 'start';
} elseif ( 'right' === $value ) {
$value = 'end';
}
}
$style_array['--cbb--h-align'] = $value;
}
return $style_array;
}
/**
* Build dependent style for justify alignment
*
* @param array $args
* @param array &$classes
* @return string
*/
private function build_justify_align_dependent_style( $args, &$classes ) {
$style = '';
$value = $args['setting_value'] ?? null;
if ( $this->is_valid_value( $value ) ) {
$block_layout = $args['block_layout'] ?? '';
if ( 'grid' === $block_layout ) {
$class = 'cbb-justify-items';
} elseif ( 'gridItem' === $block_layout ) {
$class = 'cbb-justify-self';
} else {
// if ( in_array( $block_layout, [ 'standalone', 'carouselItem', 'vstackItem' ], true ) ) {.
$class = 'cbb-layout-grid';
}
if ( ! in_array( $class, $classes, true ) ) {
$classes[] = $class;
}
}
return $style;
}
/**
* Build style for transform
*
* @param array $args
* @return string
*/
private function build_transform_style( $args ) {
$style_array = [];
$value = $args['value'];
if ( ! empty( $value ) && is_array( $value ) ) {
$style = implode( ' ', array_filter( array_map( [ $this, 'build_transform_item' ], $value ) ) );
if ( $this->is_valid_value( $value ) ) {
$style_array['--cbb--transform'] = $style;
}
}
return $style_array;
}
/**
* Build transform item: translate, rotate, scale, skew
*
* @param array $transform_item
* @return string
*/
private function build_transform_item( $transform_item ) {
$style = '';
if ( is_array( $transform_item ) && count( $transform_item ) > 0 ) {
// Get tranform type.
$transform_type = array_keys( $transform_item )[0];
$transform_value = $transform_item[ $transform_type ] ?? '';
if ( $transform_type === 'rotate' ) {
if ( is_numeric( $transform_value ) ) {
$style = "rotate({$transform_value}deg)";
}
} elseif ( is_array( $transform_value ) ) {
$suffix = 'skew' === $transform_type ? 'deg' : '';
$x = $transform_value['x'] ?? '';
$y = $transform_value['y'] ?? '';
$x_value = preg_replace( '/[^0-9.]/', '', $x );
$y_value = preg_replace( '/[^0-9.]/', '', $y );
if ( \is_numeric( $x_value ) || \is_numeric( $y_value ) ) {
if ( ! \is_numeric( $x_value ) ) {
if ( 'scale' === $transform_type ) {
$x = 1;
} else {
$x = 0;
}
}
if ( ! \is_numeric( $y_value ) ) {
if ( 'scale' === $transform_type ) {
$y = 1;
} else {
$y = 0;
}
}
$style = "{$transform_type}({$x}{$suffix}, {$y}{$suffix})";
}
}
}
return $style;
}
/**
* Build dependent style for transform
*
* @param array $args
* @param array &$classes
* @return string
*/
private function build_transform_dependent_style( $args, &$classes ) {
$transform_origin = $args['settings']['transformOrigin'] ?? [];
$x = $transform_origin['x'] ?? .5;
if ( ! \is_numeric( $x ) ) {
$x = .5;
}
$x = round( $x, 2 ) * 100;
$y = $transform_origin['y'] ?? .5;
if ( ! \is_numeric( $y ) ) {
$y = .5;
}
$y = round( $y, 2 ) * 100;
return "{$args['selector']}{--cbb--transform-origin: {$x}% {$y}%;}";
}
/**
* Build style for hidden
*
* @param array $args
* @param array &$style_array
* @param array &$classes
* @return array
*/
private function build_hidden_style( $args, &$style_array, &$classes ) {
$value = $args['value'] ?? null;
if ( ! empty( $value ) && is_array( $value ) ) {
foreach ( $value as $breakpoint ) {
$class = "cbb-hidden-{$breakpoint}";
if ( ! in_array( $class, $classes, true ) ) {
$classes[] = $class;
}
}
}
}
/**
* Build style for grid columns
*
* @param array $args
* @return string
*/
private function build_columns_style( $args ) {
$style_array = [];
$value = $args['value'] ?? null;
if ( $this->is_valid_value( $value ) ) {
$style_array['--cbb--grid-columns'] = $value;
}
return $style_array;
}
/**
* Build style for grid rows
*
* @param array $args
* @return string
*/
private function build_rows_style( $args ) {
$style_array = [];
$value = $args['value'] ?? null;
if ( $this->is_valid_value( $value ) ) {
$style_array['--cbb--grid-rows'] = $value;
}
return $style_array;
}
/**
* Build style for grid auto rows
*
* @param array $args
* @return string
*/
private function build_auto_rows_style( $args ) {
$style_array = [];
$value = $args['value'] ?? null;
if ( $this->is_valid_value( $value ) ) {
$style_array['--cbb--grid-auto-rows'] = $value;
}
return $style_array;
}
/**
* Build style for grid gap
*
* @param array $args
* @return string
*/
private function build_gap_style( $args ) {
$style_array = [];
$value = $args['value'] ?? null;
if ( $value && is_array( $value ) ) {
$column_gap = $value['column'] ?? null;
if ( $this->is_valid_value( $column_gap ) ) {
$style_array['--cbb--grid-gap-column'] = $column_gap;
}
$row_gap = $value['row'] ?? null;
if ( $this->is_valid_value( $row_gap ) ) {
$style_array['--cbb--grid-gap-row'] = $row_gap;
}
}
return $style_array;
}
/**
* Build style for grid item column span
*
* @param array $args
* @return string
*/
private function build_column_span_style( $args ) {
$style_array = [];
$value = $args['value'] ?? null;
if ( $value && is_array( $value ) ) {
$start = $value['start'] ?? null;
$span = $value['span'] ?? null;
$order = $value['order'] ?? null;
if ( $this->is_valid_value( $start ) || $this->is_valid_value( $span ) ) {
$start = $this->is_valid_value( $start ) ? $start : 'auto';
$span = $this->is_valid_value( $span ) ? $span : 'auto';
if ( 'auto' !== $span && absint( $span ) > 0 ) {
$span = "span {$span}";
}
$style_array['--cbb--grid-item-column'] = "{$start} / {$span}";
}
if ( $this->is_valid_value( $order ) && $order !== 'auto' ) {
$style_array['--cbb--grid-item-order'] = $order;
}
}
return $style_array;
}
/**
* Build style for grid item row span
*
* @param array $args
* @return string
*/
private function build_row_span_style( $args ) {
$style_array = [];
$value = $args['value'] ?? null;
if ( $value && is_array( $value ) ) {
$start = $value['start'] ?? null;
$span = $value['span'] ?? null;
if ( $this->is_valid_value( $start ) || $this->is_valid_value( $span ) ) {
$start = $this->is_valid_value( $start ) ? $start : 'auto';
$span = $this->is_valid_value( $span ) ? $span : 'auto';
if ( 'auto' !== $span && absint( $span ) > 0 ) {
$span = "span {$span}";
}
$style_array['--cbb--grid-item-row'] = "{$start} / {$span}";
}
}
return $style_array;
}
/**
* Build style for accordion gap
*
* @param array $args
* @return string
*/
private function build_accordion_gap_style( $args ) {
$style_array = [];
$value = $args['value'];
if ( $this->is_valid_value( $value ) ) {
$style_array['--cbb--accordion-gap'] = $this->get_spacing_value( $value );
}
return $style_array;
}
/**
* Build dependent style for accordion gap
*
* @param array $args
* @param array &$classes
* @return string
*/
private function build_accordion_gap_dependent_style( $args, &$classes ) {
$style = '';
$setting_values = $args['setting_values'];
if ( $this->is_valid_value( $setting_values ) && is_array( $setting_values ) ) {
foreach ( $setting_values as $breakpoint => $value ) {
if ( strpos( $value, 'var:preset|spacing|' ) !== false || absint( $value ) > 0 ) {
$classes[] = "{$breakpoint}-cbb-a-has-border";
} else {
$classes[] = "{$breakpoint}-cbb-a-no-border";
}
}
}
return $style;
}
/**
* Build style for accordion gap
*
* @param array $args
* @return string
*/
private function build_accordion_padding_style( $args ) {
$style_array = [];
$value = $args['value'];
if ( $value && is_array( $value ) ) {
$x = $value['x'] ?? null;
if ( ! $this->is_valid_value( $x ) ) {
$x = '1.25rem';
}
$x = $this->get_spacing_value( $x );
$y = $value['y'] ?? null;
if ( ! $this->is_valid_value( $y ) ) {
$y = '1rem';
}
$y = $this->get_spacing_value( $y );
$style_array['--cbb--accordion-padding'] = "{$y} {$x}";
}
return $style_array;
}
/**
* Build style for accordion border
*
* @param array $args
* @param array $style_array
* @return string
*/
private function build_accordion_border_style( $args, &$style_array ) {
$value = $args['value'] ?? null;
if ( $value && is_array( $value ) ) {
if ( ( $value['top'] ?? false ) || ( $value['right'] ?? false ) || ( $value['bottom'] ?? false ) || ( $value['left'] ?? false ) ) {
$border_styles = $this->build_border_style( $args );
} else {
$border_styles = $this->build_border_style(
[
'value' => [
'top' => $value,
'right' => $value,
'bottom' => $value,
'left' => $value,
],
]
);
}
if ( $border_styles ) {
$border_style = implode(
';',
array_map(
function ( $attr_key ) use ( $border_styles ) {
return "{$attr_key}:{$border_styles[ $attr_key ]}";
},
array_keys( $border_styles )
)
);
$style_array[] = "{$args['selector']}{{$border_style};}";
}
}
return $style_array;
}
/**
* Build style for accordion radius
*
* @param array $args
* @param array $style_array
* @return string
*/
private function build_accordion_radius_style( $args, &$style_array ) {
$value = $args['value'] ?? null;
if ( $value ) {
if ( is_array( $value ) ) {
$top_left = $value['topLeft'] ?? 0;
$top_right = $value['topRight'] ?? 0;
$bottom_right = $value['bottomRight'] ?? 0;
$bottom_left = $value['bottomLeft'] ?? 0;
$css_value = "{$top_left} {$top_right} {$bottom_right} {$bottom_left}";
} elseif ( is_string( $value ) ) {
$css_value = $value;
}
$style_array[] = "{$args['selector']}{--cbb--border-radius:{$css_value};}";
}
return $style_array;
}
/**
* Build style for sticky offset
*
* @param array $args
* @return array
*/
private function build_sticky_offset_style( $args ) {
$style_array = [];
$value = $args['value'];
if ( $value && is_array( $value ) ) {
$offset = $value['value'] ?? null;
if ( $this->is_valid_value( $offset ) ) {
$style_array['--cbb--sticky-offset'] = $offset;
}
}
return $style_array;
}
/**
* Check whether current block has custom style or not.
*
* @param array $block
* @return boolean
*/
private function get_custom_css_style( $block ) {
// There is no boldblocks value.
if ( ! isset( $block['attrs']['boldblocks'] ) || ! isset( $block['attrs']['boldblocks']['customCSS'] ) ) {
return false;
}
$custom_css = $block['attrs']['boldblocks']['customCSS'];
if ( ! is_array( $custom_css ) || empty( $custom_css['value'] ) ) {
return false;
}
return $custom_css;
}
/**
* Render dynamic link to post element
*
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
public function build_block_link_to_post( $block_content, $block, $block_instance ) {
// Ignore admin side.
if ( is_admin() ) {
return $block_content;
}
// There is no post context.
$post_id = $block_instance->context['postId'] ?? false;
if ( ! $post_id ) {
return $block_content;
}
// There is no background value.
if ( ! ( $block['attrs']['boldblocks']['background'] ?? false ) ) {
return $block_content;
}
$background = $block['attrs']['boldblocks']['background'];
$media_type = $background['mediaType'] ?? 'image';
if ( 'image' === $media_type && ( $background['image']['useFeaturedImage'] ?? false ) && ( $background['image']['linkToPost'] ?? false ) ) {
$block_content = $this->add_inner_content_to_block( $block_content, '' );
}
return $block_content;
}
/**
* Render dynamic background image
*
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
public function build_block_background( $block_content, $block, $block_instance ) {
// Ignore admin side.
if ( is_admin() ) {
return $block_content;
}
// Ignore legacy markup.
if ( strpos( $block['innerHTML'] ?? '', 'bb:block-background' ) !== false ) {
return $block_content;
}
$context_background = [];
$meta_name = $block['attrs']['metadata']['name'] ?? '';
if ( $meta_name && 'cbb/block-overrides' === ( $block['attrs']['metadata']['bindings']['background']['source'] ?? '' ) ) {
$context_background = $block_instance->context['cbb/block-overrides'][ $meta_name ]['background'] ?? [];
}
$background = $block['attrs']['boldblocks']['background'] ?? [];
if ( $context_background ) {
$background = array_replace_recursive( $background, $context_background );
}
// There is no background value.
if ( ! $background ) {
return $block_content;
}
$media_type = $background['mediaType'] ?? 'image';
$media_url = $background[ $media_type ]['url'] ?? '';
if ( 'image' === $media_type ) {
if ( $background['image']['useFeaturedImage'] ?? false ) {
return $this->build_thumbnail_background( $background, $block_content, $block, $block_instance );
} elseif ( $media_url ) {
return $this->build_custom_image_background( $background, $block_content, $block, $block_instance );
}
} elseif ( 'video' === $media_type ) {
if ( $media_url ) {
return $this->build_video_background( $background, $block_content, $block, $block_instance );
}
} elseif ( 'embed' === $media_type ) {
if ( $media_url ) {
return $this->build_embed_background( $background, $block_content, $block, $block_instance );
}
}
return $block_content;
}
/**
* Build the background using custom image
*
* @param array $background
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
private function build_custom_image_background( $background, $block_content, $block, $block_instance ) {
$block_class = 'bb:has-background bb:has-background--image';
$block_background = $this->build_image_background( $background['image']['id'] ?? false, $background, $block_class );
if ( strpos( $block['innerHTML'] ?? '', $block_background['block_class'] ) === false ) {
$block_content = $this->add_class_to_block( $block_content, $block_background['block_class'] );
}
$block_content = $this->add_inner_content_to_block( $block_content, $block_background['background_markup'] );
return $block_content;
}
/**
* Build the background using post thumbnail
*
* @param array $background
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
private function build_thumbnail_background( $background, $block_content, $block, $block_instance ) {
$post_id = $block_instance->context['postId'] ?? false;
if ( ! $post_id ) {
return $block_content;
}
$image_id = get_post_thumbnail_id( $post_id );
$block_class = 'bb:has-background bb:has-background--image';
if ( $image_id ) {
$block_background = $this->build_image_background( $image_id, $background, $block_class );
if ( strpos( $block['innerHTML'] ?? '', $block_background['block_class'] ) === false ) {
$block_content = $this->add_class_to_block( $block_content, $block_background['block_class'] );
}
$block_content = $this->add_inner_content_to_block( $block_content, $block_background['background_markup'] );
} else {
// Add custom background as a fallback.
if ( $background['image']['url'] ?? '' ) {
$block_content = $this->add_class_to_block( $block_content, 'has-no-thumbnail' );
$block_content = $this->build_custom_image_background( $background, $block_content, $block, $block_instance );
} else {
$block_content = $this->add_class_to_block( $block_content, $block_class . ' has-no-thumbnail' );
}
}
return $block_content;
}
/**
* Build image background
*
* @param int $image_id
* @param array $background
* @param string $block_class
* @return array
*/
private function build_image_background( $image_id, $background, $block_class ) {
$settings = $background['settings'] ?? [];
$media = $background['image'] ?? [];
$mobile_background = ! ( $media['useFeaturedImage'] ?? false ) && ( $media['hasMobileBackground'] ?? false ) && ( $media['mobileBackground']['url'] ?? '' ) ? $media['mobileBackground'] : false;
$background_classes = [ 'bb:block-background bb:block-background--image cbb-bg-image' ];
$background_position = round( ( $settings['focalPoint']['x'] ?? .5 ) * 100 ) . '% ' . round( ( $settings['focalPoint']['y'] ?? .5 ) * 100 ) . '%';
$image_element = '';
$dataset = [];
$image_size = $media['size'] ?? 'full';
$is_internal_image = false;
$image_url = $image_id ? wp_get_attachment_image_url( $image_id, $image_size ) : false;
if ( $image_url ) {
$is_internal_image = true;
} else {
$image_url = $media['url'] ?? '';
}
$custom_alt_text = $media['customAlt'] ?? '';
$object_fit = $settings['objectFit'] ?? 'cover';
if ( empty( $object_fit ) ) {
$object_fit = 'cover';
}
$img_style = 'display:block;width:100%;height:100%;object-fit:' . $object_fit . ';object-position:' . $background_position . ';';
$background_styles = [];
if ( $settings['isImgElement'] ?? false ) {
$attrs = [
'style' => esc_attr( $img_style ),
'class' => 'bb:block-background--img',
];
$loading_priority = $settings['loadingPriority'] ?? '';
if ( $loading_priority ) {
if ( $loading_priority === 'lazy' ) {
$attrs['loading'] = 'lazy';
} elseif ( in_array( $loading_priority, [ 'high', 'low' ], true ) ) {
$attrs['fetchpriority'] = $loading_priority;
}
}
if ( $is_internal_image ) {
$alt = trim( wp_strip_all_tags( get_post_meta( $image_id, '_wp_attachment_image_alt', true ) ) );
if ( ! $alt ) {
$attrs['alt'] = $custom_alt_text;
}
$image_element = wp_get_attachment_image(
$image_id,
$image_size,
false,
$attrs
);
} else {
$image_element = $this->generate_image_element( $image_url, array_merge( $attrs, [ 'alt' => $custom_alt_text ] ) );
}
if ( $mobile_background ) {
$background_classes[] = 'cbb-mobile-image';
$attrs['class'] .= ' is-mobile-image';
$mobile_image = '';
if ( $mobile_background['id'] ?? false ) {
$alt = trim( wp_strip_all_tags( get_post_meta( $mobile_background['id'], '_wp_attachment_image_alt', true ) ) );
$attrs['alt'] = $alt ? $alt : $custom_alt_text;
$mobile_image = wp_get_attachment_image(
$mobile_background['id'],
$image_size,
false,
$attrs
);
}
if ( ! $mobile_image ) {
$mobile_image = $this->generate_image_element( $mobile_background['url'], array_merge( $attrs, [ 'alt' => $custom_alt_text ] ) );
}
$image_element .= $mobile_image;
}
} else {
$image_as_background = $this->get_background_image_url( $image_url );
$background_styles = [
'background-image' => 'url(' . $image_as_background . ')',
'background-position' => $background_position,
];
if ( ! empty( $settings['isFixed'] ) ) {
$background_styles['background-attachment'] = 'fixed';
}
$background_styles['background-repeat'] = $settings['repeat'] ?? 'no-repeat';
$background_styles['background-size'] = $settings['size'] ?? 'cover';
if ( $mobile_background ) {
$background_styles['--cbb-mobile-background'] = "url('{$this->get_background_image_url( $mobile_background['url'] )}')";
$background_classes[] = 'cbb-mobile-background';
}
}
$animation_type = $settings['animation']['type'] ?? '';
$animation_settings = $settings['animation']['settings'] ?? [];
if ( $animation_type && 'none' !== $animation_type ) {
$background_classes[] = $animation_type;
if ( in_array( $animation_type, [ 'bg-scroll-horizontal', 'bg-scroll-vertical' ], true ) ) {
if ( ! ( $settings['isImgElement'] ?? false ) ) {
if ( $this->has_animation_setting_value( 'scroll', 'duration', $animation_settings ) ) {
$background_styles['animation-duration'] = $this->get_animation_setting_value( 'scroll', 'duration', $animation_settings ) . 's';
}
if ( $background_styles['background-repeat'] !== 'repeat' ) {
$background_styles['background-repeat'] = 'bg-scroll-horizontal' === $animation_type ? 'repeat-x' : 'repeat-y';
}
if ( $this->has_animation_setting_value( 'scroll', 'reverseDirection', $animation_settings ) ) {
$background_classes[] = 'is-reverse';
}
}
} elseif ( 'bg-parallax' === $animation_type ) {
unset( $background_styles['background-attachment'] );
$dataset['data-speed'] = $this->get_animation_setting_value( 'parallax', 'speed', $animation_settings, 0.5 );
if ( $this->get_animation_setting_value( 'parallax', 'opacity', $animation_settings, '' ) ) {
$dataset['data-opacity'] = 'true';
}
if ( $this->get_animation_setting_value( 'parallax', 'disableParallax', $animation_settings, '' ) ) {
$dataset['data-disable-parallax'] = 'true';
$breakpoint = $this->get_animation_setting_value( 'parallax', 'disableParallaxBreakpoint', $animation_settings );
if ( ! $breakpoint ) {
$breakpoint = 767;
}
$dataset['data-disable-parallax-breakpoint'] = $breakpoint;
}
} elseif ( 'bg-zoom' === $animation_type ) {
if ( isset( $animation_settings['zoom']['initialScale'] ) ) {
$background_styles['--cbb--zoom-initial-scale'] = $animation_settings['zoom']['initialScale'];
}
if ( isset( $animation_settings['zoom']['scale'] ) ) {
$background_styles['--cbb--zoom-scale'] = $animation_settings['zoom']['scale'];
}
if ( isset( $animation_settings['zoom']['duration'] ) ) {
$background_styles['--cbb--zoom-duration'] = $animation_settings['zoom']['duration'] . 's';
}
if ( isset( $animation_settings['zoom']['timingFunction'] ) ) {
$background_styles['--cbb--zoom-timing-function'] = $animation_settings['zoom']['timingFunction'];
}
$zoom_event = $animation_settings['zoom']['event'] ?? 'hover';
$background_classes[] = "bg-zoom-{$zoom_event}";
if ( 'reveal' === $zoom_event ) {
$dataset['data-reveal-animation'] = esc_attr(
wp_json_encode(
[
'name' => 'bgZoom',
'animation' => 'bgZoom var(--cbb--zoom-duration) var(--cbb--zoom-timing-function)',
'multipleTimes' => $animation_settings['zoom']['multipleTimes'] ?? false,
'forwards' => true,
]
)
);
}
if ( ! empty( $animation_settings['zoom']['withOpacity'] ) ) {
$background_styles['--cbb--zoom-initial-opacity'] = $animation_settings['zoom']['initialOpacity'] ?? 0;
$background_styles['--cbb--zoom-opacity'] = $animation_settings['zoom']['opacity'] ?? 1;
}
} elseif ( 'bg-zoomIn' === $animation_type ) {
if ( isset( $animation_settings['zoomIn']['initialScale'] ) ) {
$background_styles['--cbb--zoom-initial-scale'] = $animation_settings['zoomIn']['initialScale'];
}
if ( isset( $animation_settings['zoomIn']['duration'] ) ) {
$background_styles['--cbb--zoom-duration'] = $animation_settings['zoomIn']['duration'] . 's';
}
if ( isset( $animation_settings['zoomIn']['timingFunction'] ) ) {
$background_styles['--cbb--zoom-timing-function'] = $animation_settings['zoomIn']['timingFunction'];
}
if ( ! empty( $animation_settings['zoomIn']['withOpacity'] ) ) {
$background_styles['--cbb--zoom-initial-opacity'] = $animation_settings['zoomIn']['initialOpacity'] ?? 0;
$background_styles['--cbb--zoom-opacity'] = $animation_settings['zoomIn']['opacity'] ?? 1;
}
$dataset['data-reveal-animation'] = esc_attr(
wp_json_encode(
[
'name' => 'bgZoomIn',
'animation' => 'bgZoomIn var(--cbb--zoom-duration) var(--cbb--zoom-timing-function)',
'multipleTimes' => $animation_settings['zoomIn']['multipleTimes'] ?? false,
'forwards' => true,
]
)
);
}
}
$background_style = $background_styles ? array_reduce(
array_keys( $background_styles ),
function ( $carry, $key ) use ( $background_styles ) {
return $carry . $key . ':' . $background_styles[ $key ] . ';';
},
''
) : '';
if ( $background_style ) {
$background_style = ' style="' . $background_style . '"';
}
// Get image full size.
$image_full_size = $is_internal_image ? $this->generate_image_full_attributes( $image_id ) : false;
$dataset['data-src'] = $image_full_size ? $image_full_size['src'] : $image_url;
if ( $image_full_size ) {
$dataset['data-width'] = $image_full_size['width'];
$dataset['data-height'] = $image_full_size['height'];
}
$data_attribute = $dataset ? array_reduce(
array_keys( $dataset ),
function ( $carry, $key ) use ( $dataset ) {
return $carry . ' ' . $key . '="' . esc_attr( $dataset[ $key ] ) . '"';
},
' '
) : '';
$background_image = sprintf( '
%4$s
', \implode( ' ', $background_classes ), $background_style, $data_attribute, $image_element );
if ( $animation_type && 'none' !== $animation_type ) {
$block_class .= ' js-animation-' . $animation_type;
}
return [
'background_markup' => $background_image,
'block_class' => $block_class,
];
}
/**
* Generate data for full size of an image
*
* @param int $image_id
* @return array
*/
private function generate_image_full_attributes( $image_id ) {
$image = wp_get_attachment_image_src( $image_id, 'full' );
if ( $image ) {
list( $src, $width, $height ) = $image;
return [
'src' => $src,
'width' => $width,
'height' => $height,
];
}
return [];
}
/**
* Generate img tag from an image url
*
* @param string $image_url
* @param array $attrs
* @return string
*/
private function generate_image_element( $image_url, $attrs ) {
$attr_html = '';
foreach ( $attrs as $name => $value ) {
$attr_html .= " $name=" . '"' . esc_attr( $value ) . '"';
}
// phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage
return sprintf( '
', esc_attr( $image_url ), $attr_html );
}
/**
* Get image as background url
*
* @param string $image_url
* @return string
*/
private function get_background_image_url( $image_url ) {
return strpos( $image_url, 'data:image/svg+xml' ) === 0 ? '"' . esc_attr( $image_url ) . '"' : esc_attr( $image_url );
}
/**
* Render animations
*
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
public function build_block_animations( $block_content, $block, $block_instance ) {
// There is no animations value.
if ( ! isset( $block['attrs']['boldblocks'] ) || ! isset( $block['attrs']['boldblocks']['animations'] ) ) {
return $block_content;
}
// Get animations.
$animations = $block['attrs']['boldblocks']['animations'];
if ( ! is_array( $animations ) ) {
return $block_content;
}
$animation_names = $this->get_animation_names();
$animation_object = array_reduce(
$animations,
function ( $accumulator, $animation ) use ( $animation_names ) {
$name = $animation['name'] ?? '';
$duration = $animation['duration'] ?? '';
if ( ! $name || ! $duration ) {
return $accumulator;
}
$delay = $animation['delay'] ?? 0;
$repeat = $animation['repeat'] ?? 1;
$infinite = $animation['infinite'] ?? false;
$timing_function = $animation['timingFunction'] ?? '';
$animation_name = isset( $animation_names[ $name ]['name'] ) ? $animation_names[ $name ]['name'] : $name;
if ( ! $timing_function && isset( $animation_names[ $name ]['timingFunction'] ) ) {
$timing_function = $animation_names[ $name ]['timingFunction'];
}
$timing_function = $timing_function ? " $timing_function" : '';
$count = $infinite ? 'infinite' : $repeat;
$animation_string = "$animation_name {$duration}s{$timing_function} {$delay}s $count";
// Save animation.
$accumulator['animation'] = $accumulator['animation']
? "{$accumulator['animation']}, $animation_string"
: $animation_string;
// Save the name.
$accumulator['name'] = $accumulator['name']
? "{$accumulator['name']},$name"
: $name;
return $accumulator;
},
[
'animation' => '',
'name' => '',
]
);
if ( $animation_object['name'] && $animation_object['animation'] ) {
$animation_object['multipleTimes'] = $block['attrs']['boldblocks']['animateMultipleTimes'] ?? false;
$block_content = $this->add_data_to_block( $block_content, 'data-reveal-animation', esc_attr( wp_json_encode( $animation_object ) ) );
$block_content = $this->add_class_to_block( $block_content, 'animate__waiting' );
}
return $block_content;
}
/**
* Get all predefined animations
*
* @return array
*/
private function get_animation_names() {
$animation_names = [
'bounce' => false,
'flash' => false,
'pulse' => [ 'timingFunction' => 'ease-in-out' ],
'rubberBand' => false,
'shakeX' => false,
'shakeY' => false,
'headShake' => [ 'timingFunction' => 'ease-in-out' ],
'swing' => false,
'tada' => false,
'wobble' => false,
'jello' => false,
'heartBeat' => [ 'timingFunction' => 'ease-in-out' ],
'backInDown' => false,
'backInLeft' => false,
'backInRight' => false,
'backInUp' => false,
'bounceIn' => false,
'bounceInDown' => false,
'bounceInLeft' => false,
'bounceInRight' => false,
'bounceInUp' => false,
'fadeIn' => false,
'fadeInDown' => [ 'name' => 'fadeInXY' ],
'fadeInDownBig' => [ 'name' => 'fadeInXY' ],
'fadeInLeft' => [ 'name' => 'fadeInXY' ],
'fadeInLeftBig' => [ 'name' => 'fadeInXY' ],
'fadeInRight' => [ 'name' => 'fadeInXY' ],
'fadeInRightBig' => [ 'name' => 'fadeInXY' ],
'fadeInUp' => [ 'name' => 'fadeInXY' ],
'fadeInUpBig' => [ 'name' => 'fadeInXY' ],
'fadeInTopLeft' => [ 'name' => 'fadeInXY' ],
'fadeInTopRight' => [ 'name' => 'fadeInXY' ],
'fadeInBottomLeft' => [ 'name' => 'fadeInXY' ],
'fadeInBottomRight' => [ 'name' => 'fadeInXY' ],
'fadeInUpSmall' => [ 'name' => 'fadeInXY' ],
'fadeInRightSmall' => [ 'name' => 'fadeInXY' ],
'fadeInDownSmall' => [ 'name' => 'fadeInXY' ],
'fadeInLeftSmall' => [ 'name' => 'fadeInXY' ],
'flip' => false,
'flipInX' => false,
'flipInY' => false,
'lightSpeedInRight' => [ 'timingFunction' => 'ease-out' ],
'lightSpeedInLeft' => [ 'timingFunction' => 'ease-out' ],
'rotateIn' => [ 'name' => 'rotateInZ' ],
'rotateInDownLeft' => [ 'name' => 'rotateInZ' ],
'rotateInDownRight' => [ 'name' => 'rotateInZ' ],
'rotateInUpLeft' => [ 'name' => 'rotateInZ' ],
'rotateInUpRight' => [ 'name' => 'rotateInZ' ],
'jackInTheBox' => false,
'rollIn' => false,
'zoomIn' => false,
'zoomInDown' => [ 'name' => 'zoomInXY' ],
'zoomInLeft' => [ 'name' => 'zoomInXY' ],
'zoomInRight' => [ 'name' => 'zoomInXY' ],
'zoomInUp' => [ 'name' => 'zoomInXY' ],
'slideInDown' => [ 'name' => 'slideInXY' ],
'slideInLeft' => [ 'name' => 'slideInXY' ],
'slideInRight' => [ 'name' => 'slideInXY' ],
'slideInUp' => [ 'name' => 'slideInXY' ],
'slideInDownSmall' => [ 'name' => 'slideInXY' ],
'slideInLeftSmall' => [ 'name' => 'slideInXY' ],
'slideInRightSmall' => [ 'name' => 'slideInXY' ],
'slideInUpSmall' => [ 'name' => 'slideInXY' ],
'spin' => false,
'zoomOutIn' => false,
];
return $animation_names;
}
/**
* Has a animation setting value or not
*
* @param string $type
* @param string $name
* @param array $settings
* @return mixed
*/
private function has_animation_setting_value( $type, $name, $settings ) {
return isset( $settings[ $type ][ $name ] ) || isset( $settings[ $name ] );
}
/**
* Get background animation setting value
*
* @param string $type
* @param string $name
* @param array $settings
* @param mixed $default_val
* @return mixed
*/
private function get_animation_setting_value( $type, $name, $settings, $default_val = '' ) {
if ( isset( $settings[ $type ][ $name ] ) ) {
return $settings[ $type ][ $name ];
} else {
return $settings[ $name ] ?? $default_val;
}
}
/**
* Build the video background
*
* @param array $background
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
private function build_video_background( $background, $block_content, $block, $block_instance ) {
$settings = $background['settings'] ?? [];
$media = $background['video'] ?? [];
$block_class = 'bb:has-background bb:has-background--video';
$background_position = round( ( $settings['focalPoint']['x'] ?? .5 ) * 100 ) . '% ' . round( ( $settings['focalPoint']['y'] ?? .5 ) * 100 ) . '%';
$object_fit = $settings['objectFit'] ?? 'cover';
if ( empty( $object_fit ) ) {
$object_fit = 'cover';
}
$video_style = 'object-fit:' . $object_fit . ';object-position:' . $background_position . ';';
$block_background = sprintf( '', $media['url'], $video_style );
if ( $media['isPausable'] ?? false ) {
$block_class .= ' cbb-has-video-controls';
$position_class = ( $media['playPausePosition'] ?? '' ) ? ' is-' . esc_attr( $media['playPausePosition'] ) : '';
$block_background .= '';
}
if ( strpos( $block['innerHTML'] ?? '', $block_class ) === false ) {
$block_content = $this->add_class_to_block( $block_content, $block_class );
}
$block_content = $this->add_inner_content_to_block( $block_content, $block_background );
return $block_content;
}
/**
* Build the embed background
*
* @param array $background
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
private function build_embed_background( $background, $block_content, $block, $block_instance ) {
$media = $background['embed'] ?? [];
// Invalid data.
if ( ! is_string( $media['url'] ) ) {
return $block_content;
}
$block_background = '';
// Use WordPress's native oEmbed processing (includes caching).
$oembed_html = wp_oembed_get( $media['url'] );
if ( $oembed_html ) {
// Extract iframe src from the oEmbed HTML.
preg_match( '/src=["\']([^"\']+)["\']/', $oembed_html, $src_matches );
if ( ! empty( $src_matches[1] ) ) {
$iframe_src = $src_matches[1];
// Detect provider from iframe src URL.
$lower_src = strtolower( $iframe_src );
$provider = null;
if ( str_contains( $lower_src, 'youtube.com' ) || str_contains( $lower_src, 'youtu.be' ) ) {
$provider = 'youtube';
} elseif ( str_contains( $lower_src, 'vimeo.com' ) ) {
$provider = 'vimeo';
} elseif ( str_contains( $lower_src, 'videopress.com' ) ) {
$provider = 'videopress';
} elseif ( str_contains( $lower_src, 'wordpress.tv' ) ) {
$provider = 'wordpress-tv';
}
// Modify iframe src to add background video parameters based on provider.
$parsed_url = wp_parse_url( $iframe_src );
if ( $parsed_url && isset( $parsed_url['host'] ) ) {
// Parse existing query parameters.
$query_params = array();
if ( isset( $parsed_url['query'] ) ) {
parse_str( $parsed_url['query'], $query_params );
}
// Add background video parameters based on provider.
if ( 'youtube' === $provider ) {
$query_params['autoplay'] = '1';
$query_params['mute'] = '1';
$query_params['loop'] = '1';
$query_params['controls'] = '0';
$query_params['playsinline'] = '1';
$query_params['rel'] = '1';
// For loop to work, we need the playlist parameter.
$path = $parsed_url['path'] ?? '';
$path_segments = explode( '/', $path );
$video_id = end( $path_segments );
if ( $video_id ) {
$query_params['playlist'] = $video_id;
}
} elseif ( 'vimeo' === $provider ) {
$query_params['autoplay'] = '1';
$query_params['muted'] = '1';
$query_params['loop'] = '1';
$query_params['background'] = '1';
$query_params['controls'] = '0';
$query_params['transparent'] = '0';
} else {
$query_params['autoplay'] = '1';
$query_params['loop'] = '1';
$query_params['muted'] = '1';
}
// Rebuild the URL with new parameters.
$iframe_src = $parsed_url['scheme'] . '://' . $parsed_url['host'];
if ( isset( $parsed_url['path'] ) ) {
$iframe_src .= $parsed_url['path'];
}
if ( ! empty( $query_params ) ) {
$iframe_src .= '?' . http_build_query( $query_params );
}
}
// Build the iframe HTML that will replace the figure.
$iframe_html = sprintf(
'',
esc_url( $iframe_src )
);
$block_background = sprintf( '%s
', $iframe_html );
}
}
if ( ! $block_background ) {
return $block_content;
}
$block_class = 'bb:has-background bb:has-background--embed';
if ( strpos( $block['innerHTML'] ?? '', $block_class ) === false ) {
$block_content = $this->add_class_to_block( $block_content, $block_class );
}
$block_content = $this->add_inner_content_to_block( $block_content, $block_background );
return $block_content;
}
/**
* Render block overlay
*
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
public function build_block_overlay( $block_content, $block, $block_instance ) {
// Ignore admin side.
if ( is_admin() ) {
return $block_content;
}
$block_overlay = $block['attrs']['boldblocks']['overlay'] ?? false;
// There is no overlay value.
if ( ! ( $block_overlay['overlayColor'] ?? false ) ) {
return $block_content;
}
// Ignore legacy markup.
if ( strpos( $block['innerHTML'] ?? '', 'bb:block-overlay' ) !== false ) {
return $block_content;
}
$bg_color = '';
$overlay_color = $block_overlay['overlayColor'] ?? [];
if ( ( $overlay_color['gradientSlug'] ?? false ) || ( $overlay_color['gradientValue'] ?? false ) ) {
if ( $overlay_color['gradientSlug'] ?? false ) {
$bg_color = sprintf( 'var(--wp--preset--gradient--%1$s, %2$s)', $overlay_color['gradientSlug'], $overlay_color['gradientValue'] ?? '' );
} else {
$bg_color = $overlay_color['gradientValue'];
}
} elseif ( ( $overlay_color['slug'] ?? false ) || ( $overlay_color['value'] ?? false ) ) {
if ( $overlay_color['slug'] ?? false ) {
$bg_color = sprintf( 'var(--wp--preset--color--%1$s, %2$s)', $overlay_color['slug'], $overlay_color['value'] ?? '' );
} else {
$bg_color = $overlay_color['value'];
}
}
if ( empty( $bg_color ) ) {
return $block_content;
}
$opacity = absint( $block_overlay['opacity'] ?? 100 );
// Build the markup.
$overlay_markup = sprintf( '', $bg_color, $opacity / 100 );
$block_class = 'bb:has-overlay';
if ( strpos( $block['innerHTML'] ?? '', $block_class ) === false ) {
$block_content = $this->add_class_to_block( $block_content, $block_class );
}
$block_content = $this->add_inner_content_to_block( $block_content, $overlay_markup );
return $block_content;
}
/**
* Render accordions block data
*
* @param array $parsed_block
* @return array
*/
public function render_accordion_block_data( $parsed_block ) {
$block_type = \WP_Block_Type_Registry::get_instance()->get_registered( $parsed_block['blockName'] );
if ( ! $block_type ) {
return $parsed_block;
}
$layout_type = $block_type->supports['layoutType'] ?? '';
if ( ! in_array( $layout_type, [ 'accordion', 'accordionItem' ], true ) ) {
return $parsed_block;
}
static $accordion_counter = 0;
static $accordion_id = '';
static $item_index = [];
if ( 'accordion' === $layout_type ) {
++$accordion_counter;
$processor = new \WP_HTML_Tag_Processor( $parsed_block['innerHTML'] ?? '' );
$accordion_id = '';
if ( $processor->next_tag() ) {
$accordion_id = $processor->get_attribute( 'id' );
}
if ( ! $accordion_id ) {
$accordion_id = 'accordion-' . $accordion_counter;
}
} elseif ( 'accordionItem' === $layout_type ) {
if ( ! isset( $item_index[ $accordion_id ] ) ) {
$item_index[ $accordion_id ] = 1;
} else {
++$item_index[ $accordion_id ];
}
$parsed_block['attrs']['boldblocks']['accordionItem']['block_id'] = $accordion_id . '-item-' . $item_index[ $accordion_id ];
}
return $parsed_block;
}
/**
* Render accordion item blocks
*
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
public function render_accordion_item_block( $block_content, $block, $block_instance ) {
// Ignore admin side.
if ( is_admin() ) {
return $block_content;
}
// Bail if it is not an accordion item block.
if ( 'accordionItem' !== ( $block_instance->block_type->supports['layoutType'] ?? '' ) ) {
return $block_content;
}
$block_id = $block['attrs']['boldblocks']['accordionItem']['block_id'] ?? '';
$block_id_selector = '#' . $block_id;
if ( $block_id ) {
$processor = new \WP_HTML_Tag_Processor( $block_content );
while ( $processor->next_tag() ) {
$tag_class = $processor->get_attribute( 'class' );
if ( ! $tag_class ) {
continue;
}
if ( strpos( $tag_class, 'accordion-header' ) !== false ) {
$processor->set_attribute( 'data-toggle-target', $block_id_selector );
$processor->set_attribute( 'aria-controls', $block_id );
} elseif ( strpos( $tag_class, 'accordion-link' ) !== false ) {
$processor->set_attribute( 'href', $block_id_selector );
} elseif ( strpos( $tag_class, 'accordion-collapse' ) !== false ) {
$processor->set_attribute( 'id', $block_id );
$processor->set_attribute( 'role', 'region' );
break;
}
}
$block_content = $processor->get_updated_html();
}
return $block_content;
}
/**
* Render grid style for the query loop's grid layout
*
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
public function render_query_loop_grid_style( $block_content, $block, $block_instance ) {
// Ignore admin side.
if ( is_admin() ) {
return $block_content;
}
// If this is a core/query block that has grid layout.
$grid_data = $this->get_query_loop_grid_data( $block, $block_instance );
if ( ! $grid_data ) {
return $block_content;
}
$display_layout = $grid_data['displayType'] ?? '';
if ( 'responsiveGrid' === $display_layout && ( $grid_data['grid'] ?? false ) ) {
// Buil selector.
$selector = 'cbb-query-' . $this->get_query_loop_id( $block );
// Get responsive settings.
$breakpoints = $this->get_breakpoints();
// Get custom style.
$block_style = $this->get_query_grid_style(
[
'selector' => ".{$selector} > ul",
'block' => $block,
'data' => $grid_data['grid'],
'breakpoints' => $breakpoints,
]
);
if ( empty( $block_style ) ) {
return $block_content;
}
$handle = $selector;
// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
wp_register_style( $handle, '' );
wp_add_inline_style( $handle, $block_style );
wp_enqueue_style( $handle );
// Add selector to block wrapper element.
$block_content = $this->add_class_to_block( $block_content, $selector );
}
return $block_content;
}
/**
* Build an unique ID for a query loop
*
* @param array $block
* @return string
*/
private function get_query_loop_id( $block ) {
$query_id = $block['attrs']['queryId'] ?? 1;
$key = "id_{$query_id}";
if ( ! isset( $this->query_ids[ $key ] ) ) {
$this->query_ids[ $key ] = 1;
} else {
++$this->query_ids[ $key ];
}
return $query_id . '-' . $this->query_ids[ $key ];
}
/**
* Get grid layout data
*
* @param array $block
* @param WP_Block $block_instance
* @return array
*/
private function get_query_loop_grid_data( $block, $block_instance ) {
$data = [];
$old_layout_type = $block['attrs']['displayLayout']['type'] ?? '';
if ( 'grid' === $old_layout_type ) {
// Convert to the new name.
$old_layout_type = 'responsiveGrid';
}
$data['displayType'] = $old_layout_type;
$data['grid'] = $block['attrs']['boldblocks']['grid'] ?? [];
if ( $block_instance->block_type->api_version >= 3 ) {
$post_template = $this->get_nested_post_template( $block_instance );
if ( $post_template ) {
if ( 'responsiveGrid' === ( $post_template->attributes['boldblocks']['layout']['type'] ?? '' ) ) {
$data['displayType'] = 'responsiveGrid';
$data['grid'] = $post_template->attributes['boldblocks']['grid'] ?? [];
}
}
}
return $data;
}
/**
* Build grid style
*
* @param array $args
* @return string
*/
private function get_query_grid_style( $args ) {
$selector = $args['selector'];
$data = $args['data'];
// None-responsive style.
$style = "{$selector}{display:grid;padding:0;margin:0;}{$selector} > li{padding:0;margin:0;}{$selector} > li + li{margin-block-start:0;margin-block-end:0;}";
// Columns style.
$style .= $this->build_query_loop_responsive_style(
array_merge(
$args,
[
'setting_value' => $data['columns'] ?? null,
'func_build_responsive_style' => function ( $args ) {
$style_array = [];
$value = $args['value'];
if ( $value ) {
$style_array = [
'--cbb--grid--columns' => $value,
];
}
return $style_array;
},
'func_build_dependent_style' => function ( $args ) {
$responsive_styles = $args['responsive_styles'] ?? [];
$selector = $args['selector'];
$breakpoints = $args['breakpoints'] ?? [];
$style = "{$selector}{grid-template-columns:var(--cbb--grid--columns);}";
foreach ( $responsive_styles as $breakpoint => $attribute_array ) {
$media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
if ( $media_query ) {
$style = \str_replace( '##CONTENT##', $style, $media_query );
}
break;
}
return $style;
},
]
)
);
// Rows style.
$style .= $this->build_query_loop_responsive_style(
array_merge(
$args,
[
'setting_value' => $data['rows'] ?? null,
'func_build_responsive_style' => function ( $args ) {
$style_array = [];
$value = $args['value'];
if ( $value ) {
$style_array = [
'--cbb--grid--rows' => $value,
];
}
return $style_array;
},
'func_build_dependent_style' => function ( $args ) {
$responsive_styles = $args['responsive_styles'] ?? [];
$selector = $args['selector'];
$breakpoints = $args['breakpoints'] ?? [];
$style = "{$selector}{grid-template-rows:var(--cbb--grid--rows);}";
foreach ( $responsive_styles as $breakpoint => $attribute_array ) {
$media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
if ( $media_query ) {
$style = \str_replace( '##CONTENT##', $style, $media_query );
}
break;
}
return $style;
},
]
)
);
// Auto rows style.
$style .= $this->build_query_loop_responsive_style(
array_merge(
$args,
[
'setting_value' => $data['autoRows'] ?? null,
'func_build_responsive_style' => function ( $args ) {
$style_array = [];
$value = $args['value'];
if ( $value ) {
$style_array = [
'--cbb--grid--auto-rows' => $value,
];
}
return $style_array;
},
'func_build_dependent_style' => function ( $args ) {
$responsive_styles = $args['responsive_styles'] ?? [];
$selector = $args['selector'];
$breakpoints = $args['breakpoints'] ?? [];
$style = "{$selector}{grid-auto-rows:var(--cbb--grid--auto-rows);}";
foreach ( $responsive_styles as $breakpoint => $attribute_array ) {
$media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
if ( $media_query ) {
$style = \str_replace( '##CONTENT##', $style, $media_query );
}
break;
}
return $style;
},
]
)
);
// Gap style.
$style .= $this->build_query_loop_responsive_style(
array_merge(
$args,
[
'setting_value' => $data['gap'] ?? null,
'func_build_responsive_style' => function ( $args ) {
$style_array = [];
$value = $args['value'];
if ( $value && is_array( $value ) ) {
$row = $value['row'] ?? null;
if ( $this->is_valid_value( $row ) ) {
$style_array['--cbb--grid--gap--row'] = $row;
}
$column = $value['column'] ?? null;
if ( $this->is_valid_value( $column ) ) {
$style_array['--cbb--grid--gap--column'] = $column;
}
}
return $style_array;
},
'func_build_dependent_style' => function ( $args ) {
$responsive_styles = $args['responsive_styles'] ?? [];
$selector = $args['selector'];
$breakpoints = $args['breakpoints'] ?? [];
$style = '';
foreach ( $responsive_styles as $breakpoint => $style_array ) {
if ( strpos( $style, 'row-gap' ) !== false && strpos( $style, 'column-gap' ) !== false ) {
break;
}
$media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
if ( strpos( $style, 'row-gap' ) === false && isset( $style_array['--cbb--grid--gap--row'] ) ) {
$row_style = "{$selector}{row-gap:var(--cbb--grid--gap--row);}";
if ( $media_query ) {
$style .= \str_replace( '##CONTENT##', $row_style, $media_query );
} else {
$style .= $row_style;
}
}
if ( strpos( $style, 'column-gap' ) === false && isset( $style_array['--cbb--grid--gap--column'] ) ) {
$column_style = "{$selector}{column-gap:var(--cbb--grid--gap--column);}";
if ( $media_query ) {
$style .= \str_replace( '##CONTENT##', $column_style, $media_query );
} else {
$style .= $column_style;
}
}
}
return $style;
},
]
)
);
$items = $data['items'] ?? [];
if ( $items ) {
foreach ( $items as $item_key => $item_value ) {
$item_index = str_replace( 'item', '', $item_key );
// Column style.
$style .= $this->build_query_loop_responsive_style(
array_merge(
$args,
[
'setting_value' => $item_value['columnSpan'] ?? [],
'setting_name' => 'columnSpan',
'selector' => "{$selector} > li:nth-of-type({$item_index})",
'func_build_responsive_style' => [ $this, 'build_grid_item_responsive_style' ],
'func_build_dependent_style' => [ $this, 'build_grid_item_dependent_style' ],
]
)
);
// Row style.
$style .= $this->build_query_loop_responsive_style(
array_merge(
$args,
[
'setting_value' => $item_value['rowSpan'] ?? [],
'setting_name' => 'rowSpan',
'selector' => "{$selector} > li:nth-of-type({$item_index})",
'func_build_responsive_style' => [ $this, 'build_grid_item_responsive_style' ],
'func_build_dependent_style' => [ $this, 'build_grid_item_dependent_style' ],
]
)
);
}
}
return $style;
}
/**
* Build responsive style for grid item
*
* @param array $args
* @return array
*/
private function build_grid_item_responsive_style( $args ) {
$setting_name = $args['setting_name'] ?? 'columnSpan';
$variable_name = 'columnSpan' === $setting_name ? '--cbb--grid-item--column' : '--cbb--grid-item--row';
$style_array = [];
$value = $args['value'];
if ( $value && is_array( $value ) ) {
$span = $value['span'] ?? null;
$start = $value['start'] ?? null;
if ( $this->is_valid_value( $start ) || $this->is_valid_value( $span ) ) {
if ( ! $this->is_valid_value( $start ) ) {
$start = 'auto';
}
if ( ! $this->is_valid_value( $span ) ) {
$span = 'auto';
}
if ( $span !== 'auto' && $span > 0 ) {
$span = "span {$span}";
}
$style_array[ $variable_name ] = "{$start} / {$span}";
}
if ( 'columnSpan' === $setting_name ) {
$order = $value['order'] ?? null;
if ( is_numeric( $order ) ) {
$style_array['--cbb--grid-item--order'] = $order;
}
}
}
return $style_array;
}
/**
* Build dependent style for grid item
*
* @param array $args
* @return array
*/
private function build_grid_item_dependent_style( $args ) {
$setting_name = $args['setting_name'] ?? 'columnSpan';
$variable_name = 'columnSpan' === $setting_name ? '--cbb--grid-item--column' : '--cbb--grid-item--row';
$css_name = 'columnSpan' === $setting_name ? 'grid-column' : 'grid-row';
$responsive_styles = $args['responsive_styles'] ?? [];
$selector = $args['selector'];
$breakpoints = $args['breakpoints'] ?? [];
$style = '';
foreach ( $responsive_styles as $breakpoint => $style_array ) {
if ( 'columnSpan' === $setting_name ) {
if ( strpos( $style, $css_name ) !== false && strpos( $style, 'order' ) !== false ) {
break;
}
} elseif ( strpos( $style, $css_name ) !== false ) {
break;
}
$media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
if ( strpos( $style, $css_name ) === false && isset( $style_array[ $variable_name ] ) ) {
$column_style = "{$selector}{{$css_name}:var($variable_name);}";
if ( $media_query ) {
$style .= \str_replace( '##CONTENT##', $column_style, $media_query );
} else {
$style .= $column_style;
}
}
if ( 'columnSpan' === $setting_name && strpos( $style, 'order' ) === false && isset( $style_array['--cbb--grid-item--order'] ) ) {
$order_style = "{$selector}{order:var(--cbb--grid-item--order);}";
if ( $media_query ) {
$style .= \str_replace( '##CONTENT##', $order_style, $media_query );
} else {
$style .= $order_style;
}
}
}
return $style;
}
/**
* Render carousel layout for query loop blocks
*
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
public function render_query_loop_carousel_layout( $block_content, $block, $block_instance ) {
// Ignore admin side.
if ( is_admin() ) {
return $block_content;
}
$post_template = $this->get_nested_post_template( $block_instance );
if ( $post_template ) {
// If this has carousel layout.
if ( 'carousel' === ( $post_template->attributes['boldblocks']['layout']['type'] ?? '' ) ) {
// Build carousel settings.
$carousel_settings = $post_template->attributes['boldblocks']['carousel'] ?? [];
if ( $carousel_settings ) {
if ( ! $this->block_has_attribute( 'data-carousel-settings', $block_content ) ) {
$carousel_attr = $this->build_carousel_settings( $carousel_settings, true, $post_template->parsed_block, $post_template );
if ( $carousel_attr ) {
$block_content = $this->add_data_to_block( $block_content, 'data-carousel-settings', esc_attr( $carousel_attr ) );
}
}
// Buil selector.
$selector = 'cbb-query-' . $this->get_query_loop_id( $block );
// Get responsive settings.
$breakpoints = $this->get_breakpoints();
// Get custom style.
$block_style = $this->get_carousel_preload_style(
'',
[
'selector' => ".{$selector}",
'settings' => [ 'carousel' => $carousel_settings ],
'block_layout' => 'carousel',
'breakpoints' => $breakpoints,
]
);
$carousel_class = 'js-carousel-layout cbb-carousel-preload';
if ( $block_style ) {
$handle = $selector;
// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
wp_register_style( $handle, '' );
wp_add_inline_style( $handle, $block_style );
wp_enqueue_style( $handle );
$carousel_class .= ' ' . $selector;
}
if ( $carousel_settings['displayLoader'] ?? false ) {
$carousel_class .= ' has-preloader';
if ( 'coverflow' === ( $carousel_settings['effect'] ?? '' ) ) {
$carousel_class .= ' is-fading-loader';
}
// Add preloader.
$block_content = $this->add_inner_content_to_block( $block_content, $this->get_carousel_preloader_markup( $block, $block_instance ) );
}
// Add selector to block wrapper element.
$block_content = $this->add_class_to_block( $block_content, $carousel_class );
}
}
}
return $block_content;
}
/**
* Add preload style for carousels
*
* @param string $style
* @param array $args
*/
public function get_carousel_preload_style( $style, $args ) {
if ( 'carousel' === ( $args['block_layout'] ?? '' ) ) {
$preload_style = '';
$value = $args['settings']['carousel'] ?? false;
$selector = $args['selector'];
$breakpoints = $args['breakpoints'];
if ( $value && is_array( $value ) ) {
if ( $value['enableResponsive'] ?? false ) {
$setting_value = $value['breakpoints'] ?? [];
$responsive_styles = [];
foreach ( $setting_value as $breakpoint => $value_by_breakpoint ) {
$value = null;
if ( $this->is_valid_value( $value_by_breakpoint['value'] ?? null ) ) {
$value = $value_by_breakpoint['value'];
} elseif ( isset( $value_by_breakpoint['inherit'] ) && is_string( $value_by_breakpoint['inherit'] ) ) {
$value = $setting_value[ $value_by_breakpoint['inherit'] ]['value'] ?? null;
}
if ( $this->is_valid_value( $value ) ) {
$style_by_breakpoint = $this->build_carousel_preload_style( $value );
if ( $style_by_breakpoint ) {
$responsive_styles[ $breakpoint ] = $style_by_breakpoint;
}
}
}
$this->sort_styles( $responsive_styles );
$last_responsive_style = '';
foreach ( $responsive_styles as $breakpoint => $style_by_breakpoint ) {
if ( ! $style_by_breakpoint || ! is_array( $style_by_breakpoint ) ) {
continue;
}
$responsive_style = '';
foreach ( $style_by_breakpoint as $attr_key => $attr_value ) {
$responsive_style .= "{$attr_key}:{$attr_value};";
}
if ( $responsive_style !== $last_responsive_style ) {
$style_with_selector = "{$selector}{{$responsive_style}}";
$media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
if ( $media_query ) {
$preload_style .= \str_replace( '##CONTENT##', $style_with_selector, $media_query );
} else {
$preload_style .= $style_with_selector;
}
$last_responsive_style = $responsive_style;
}
}
} else {
$style_array = $this->build_carousel_preload_style( $value );
$attr_style = '';
foreach ( $style_array as $attr_key => $attr_value ) {
$attr_style .= "{$attr_key}:{$attr_value};";
}
if ( $attr_style ) {
$preload_style .= "{$selector}{{$attr_style}}";
}
}
}
if ( $preload_style ) {
$style .= $preload_style;
}
}
return $style;
}
/**
* Build preload style for carousels
*
* @param array $value
* @return void
*/
private function build_carousel_preload_style( $value ) {
$style_array = [];
$slides_perview = $value['slidesPerView'] ?? 1;
if ( is_array( $slides_perview ) ) {
if ( $slides_perview['auto'] ?? false ) {
$slides_perview = 'auto';
} else {
$slides_perview = $slides_perview['value'] ?? 1;
}
}
if ( $slides_perview !== 'auto' ) {
$gap_count = absint( ceil( $slides_perview ) ) - 1;
if ( $slides_perview <= 0 ) {
$slides_perview = 1;
}
} else {
$gap_count = 0;
}
$space_between = $value['spaceBetween'] ?? 0;
if ( $space_between < 0 ) {
$space_between = 0;
}
$total_gap = $gap_count * $space_between;
$style_array['--cbb-slide-width'] = $slides_perview === 'auto' ? 'auto' : "calc((100% - {$total_gap}px ) / {$slides_perview})";
$style_array['--cbb-carousel-gap'] = "{$space_between}px";
return $style_array;
}
/**
* Build a json string of carousel settings
*
* @param array $carousel_settings
* @param boolean $exclude_li_role
* @param array $parsed_block
* @param WP_Block $block_instance
* @return string
*/
private function build_carousel_settings( $carousel_settings, $exclude_li_role = false, $parsed_block = null, $block_instance = null ) {
// phpcs:disable
$dataset = [];
// Speed.
if ( $carousel_settings['speed'] ?? false ) {
$dataset['speed'] = $carousel_settings['speed'];
}
// Loop and rewind.
$dataset['loop'] = false;
$dataset['rewind'] = false;
$loop_type = $carousel_settings['loopType'] ?? '';
if ( 'infinite' === $loop_type ) {
$dataset['loop'] = true;
} elseif ( 'rewind' === $loop_type ) {
$dataset['rewind'] = true;
}
// Autoplay.
$dataset['autoplay'] = false;
$autoplay = $carousel_settings['autoplay'] ?? false;
if ( $autoplay && is_array( $autoplay ) && ( $autoplay['enable'] ?? false ) ) {
unset( $autoplay['enable'] );
if ( !$dataset['loop'] && !$dataset['rewind']) {
$autoplay['stopOnLastSlide'] = true;
}
$dataset['autoplay'] = $autoplay;
}
// Play/pause.
if ( $carousel_settings['playPause']['enable'] ?? false ) {
$dataset['playPause'] = $carousel_settings['playPause'];
}
// Direction.
if ( $carousel_settings['direction'] ?? false ) {
$dataset['direction'] = $carousel_settings['direction'];
}
// Effect.
$effect = $carousel_settings['effect'] ?? 'slide';
// Handle wrong default data.
if ( is_array( $effect ) ) {
$effect = 'slide';
}
if ( $effect ) {
$dataset['effect'] = $effect;
$effectSettings = $carousel_settings['effectSettings'] ?? [];
$settings = $effectSettings[ $effect . 'Effect' ] ?? false;
// Fade.
if ( 'fade' === $effect ) {
$dataset['fadeEffect'] = [ 'crossFade' => true ];
} elseif ( 'coverflow' === $effect ) {
// Coverflow.
if ( is_array( $settings ) ) {
$dataset['coverflowEffect'] = $settings;
} else {
$dataset['coverflowEffect'] = [ 'slideShadows' => false ];
}
} elseif ( 'creative' === $effect ) {
// Creative.
if ( is_array( $settings ) ) {
$dataset['creativeEffect'] = $settings;
} else {
$dataset['creativeEffect'] = [
'prev' => [
'translate' => [ '-20%', 0, -1 ],
],
'next' => [
'translate' => [ '100%', 0, 0 ],
],
];
}
$dataset['direction'] = "horizontal";
}
}
// slidesPerView.
$slidesPerViewDependencies = [
'fade',
'flip',
'cube',
'cards',
'creative',
];
if ( in_array($effect, $slidesPerViewDependencies, true ) ) {
$dataset['slidesPerView'] = 1;
} else {
// centeredSlides.
$dataset['centeredSlides'] = $carousel_settings['centeredSlides'] ?? false;
if ($dataset['centeredSlides'] && $effect === 'slide' && ($carousel_settings['centeredSlidesSettings']['enable'] ?? '')) {
$dataset['centeredSlidesSettings'] = $carousel_settings['centeredSlidesSettings'];
}
$slidesPerView = $this->carousel_refine_slides_per_view( $carousel_settings['slidesPerView'] ?? 1 );
$slidesPerGroup = absint( $carousel_settings['slidesPerGroup'] ?? 0 );
if ( $slidesPerView ) {
$dataset['slidesPerView'] = $slidesPerView;
if ( $slidesPerView >= 2 && $slidesPerGroup ) {
$dataset['slidesPerGroup'] = $slidesPerGroup;
}
}
// spaceBetween.
$spaceBetween = absint( $carousel_settings['spaceBetween'] ?? 0 );
if ( $spaceBetween ) {
$dataset['spaceBetween'] = $spaceBetween;
}
// Breakpoints.
$breakpoint_settings = $this->get_breakpoints();
$breakpoint_values = [
'sm' => $breakpoint_settings['sm']['breakpoint'] ?? 576,
'md' => $breakpoint_settings['md']['breakpoint'] ?? 768,
'lg' => $breakpoint_settings['lg']['breakpoint'] ?? 1024,
];
$breakpoints = $carousel_settings['breakpoints'] ?? [];
if ( ($carousel_settings['enableResponsive'] ?? false) && is_array( $breakpoints ) ) {
// Mobile first.
$this->sort_styles( $breakpoints );
$breakpoint_atts = [];
foreach ( array_keys( $breakpoints ) as $breakpoint ) :
$value = $breakpoints[ $breakpoint ]['value'] ?? '';
$inherit = $breakpoints[ $breakpoint ]['inherit'] ?? '';
$value_by_breakpoint = false;
if ( $value && is_array( $value ) ) {
$value_by_breakpoint = $value;
}
if ( ! $value_by_breakpoint && is_string( $inherit ) ) {
$inheritValue = $breakpoints[ $inherit ] ?? '';
if ( $inheritValue && is_array( $inheritValue ) ) {
$value_by_breakpoint = $inheritValue;
}
}
if ( is_array( $value_by_breakpoint ) ) {
$value_by_breakpoint['slidesPerView'] = $this->carousel_refine_slides_per_view( $value_by_breakpoint['slidesPerView'] ?? 1);
$breakpoint_atts[ $breakpoint_values[ $breakpoint ] ] = $value_by_breakpoint;
}
endforeach;
if ( $breakpoint_atts ) {
// Replace global params.
$first_breakpoint = array_values($breakpoint_atts)[0];
$dataset['slidesPerView'] = $first_breakpoint['slidesPerView'];
if ($dataset['slidesPerView'] > 1 && ($first_breakpoint['slidesPerGroup'] ?? 0)) {
$dataset['slidesPerGroup'] = $first_breakpoint['slidesPerGroup'];
}
if ( ! empty( $first_breakpoint['spaceBetween'] ) ) {
$dataset['spaceBetween'] = $first_breakpoint['spaceBetween'];
}
// Set the breakpoints.
$dataset['breakpoints'] = $breakpoint_atts;
}
} else {
if ( $carousel_settings['oneSlidePerViewInMobile'] ?? false ) {
$dataset['slidesPerView'] = 1;
if ( $slidesPerGroup ) {
$dataset['slidesPerGroup'] = 1;
}
$dataset['breakpoints'] = [
$breakpoint_values['sm'] => [
'slidesPerView' => 1,
'slidesPerGroup' => 1,
],
$breakpoint_values['md'] => [
'slidesPerView' => $slidesPerView,
'slidesPerGroup' => $slidesPerGroup ? $slidesPerGroup : 1,
],
];
}
// Reset centeredSlides
if ( ! ( $slidesPerView > 1 || 'auto' === $slidesPerView ) ) {
unset( $dataset['centeredSlides'] );
unset( $dataset['centeredSlidesSettings'] );
}
}
}
// Pagination.
$pagination = $carousel_settings['pagination'] ?? [];
$dataset['pagination'] = $pagination['enable'] ?? false;
if ( $dataset['pagination'] ) {
$dataset['paginationSettings'] = $pagination;
}
// Navigation.
$navigation = $carousel_settings['navigation'] ?? [];
$dataset['navigation'] = $navigation['enable'] ?? false;
if ( $dataset['navigation'] ) {
$next_icon = trim($navigation['nextIcon']['value'] ?? '');
$prev_icon = trim($navigation['prevIcon']['value'] ?? '');
if ( $next_icon || $prev_icon ) {
$svg_sanitizer = $this->the_plugin_instance->get_component( IconLibrary::class );
if ($svg_sanitizer) {
$next_icon = $next_icon ? $svg_sanitizer->sanitize_svg( $next_icon ) : '';
$prev_icon = $prev_icon ? $svg_sanitizer->sanitize_svg( $prev_icon ) : '';
$navigation['nextIcon']['value'] = $next_icon;
$navigation['prevIcon']['value'] = $prev_icon;
}
}
$dataset['navigationSettings'] = $navigation;
}
// Scrollbar.
$scrollbar = $carousel_settings['scrollbar'] ?? [];
$dataset['scrollbar'] = $scrollbar['enable'] ?? false;
if ( $dataset['scrollbar'] ) {
$dataset['scrollbarSettings'] = $scrollbar;
}
// Equal height?.
$equalHeight = $carousel_settings['equalHeight'] ?? false;
if ( $equalHeight ) {
// Only add the property when it is true to prevent from invalid content.
$dataset['equalHeight'] = $equalHeight;
}
// Sync carousels.
$thumbs = $carousel_settings['thumbs'] ?? [];
if ( $thumbs['enable'] ?? false ) {
$dataset['thumbsSettings'] = $thumbs;
}
// Exclude li role.
if ( $exclude_li_role ) {
$dataset['a11y'] = ['slideRole' => '', 'slideLabelMessage' => ''];
}
return wp_json_encode( apply_filters( 'cbb_get_carousel_settings', $dataset, $parsed_block, $block_instance ) );
// phpcs:enable
}
/**
* Refine the slides per view
*
* @param mixed $raw_value
* @return string|float 'auto' || number
*/
private function carousel_refine_slides_per_view( $raw_value ) {
$value = $raw_value;
if ( is_array( $raw_value ) ) {
$value = $raw_value['auto'] ?? false
? 'auto'
: floatval( $raw_value['value'] ?? 1 );
}
return $value ? $value : 1;
}
/**
* Get nested post template block for core/query
*
* @param WP_Block $block_instance
* @return boolean|WP_Block
*/
private function get_nested_post_template( $block_instance ) {
return $this->find_nested_block( $block_instance, 'core/post-template' );
}
/**
* Find a specific nested block from a parent block
*
* @param WP_Block $block
* @param string $block_name
* @return mixed
*/
public function find_nested_block( $block, $block_name ) {
if ( $block->name === $block_name ) {
return $block;
}
foreach ( $block->inner_blocks as $inner_block ) {
$result = $this->find_nested_block( $inner_block, $block_name );
if ( $result ) {
return $result;
}
}
return false;
}
/**
* Build responsive style
*
* @param array $args
* @param array &$style_array
* @param array &$responsive_style_array
* @return string
*/
private function build_query_loop_responsive_style( $args ) {
$func_build_responsive_style = $args['func_build_responsive_style'] ?? '';
if ( ! \is_callable( $func_build_responsive_style ) ) {
return '';
}
$setting_value = $args['setting_value'] ?? [];
if ( empty( $setting_value ) || ! \is_array( $setting_value ) ) {
return '';
}
$breakpoints = $args['breakpoints'];
$selector = $args['selector'];
$responsive_styles = [];
foreach ( $setting_value as $breakpoint => $value_by_breakpoint ) {
$value = null;
if ( $this->is_valid_value( $value_by_breakpoint['value'] ?? null ) ) {
$value = $value_by_breakpoint['value'];
} elseif ( isset( $value_by_breakpoint['inherit'] ) && is_string( $value_by_breakpoint['inherit'] ) ) {
$value = $setting_value[ $value_by_breakpoint['inherit'] ]['value'] ?? null;
}
if ( $this->is_valid_value( $value ) ) {
$style_by_breakpoint = $func_build_responsive_style( array_merge( $args, [ 'value' => $value ] ) );
if ( $style_by_breakpoint ) {
$responsive_styles[ $breakpoint ] = $style_by_breakpoint;
}
}
}
$this->sort_styles( $responsive_styles );
$dependent_style = '';
$func_build_dependent_style = $args['func_build_dependent_style'] ?? '';
if ( \is_callable( $func_build_dependent_style ) ) {
$dependent_style = $func_build_dependent_style( array_merge( $args, [ 'responsive_styles' => $responsive_styles ] ) );
}
$style = '';
$last_responsive_style = '';
foreach ( $responsive_styles as $breakpoint => $style_by_breakpoint ) {
if ( ! $style_by_breakpoint || ! is_array( $style_by_breakpoint ) ) {
continue;
}
$responsive_style = '';
foreach ( $style_by_breakpoint as $attr_key => $attr_value ) {
$responsive_style .= "{$attr_key}:{$attr_value};";
}
if ( $responsive_style !== $last_responsive_style ) {
$style_with_selector = "{$selector}{{$responsive_style}}";
$media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
if ( $media_query ) {
$style .= \str_replace( '##CONTENT##', $style_with_selector, $media_query );
} else {
$style .= $style_with_selector;
}
$last_responsive_style = $responsive_style;
}
}
return $style . $dependent_style;
}
/**
* Build the breakpoints
*
* @return array
*/
private function get_breakpoints() {
if ( ! $this->breakpoints ) {
$breakpoints = get_option( 'cbb_breakpoints' );
if ( empty( $breakpoints ) ) {
$breakpoints = [
[
'breakpoint' => 576,
'prefix' => 'sm',
],
[
'breakpoint' => 768,
'prefix' => 'md',
],
[
'breakpoint' => 1024,
'prefix' => 'lg',
],
];
}
$breakpoints = array_filter(
array_map(
function ( $item ) {
if ( empty( $item['breakpoint'] ) || empty( $item['prefix'] ) ) {
return false;
}
$breakpoint = absint( $item['breakpoint'] );
if ( $breakpoint < 10 ) {
return false;
}
$max_breakpoint = $breakpoint - 1;
$item['breakpointMax'] = $max_breakpoint;
if ( $item['prefix'] !== 'sm' ) {
$item['minQuery'] = "@media (min-width: {$breakpoint}px){##CONTENT##}";
$item['maxQuery'] = "@media (max-width: {$max_breakpoint}px){##MAX_CONTENT##}";
} else {
$item['minQuery'] = '';
$item['maxQuery'] = "@media (max-width: {$max_breakpoint}px){##MAX_CONTENT##}";
}
return $item;
},
$breakpoints
)
);
$this->breakpoints = array_column( $breakpoints, null, 'prefix' );
}
return $this->breakpoints;
}
/**
* Generate block selector.
*
* @param string $block_name
* @param WP_Block $block_instance
* @return string
*/
private function generate_selector( $block_name, $block_instance ) {
$selector_prefix = 'cbb';
if ( '' !== $this->style_contexts['loop'] ) {
$post_id = $block_instance->context['postId'] ?? 0;
if ( ! $this->style_contexts['post'] || $this->style_contexts['post'] !== $post_id ) {
$this->style_contexts['post'] = $post_id;
$this->style_contexts['counter'] = 0;
$this->style_contexts['content_counter'] = 0;
}
if ( $this->style_contexts['content'] ) {
// The content context selector.
$content_counter = ++$this->style_contexts['content_counter'];
return "{$selector_prefix}-p{$this->style_contexts['post']}-{$content_counter}";
}
$counter = ++$this->style_contexts['counter'];
// The selector inside the loop.
return "{$selector_prefix}-{$this->style_contexts['loop']}-{$counter}";
} else {
// The global selector.
return wp_unique_id( "{$selector_prefix}-g-" );
}
}
/**
* Update loop id when having a post template block
*
* @param array $context
* @param array $block
* @return array
*/
public function update_loop_id( $context, $block ) {
if ( $this->is_loop_temlate_block( $block['blockName'] ?? '' ) ) {
if ( isset( $context['queryId'] ) ) {
$query_id = $context['queryId'];
if ( in_array( $query_id, $this->style_contexts['loops'], true ) ) {
$count = count(
array_filter(
$this->style_contexts['loops'],
function ( $value ) use ( $query_id ) {
return $value === $query_id;
}
)
);
$this->style_contexts['loop'] = "{$query_id}i{$count}";
} else {
$this->style_contexts['loop'] = $query_id;
}
$this->style_contexts['loops'][] = $query_id;
}
}
// Post content inside a loop.
if ( $this->style_contexts['loop'] && 'core/post-content' === ( $block['blockName'] ?? '' ) ) {
$this->style_contexts['content'] = true;
}
return $context;
}
/**
* Reset loop id when the post template is rendered
*
* @param string $content
* @param array $block
* @return string
*/
public function reset_loop_id( $content, $block ) {
if ( $this->is_loop_temlate_block( $block['blockName'] ?? '' ) ) {
$this->style_contexts['loop'] = '';
$this->style_contexts['post'] = 0;
}
// Post content inside a loop.
if ( 'core/post-content' === ( $block['blockName'] ?? '' ) ) {
$this->style_contexts['content'] = false;
}
return $content;
}
/**
* Render sticky attributes for core template part blocks
*
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
public function render_template_part_sticky( $block_content, $block, $block_instance ) {
// Ignore admin side.
if ( is_admin() ) {
return $block_content;
}
// If this block has sticky data.
$sticky_data = $block['attrs']['boldblocks']['sticky'] ?? false;
if ( $sticky_data && ( $sticky_data['enable'] ?? false ) ) {
$sticky_classes = [ 'is-sticky-block' ];
$sticky_classes[] = 'is-stick-to-' . ( $sticky_data['stickTo'] ?? 'top' );
if ( $sticky_data['isStickOnScrollingUp'] ?? false ) {
$sticky_classes[] = 'is-sticky-on-scrollup';
} elseif ( $sticky_data['isFixed'] ?? false ) {
$sticky_classes[] = 'is-fixed';
if ( $sticky_data['isInFlow'] ?? false ) {
$sticky_classes[] = 'is-in-flow';
}
if ( $sticky_data['isDetectingScroll'] ?? false ) {
$sticky_classes[] = 'is-detecting-scroll';
}
} elseif ( $sticky_data['isDetectingStuck'] ?? false ) {
$sticky_classes[] = 'is-detecting-stuck';
}
if ( in_array( $block['attrs']['area'] ?? '', [ 'header', 'footer' ], true ) ) {
$sticky_classes[] = 'is-sticky-' . $block['attrs']['area'];
}
$block_content = $this->add_class_to_block( $block_content, implode( ' ', $sticky_classes ) );
}
return $block_content;
}
/**
* Render settings for carousel layout blocks
*
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
public function render_carousel_settings( $block_content, $block, $block_instance ) {
// Ignore admin side.
if ( is_admin() ) {
return $block_content;
}
// There is no carousel layout.
if ( 'carousel' !== ( $block_instance->block_type->supports['layoutType'] ?? '' ) ) {
return $block_content;
}
// Carousel settings.
$carousel_settings = $block['attrs']['boldblocks']['carousel'] ?? [];
// Dataset attribute.
$carousel_dataset = $this->build_carousel_settings( $carousel_settings, false, $block, $block_instance );
if ( $carousel_dataset ) {
$block_content = $this->add_data_to_block( $block_content, 'data-carousel-settings', esc_attr( $carousel_dataset ) );
}
// Preload class.
$preloader_class = 'cbb-carousel-preload';
// Has preloader?
$has_preloader = $carousel_settings['displayLoader'] ?? false;
if ( $has_preloader ) {
$preloader_class .= ' has-preloader';
if ( 'coverflow' === ( $carousel_settings['effect'] ?? '' ) ) {
$preloader_class .= ' is-fading-loader';
}
// Add the loader to the markup.
$block_content = $this->add_inner_content_to_block( $block_content, $this->get_carousel_preloader_markup( $block, $block_instance ) );
}
// Mark it as having preloader.
$block_content = $this->add_class_to_block( $block_content, $preloader_class );
return $block_content;
}
/**
* Get the preloader markup for carousels
*
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
private function get_carousel_preloader_markup( $block, $block_instance ) {
return apply_filters( 'cbb_carousel_preloader', '', $block, $block_instance );
}
/**
* Migrate cbb class for old CBB blocks.
*
* @param string $block_content
* @param array $block
* @param WP_Block $block_instance
* @return string
*/
public function migrate_cbb_class( $block_content, $block, $block_instance ) {
// Ignore admin side or empty content.
if ( is_admin() || ! $block_content ) {
return $block_content;
}
// There is no need to migrate.
$migrate_cbb_class = $block_instance->block_type->supports['migrateClass'] ?? '';
if ( ! $migrate_cbb_class ) {
return $block_content;
}
// Replace the class.
$block_reader = new \WP_HTML_Tag_Processor( $block_content );
if ( $block_reader->next_tag( [ 'class_name' => $migrate_cbb_class ] ) ) {
$new_class = str_ends_with( $migrate_cbb_class, 'custom' ) ? 'cbb-block' : 'cbb-block-parent';
$block_reader->set_attribute( 'class', str_replace( $migrate_cbb_class, $new_class, $block_reader->get_attribute( 'class' ) ) );
// Render the updated HTML.
return $block_reader->get_updated_html();
}
return $block_content;
}
/**
* Render steps block data
*
* @param array $parsed_block
* @param array $block
* @param WP_Block $parent_block
* @return array
*/
public function render_steps_block_data( $parsed_block, $block, $parent_block ) {
if ( ! $parent_block || empty( $parent_block->attributes['boldblocks']['steps']['enableSteps'] ) ) {
return $parsed_block;
}
// Mark it as a child of steps.
$parsed_block['attrs']['boldblocks']['childStep'] = true;
return $parsed_block;
}
/**
* Render aria attributes for steps blocks
*
* @param string $block_content
* @param array $block
* @return string
*/
public function render_steps_attributes( $block_content, $block ) {
if ( ! empty( $block['attrs']['boldblocks']['steps']['enableSteps'] ) ) {
$block_content = $this->add_data_to_block( $block_content, 'role', 'list' );
} elseif ( ! empty( $block['attrs']['boldblocks']['childStep'] ) ) {
$block_content = $this->add_data_to_block( $block_content, 'role', 'listitem' );
}
return $block_content;
}
/**
* Support block style for non-cbb blocks
*
* @param string $is_supported
* @param array $block
* @return boolean
*/
public function support_block_style( $is_supported, $block ) {
if ( in_array( $block['blockName'] ?? '', $this->non_cbb_blocks, true ) ) {
$is_supported = true;
}
return $is_supported;
}
/**
* Set a valid layout for none-cbb blocks
*
* @param string $block_layout
* @param array $block
* @return string
*/
public function get_block_layout( $block_layout, $block ) {
if ( in_array( $block['blockName'] ?? '', $this->non_cbb_blocks, true ) ) {
$block_layout = 'standalone';
}
return $block_layout;
}
/**
* Check whether the block is a loop template block.
*
* @param string $block_name
* @return array
*/
private function is_loop_temlate_block( $block_name ) {
$loop_template_blocks = apply_filters( 'cbb_get_loop_temlate_blocks', [ 'core/post-template' ] );
return in_array( $block_name, $loop_template_blocks, true );
}
/**
* Check whether current block has custom style or not.
*
* @param array $block
* @param WP_Block $block_instance
* @return boolean
*/
private function has_style( $block, $block_instance ) {
// There is no boldblocks value.
if ( ! isset( $block['attrs']['boldblocks'] ) ) {
return false;
}
return apply_filters( 'cbb_support_block_style', isset( $block_instance->block_type->supports['cbb'] ), $block, $block_instance );
}
/**
* Order style array by breakpoints
*
* @param array $styles
* @return void
*/
private function sort_styles( &$styles ) {
uksort(
$styles,
function ( $a, $b ) {
if ( 'lg' === $a || 'sm' === $b ) {
return 1;
}
if ( 'sm' === $a || 'lg' === $b ) {
return -1;
}
return 0;
}
);
}
/**
* Build responsive style
*
* @param array $args
* @param array &$responsive_style_array
* @param array &$classes
* @return string
*/
private function build_responsive_style( $args, &$responsive_style_array, &$classes ) {
$return_value = false;
$func_build_responsive_style = $args['func_build_responsive_style'] ?? '';
if ( ! \is_callable( $func_build_responsive_style ) ) {
return $return_value;
}
$setting_value = $args['setting_value'] ?? [];
if ( empty( $setting_value ) || ! \is_array( $setting_value ) ) {
return $return_value;
}
$breakpoints = $args['breakpoints'];
$selector = $args['selector'];
$setting_values = [];
$responsive_styles = [];
foreach ( $setting_value as $breakpoint => $value_by_breakpoint ) {
$value = null;
if ( $this->is_valid_value( $value_by_breakpoint['value'] ?? null ) ) {
$value = $value_by_breakpoint['value'];
} elseif ( isset( $value_by_breakpoint['inherit'] ) && is_string( $value_by_breakpoint['inherit'] ) ) {
$value = $setting_value[ $value_by_breakpoint['inherit'] ]['value'] ?? null;
}
if ( $this->is_valid_value( $value ) ) {
$style_by_breakpoint = $func_build_responsive_style( array_merge( $args, [ 'value' => $value ] ) );
if ( $style_by_breakpoint ) {
$responsive_styles[ $breakpoint ] = $style_by_breakpoint;
}
}
$setting_values[ $breakpoint ] = $value;
}
$this->sort_styles( $responsive_styles );
$dependent_style = '';
$func_build_dependent_style = $args['func_build_dependent_style'] ?? '';
if ( \is_callable( $func_build_dependent_style ) ) {
$dependent_style = $func_build_dependent_style(
array_merge(
$args,
[
'setting_values' => $setting_values,
'responsive_styles' => $responsive_styles,
]
),
$classes
);
}
$keys = [];
$style = '';
$last_responsive_style = '';
foreach ( $responsive_styles as $breakpoint => $style_by_breakpoint ) {
$responsive_style = '';
foreach ( $style_by_breakpoint as $attr_key => $attr_value ) {
$responsive_style .= "{$attr_key}:{$attr_value};";
if ( ! in_array( $attr_key, $keys, true ) ) {
$keys[] = $attr_key;
$classes[] = $breakpoint . '-' . str_replace( '--cbb--', 'cbb-', $attr_key );
}
}
if ( $responsive_style !== $last_responsive_style ) {
$style_with_selector = "{$selector}{{$responsive_style}}";
$min_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
if ( $min_query ) {
$style_with_selector = \str_replace( '##CONTENT##', $style_with_selector, $min_query );
}
$style .= $style_with_selector;
$last_responsive_style = $responsive_style;
}
}
// Add style to the style_array.
if ( $style ) {
$style .= $dependent_style;
$responsive_style_array[] = $style;
return $style;
}
return $return_value;
}
/**
* Check wether the block markup has a attribute or not.
*
* @param string $attribute
* @param string $block_content
* @return boolean
*/
private function block_has_attribute( $attribute, $block_content ) {
$tags = new \WP_HTML_Tag_Processor( $block_content );
if ( $tags->next_tag() ) {
return null !== $tags->get_attribute( $attribute );
}
return false;
}
/**
* Add CSS class to block wrapper
*
* @param string $block_content
* @param string $classes
* @return string
*/
public function add_class_to_block( $block_content, $classes ) {
$tags = new \WP_HTML_Tag_Processor( $block_content );
if ( $tags->next_tag() ) {
$tags->add_class( $classes );
}
return $tags->get_updated_html();
}
/**
* Add data attribute to block wrapper
*
* @param string $block_content
* @param string $name
* @param string $value
* @return string
*/
public function add_data_to_block( $block_content, $name, $value ) {
$tags = new \WP_HTML_Tag_Processor( $block_content );
if ( $tags->next_tag() ) {
$tags->set_attribute( $name, $value );
}
return $tags->get_updated_html();
}
/**
* Add inner content to block markup
*
* @param string $block_content
* @param string $inner_content
* @return string
*/
public function add_inner_content_to_block( $block_content, $inner_content ) {
if ( $inner_content ) {
$block_content = \preg_replace(
'/' . \preg_quote( '>', '/' ) . '/',
'>' . $inner_content,
$block_content,
1
);
}
return $block_content;
}
/**
* Get inner html of a tag
*
* @param string $content
* @param string $tag_name
* @return string
*/
public function get_element_inner_html( $content, $tag_name ) {
$processor = new class( $content ) extends \WP_HTML_Tag_Processor {
public function get_inner_html( $tag_name ) {
$this->set_bookmark( 'open' );
$opener = $this->bookmarks['open'];
while ( $this->next_token() ) {
if ( $this->get_tag() === $tag_name && $this->is_tag_closer() ) {
$this->set_bookmark( 'close' );
$closer = $this->bookmarks['close'];
return substr(
$this->html,
$opener->start + $opener->length,
$closer->start - $opener->start - $opener->length
);
}
}
return '';
}
};
if ( ! $processor->next_tag( $tag_name ) ) {
return '';
}
return $processor->get_inner_html( $tag_name );
}
/**
* Detect a value is valid
*
* @param mixed $value
* @return boolean
*/
private function is_valid_value( $value ) {
return isset( $value ) && $value !== '';
}
/**
* Detect a responsive value is valid
*
* @param mixed $value
* @return boolean
*/
private function is_valid_responsive_value( $value ) {
return ! empty( $value['lg']['value'] ) || ! empty( $value['md']['value'] ) || ! empty( $value['sm']['value'] );
}
/**
* Get value for spacing var
*
* @param string $value
* @return string
*/
private function get_spacing_value( $value ) {
if ( ! $this->is_valid_value( $value ) ) {
return '';
}
if ( strpos( $value, 'var:preset|spacing|' ) !== false ) {
preg_match( '/var:preset\|spacing\|(.+)/', $value, $slug );
if ( ! $slug ) {
return $value;
}
return "var(--wp--preset--spacing--{$slug[1]})";
}
return $value;
}
/**
* Get CSS value for a color object.
*
* @param array $color_array
* @return string
*/
private function get_css_color_value( $color_array ) {
$value = '';
if ( ! empty( $color_array ) ) {
if ( is_string( $color_array ) ) {
$value = $color_array;
} elseif ( is_array( $color_array ) ) {
if ( $color_array['gradientSlug'] ?? false ) {
$value = "var(--wp--preset--gradient--{$color_array['gradientSlug']}, {$color_array['gradientValue']})";
} elseif ( $color_array['gradientValue'] ?? false ) {
$value = $color_array['gradientValue'];
} elseif ( $color_array['slug'] ?? false ) {
$value = "var(--wp--preset--color--{$color_array['slug']}, {$color_array['value']})";
} elseif ( $color_array['value'] ?? false ) {
$value = $color_array['value'];
}
}
}
return $value;
}
/**
* Sanitize post data
*
* @param array $data
* @return array
*/
public function sanitize_post_data( $data ) {
if ( empty( $data['post_content'] ) ) {
return $data;
}
if ( ! $this->user_can_edit_css() ) {
$blocks = parse_blocks( wp_unslash( $data['post_content'] ) );
// Strip custom css from blocks.
$modified = false;
$blocks = $this->remove_custom_css_from_blocks( $blocks, $modified );
if ( $modified ) {
$data['post_content'] = wp_slash( serialize_blocks( $blocks ) );
}
}
return $data;
}
/**
* Remove custom css if any
*
* @param array $blocks
* @param boolean $modified
* @return array
*/
private function remove_custom_css_from_blocks( $blocks, &$modified = false ) {
foreach ( $blocks as &$block ) {
if ( ! empty( $block['attrs']['boldblocks']['customCSS']['value'] ) ) {
$block['attrs']['boldblocks']['customCSS']['value'] = '';
$modified = true;
}
// Recursive for inner blocks.
if ( ! empty( $block['innerBlocks'] ) ) {
$block['innerBlocks'] = $this->remove_custom_css_from_blocks( $block['innerBlocks'], $modified );
}
}
return $blocks;
}
/**
* If the current user can edit css
*
* @return boolean
*/
public function user_can_edit_css() {
return apply_filters( 'cbb_user_can_edit_css', current_user_can( 'manage_options' ) );
}
/**
* Refine custom JS and CSS value
*
* @param string $code
* @param array $tokens
* @param string $type
* @return string
*/
public function refine_custom_value( $code, $tokens = [], $type = 'CSS' ) {
// Remove tags.
if ( 'CSS' === $type ) {
$unsafe_tags = [ 'script', 'style', 'iframe', 'object', 'embed', 'img', 'video', 'audio', 'math', 'form' ];
// Build a regex pattern that matches:
// 1. ... (full tag with content)
// 2. or (self-closing or empty).
$pattern = sprintf(
'/<(%s)\b[^>]*>(?:.*?<\/\1\s*>)?/is',
implode( '|', $unsafe_tags )
);
// Remove all unsafe tags in one go.
$code = preg_replace( $pattern, '', $code );
}
// Replace breakpoint tokens.
$breakpoints = $this->get_breakpoints();
$tokens = array_merge(
$tokens,
[
'TABLET_UP' => ( $breakpoints['md']['breakpoint'] ?? 768 ) . 'px',
'TABLET_DOWN' => ( $breakpoints['md']['breakpointMax'] ?? 767 ) . 'px',
'DESKTOP_UP' => ( $breakpoints['lg']['breakpoint'] ?? 1024 ) . 'px',
'DESKTOP_DOWN' => ( $breakpoints['lg']['breakpointMax'] ?? 1023 ) . 'px',
]
);
$code = str_replace( array_keys( $tokens ), array_values( $tokens ), $code );
// Minify it.
return $this->minify( $code, $type );
}
/**
* Simple minify JS and CSS
*
* @param string $code
* @param string $type
* @return string
*/
public function minify( $code, $type = 'CSS' ) {
if ( $this->the_plugin_instance->is_debug_mode() ) {
return $code;
}
if ( 'CSS' === $type ) {
$code = preg_replace( '/\s+/', ' ', $code );
} elseif ( 'JS' === $type ) {
// Remove comments first - https://stackoverflow.com/a/31907095/1038868.
$pattern = '/(?:(?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:(?