PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 2.1.3
ShopBuilder – WooCommerce Builder For Elementor v2.1.3
3.4.2 3.4.1 3.4.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 All 63 releases
shopbuilder / app / Elementor / Render / Render.php

Render.php in ShopBuilder – WooCommerce Builder For Elementor 2.1.3, at app/Elementor/Render/Render.php

1,388 lines 40.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Elementor Render Class.
4 *
5 * This class contains render logics & output. It utilizes WC_Products_Query().
6 *
7 * @package RadiusTheme\SB
8 */
9
10 namespace RadiusTheme\SB\Elementor\Render;
11
12 use WC_Query;
13 use WP_Query;
14 use WC_Product;
15 use Elementor\Utils;
16 use WC_Product_Query;
17 use RadiusTheme\SB\Helpers\Fns;
18 use RadiusTheme\SB\Models\QueryArgs;
19 use RadiusTheme\SB\Helpers\BuilderFns;
20 use RadiusTheme\SB\Traits\SingletonTrait;
21 use RadiusTheme\SB\Traits\ActionBtnTraits;
22 use RadiusTheme\SB\Elementor\Helper\RenderHelpers;
23
24 // Do not allow directly accessing this file.
25 if ( ! defined( 'ABSPATH' ) ) {
26 exit( 'This script cannot be accessed directly.' );
27 }
28
29 /**
30 * Elementor Render Class.
31 */
32 class Render {
33 /**
34 * Singleton Trait.
35 */
36 use SingletonTrait;
37
38 /**
39 * Action button trait.
40 */
41 use ActionBtnTraits;
42
43 /**
44 * HTML.
45 *
46 * @var string
47 */
48 private $html = null;
49
50 /**
51 * Settings array
52 *
53 * @var array
54 */
55 public $get_settings;
56
57 /**
58 * Element attributes.
59 *
60 * @var array
61 */
62 private $attributes = [];
63
64 /**
65 * Get elementor settings.
66 *
67 * @return array
68 */
69 public function get_settings_for_display() {
70 return $this->get_settings;
71 }
72
73 /**
74 * Container Wrapper HTML.
75 *
76 * @param string $content The content.
77 * @param array $settings Settings.
78 * @param string $class Class.
79 * @param string $template Template name.
80 * @param bool $fullwidth Fullwidth check.
81 * @param bool $ajax Ajax attributes.
82 *
83 * @return string
84 */
85 public function container( $content, $settings, $class = '', $template = '', $fullwidth = false, $ajax = true ) {
86 $rand = wp_rand();
87 $layout_id = 'rtsb-container-' . $rand;
88 $metas = RenderHelpers::meta_dataset( $settings, $template );
89 $layout = $metas['layout'];
90 $style_attr = '--rtsb-default-columns: ' . esc_attr( RenderHelpers::default_columns( $layout ) );
91 $view_mode = $settings['view_mode'] ?? 'grid';
92 $view_mode = isset( $_GET['displayview'] ) ? sanitize_text_field( wp_unslash( $_GET['displayview'] ) ) : $view_mode; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
93 $tooltip_attr = 'list' == $view_mode && ! empty( $metas['tooltip_position_list'] ) ? esc_attr( $metas['tooltip_position_list'] ) : $metas['tooltip_position'];
94 $has_pro = rtsb()->has_pro();
95 $has_filters = $has_pro && ! empty( $settings['tax_filter'] );
96
97 // Set default layout.
98 if ( ! $has_pro && ! in_array( $layout, array_keys( Fns::free_layouts() ), true ) ) {
99 $layout = RenderHelpers::set_default_layout( $layout );
100 }
101
102 // Container classes.
103 $container_classes = RenderHelpers::prepare_container_classes( $metas, $settings, $class );
104
105 // Pagination attributes.
106 $pagination_attributes = RenderHelpers::prepare_pagination_attr( $layout, $has_filters, $template, $settings, $ajax );
107
108 // Attributes for the container.
109 $container_attributes = [
110 'id' => $layout_id,
111 'class' => $container_classes,
112 'style' => $style_attr,
113 'data-layout' => $layout,
114 'data-tooltip-position' => $tooltip_attr,
115 ];
116
117 if ( ! empty( $pagination_attributes ) ) {
118 $container_attributes['data-rtsb-ajax'] = $pagination_attributes;
119 }
120
121 // Adding render attributes.
122 $this->add_attribute( 'rtsb_container_attr_' . $rand, $container_attributes );
123
124 // Start rendering.
125 $this->html = '<div ' . $this->get_attribute_string( 'rtsb_container_attr_' . $rand ) . '>';
126
127 // Section title.
128 $this->html .= $this->render_section_title( $settings );
129
130 // Content.
131 $this->html .= $content;
132
133 // End rendering.
134 $this->html .= '</div><!-- .rtsb-elementor-container -->';
135
136 // Action button icon reset.
137 $this->action_button_icon_set_default();
138
139 // Script generator.
140 $script_generator = [];
141 $script_generator['layout'] = $layout_id;
142 $script_generator['rand'] = absint( $rand );
143 $script_generator['isCarousel'] = preg_match( '/slider/', $layout );
144
145 // Register scripts in the wp_footer action.
146 add_action(
147 'wp_footer',
148 static function () use ( $script_generator ) {
149 RenderHelpers::register_scripts( $script_generator );
150 }
151 );
152
153 return $this->html;
154 }
155
156 /**
157 * Row Wrapper HTML.
158 *
159 * @param string $content The content.
160 * @param array $settings Settings.
161 * @param string $hook_html Hook HTML.
162 * @param array $raw_settings Raw Settings.
163 *
164 * @return string
165 */
166 public function row( $content, $settings, $hook_html = '', $raw_settings = [] ) {
167 $layout = $settings['layout'];
168 $rand = wp_rand();
169 $is_carousel = preg_match( '/slider/', $layout );
170
171 if ( preg_match( '/category/', $layout ) && ( ! empty( $settings['active_cat_slider'] ) ) ) {
172 $is_carousel = true;
173 }
174
175 // Row classes.
176 $row_classes = RenderHelpers::prepare_row_classes( $settings );
177
178 // Adding render attributes.
179 $this->add_attribute( 'rtsb_row_attr_' . $rand, 'class', $row_classes );
180
181 // Start rendering.
182 $row_wrapper = '<div ' . $this->get_attribute_string( 'rtsb_row_attr_' . $rand ) . '>';
183 $start_wrapper = apply_filters( 'rtsb/elementor/row/start', $row_wrapper, $raw_settings );
184
185 $this->html = ! empty( $settings['show_filter'] ) ? $hook_html . $start_wrapper : $start_wrapper;
186
187 if ( $is_carousel ) {
188 $slider_data = RenderHelpers::slider_data( $settings, $raw_settings );
189
190 // Adding slider render attributes.
191 $this->add_attribute(
192 'rtsb_slider_attr_' . $rand,
193 [
194 'class' => 'rtsb-col-grid ' . $slider_data['class'],
195 'data-options' => $slider_data['data'],
196 ]
197 );
198
199 $this->html .= '<div ' . $this->get_attribute_string( 'rtsb_slider_attr_' . $rand ) . '>';
200 $this->html .= '<div class="swiper-wrapper">';
201 }
202
203 $this->html .= $content;
204
205 if ( $is_carousel ) {
206 $this->html .= '</div><!-- .swiper-wrapper -->';
207
208 // Slider buttons.
209 $this->html .= $this->render_slider_buttons( $settings );
210 $this->html .= '</div><!-- .rtsb-carousel-slider -->';
211 }
212
213 $row_end = '</div><!-- .rtsb-row -->';
214
215 $this->html .= apply_filters( 'rtsb/elementor/row/end', $row_end );
216
217 // Preloader.
218 $this->html .= RenderHelpers::pre_loader( $layout );
219
220 return $this->html;
221 }
222
223 /**
224 * Product loop.
225 *
226 * @param object $products Products.
227 * @param array $settings Settings.
228 * @param string $template Template.
229 *
230 * @return string|null
231 */
232 public function product_loop( $products, $settings, $template ) {
233 $html = null;
234
235 if ( empty( $products ) ) {
236 return null;
237 }
238
239 $i = 0;
240
241 foreach ( $products as $product ) {
242 $i++;
243 $GLOBALS['product'] = $product;
244
245 /**
246 * Before product template render hook.
247 */
248 do_action( 'rtsb_before_product_template_render' );
249
250 // Loop arg.
251 $arg = $this->arg_dataset( $settings, $product, $settings['lazy_load'], $i );
252
253 // Get template.
254 $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
255
256 /**
257 * After product template render hook.
258 */
259 do_action( 'rtsb_after_product_template_render' );
260
261 unset( $GLOBALS['product'] );
262 }
263
264 return $html;
265 }
266
267 /**
268 * WC Product loop.
269 *
270 * @param object $query The query.
271 * @param array $settings Settings.
272 * @param string $template Template.
273 *
274 * @return string|null
275 */
276 public function wc_product_loop( $query, $settings, $template ) {
277 $html = null;
278
279 if ( empty( $query ) ) {
280 return null;
281 }
282
283 $i = 0;
284
285 while ( $query->have_posts() ) {
286 $query->the_post();
287 $i++;
288
289 // Loop arg.
290 $arg = $this->arg_dataset( $settings, wc_get_product( get_the_ID() ), $settings['lazy_load'], $i );
291
292 // Get template.
293 $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
294 }
295
296 return $html;
297 }
298
299 /**
300 * Category loop.
301 *
302 * @param object $query The query.
303 * @param array $settings Settings.
304 * @param string $template Template.
305 * @param bool $is_single Single category.
306 *
307 * @return string|null
308 */
309 public function category_loop( $query, $settings, $template, $is_single = false ) {
310 $html = null;
311 $category_loop = false;
312
313 if ( 'category-layout3' === $settings['raw_settings']['layout'] || 'category-single-layout2' === $settings['raw_settings']['layout'] ) {
314 $category_loop = true;
315 }
316
317 if ( $is_single ) {
318 // Loop arg.
319 $arg = $this->cat_arg_dataset( $settings, $query, true, false, $category_loop );
320
321 // Get template.
322 $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
323 } else {
324 foreach ( $query as $term ) {
325 // Loop arg.
326 $arg = $this->cat_arg_dataset( $settings, $term, false, false, $category_loop );
327
328 // Get template.
329 $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
330 }
331 }
332
333 return $html;
334 }
335
336 /**
337 * Render Product View.
338 *
339 * @param string $template Template name.
340 * @param array $settings Control settings.
341 *
342 * @return string
343 */
344 public function product_view( $template, $settings ) {
345 $this->get_settings = $settings;
346
347 $metas = RenderHelpers::meta_dataset( $settings, $template, $this->get_settings_for_display() );
348 $is_carousel = preg_match( '/slider/', $metas['layout'] );
349 $pagination = $metas['pagination'];
350 $hook_html = '';
351
352 // Query Args.
353 $args = ( new QueryArgs() )->buildArgs( $metas, 'product', $is_carousel );
354
355 $temp_args = $args;
356 $default_args = [
357 'visibility' => 'visible',
358 'status' => 'publish',
359 ];
360 $custom_args = apply_filters( 'rtsb/elementor/args_before_loop', $args, $metas );
361 $args = wp_parse_args( $custom_args, $default_args );
362
363 /**
364 * Before Product query hook.
365 *
366 * @hooked RadiusTheme\SB\Controllers\Hooks\ActionHooks::modify_wc_query_args 10
367 */
368 do_action( 'rtsb/elements/render/before_query', $settings, $args );
369
370 // Query.
371 $product_query = new WC_Product_Query( $args );
372 $products = $pagination ? $product_query->get_products()->products : $product_query->get_products();
373
374 if ( ! empty( $products ) ) {
375 // Start rendering.
376 $hook_html .= $this->render_start( $metas, $settings, $temp_args );
377
378 // Row.
379 $this->row( $this->product_loop( $products, $metas, $template ), $metas, $hook_html, $settings );
380
381 // Pagination.
382 if ( $pagination ) {
383 $pagination_args = [
384 'query' => $product_query,
385 'meta' => $metas,
386 'limit' => $settings['posts_limit'],
387 'per_page' => $settings['pagination_per_page'],
388 ];
389
390 $this->html .= $this->render_products_pagination( $pagination_args );
391 }
392
393 // End rendering.
394 $this->html .= $this->render_end( $metas, $settings, $products );
395 } else {
396 $this->no_products_msg( $metas );
397 }
398
399 wp_reset_postdata();
400
401 unset( $args['tax_query'] );
402
403 /**
404 * After Product query hook.
405 *
406 * @hooked RadiusTheme\SB\Controllers\Hooks\ActionHooks::reset_wc_query_args 10
407 */
408 do_action( 'rtsb/elements/render/after_query', $settings, $args );
409
410 // Container.
411 $this->container( $this->html, $settings, 'rtsb-products-container', $template, $is_carousel );
412
413 return $this->html;
414 }
415
416 /**
417 * Render Product View.
418 *
419 * @param string $template Template name.
420 * @param array $settings Control settings.
421 * @param object $widget Reference widget.
422 *
423 * @return string
424 */
425 public function wc_loop_for_product_view( $template, $settings, $widget ) {
426 global $wp_query, $post;
427
428 $this->get_settings = $settings;
429 $rand = wp_rand();
430 $metas = RenderHelpers::meta_dataset( $settings, $template, $widget->get_settings_for_display() );
431
432 $settings['rtsb_order'] = ! empty( $wp_query->query_vars['order'] ) ? $wp_query->query_vars['order'] : 'ASC';
433 $settings['rtsb_orderby'] = ! empty( $wp_query->query_vars['orderby'] ) ? $wp_query->query_vars['orderby'] : 'menu_order';
434
435 $pagination = $metas['pagination'];
436
437 $page_type = BuilderFns::builder_type( get_the_ID() );
438 $is_preview = BuilderFns::is_builder_preview() && array_key_exists( $page_type, BuilderFns::builder_page_types() );
439 $posts_per_page = wc_get_default_products_per_row() * wc_get_default_product_rows_per_page();
440
441 if ( $is_preview ) {
442 $main_query = clone $wp_query;
443 $main_post = clone $post;
444
445 $ordering = ( new WC_Query() )->get_catalog_ordering_args();
446 $wp_query_args = [
447 'post_type' => 'product',
448 'posts_per_page' => $posts_per_page,
449 ];
450
451 if ( ! empty( $ordering['orderby'] ) ) {
452 $wp_query_args['orderby'] = $ordering['orderby'];
453 }
454
455 if ( ! empty( $ordering['order'] ) ) {
456 $wp_query_args['order'] = $ordering['order'];
457 }
458
459 $wp_query = new WP_Query( $wp_query_args ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
460
461 wc_set_loop_prop( 'total', $wp_query->found_posts );
462 wc_set_loop_prop( 'total_pages', $wp_query->max_num_pages );
463 }
464
465 if ( Fns::product_has_applied_filters( 'shop' ) || Fns::product_has_applied_filters( 'archive' ) || 'rtsb_builder' === get_post_type( get_the_ID() ) ) {
466 echo '<div class="rtsb-active-filters-wrapper"></div>';
467 }
468
469 // column_per_row.
470 if ( woocommerce_product_loop() ) {
471 if ( wc_get_loop_prop( 'total' ) ) {
472
473 // Row.
474 $this->row( $this->wc_product_loop( $wp_query, $metas, $template ), $metas, '', $settings );
475 }
476
477 // Pagination.
478 $this->html .= '<div class="rtsb-archive-pagination-wrap">';
479
480 if ( $pagination ) {
481 if ( Fns::product_filters_has_ajax( 'shop' ) || Fns::product_filters_has_ajax( 'archive' ) ) {
482 if ( $wp_query->max_num_pages > 1 ) {
483 $this->add_attribute(
484 'rtsb_load_more_btn_attr_' . $rand,
485 [
486 'data-paged' => '2',
487 'data-max-page' => $wp_query->max_num_pages,
488 ]
489 );
490
491 $this->html .=
492 "<div class='rtsb-load-more rtsb-pos-r'>
493 <button '{$this->get_attribute_string( 'rtsb_load_more_btn_attr_' . $rand )}'>
494 <span>" . esc_html__( 'Load More', 'shopbuilder' ) . "</span>
495 </button>
496 <div class='rtsb-loadmore-loading rtsb-ball-clip-rotate'><div></div></div>
497 </div>";
498 }
499 } else {
500 $pagination_args = [
501 'query' => $wp_query,
502 'meta' => $metas,
503 'limit' => wc_get_loop_prop( 'total' ),
504 'per_page' => $posts_per_page,
505 ];
506
507 $this->html .= $this->render_products_pagination( $pagination_args, 'wp' );
508 }
509 }
510
511 $this->html .= '</div>';
512 } else {
513 $this->no_products_msg( $metas );
514 }
515
516 if ( $is_preview ) {
517 // phpcs:disable
518 $wp_query = $main_query;
519 $post = $main_post;
520
521 wp_reset_query();
522 wp_reset_postdata();
523 // phpcs:enable
524 }
525
526 // Container.
527 $this->container( $this->html, $settings, 'rtsb-products-container', $template );
528
529 return $this->html;
530 }
531
532 /**
533 * Loop Setup for Product View.
534 *
535 * @param string $template Template name.
536 * @param array $settings Control settings.
537 * @param object $widget Widget object.
538 *
539 * @return string
540 */
541 public function setup_loop_for_product_view( $template, $settings, $widget ) {
542 if ( empty( $settings['query_products'] ) ) {
543 return $this->html;
544 }
545
546 $html = null;
547
548 $metas = RenderHelpers::meta_dataset( $settings, '', $widget->get_settings_for_display() );
549
550 foreach ( $settings['query_products'] as $_product ) {
551 $post_object = get_post( $_product->get_id() );
552
553 setup_postdata( $GLOBALS['post'] =& $post_object ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited, Squiz.PHP.DisallowMultipleAssignments.Found
554
555 // Loop arg.
556 $arg = $this->arg_dataset( $metas, $_product );
557 // Get template.
558 $html .= Fns::load_template( $template . $settings['layout'], $arg, true );
559
560 $this->row( $html, $metas, '', $settings );
561 }
562
563 // Container.
564 $this->container( $this->html, $settings, 'rtsb-products-container', '', false, false );
565
566 wp_reset_postdata();
567
568 return $this->html;
569 }
570
571 /**
572 * Render Category View.
573 *
574 * @param string $template Template name.
575 * @param array $settings Control settings.
576 * @param bool $is_single Single category.
577 *
578 * @return string
579 */
580 public function category_view( $template, $settings, $is_single = false ) {
581 $this->get_settings = $settings;
582
583 $metas = RenderHelpers::meta_dataset( $settings, $template, $this->get_settings_for_display() );
584 $taxonomy = $metas['category_source'];
585 $term = $metas['category_term'];
586
587 if ( rtsb()->has_pro() && ( ! empty( $metas['tag_term'] ) ) ) {
588 $term = $metas['tag_term'];
589 }
590
591 // Query.
592 if ( $is_single ) {
593 if ( empty( $term ) ) {
594 return '<p>' . esc_html__( 'Please select a category.', 'shopbuilder' ) . '</p>';
595 }
596
597 $taxonomy_query = get_term( $term, $taxonomy );
598 } else {
599 // Query Args.
600 $args = ( new QueryArgs() )->buildArgs( $metas, 'category' );
601
602 $taxonomy_query = get_terms( $args );
603 }
604
605 if ( ! is_wp_error( $taxonomy_query ) && ! empty( $taxonomy_query ) ) {
606 // Row.
607 $this->row( $this->category_loop( $taxonomy_query, $metas, $template, $is_single ), $metas );
608 } else {
609 $this->html .= '<p>' . esc_html__( 'No categories found', 'shopbuilder' ) . '</p>';
610 }
611
612 // Container.
613 $this->container( $this->html, $settings, 'rtsb-categories-container', $is_single );
614
615 return $this->html;
616 }
617
618 /**
619 * Render Category View.
620 *
621 * @param string $template Template name.
622 * @param array $settings Control settings.
623 *
624 * @return string
625 */
626 public function social_share_view( $template, $settings ) {
627 $this->get_settings = $settings;
628 $share_items = [];
629
630 foreach ( $settings['share_platforms'] as $platform ) {
631 $share_items[] = [
632 'share_items' => $platform['share_items'],
633 'share_text' => $platform['share_text'],
634 ];
635 }
636
637 if ( ! empty( $share_items ) && is_array( $share_items ) ) {
638 $arg['p_id'] = get_the_ID();
639 $arg['share_items'] = $share_items;
640 $arg['preset'] = $settings['layout'];
641 $arg['direction'] = ! empty( $settings['layout_direction'] ) ? $settings['layout_direction'] : 'horizontal';
642 $arg['show_icon'] = ! empty( $settings['show_share_icon'] );
643 $arg['show_text'] = ! empty( $settings['show_share_text'] );
644 $arg['show_pre_text'] = ! empty( $settings['show_share_pre_text'] );
645 $arg['share_pre_text'] = ! empty( $settings['share_pre_text'] ) ? $settings['share_pre_text'] : '';
646 $arg['raw_settings'] = $this->get_settings_for_display();
647 $this->html = Fns::load_template( $template, $arg, true );
648 } else {
649 $this->html = '<p>' . esc_html__( 'Please select a social sharing platform.', 'shopbuilder' ) . '</p>';
650 }
651
652 return $this->html;
653 }
654
655 /**
656 * Builds an array with meta values.
657 *
658 * @param array $meta Meta values.
659 * @param object $product Product object.
660 * @param bool $lazy_load Lazy Load.
661 * @param int $i Integer.
662 *
663 * @return array
664 */
665 public function arg_dataset( array $meta, object $product, bool $lazy_load = false, $i = 0 ) {
666 $post_id = $product->get_id();
667 $arg = $this->build_product_arg(
668 [
669 'product' => $product,
670 'id' => $product->get_id(),
671 'type' => $product->get_type(),
672 'meta' => $meta,
673 'lazy_load' => $lazy_load,
674 'i' => $i,
675 ]
676 );
677
678 return apply_filters( 'rtsb/elementor/render/arg_dataset', $arg, $post_id, $meta, $lazy_load );
679 }
680
681 /**
682 * Builds an array with meta values for WP_Query.
683 *
684 * @param array $meta Meta values.
685 * @param int $post_id Post ID.
686 * @param bool $lazy_load Lazy Load.
687 * @param int $i Integer.
688 *
689 * @return array
690 */
691 public function wp_arg_dataset( array $meta, int $post_id, bool $lazy_load = false, $i = 0 ) {
692 if ( ( empty( $meta ) && ! $post_id ) || ! in_array( get_post_type( $post_id ), [ 'product_variation', 'product' ] ) ) {
693 return [];
694 }
695
696 global $product;
697
698 if ( ! $product instanceof WC_Product && $post_id ) {
699 $product = wc_get_product( $post_id );
700 }
701
702 $post_id = $product->get_id();
703 $arg = $this->build_product_arg(
704 [
705 'product' => $product,
706 'id' => $post_id,
707 'type' => $product->get_type(),
708 'meta' => $meta,
709 'lazy_load' => $lazy_load,
710 'i' => $i,
711 ]
712 );
713
714 return apply_filters( 'rtsb/elementor/render/arg_dataset', $arg, $post_id, $meta, $lazy_load );
715 }
716
717 /**
718 * Builds an array with meta values.
719 *
720 * @param array $meta Meta values.
721 * @param Object $query Category object.
722 * @param bool $fullwidth Fullwidth check.
723 * @param bool $lazy_load Lazy Load.
724 * @param bool $category_loop Category loop.
725 *
726 * @return array
727 */
728 public function cat_arg_dataset( $meta, $query = null, $fullwidth = false, $lazy_load = false, $category_loop = false ) {
729 if ( empty( $meta ) ) {
730 return [];
731 }
732
733 $arg = [];
734
735 $products = null;
736 $arg['class'] = null;
737 $arg['grid'] = null;
738 $arg['anchor_class'] = null;
739 $arg['p_sale'] = null;
740 $arg['count'] = max( $query->count, 0 );
741 $arg['count_position'] = $meta['count_position'];
742 $arg['excerpt_position'] = $meta['cat_excerpt_position'];
743 $arg['cat_link'] = get_term_link( $query );
744 $arg['target'] = '_self';
745 $arg['raw_settings'] = ! empty( $meta['raw_settings'] ) ? $meta['raw_settings'] : [];
746
747 if ( ! empty( $meta['tag_term'] ) ) {
748 $arg['tag_term'] = $meta['tag_term'];
749 }
750
751 if ( ! empty( $meta['custom_link']['url'] ) ) {
752 $arg['cat_link'] = $meta['custom_link']['url'];
753 $arg['target'] = $meta['custom_link']['is_external'] ? '_blank' : '_self';
754 }
755
756 $title = $query->name;
757 $excerpt = $query->description;
758
759 if ( in_array( 'badges', $meta['visibility'], true ) ) {
760 $arg['p_sale'] = $meta['custom_badge_text'];
761 }
762
763 $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
764
765 if ( $arg['count'] >= 0 ) {
766 $prefix = ! empty( $meta['before_count'] ) ? $meta['before_count'] : '';
767 $suffix = ! empty( $meta['after_count'] ) ? $meta['after_count'] : '';
768
769 $arg['count'] = $prefix . $arg['count'] . $suffix;
770 }
771
772 if ( $meta['show_custom_title'] && ! empty( $meta['cat_custom_title'] ) ) {
773 $title = $meta['cat_custom_title'];
774 }
775
776 if ( $meta['show_custom_excerpt'] && ! empty( $meta['cat_custom_excerpt'] ) ) {
777 $excerpt = $meta['cat_custom_excerpt'];
778 }
779
780 $arg['c_img'] = $meta['c_img'];
781 $arg['items'] = $meta['visibility'];
782 $arg['title_tag'] = $meta['title_tag'];
783 $arg['title_class'] = 'category-title rtsb-text-limit limit-' . $meta['title_limit'] . ( ! $meta['title_link'] ? ' no-link' : '' );
784 $arg['title'] = Fns::text_truncation( $title, $meta['title_limit_custom'] );
785 $arg['excerpt_limit'] = $meta['excerpt_limit'];
786 $arg['excerpt'] = wpautop( Fns::text_truncation( $excerpt, $meta['excerpt_limit_custom'] ) );
787 $arg['title_link'] = $meta['title_link'];
788 $arg['image_link'] = $meta['image_link'];
789 $arg['grid'] .= 'rtsb-col-grid ';
790
791 if ( ! $fullwidth ) {
792 $arg['class'] .= ' even-grid-item ';
793 }
794
795 if ( ! empty( $meta['active_cat_slider'] ) ) {
796 $arg['class'] .= ' rtsb-slide-item swiper-slide animated rtFadeIn ';
797 }
798
799 $arg['class'] .= 'rtsb-category-grid';
800
801 ob_start();
802 do_action( 'rtsb/categories/category_image_override', $meta, $query->term_id );
803 $override_category_image = ob_get_clean();
804
805 $arg['img_html'] = $meta['c_img'] ? Fns::get_product_image_html(
806 'category',
807 $query->term_id,
808 $meta['f_img_size'],
809 $meta['default_img_id'],
810 $meta['custom_img_size'],
811 $lazy_load,
812 false,
813 false,
814 $override_category_image
815 ) : null;
816
817 if ( $meta['show_custom_image'] && ! empty( $meta['custom_image'] ) ) {
818 $arg['img_html'] = Fns::get_product_image_html(
819 'custom',
820 null,
821 $meta['f_img_size'],
822 $meta['custom_image'],
823 $meta['custom_img_size'],
824 $lazy_load
825 );
826 }
827
828 $arg['excerpt_class'] = 'category-info' . ( 'block' === $meta['count_position'] ? ' block-count' : ' inline-count' ) . ( 'above' === $meta['cat_excerpt_position'] ? ' excerpt-above' : '' ) . ( empty( $arg['excerpt'] ) ? ' no-excerpt' : '' );
829
830 if ( $category_loop ) {
831 $query_args = [
832 'product_category_id' => $query->term_id,
833 'limit' => -1,
834 'order' => 'DESC',
835 'orderby' => 'date',
836 ];
837
838 $products_query = new WC_Product_Query( $query_args );
839 $products = $products_query->get_products();
840 }
841
842 return apply_filters( 'rtsb/elementor/render/cat_arg_dataset', $arg, $meta, $query, $fullwidth, $lazy_load, $products );
843 }
844
845 /**
846 * Building product args.
847 *
848 * @param array $args Args.
849 * @return array
850 */
851 public function build_product_arg( $args ) {
852 list(
853 'product' => $product,
854 'id' => $post_id,
855 'type' => $p_type,
856 'meta' => $meta,
857 'lazy_load' => $lazy_load,
858 'i' => $i,
859 ) = $args;
860
861 $arg = [];
862 $arg['class'] = null;
863 $arg['grid'] = null;
864 $arg['anchor_class'] = null;
865 $arg['hover_img_html'] = null;
866 $arg['s_link'] = [];
867 $arg['add_to_cart'] = '';
868 $arg['p_id'] = $post_id;
869 $arg['product'] = $product;
870 $arg['p_link'] = $product->get_permalink();
871 $arg['p_sale'] = '';
872 $arg['rating_args'] = [];
873 $arg['has_attributes'] = $product->is_type( 'variable' ) && $product->get_attributes();
874 $arg['raw_settings'] = ! empty( $meta['raw_settings'] ) ? $meta['raw_settings'] : [];
875 $cart_icon_html = '';
876 $layout = $meta['layout'];
877
878 // Building the arg.
879 $arg['target'] = '_self';
880 $arg['f_img'] = $meta['f_img'];
881 $arg['items'] = $meta['visibility'];
882 $arg['title_tag'] = $meta['title_tag'];
883 $arg['title_class'] = 'product-title rtsb-text-limit limit-' . $meta['title_limit'] . ( ! $meta['title_link'] ? ' no-link' : '' );
884 $arg['title'] = Fns::text_truncation( $product->get_title(), $meta['title_limit_custom'] );
885 $arg['excerpt_limit'] = $meta['excerpt_limit'];
886 $arg['excerpt'] = wpautop( Fns::text_truncation( do_shortcode( get_post_field( 'post_excerpt', $post_id ) ), $meta['excerpt_limit_custom'] ) );
887 $arg['title_link'] = $meta['title_link'];
888 $arg['image_link'] = $meta['image_link'];
889 $arg['out_of_stock'] = $meta['out_of_stock_badge_text'];
890 $arg['action_btn_preset'] = $meta['btn_preset'];
891 $arg['action_btn_position'] = $meta['btn_position'];
892 $arg['swatch_position'] = $meta['swatch_position'];
893 $arg['swatch_type'] = $meta['swatch_type'];
894 $arg['tooltip_position'] = $meta['tooltip_position'];
895
896 $is_list = preg_match( '/list/', $layout );
897 $is_carousel = preg_match( '/slider/', $layout );
898
899 if ( ! $is_carousel ) {
900 $arg['grid'] .= 'rtsb-col-grid ';
901 $arg['class'] .= ' even-grid-item ';
902 } else {
903 $arg['grid'] .= 'rtsb-col-grid rtsb-col-full rtsb-slide-item swiper-slide ';
904 }
905
906 $d_cols = 0 === $meta['d_cols'] ? RenderHelpers::default_columns( $layout ) : $meta['d_cols'];
907 $t_cols = 0 === $meta['t_cols'] ? 2 : $meta['t_cols'];
908 $m_cols = 0 === $meta['m_cols'] ? 1 : $meta['m_cols'];
909
910 $arg['grid'] .= 0 === $i % $d_cols ? ' row-last desktop-row-last' : '';
911 $arg['grid'] .= 0 === $i % $t_cols ? ' row-last tablet-row-last' : '';
912 $arg['grid'] .= 0 === $i % $m_cols ? ' row-last mobile-row-last' : '';
913
914 $arg['class'] .= 'rtsb-product';
915
916 if ( $is_list ) {
917 $arg['class'] .= ' rtsb-product-list';
918 }
919
920 $arg['class'] .= ' animated rtFadeIn';
921
922 if ( in_array( 'badges', $meta['visibility'], true ) ) {
923 $arg['p_sale'] = Fns::get_promo_badge(
924 $product,
925 $meta['sale_badge_type'],
926 $meta['sale_badge_text'],
927 $meta['out_of_stock_badge_text']
928 );
929 }
930
931 $arg['badge_class'] = RenderHelpers::badge_class( $meta['badge_preset'] );
932
933 if ( ! $meta['show_cart_text'] ) {
934 $meta['add_to_cart_text'] = '';
935 $meta['add_to_cart_success_text'] = '';
936 }
937
938 if ( in_array( 'add_to_cart', $meta['visibility'], true ) ) {
939 if ( ! empty( $meta['add_to_cart_icon'] ) ) {
940 $cart_icon_html .= Fns::icons_manager( $meta['add_to_cart_icon'], 'cart-icon' );
941 }
942
943 if ( ! empty( $meta['add_to_cart_success_icon'] ) ) {
944 $cart_icon_html .= Fns::icons_manager( $meta['add_to_cart_success_icon'], 'cart-success-icon' );
945 }
946
947 $cart_icon = ! empty( $cart_icon_html ) ? '<span class="icon">' . $cart_icon_html . '</span>' : '';
948
949 if ( $product->is_purchasable() || 'grouped' === $p_type || 'external' === $p_type ) {
950 if ( $product->is_in_stock() ) {
951 $arg['add_to_cart'] = $this->render_add_to_cart_button(
952 [
953 'link' => $product->get_permalink(),
954 'id' => $post_id,
955 'type' => $p_type,
956 'text' => $meta['add_to_cart_text'],
957 'success' => $meta['add_to_cart_success_text'],
958 'icon_html' => $cart_icon,
959 'alignment' => $meta['add_to_cart_alignment'],
960 ]
961 );
962 }
963 }
964 }
965
966 if ( in_array( 'quick_view', $meta['visibility'], true ) ) {
967 add_filter( 'rtsb/module/quick_view/button_params', [ $this, 'button_classes' ] );
968 add_filter( 'rtsb/module/quick_view/icon_html', [ $this, 'quick_view_icon' ] );
969 }
970
971 if ( in_array( 'compare', $meta['visibility'], true ) ) {
972 add_filter( 'rtsb/module/compare/button_params', [ $this, 'button_classes' ] );
973 add_filter( 'rtsb/module/compare/icon_html', [ $this, 'compare_icon' ] );
974 }
975
976 if ( in_array( 'wishlist', $meta['visibility'], true ) ) {
977 add_filter( 'rtsb/module/wishlist/button_params', [ $this, 'button_classes' ] );
978 add_filter( 'rtsb/module/wishlist/icon_html', [ $this, 'wishlist_icon' ] );
979 }
980
981 $arg['img_html'] = $meta['f_img'] ? Fns::get_product_image_html(
982 'product',
983 $post_id,
984 $meta['f_img_size'],
985 $meta['default_img_id'],
986 $meta['custom_img_size'],
987 $lazy_load
988 ) : null;
989
990 if ( ! ( function_exists( 'rtwpvsp' ) && $product->is_type( 'variable' ) && $product->get_attributes() ) ) {
991 $gallery_ids = $product->get_gallery_image_ids();
992
993 if ( $meta['hover_img'] && ! empty( $gallery_ids ) ) {
994 $arg['class'] .= ! $meta['gallery_img'] ? ' rtsb-double-img' : '';
995
996 if ( ! rtsb()->has_pro() ) {
997 $arg['class'] .= ' rtsb-double-img';
998 }
999
1000 $arg['hover_img_html'] = Fns::get_product_image_html(
1001 'product',
1002 $gallery_ids[0],
1003 $meta['f_img_size'],
1004 null,
1005 $meta['custom_img_size'],
1006 $lazy_load,
1007 true
1008 );
1009 }
1010 }
1011
1012 $arg['hover_btn_text'] = $meta['hover_btn_text'];
1013
1014 if ( $meta['show_hover_btn_icon'] && ! empty( $meta['hover_btn_icon'] ) ) {
1015 $arg['hover_btn_icon'] = '<span class="icon">' . Fns::icons_manager( $meta['hover_btn_icon'], 'hover-btn-icon' ) . '</span>';
1016 }
1017
1018 $arg['img_args'] = [
1019 'p_id' => $arg['p_id'],
1020 'title' => get_the_title(),
1021 'p_link' => $arg['p_link'],
1022 'image_link' => $arg['image_link'],
1023 'img_html' => $arg['img_html'],
1024 'hover_img_html' => $arg['hover_img_html'],
1025 'meta' => $meta,
1026 ];
1027
1028 return $arg;
1029 }
1030
1031 /**
1032 * Renders add to cart button.
1033 *
1034 * @param array $args Cart args.
1035 *
1036 * @return string
1037 */
1038 public function render_add_to_cart_button( $args ) {
1039 list(
1040 'link' => $link,
1041 'id' => $id,
1042 'type' => $type,
1043 'text' => $text,
1044 'success' => $success,
1045 'icon_html' => $icon_html,
1046 'alignment' => $alignment
1047 ) = $args;
1048
1049 $content = null;
1050 $rand = wp_rand();
1051 $ext_link = get_post_meta( $id, '_product_url', true );
1052 $btn_txt = get_post_meta( $id, '_button_text', true );
1053
1054 ob_start();
1055 do_action( 'rtsb/before/cart/button' );
1056 $content .= ob_get_clean();
1057
1058 $cart_attr = [
1059 'class' => 'rtsb-action-btn ' . $type . '-product tipsy icon-' . $alignment . ' ' . ( empty( $text ) ? 'no-text' : 'has-text' ),
1060 'href' => esc_url( $link ),
1061 'rel' => 'nofollow',
1062 'data-quantity' => '1',
1063 'data-product_id' => absint( $id ),
1064 'data-id' => absint( $id ),
1065 ];
1066
1067 if ( empty( $text ) ) {
1068 $tooltip_mapping = [
1069 'simple' => esc_attr__( 'Add to Cart', 'shopbuilder' ),
1070 'variable' => esc_attr__( 'Select Options', 'shopbuilder' ),
1071 'grouped' => esc_attr__( 'View Products', 'shopbuilder' ),
1072 'external' => ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ),
1073 ];
1074
1075 $cart_attr['title'] = $tooltip_mapping[ $type ];
1076 }
1077
1078 $content .= '<div class="rtsb-wc-add-to-cart-wrap">';
1079
1080 if ( 'variable' === $type || 'grouped' === $type ) {
1081 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1082
1083 $text_btn = 'grouped' === $type ? __( 'View Products', 'shopbuilder' ) : __( 'Select Options', 'shopbuilder' );
1084
1085 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1086 $content .= $icon_html;
1087 $content .= '<span class="text ' . ( ! empty( $success ) ? 'has-success-text' : 'no-success-text' ) . '" data-success="' . esc_html( $success ) . '" data-cart-text="' . ( ! empty( $text ) ? esc_attr( $text ) : $tooltip_mapping['simple'] ) . '" data-variable-text="' . esc_attr( $text_btn ) . '">' . esc_html( $text_btn ) . '</span>';
1088 $content .= '<span></span>';
1089 } elseif ( 'external' === $type ) {
1090 if ( ! empty( $ext_link ) ) {
1091 $cart_attr['target'] = '_blank';
1092 }
1093
1094 $cart_attr['href'] = ! empty( $ext_link ) ? esc_url( $ext_link ) : esc_url( $link );
1095
1096 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1097
1098 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1099 $content .= $icon_html;
1100 $content .= '<span class="text">' . ( ! empty( $btn_txt ) ? esc_html( $btn_txt ) : esc_html__( 'Buy Product', 'shopbuilder' ) ) . '</span>';
1101 } else {
1102 $cart_attr['href'] = $cart_attr['href'] . '?add-to-cart=' . absint( $id );
1103 $cart_attr['class'] .= ' rtsb-add-to-cart-btn';
1104 $cart_attr['data-type'] = $type;
1105
1106 $this->add_attribute( 'rtsb_add_to_cart_button_' . $id . $rand, $cart_attr );
1107
1108 $content .= '<a ' . $this->get_attribute_string( 'rtsb_add_to_cart_button_' . $id . $rand ) . '>';
1109 $content .= $icon_html;
1110 $content .= '<span class="text ' . ( ! empty( $success ) ? 'has-success-text' : 'no-success-text' ) . '" data-success="' . esc_html( $success ) . '">' . esc_html( $text ) . '</span>';
1111 $content .= '<span></span>';
1112 }
1113
1114 $content .= '</a>';
1115 $content .= '</div>';
1116
1117 ob_start();
1118 do_action( 'rtsb/after/cart/button' );
1119 $content .= ob_get_clean();
1120
1121 return apply_filters( 'rtsb/render/cart/button', $content, $args );
1122 }
1123
1124 /**
1125 * Button classes.
1126 *
1127 * @param array $params Button params.
1128 *
1129 * @return array
1130 */
1131 public function button_classes( $params ) {
1132 $params['classes'] = array_merge( $params['classes'], [ 'rtsb-action-btn', 'tipsy' ] );
1133
1134 return $params;
1135 }
1136
1137 /**
1138 * Render the section title.
1139 *
1140 * @param array $settings The Elementor settings.
1141 *
1142 * @return string
1143 */
1144 public function render_section_title( $settings ) {
1145 $html = '';
1146 $rand = wp_rand();
1147
1148 if ( empty( $settings['show_section_title'] ) || ( empty( $settings['query_products'] ) && 0 === count( $settings['query_products'] ) ) ) {
1149 return $html;
1150 }
1151
1152 // Set attributes for the section title.
1153 $this->add_attribute( 'rtsb_section_title_wrapper_' . $rand, 'class', 'rtsb-section-title-wrapper' );
1154 $this->add_attribute( 'rtsb_section_title_' . $rand, 'class', 'rtsb-section-title' );
1155
1156 // Start rendering.
1157 $html .= '<div ' . $this->get_attribute_string( 'rtsb_section_title_wrapper_' . $rand ) . '>';
1158 $html .= '<' . esc_attr( $settings['section_title_tag'] ) . ' ' . $this->get_attribute_string( 'rtsb_section_title_' . $rand ) . '>';
1159 $html .= esc_html( $settings['section_title_text'] );
1160 $html .= '</' . esc_attr( $settings['section_title_tag'] ) . '>';
1161 $html .= '</div>';
1162
1163 return $html;
1164 }
1165
1166 /**
1167 * Renders products pagination
1168 *
1169 * @param array $args Pagination args.
1170 * @param string $query_type Query type.
1171 *
1172 * @return string
1173 */
1174 public function render_products_pagination( $args, $query_type = 'wc' ) {
1175 list(
1176 'query' => $query,
1177 'meta' => $meta,
1178 'limit' => $limit,
1179 'per_page' => $per_page
1180 ) = $args;
1181
1182 $html_utility = null;
1183 $html = null;
1184 $ajax = false;
1185 $is_grid = preg_match( '/grid-layout/', $meta['layout'] ) || preg_match( '/list-layout/', $meta['layout'] );
1186 $posts_per_page = 'wp' === $query_type ? $query->query_vars['posts_per_page'] : null;
1187 $total_page = 'wp' === $query_type ? $query->max_num_pages : $query->get_products()->max_num_pages;
1188
1189 if ( $limit ) {
1190 if ( 'wc' === $query_type ) {
1191 $found_post = $query->get_products()->total;
1192 } elseif ( 'wp' === $query_type && ( empty( $wp_query->query['tax_query'] ) ) ) {
1193 $found_post = $query->found_posts;
1194 }
1195
1196 if ( $per_page && $found_post > $per_page ) {
1197 $found_post = $limit;
1198 $total_page = ceil( $found_post / $per_page );
1199 }
1200 }
1201
1202 $total_page = absint( $total_page );
1203
1204 $args = [
1205 'total_page' => $total_page,
1206 'posts_per_page' => 'wc' === $query_type ? $per_page : $posts_per_page,
1207 'ajax' => $ajax,
1208 ];
1209
1210 if ( 'pagination' === $meta['posts_loading_type'] ) {
1211 if ( $is_grid ) {
1212 $html_utility .= Fns::custom_pagination(
1213 $total_page,
1214 'wc' === $query_type ? $per_page : $posts_per_page
1215 );
1216 }
1217 } else {
1218 ob_start();
1219 do_action( 'rtsb/elementor/render/pagination', $meta, $query, $args );
1220 $html_utility .= ob_get_clean();
1221 }
1222
1223 if ( $html_utility ) {
1224 $rand = wp_rand();
1225
1226 // Adding pagination render attributes.
1227 $this->add_attribute(
1228 'rtsb_pagination_attr_' . $rand,
1229 [
1230 'class' => 'rtsb-pagination-wrap element-loading',
1231 'data-total-pages' => $total_page,
1232 'data-posts-per-page' => 'wc' === $query_type ? $per_page : $posts_per_page,
1233 'data-type' => $meta['posts_loading_type'],
1234 ]
1235 );
1236
1237 // Start rendering.
1238 $html = '<div ' . $this->get_attribute_string( 'rtsb_pagination_attr_' . $rand ) . '>';
1239 $html .= $html_utility;
1240 $html .= '</div><!-- .rtsb-pagination-wrap -->';
1241 }
1242
1243 return $html;
1244 }
1245
1246 /**
1247 * Renders slider buttons
1248 *
1249 * @param array $settings Settings array.
1250 *
1251 * @return string
1252 */
1253 public function render_slider_buttons( $settings ) {
1254 $html = '';
1255 $left_icon = $settings['left_arrow_icon'];
1256 $right_icon = $settings['right_arrow_icon'];
1257
1258 if ( ! empty( $settings['slider_nav'] ) ) {
1259 $html .=
1260 '<div class="swiper-nav">
1261 <div class="swiper-arrow swiper-button-next">
1262 ' . Fns::icons_manager( $right_icon ) . '
1263 </div>
1264 <div class="swiper-arrow swiper-button-prev">
1265 ' . Fns::icons_manager( $left_icon ) . '
1266 </div>
1267 </div>';
1268 }
1269
1270 $html .= ! empty( $settings['slider_dot'] ) ? '<div class="swiper-pagination rtsb-pos-s"></div>' : '';
1271
1272 return $html;
1273 }
1274
1275 /**
1276 * No products message.
1277 *
1278 * @param array $metas Meta data.
1279 *
1280 * @return void
1281 */
1282 public function no_products_msg( $metas ) {
1283 $content =
1284 '<div class="woocommerce-no-products-found">
1285 <div class="woocommerce-info">' . esc_html__( 'No products were found matching your selection.', 'shopbuilder' ) . '</div>
1286 </div>';
1287
1288 $this->row( $content, $metas );
1289 }
1290
1291 /**
1292 * Start the rendering.
1293 *
1294 * @param array $metas Meta Data.
1295 * @param array $settings Widget settings.
1296 * @param array $temp_args Additional temporary arguments for rendering.
1297 *
1298 * @return string
1299 */
1300 public function render_start( $metas, $settings, $temp_args ) {
1301 ob_start();
1302
1303 /**
1304 * Before render row hook.
1305 */
1306 do_action( 'rtsb/elementor/render/before_row', $metas, $settings, $temp_args, $this );
1307
1308 return ob_get_clean();
1309 }
1310
1311 /**
1312 * End the rendering.
1313 *
1314 * @param array $metas Meta Data.
1315 * @param array $settings Widget settings.
1316 * @param array $products Array of products being rendered.
1317 *
1318 * @return string
1319 */
1320 public function render_end( $metas, $settings, $products ) {
1321 ob_start();
1322
1323 /**
1324 * After render row hook.
1325 */
1326 do_action( 'rtsb/elementor/render/after_row', $metas, $settings, $products );
1327
1328 return ob_get_clean();
1329 }
1330
1331 /**
1332 * Add render attribute.
1333 *
1334 * @param array|string $element The HTML element.
1335 * @param array|string $key Optional. Attribute key. Default is null.
1336 * @param array|string $value Optional. Attribute value. Default is null.
1337 * @param bool $overwrite Optional. Whether to overwrite existing.
1338 *
1339 * @return self
1340 */
1341 public function add_attribute( $element, $key = null, $value = null, $overwrite = false ) {
1342 if ( is_array( $element ) ) {
1343 foreach ( $element as $element_key => $attributes ) {
1344 $this->add_attribute( $element_key, $attributes, null, $overwrite );
1345 }
1346
1347 return $this;
1348 }
1349
1350 if ( is_array( $key ) ) {
1351 foreach ( $key as $attribute_key => $attributes ) {
1352 $this->add_attribute( $element, $attribute_key, $attributes, $overwrite );
1353 }
1354
1355 return $this;
1356 }
1357
1358 if ( empty( $this->attributes[ $element ][ $key ] ) ) {
1359 $this->attributes[ $element ][ $key ] = [];
1360 }
1361
1362 settype( $value, 'array' );
1363
1364 if ( $overwrite ) {
1365 $this->attributes[ $element ][ $key ] = $value;
1366 } else {
1367 $this->attributes[ $element ][ $key ] = array_merge( $this->attributes[ $element ][ $key ], $value );
1368 }
1369
1370 return $this;
1371 }
1372
1373 /**
1374 * Get render attribute string.
1375 *
1376 * @param string $element The element.
1377 *
1378 * @return string
1379 */
1380 public function get_attribute_string( $element ) {
1381 if ( empty( $this->attributes[ $element ] ) ) {
1382 return '';
1383 }
1384
1385 return Utils::render_html_attributes( $this->attributes[ $element ] );
1386 }
1387 }
1388