PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 3.2.2
ShopBuilder – WooCommerce Builder For Elementor v3.2.2
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 / GeneralAddons.php

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

680 lines 20.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Elementor GeneralAddons Class.
4 *
5 * This class contains render logics & output for general addons.
6 *
7 * @package RadiusTheme\SB
8 */
9
10 namespace RadiusTheme\SB\Elementor\Render;
11
12 use RadiusTheme\SB\Elementor\Helper\RenderHelpers;
13 use RadiusTheme\SB\Helpers\Fns;
14
15 // Do not allow directly accessing this file.
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit( 'This script cannot be accessed directly.' );
18 }
19
20 /**
21 * Elementor Render Class.
22 */
23 class GeneralAddons extends Render {
24 /**
25 * HTML.
26 *
27 * @var string
28 */
29 private $addon_html = null;
30
31 /**
32 * Element ID.
33 *
34 * @var string
35 */
36 private $id = null;
37
38 /**
39 * Unique name.
40 *
41 * @var string
42 */
43 public $unique_name = '';
44
45 /**
46 * Template name.
47 *
48 * @var string
49 */
50 public $template_name = '';
51
52 /**
53 * Settings.
54 *
55 * @var array
56 */
57 public $settings = [];
58
59 /**
60 * Carousel check.
61 *
62 * @var bool
63 */
64 public $is_carousel = false;
65
66 /**
67 * Blog Post check.
68 *
69 * @var bool
70 */
71 public $is_post_query = false;
72
73 /**
74 * Blog Post Pagination.
75 *
76 * @var bool
77 */
78 public $is_post_pagination = false;
79
80 /**
81 * Grid check.
82 *
83 * @var bool
84 */
85 public $is_grid = false;
86
87 /**
88 * Content classes.
89 *
90 * @var bool
91 */
92 public $content_classes = [
93 'rtsb-col-grid',
94 ];
95
96 /**
97 * Renders the addon view.
98 *
99 * @param array $data The addon data.
100 * @param array $settings The addon settings.
101 *
102 * @return string
103 */
104 public function addon_view( $data, $settings = [] ) {
105 $this->id = $data['id'];
106 $this->unique_name = $data['unique_name'];
107 $this->template_name = $data['template'];
108 $this->settings = $settings;
109 $this->is_carousel = $data['is_carousel'] ?? false;
110 $this->is_post_query = $data['is_post_query'] ?? false;
111 $this->is_grid = $data['is_grid'] ?? false;
112 $this->is_post_pagination = $data['is_post_pagination'] ?? false;
113 $content = $data['content'];
114
115 if ( ! ( $this->is_grid || empty( $data['content_class'] ) ) ) {
116 $this->content_classes[] = $data['content_class'];
117 $this->content_classes = implode( ' ', $this->content_classes );
118 }
119
120 // Container.
121 $this->addon_container( $content, $data );
122
123 return $this->addon_html;
124 }
125
126 /**
127 * Generates the container HTML.
128 *
129 * @param string $content The content inside the container.
130 * @param array $args Additional arguments for the container.
131 *
132 * @return string
133 */
134 public function addon_container( $content, $args = [] ) {
135 $container_id = 'rtsb-container-' . $this->id;
136 $container_classes = $this->get_container_classes( $args['container_class'] ?? '' );
137 $raw_layout = $args['layout'] ?? '';
138 $template = $args['template'];
139 $layout = RenderHelpers::filter_layout( $raw_layout, $template );
140 $style_attr = '--rtsb-default-columns: ' . esc_attr( RenderHelpers::default_columns( $layout ) );
141 $container_attributes = [
142 'id' => $container_id,
143 'class' => $container_classes,
144 'data-layout' => $layout,
145 ];
146 if ( $this->is_grid ) {
147 $container_attributes['style'] = apply_filters( 'rtsb/general_widget/container/attr', $style_attr, $this->settings );
148 }
149
150 if ( ! empty( $args['container_attr'] ) ) {
151 $container_attributes = array_merge( $container_attributes, $args['container_attr'] );
152 }
153
154 $row_args = wp_parse_args(
155 $this->get_row_args(),
156 [
157 'layout' => $raw_layout,
158 'template' => $template,
159 'content_class' => $args['content_class'] ?? '',
160 'post_query' => $args['post_query'] ?? '',
161 ]
162 );
163
164 // Adding render attributes.
165 $this->add_attribute( 'rtsb_addon_container_attr_' . $this->id, $container_attributes );
166
167 // Generate HTML for the container.
168 $this->addon_html = '<div ' . $this->get_attribute_string( 'rtsb_addon_container_attr_' . $this->id ) . '>';
169 $this->addon_row( $content, $row_args );
170 $this->addon_html .= '</div><!-- .rtsb-container -->';
171
172 return $this->addon_html;
173 }
174
175 /**
176 * Generates the row HTML.
177 *
178 * @param string $content The content inside the row.
179 * @param array $args Additional arguments for the row.
180 *
181 * @return string
182 */
183 public function addon_row( $content, $args = [] ) {
184 $layout = RenderHelpers::filter_layout( $args['layout'] ?? '' );
185 $row_classes = $this->get_row_classes( $layout );
186
187 // Adding render attributes.
188 $this->add_attribute( 'rtsb_addon_row_attr_' . $this->id, 'class', $row_classes );
189
190 // Start rendering the row.
191 $this->addon_html .= '<div ' . $this->get_attribute_string( 'rtsb_addon_row_attr_' . $this->id ) . '>';
192 if ( $this->is_carousel ) {
193 $this->slider_wrapper_start();
194 }
195
196 // Rendering the content.
197 $this->get_content( $content, $args );
198
199 if ( $this->is_carousel ) {
200 $this->slider_wrapper_end();
201 }
202 $this->addon_html .= '</div><!-- .rtsb-row -->';
203
204 if ( $this->is_post_query && $this->is_post_pagination && ! empty( $args['post_query'] ) ) {
205 $this->addon_html .= $this->render_posts_pagination( $args );
206 }
207 // Preloader.
208
209 $this->addon_html .= $this->pre_loader();
210
211 return $this->addon_html;
212 }
213
214 /**
215 * Renders the content inside the row.
216 *
217 * @param string $content The content.
218 * @param array $args Additional arguments for the content.
219 *
220 * @return void
221 */
222 protected function get_content( $content, $args = [] ) {
223 // Adding render attributes.
224 $this->add_attribute( 'rtsb_addon_content_attr_' . $this->id, 'class', $this->content_classes );
225
226 // Render content HTML.
227 $this->addon_html .= ! $this->is_grid ? '<div ' . $this->get_attribute_string( 'rtsb_addon_content_attr_' . $this->id ) . '>' : '';
228 $this->addon_html .= $content;
229 $this->addon_html .= ! $this->is_grid ? '</div><!-- .rtsb-col-grid -->' : '';
230 }
231
232 /**
233 * Gets container classes.
234 *
235 * @param string $custom Optional custom classes.
236 *
237 * @return string
238 */
239 protected function get_container_classes( $custom = '' ) {
240 $classes = [
241 'rtsb-elementor-container',
242 'rtsb-pos-r',
243 $this->unique_name,
244 ];
245
246 if ( ! empty( $custom ) ) {
247 $classes[] = $custom;
248 }
249 if ( ! empty( $this->settings['slider_slide_animation'] ) ) {
250 $classes[] = 'has-slide-animation';
251 }
252 $classes = implode( ' ', $classes );
253
254 return apply_filters( 'rtsb/general/addon_container/class', $classes, $this->settings );
255 }
256
257 /**
258 * Gets row classes.
259 *
260 * @param string $layout Optional layout name.
261 *
262 * @return string
263 */
264 protected function get_row_classes( $layout = '' ) {
265 $classes = [
266 'rtsb-row',
267 'rtsb-content-loader',
268 ];
269
270 if ( ! empty( $layout ) ) {
271 $classes[] = $layout;
272 }
273
274 if ( $this->is_carousel ) {
275 $classes[] = 'rtsb-slider-layout';
276 }
277 if ( $this->is_carousel && ! empty( $this->settings['always_show_nav'] ) ) {
278 $classes[] = 'always-show-nav';
279 }
280 if ( ! empty( $this->settings['cols_mobile'] ) && '1' == $this->settings['cols_mobile'] ) {
281 $classes[] = ' rtsb-mobile-flex-row';
282 }
283 $classes = implode( ' ', $classes );
284
285 return apply_filters( 'rtsb/general/addon_row/class', $classes, $this->settings );
286 }
287
288 /**
289 * Gets row arguments.
290 *
291 * @return array Row arguments.
292 */
293 protected function get_row_args() {
294 return [];
295 }
296
297 /**
298 * Starts the carousel slider wrapper.
299 *
300 * @return void
301 */
302 private function slider_wrapper_start() {
303 if ( 'rtsb-hero-slider' === $this->unique_name ) {
304 $slider_data = self::get_hero_slider_data( $this->settings );
305 } else {
306 $slider_data = $this->get_slider_data();
307 $slider_options = $slider_data['data'] ?? '';
308 $slider_classes = $slider_data['class'] ?? '';
309 $slider_data = [
310 'class' => 'rtsb-carousel-slider swiper ' . $slider_classes,
311 'data' => $slider_options,
312 ];
313 }
314
315 // Adding slider attributes.
316 $this->add_attribute(
317 'rtsb_slider_attr_' . $this->id,
318 [
319 'class' => 'rtsb-col-grid ' . $slider_data['class'],
320 'data-options' => $slider_data['data'],
321 ]
322 );
323
324 // Render slider wrapper.
325 $this->addon_html .= '<div ' . $this->get_attribute_string( 'rtsb_slider_attr_' . $this->id ) . '>';
326 $this->addon_html .= '<div class="swiper-wrapper">';
327 }
328
329 /**
330 * Ends the carousel slider wrapper.
331 *
332 * @return void
333 */
334 private function slider_wrapper_end() {
335 $this->addon_html .= '</div><!-- .swiper-wrapper -->';
336 $this->addon_html .= $this->slider_buttons( $this->settings );
337 $this->addon_html .= '</div><!-- .rtsb-carousel-slider -->';
338 }
339
340 /**
341 * Gets slider data for the carousel.
342 *
343 * @return array
344 */
345 protected function get_slider_data() {
346 $metas = $this->get_slider_meta_dataset( $this->settings, $this->template_name, $this->settings );
347
348 return RenderHelpers::slider_data( (array) $metas, $this->settings );
349 }
350
351 /**
352 * Prepares and returns slider configuration dataset for rendering in Elementor or frontend use.
353 *
354 * This method collects various slider-related metadata settings, normalizes them using helper
355 * functions, and returns a structured dataset used to initialize and render a responsive slider.
356 *
357 * @since 1.0.0
358 *
359 * @param array $meta Metadata array containing configuration values for the slider.
360 * Example keys: 'cols', 'cols_tablet', 'slider_loop', etc.
361 * @param string $template The template identifier or slug used for rendering slider content.
362 * @param array $raw_settings Raw settings passed directly from Elementor widget settings or similar source.
363 *
364 * @return array|null Returns an array of slider settings suitable for frontend use,
365 * optionally modified by the 'rtsb/elementor/render/slider_dataset_final' filter.
366 */
367 protected function get_slider_meta_dataset( array $meta, $template = '', $raw_settings = [] ) {
368 $data = [
369 'widget' => 'custom',
370 'widget_name' => $this->unique_name,
371 'template' => RenderHelpers::get_data( $template, '', '' ),
372 'raw_settings' => $raw_settings,
373 'd_cols' => RenderHelpers::get_data( $meta, 'cols', 0 ),
374 't_cols' => RenderHelpers::get_data( $meta, 'cols_tablet', 2 ),
375 'm_cols' => RenderHelpers::get_data( $meta, 'cols_mobile', 1 ),
376 'd_group' => RenderHelpers::get_data( $meta, 'cols_group', 1 ),
377 't_group' => RenderHelpers::get_data( $meta, 'cols_group_tablet', 1 ),
378 'm_group' => RenderHelpers::get_data( $meta, 'cols_group_mobile', 1 ),
379 'layout' => RenderHelpers::get_data( $meta, 'layout_style', 'layout1' ),
380 'auto_play' => ! empty( $meta['slide_autoplay'] ),
381 'stop_on_hover' => ! empty( $meta['pause_hover'] ),
382 'slider_nav' => ! empty( $meta['slider_nav'] ),
383 'slider_dot' => ! empty( $meta['slider_pagi'] ),
384 'slider_dynamic_dot' => ! empty( $meta['slider_dynamic_pagi'] ),
385 'loop' => ! empty( $meta['slider_loop'] ),
386 'lazy_load' => ! empty( $meta['slider_lazy_load'] ),
387 'auto_height' => ! empty( $meta['slider_auto_height'] ),
388 'speed' => RenderHelpers::get_data( $meta, 'slide_speed', 2000 ),
389 'space_between' => isset( $meta['grid_gap']['size'] ) && strlen( $meta['grid_gap']['size'] ) ? $meta['grid_gap']['size'] : 30,
390 'auto_play_timeout' => RenderHelpers::get_data( $meta, 'autoplay_timeout', 5000 ),
391 'nav_position' => RenderHelpers::get_data( $meta, 'slider_nav_position', 'standard' ),
392 'left_arrow_icon' => RenderHelpers::get_data( $meta, 'slider_left_arrow_icon', [] ),
393 'right_arrow_icon' => RenderHelpers::get_data( $meta, 'slider_right_arrow_icon', [] ),
394 ];
395 if ( ! empty( $meta['cols_widescreen'] ) ) {
396 $data['w_cols'] = $meta['cols_widescreen'];
397 }
398
399 if ( ! empty( $meta['cols_laptop'] ) ) {
400 $data['l_cols'] = $meta['cols_laptop'];
401 }
402
403 if ( ! empty( $meta['cols_tablet_extra'] ) ) {
404 $data['te_cols'] = $meta['cols_tablet_extra'];
405 }
406
407 if ( ! empty( $meta['cols_mobile_extra'] ) ) {
408 $data['me_cols'] = $meta['cols_mobile_extra'];
409 }
410
411 return apply_filters( 'rtsb/elementor/render/slider_dataset_final', $data, $meta, $raw_settings );
412 }
413
414 /**
415 * Generates the configuration and CSS class for the hero slider carousel.
416 *
417 * Prepares Swiper-compatible slider options and associated class names based on
418 * the provided metadata. This includes autoplay settings, pagination type,
419 * lazy loading, effects (e.g., parallax, fade), and responsive breakpoints.
420 *
421 * @since 1.0.0
422 *
423 * @param array $meta Metadata array for the slider settings, including keys like
424 * 'slide_speed', 'slider_loop', 'slider_pagi_type', etc.
425 *
426 * @return array Returns an associative array containing:
427 * - 'data' (string): JSON-encoded slider options for Swiper.
428 * - 'class' (string): Additional CSS class names for the carousel container.
429 */
430 protected function get_hero_slider_data( array $meta ) {
431 $has_dots = $meta['slider_pagi'] ? ' has-dot' : ' no-dot';
432 $has_dots .= $meta['slider_nav'] ? ' has-nav' : ' no-nav';
433 $has_dynamic_dots = $meta['slider_dynamic_pagi'] ? true : false;
434 $breakpoints = [
435 0 => [
436 'slidesPerView' => 1,
437 'slidesPerGroup' => 1,
438 'pagination' => [
439 'dynamicBullets' => $has_dynamic_dots,
440 'type' => RenderHelpers::get_data( $meta, 'slider_pagi_type', 'bullet' ),
441 ],
442 ],
443 768 => [
444 'slidesPerView' => 1,
445 'slidesPerGroup' => 1,
446 'pagination' => [
447 'dynamicBullets' => $has_dynamic_dots,
448 'type' => RenderHelpers::get_data( $meta, 'slider_pagi_type', 'bullet' ),
449 ],
450 ],
451 1025 => [
452 'slidesPerView' => 1,
453 'slidesPerGroup' => 1,
454 'pagination' => [
455 'dynamicBullets' => $has_dynamic_dots,
456 'type' => RenderHelpers::get_data( $meta, 'slider_pagi_type', 'bullet' ),
457 ],
458 ],
459 ];
460 $slider_options = [
461 'slidesPerView' => 1,
462 'slidesPerGroup' => 1,
463 'speed' => absint( RenderHelpers::get_data( $meta, 'slide_speed', 2000 ) ),
464 'loop' => ! empty( $meta['slider_loop'] ),
465 'autoHeight' => ! empty( $meta['slider_auto_height'] ),
466 'preloadImages' => ! empty( $meta['slider_lazy_load'] ),
467 'lazy' => ! empty( $meta['slider_lazy_load'] ),
468 'breakpoints' => $breakpoints,
469 ];
470 if ( $meta['slide_autoplay'] ) {
471 $slider_options['autoplay'] = [
472 'delay' => absint( RenderHelpers::get_data( $meta, 'autoplay_timeout', 5000 ) ),
473 'pauseOnMouseEnter' => ! empty( $meta['pause_hover'] ),
474 'disableOnInteraction' => false,
475 ];
476 }
477 if ( 'fraction' === $meta['slider_pagi_type'] ) {
478 unset( $slider_options['pagination']['dynamicBullets'] );
479 }
480 if ( 'parallax' === $meta['slider_effect'] ) {
481 $slider_options['parallax'] = true;
482 } else {
483 $slider_options['effect'] = $meta['slider_effect'];
484 }
485 if ( 'fade' === $meta['slider_effect'] ) {
486 $slider_options['crossFade'] = true;
487 }
488
489 $slider_options = apply_filters( 'rtsb/elementor/render/hero_slider_dataset', $slider_options, $this->settings );
490 $carouselClass = 'rtsb-hero-carousel-slider slider-loading rtsb-pos-s ' . $has_dots;
491 return [
492 'data' => esc_js( wp_json_encode( $slider_options ) ),
493 'class' => $carouselClass,
494 ];
495 }
496
497
498 /**
499 * Renders the carousel slider navigation arrows and pagination markup.
500 *
501 * This method returns HTML for Swiper navigation buttons (left/right arrows)
502 * and optional pagination dots, depending on the provided slider settings.
503 *
504 * @since 1.0.0
505 *
506 * @param array $settings Slider configuration options. Expected keys:
507 * - 'slider_left_arrow_icon' (string) Icon for the left arrow.
508 * - 'slider_right_arrow_icon' (string) Icon for the right arrow.
509 * - 'slider_nav' (bool) Whether to show navigation arrows.
510 * - 'slider_pagi' (bool) Whether to show pagination.
511 * - 'slider_pagi_type' (string) (Optional) Pagination style (e.g., 'bullets', 'fraction').
512 *
513 * @return string HTML markup for the slider navigation and pagination.
514 */
515 protected function slider_buttons( $settings ) {
516 $html = '';
517 $pagination_type = '';
518 $left_icon = $settings['slider_left_arrow_icon'] ?? '';
519 $right_icon = $settings['slider_right_arrow_icon'] ?? '';
520 $is_hero_carousel = 'rtsb-hero-slider' === $this->unique_name;
521 if ( $is_hero_carousel && ! empty( $settings['slider_pagi_type'] ) ) {
522 $pagination_type = $settings['slider_pagi_type'];
523 }
524 if ( ! empty( $settings['slider_nav'] ) ) {
525 $html .=
526 '<div class="swiper-nav">
527 <div class="swiper-arrow swiper-button-next">
528 ' . Fns::icons_manager( $right_icon ) . '
529 </div>
530 <div class="swiper-arrow swiper-button-prev">
531 ' . Fns::icons_manager( $left_icon ) . '
532 </div>
533 </div>';
534 }
535
536 $html .= ! empty( $settings['slider_pagi'] ) ? '<div class="swiper-pagination rtsb-pos-s ' . esc_attr( $pagination_type ) . '"></div>' : '';
537
538 return $html;
539 }
540 /**
541 * Preloader.
542 *
543 * @return string
544 */
545 public function pre_loader() {
546 if ( ! Fns::enable_loader() ) {
547 return '';
548 }
549 if ( $this->is_carousel ) {
550 return '<div class="rtsb-elements-loading rtsb-ball-clip-rotate"><div></div></div>';
551 }
552 return '';
553 }
554 /**
555 * No posts message.
556 *
557 * @return string
558 */
559 public function no_posts_msg() {
560 return '<div class="rtsb-notice">
561 <div class="woocommerce-info">' . esc_html__( 'No posts were found matching your selection.', 'shopbuilder' ) . '</div>
562 </div>';
563 }
564 /**
565 * Renders posts pagination
566 *
567 * @param array $args Pagination args.
568 *
569 * @return string
570 */
571 public function render_posts_pagination( $args ) {
572 list(
573 'post_query' => $query,
574 'posts_limit' => $limit,
575 'posts_per_page' => $per_page
576 ) = $args;
577 $html_utility = null;
578 $html = null;
579 $posts_per_page = $query->query_vars['posts_per_page'];
580 $found_posts = $query->found_posts;
581 $total_page = $query->max_num_pages;
582 if ( $limit ) {
583 if ( ( empty( $wp_query->query['tax_query'] ) ) ) {
584 $found_posts = $query->found_posts;
585 }
586
587 if ( $per_page && $found_posts > $per_page ) {
588 $found_posts = $limit;
589 $total_page = ceil( $found_posts / $per_page );
590 }
591 }
592
593 $total_page = absint( $total_page );
594
595 $html_utility .= Fns::custom_pagination(
596 $total_page,
597 $posts_per_page
598 );
599
600 if ( $html_utility ) {
601 $rand = wp_rand();
602
603 // Adding pagination render attributes.
604 $this->add_attribute(
605 'rtsb_pagination_attr_' . $rand,
606 [
607 'class' => 'rtsb-pagination-wrap element-loading',
608 'data-total-pages' => $total_page,
609 'data-posts-per-page' => $posts_per_page,
610 'data-type' => 'pagination',
611 ]
612 );
613
614 // Start rendering.
615 $html = '<div ' . $this->get_attribute_string( 'rtsb_pagination_attr_' . $rand ) . '>';
616 $html .= $html_utility;
617 $html .= '</div><!-- .rtsb-pagination-wrap -->';
618 }
619
620 return $html;
621 }
622
623 /**
624 * Function to render buttom html.
625 *
626 * @param array $args Button Arguments.
627 *
628 * @return void
629 */
630 public static function render_buttom_html( $args ) {
631 $default_args = [
632 'button_attributes' => '',
633 'sb_button_icon' => '',
634 'sb_button_icon_position' => 'right',
635 'sb_button_content' => esc_html__( 'ShopBuilder Button', 'shopbuilder' ),
636 ];
637 $args = wp_parse_args( $args, $default_args );
638 ?>
639 <a <?php Fns::print_html( $args['button_attributes'] ); ?>>
640 <span class="text-wrap <?php echo esc_attr( 'icon-' . $args['sb_button_icon_position'] ); ?>">
641 <?php
642 Fns::print_html( self::render_button_icon( 'left', $args['sb_button_icon'], $args['sb_button_icon_position'] ) );
643 Fns::print_html( '<span class="text">' );
644 Fns::print_html( $args['sb_button_content'] );
645 Fns::print_html( '</span>' );
646 Fns::print_html( '<span class="hover-text">' );
647 Fns::print_html( $args['sb_button_content'] );
648 Fns::print_html( '</span>' );
649 Fns::print_html( self::render_button_icon( 'right', $args['sb_button_icon'], $args['sb_button_icon_position'] ) );
650 ?>
651 </span>
652 </a>
653 <?php
654 }
655 /**
656 * Renders an icon HTML markup based on position match.
657 *
658 * Compares the desired icon position with the configured position
659 * and renders the icon HTML if they match.
660 *
661 * @since 1.0.0
662 *
663 * @param string $position Desired position to render the icon ('left' or 'right').
664 * @param string $icon Icon identifier or SVG markup.
665 * @param string $icon_position Actual position set in the settings ('left' or 'right').
666 *
667 * @return string Icon HTML if position matches; otherwise, an empty string.
668 */
669 public static function render_button_icon( $position, $icon, $icon_position ) {
670 $html = '';
671
672 // Render the left icon.
673 if ( $position === $icon_position ) {
674 $html .= '<span class="icon">' . Fns::icons_manager( $icon ) . '</span>';
675 }
676
677 return $html;
678 }
679 }
680