PluginProbe
MarqueeX – Smooth Marquee Slider, News Ticker & Post Marquee for Block Editor & Elementor / 0.6.0
MarqueeX – Smooth Marquee Slider, News Ticker & Post Marquee for Block Editor & Elementor v0.6.0
0.6.0 0.5.4 0.5.3 0.5.2 0.5.1 0.5.0 0.4.0 0.3.3 0.3.2 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.1.0 0.1.1 0.1.2 0.2.0 0.2.1 0.2.2 0.2.3 0.3.0 0.3.1
marqueex / includes / Integrations / Elementor / Widgets / NewsTicker.php

NewsTicker.php in MarqueeX – Smooth Marquee Slider, News Ticker & Post Marquee for Block Editor & Elementor 0.6.0, at includes/Integrations/Elementor/Widgets/NewsTicker.php

1,937 lines 63.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Wpxero\Marqueex\Integrations\Elementor\Widgets;
4
5 use Elementor\Widget_Base;
6 use Elementor\Controls_Manager;
7 use Elementor\Group_Control_Typography;
8 use Elementor\Group_Control_Border;
9 use Elementor\Group_Control_Box_Shadow;
10 use Elementor\Group_Control_Background;
11 use Elementor\Group_Control_Text_Shadow;
12 use Elementor\Icons_Manager;
13 use Wpxero\Marqueex\Traits\MarqueeSource;
14
15 if (!defined('ABSPATH')) {
16 exit;
17 }
18
19 /**
20 * MarqueeX News Ticker Widget for Elementor
21 */
22 class NewsTicker extends Widget_Base {
23
24 use MarqueeSource;
25
26 /**
27 * Get widget name
28 */
29 public function get_name() {
30 return 'marqueex-news-ticker';
31 }
32
33 /**
34 * Get widget title
35 */
36 public function get_title() {
37 return __('News Ticker', 'marqueex');
38 }
39
40 /**
41 * Get widget icon
42 */
43 public function get_icon() {
44 return 'eicon-posts-ticker eicon-marqueex';
45 }
46
47 /**
48 * Get widget categories
49 */
50 public function get_categories() {
51 return ['marqueex'];
52 }
53
54 /**
55 * Enqueue widget scripts and styles
56 */
57 public function get_script_depends() {
58 return ['marqueex-news-ticker'];
59 }
60
61 public function get_style_depends() {
62 return ['marqueex-news-ticker'];
63 }
64 /**
65 * Get widget keywords
66 */
67 public function get_keywords() {
68 return ['news', 'ticker', 'marquee', 'posts', 'slider', 'scroll'];
69 }
70
71
72 /**
73 * Register widget controls
74 */
75 protected function register_controls() {
76 // Content Type Section
77 $this->start_controls_section(
78 'content_type_section',
79 [
80 'label' => __('Content Type', 'marqueex'),
81 'tab' => Controls_Manager::TAB_CONTENT,
82 ]
83 );
84
85 $this->add_control(
86 'content_type',
87 [
88 'label' => __('Content Type', 'marqueex'),
89 'type' => Controls_Manager::SELECT,
90 'default' => 'posts',
91 'options' => [
92 'posts' => __('Dynamic Posts', 'marqueex'),
93 'custom' => __('Custom Text', 'marqueex'),
94 ],
95 ]
96 );
97
98 $this->add_control(
99 'ticker_content_custom',
100 [
101 'label' => __('Ticker Content', 'marqueex'),
102 'type' => Controls_Manager::REPEATER,
103 'condition' => [
104 'content_type' => 'custom',
105 ],
106 'fields' => [
107 [
108 'name' => 'text',
109 'label' => __('Text', 'marqueex'),
110 'type' => Controls_Manager::TEXT,
111 'default' => __('Breaking news: Your news content here...', 'marqueex'),
112 'description' => __('Enter the news content that will scroll in the ticker.', 'marqueex'),
113 ],
114 [
115 'name' => 'link',
116 'label' => __('Link', 'marqueex'),
117 'type' => Controls_Manager::URL,
118 'default' => [
119 'url' => '',
120 'is_external' => '',
121 'nofollow' => '',
122 ],
123 'description' => __('Enter the link for the news content.', 'marqueex'),
124 ],
125 ],
126 'condition' => [
127 'content_type' => 'custom',
128 ],
129 'title_field' => '{{{ text }}}',
130 'default' => [
131 [
132 'text' => __('Breaking news: Your first news content here...', 'marqueex'),
133 'link' => [
134 'url' => '',
135 'is_external' => '',
136 'nofollow' => '',
137 ],
138 ],
139 [
140 'text' => __('Latest update: Your second news content here...', 'marqueex'),
141 'link' => [
142 'url' => '',
143 'is_external' => '',
144 'nofollow' => '',
145 ],
146 ],
147 [
148 'text' => __('Important announcement: Your third news content here...', 'marqueex'),
149 'link' => [
150 'url' => '',
151 'is_external' => '',
152 'nofollow' => '',
153 ],
154 ],
155 ],
156 ]
157 );
158
159 $this->end_controls_section();
160
161 // Query Section
162 $this->start_controls_section(
163 'query_section',
164 [
165 'label' => __('Query', 'marqueex'),
166 'tab' => Controls_Manager::TAB_CONTENT,
167 'condition' => [
168 'content_type' => 'posts',
169 ],
170 ]
171 );
172
173 $this->add_control(
174 'post_type',
175 [
176 'label' => __('Post Type', 'marqueex'),
177 'type' => Controls_Manager::SELECT,
178 'default' => 'post',
179 'options' => $this->get_post_types(),
180 ]
181 );
182
183 $this->add_control(
184 'posts_per_page',
185 [
186 'label' => __('Number of Posts', 'marqueex'),
187 'type' => Controls_Manager::NUMBER,
188 'default' => 6,
189 'min' => 1,
190 'max' => 50,
191 ]
192 );
193
194 $this->add_control(
195 'orderby',
196 [
197 'label' => __('Order By', 'marqueex'),
198 'type' => Controls_Manager::SELECT,
199 'default' => 'date',
200 'options' => [
201 'date' => __('Date', 'marqueex'),
202 'title' => __('Title', 'marqueex'),
203 'rand' => __('Random', 'marqueex'),
204 'menu_order' => __('Menu Order', 'marqueex'),
205 ],
206 ]
207 );
208
209 $this->add_control(
210 'order',
211 [
212 'label' => __('Order', 'marqueex'),
213 'type' => Controls_Manager::SELECT,
214 'default' => 'desc',
215 'options' => [
216 'asc' => __('ASC', 'marqueex'),
217 'desc' => __('DESC', 'marqueex'),
218 ],
219 'condition' => [
220 'orderby!' => 'rand',
221 ],
222 ]
223 );
224
225 $this->add_control(
226 'category_ids',
227 [
228 'label' => __('Categories', 'marqueex'),
229 'type' => Controls_Manager::SELECT2,
230 'multiple' => true,
231 'options' => $this->get_post_categories(),
232 'condition' => [
233 'post_type' => 'post',
234 ],
235 ]
236 );
237
238 $this->end_controls_section();
239
240 // Label Section
241 $this->start_controls_section(
242 'label_section',
243 [
244 'label' => __('Label', 'marqueex'),
245 'tab' => Controls_Manager::TAB_CONTENT,
246 ]
247 );
248
249 $this->add_control(
250 'show_label',
251 [
252 'label' => __('Show Label', 'marqueex'),
253 'type' => Controls_Manager::SWITCHER,
254 'label_on' => __('Yes', 'marqueex'),
255 'label_off' => __('No', 'marqueex'),
256 'return_value' => 'yes',
257 'default' => 'yes',
258 ]
259 );
260
261 $this->add_control(
262 'label_text',
263 [
264 'label' => __('Label Text', 'marqueex'),
265 'type' => Controls_Manager::TEXT,
266 'default' => __('BREAKING', 'marqueex'),
267 'condition' => [
268 'show_label' => 'yes',
269 ],
270 ]
271 );
272
273 $this->add_control(
274 'label_icon',
275 [
276 'label' => __('Label Icon', 'marqueex'),
277 'type' => Controls_Manager::ICONS,
278 'condition' => [
279 'show_label' => 'yes',
280 ],
281 ]
282 );
283
284 $this->add_control(
285 'label_heading_tag',
286 [
287 'label' => __('Label HTML Tag', 'marqueex'),
288 'type' => Controls_Manager::SELECT,
289 'default' => 'h4',
290 'options' => [
291 'h1' => 'H1',
292 'h2' => 'H2',
293 'h3' => 'H3',
294 'h4' => 'H4',
295 'h5' => 'H5',
296 'h6' => 'H6',
297 'div' => 'div',
298 'span' => 'span',
299 ],
300 'condition' => [
301 'show_label' => 'yes',
302 ],
303 ]
304 );
305
306 $this->end_controls_section();
307
308 // Content Options Section
309 $this->start_controls_section(
310 'content_options_section',
311 [
312 'label' => __('Content Options', 'marqueex'),
313 'tab' => Controls_Manager::TAB_CONTENT,
314 'condition' => [
315 'content_type' => 'posts',
316 ],
317 ]
318 );
319
320 $this->add_control(
321 'title_trim_enable',
322 [
323 'label' => __('Trim Title', 'marqueex'),
324 'type' => Controls_Manager::SWITCHER,
325 'label_on' => __('Yes', 'marqueex'),
326 'label_off' => __('No', 'marqueex'),
327 'return_value' => 'yes',
328 'default' => 'no',
329 ]
330 );
331
332 $this->add_control(
333 'title_trim_length',
334 [
335 'label' => __('Title Length', 'marqueex'),
336 'type' => Controls_Manager::NUMBER,
337 'default' => 50,
338 'min' => 1,
339 'max' => 200,
340 'condition' => [
341 'title_trim_enable' => 'yes',
342 ],
343 ]
344 );
345
346 $this->add_control(
347 'open_in_new_tab',
348 [
349 'label' => __('Open in New Tab', 'marqueex'),
350 'type' => Controls_Manager::SWITCHER,
351 'label_on' => __('Yes', 'marqueex'),
352 'label_off' => __('No', 'marqueex'),
353 'return_value' => 'yes',
354 'default' => 'no',
355 ]
356 );
357
358 $this->end_controls_section();
359
360 // Separator Section
361 $this->start_controls_section(
362 'separator_section',
363 [
364 'label' => __('Separator', 'marqueex'),
365 'tab' => Controls_Manager::TAB_CONTENT,
366 ]
367 );
368
369 $this->add_control(
370 'separator_type',
371 [
372 'label' => __('Separator Type', 'marqueex'),
373 'type' => Controls_Manager::SELECT,
374 'default' => 'text',
375 'options' => [
376 'none' => __('None', 'marqueex'),
377 'text' => __('Text', 'marqueex'),
378 'icon' => __('Icon', 'marqueex'),
379 // 'date' => __('Date', 'marqueex'),
380 // 'featured_image' => __('Featured Image', 'marqueex'),
381 ],
382 ]
383 );
384
385 // $this->add_control(
386 // 'separator_style_preset',
387 // [
388 // 'label' => __('Separator Style Preset', 'marqueex'),
389 // 'type' => Controls_Manager::SELECT,
390 // 'default' => 'modern',
391 // 'options' => [
392 // 'modern' => __('Modern Glass', 'marqueex'),
393 // 'minimal' => __('Minimal Clean', 'marqueex'),
394 // 'bold' => __('Bold Accent', 'marqueex'),
395 // 'neon' => __('Neon Glow', 'marqueex'),
396 // 'classic' => __('Classic Simple', 'marqueex'),
397 // 'gradient' => __('Gradient Flow', 'marqueex'),
398 // ],
399 // 'condition' => [
400 // 'separator_type!' => 'none',
401 // ],
402 // ]
403 // );
404
405 $this->add_control(
406 'separator_icon',
407 [
408 'label' => __('Separator Icon', 'marqueex'),
409 'type' => Controls_Manager::ICONS,
410 'default' => [
411 'value' => 'fas fa-circle',
412 'library' => 'fa-solid',
413 ],
414 'condition' => [
415 'separator_type' => 'icon',
416 ],
417 ]
418 );
419
420 $this->add_control(
421 'separator_text',
422 [
423 'label' => __('Separator Text', 'marqueex'),
424 'type' => Controls_Manager::TEXT,
425 'default' => '',
426 'condition' => [
427 'separator_type' => 'text',
428 ],
429 ]
430 );
431
432 $this->add_responsive_control(
433 'item_spacing',
434 [
435 'label' => __('Item Spacing', 'marqueex'),
436 'type' => Controls_Manager::SLIDER,
437 'size_units' => ['px', 'em', 'rem'],
438 'range' => [
439 'px' => [
440 'min' => 0,
441 'max' => 100,
442 'step' => 1,
443 ],
444 'em' => [
445 'min' => 0,
446 'max' => 10,
447 'step' => 0.1,
448 ],
449 'rem' => [
450 'min' => 0,
451 'max' => 10,
452 'step' => 0.1,
453 ],
454 ],
455 'default' => [
456 'unit' => 'rem',
457 'size' => 2,
458 ],
459 'selectors' => [
460 '{{WRAPPER}} .marqueex-marquee-track-wrapper' => 'gap: {{SIZE}}{{UNIT}};',
461 '{{WRAPPER}} .marqueex-marquee-track' => 'gap: {{SIZE}}{{UNIT}};',
462 '{{WRAPPER}} .marqueex-marquee-wrapper' => 'gap: {{SIZE}}{{UNIT}};',
463 ],
464 ]
465 );
466
467 $this->end_controls_section();
468
469 // Marquee Settings Section
470 $this->start_controls_section(
471 'marquee_settings_section',
472 [
473 'label' => __('Marquee Settings', 'marqueex'),
474 'tab' => Controls_Manager::TAB_CONTENT,
475 ]
476 );
477
478 $this->add_control(
479 'speed',
480 [
481 'label' => __('Speed', 'marqueex'),
482 'type' => Controls_Manager::SLIDER,
483 'size_units' => ['px/s'],
484 'range' => [
485 'px' => [
486 'min' => 10,
487 'max' => 100,
488 'step' => 1,
489 ],
490 ],
491 'default' => [
492 'unit' => 'px',
493 'size' => 10,
494 ],
495 ]
496 );
497
498 $this->add_control(
499 'pause_on_hover',
500 [
501 'label' => __('Pause on Hover', 'marqueex'),
502 'type' => Controls_Manager::SWITCHER,
503 'label_on' => __('Yes', 'marqueex'),
504 'label_off' => __('No', 'marqueex'),
505 'return_value' => 'yes',
506 'default' => 'yes',
507 ]
508 );
509
510 $this->add_control(
511 'reverse_direction',
512 [
513 'label' => __('Reverse Direction', 'marqueex'),
514 'type' => Controls_Manager::SWITCHER,
515 'label_on' => __('Yes', 'marqueex'),
516 'label_off' => __('No', 'marqueex'),
517 'return_value' => 'yes',
518 'default' => 'no',
519 ]
520 );
521
522 $this->end_controls_section();
523
524 // Container Style Section
525 $this->start_controls_section(
526 'container_style_section',
527 [
528 'label' => __('Container', 'marqueex'),
529 'tab' => Controls_Manager::TAB_STYLE,
530 ]
531 );
532
533 $this->add_group_control(
534 Group_Control_Background::get_type(),
535 [
536 'name' => 'container_background',
537 'label' => __('Background', 'marqueex'),
538 'types' => ['classic', 'gradient'],
539 'selector' => '{{WRAPPER}} .marqueex-marquee-block',
540 ]
541 );
542
543 $this->add_group_control(
544 Group_Control_Border::get_type(),
545 [
546 'name' => 'container_border',
547 'label' => __('Border', 'marqueex'),
548 'selector' => '{{WRAPPER}} .marqueex-marquee-block',
549 ]
550 );
551
552 $this->add_responsive_control(
553 'container_border_radius',
554 [
555 'label' => __('Border Radius', 'marqueex'),
556 'type' => Controls_Manager::DIMENSIONS,
557 'size_units' => ['px', '%', 'em'],
558 'selectors' => [
559 '{{WRAPPER}} .marqueex-marquee-block' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
560 ],
561 ]
562 );
563
564 $this->add_group_control(
565 Group_Control_Box_Shadow::get_type(),
566 [
567 'name' => 'container_box_shadow',
568 'label' => __('Box Shadow', 'marqueex'),
569 'selector' => '{{WRAPPER}} .marqueex-marquee-block',
570 ]
571 );
572
573 $this->add_responsive_control(
574 'container_padding',
575 [
576 'label' => __('Padding', 'marqueex'),
577 'type' => Controls_Manager::DIMENSIONS,
578 'size_units' => ['px', 'em', '%'],
579 'selectors' => [
580 '{{WRAPPER}} .marqueex-marquee-block' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
581 ],
582 ]
583 );
584
585 $this->add_responsive_control(
586 'container_margin',
587 [
588 'label' => __('Margin', 'marqueex'),
589 'type' => Controls_Manager::DIMENSIONS,
590 'size_units' => ['px', 'em', '%'],
591 'selectors' => [
592 '{{WRAPPER}} .marqueex-marquee-block' => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
593 ],
594 ]
595 );
596
597 $this->add_responsive_control(
598 'container_height',
599 [
600 'label' => __('Height', 'marqueex'),
601 'type' => Controls_Manager::SLIDER,
602 'size_units' => ['px', 'vh', 'em'],
603 'range' => [
604 'px' => [
605 'min' => 30,
606 'max' => 200,
607 ],
608 'vh' => [
609 'min' => 1,
610 'max' => 20,
611 ],
612 ],
613 'selectors' => [
614 '{{WRAPPER}} .marqueex-marquee-block' => 'height: {{SIZE}}{{UNIT}};',
615 ],
616 ]
617 );
618
619 $this->add_control(
620 'container_overflow',
621 [
622 'label' => __('Overflow', 'marqueex'),
623 'type' => Controls_Manager::SELECT,
624 'default' => 'hidden',
625 'options' => [
626 'visible' => __('Visible', 'marqueex'),
627 'hidden' => __('Hidden', 'marqueex'),
628 'scroll' => __('Scroll', 'marqueex'),
629 'auto' => __('Auto', 'marqueex'),
630 ],
631 'selectors' => [
632 '{{WRAPPER}} .marqueex-marquee-block' => 'overflow: {{VALUE}};',
633 ],
634 ]
635 );
636
637 $this->end_controls_section();
638
639 // Text Style Section
640 $this->start_controls_section(
641 'text_style_section',
642 [
643 'label' => __('Text', 'marqueex'),
644 'tab' => Controls_Manager::TAB_STYLE,
645 ]
646 );
647 $this->start_controls_tabs(
648 'style_tabs'
649 );
650
651 $this->start_controls_tab(
652 'style_normal_tab',
653 [
654 'label' => esc_html__('Normal', 'marqueex'),
655 ]
656 );
657 $this->add_control(
658 'text_color',
659 [
660 'label' => __('Text Color', 'marqueex'),
661 'type' => Controls_Manager::COLOR,
662 'selectors' => [
663 '{{WRAPPER}} .marqueex-news-text' => 'color: {{VALUE}};',
664 '{{WRAPPER}} .marqueex-title-link' => 'color: {{VALUE}};',
665 ],
666 ]
667 );
668
669 $this->add_group_control(
670 Group_Control_Typography::get_type(),
671 [
672 'name' => 'text_typography',
673 'selector' => '{{WRAPPER}} .marqueex-news-text, {{WRAPPER}} .marqueex-title-link',
674 ]
675 );
676
677 $this->add_group_control(
678 Group_Control_Text_Shadow::get_type(),
679 [
680 'name' => 'text_shadow',
681 'selector' => '{{WRAPPER}} .marqueex-news-text, {{WRAPPER}} .marqueex-title-link',
682 ]
683 );
684
685 $this->add_responsive_control(
686 'text_spacing',
687 [
688 'label' => __('Letter Spacing', 'marqueex'),
689 'type' => Controls_Manager::SLIDER,
690 'size_units' => ['px', 'em'],
691 'range' => [
692 'px' => [
693 'min' => -2,
694 'max' => 10,
695 'step' => 0.1,
696 ],
697 ],
698 'selectors' => [
699 '{{WRAPPER}} .marqueex-news-text' => 'letter-spacing: {{SIZE}}{{UNIT}};',
700 ],
701 ]
702 );
703
704 $this->add_responsive_control(
705 'word_spacing',
706 [
707 'label' => __('Word Spacing', 'marqueex'),
708 'type' => Controls_Manager::SLIDER,
709 'size_units' => ['px', 'em'],
710 'range' => [
711 'px' => [
712 'min' => 0,
713 'max' => 50,
714 ],
715 ],
716 'selectors' => [
717 '{{WRAPPER}} .marqueex-news-text' => 'word-spacing: {{SIZE}}{{UNIT}};',
718 ],
719 ]
720 );
721
722 $this->end_controls_tab();
723 $this->start_controls_tab(
724 'link_normal_tab',
725 [
726 'label' => __('Link', 'marqueex'),
727 ]
728 );
729
730 $this->add_control(
731 'link_color',
732 [
733 'label' => __('Link Color', 'marqueex'),
734 'type' => Controls_Manager::COLOR,
735 'selectors' => [
736 '{{WRAPPER}} .marqueex-title-link' => 'color: {{VALUE}};',
737 ],
738 ]
739 );
740
741 $this->add_control(
742 'link_decoration',
743 [
744 'label' => __('Text Decoration', 'marqueex'),
745 'type' => Controls_Manager::SELECT,
746 'default' => 'none',
747 'options' => [
748 'none' => __('None', 'marqueex'),
749 'underline' => __('Underline', 'marqueex'),
750 'overline' => __('Overline', 'marqueex'),
751 'line-through' => __('Line Through', 'marqueex'),
752 ],
753 'selectors' => [
754 '{{WRAPPER}} .marqueex-title-link' => 'text-decoration: {{VALUE}};',
755 ],
756 ]
757 );
758 $this->add_control(
759 'link_hover_heading',
760 [
761 'label' => __('H O V E R', 'marqueex'),
762 'type' => Controls_Manager::HEADING,
763 'separator' => 'before',
764 ]
765 );
766 $this->add_control(
767 'link_hover_color',
768 [
769 'label' => __('Color', 'marqueex'),
770 'type' => Controls_Manager::COLOR,
771 'selectors' => [
772 '{{WRAPPER}} .marqueex-title-link:hover' => 'color: {{VALUE}};',
773 ],
774 ]
775 );
776
777 $this->add_control(
778 'link_hover_decoration',
779 [
780 'label' => __('Text Decoration', 'marqueex'),
781 'type' => Controls_Manager::SELECT,
782 'default' => 'underline',
783 'options' => [
784 'none' => __('None', 'marqueex'),
785 'underline' => __('Underline', 'marqueex'),
786 'overline' => __('Overline', 'marqueex'),
787 'line-through' => __('Line Through', 'marqueex'),
788 ],
789 'selectors' => [
790 '{{WRAPPER}} .marqueex-title-link:hover' => 'text-decoration: {{VALUE}};',
791 ],
792 ]
793 );
794
795 $this->add_control(
796 'link_hover_transition',
797 [
798 'label' => __('Transition Duration', 'marqueex'),
799 'type' => Controls_Manager::SLIDER,
800 'size_units' => ['ms', 's'],
801 'range' => [
802 'ms' => [
803 'min' => 100,
804 'max' => 1000,
805 'step' => 50,
806 ],
807 's' => [
808 'min' => 0.1,
809 'max' => 2,
810 'step' => 0.1,
811 ],
812 ],
813 'default' => [
814 'unit' => 'ms',
815 'size' => 300,
816 ],
817 'selectors' => [
818 '{{WRAPPER}} .marqueex-title-link' => 'transition: all {{SIZE}}{{UNIT}} ease;',
819 ],
820 ]
821 );
822 $this->end_controls_tab();
823
824 $this->end_controls_tabs();
825
826 $this->end_controls_section();
827
828 // // Link Style Section
829 // $this->start_controls_section(
830 // 'link_style_section',
831 // [
832 // 'label' => __('Link', 'marqueex'),
833 // 'tab' => Controls_Manager::TAB_STYLE,
834 // ]
835 // );
836
837 // $this->start_controls_tabs('link_style_tabs');
838
839
840
841 // $this->end_controls_tab();
842
843 // $this->start_controls_tab(
844 // 'link_hover_tab',
845 // [
846 // 'label' => __('Hover', 'marqueex'),
847 // ]
848 // );
849
850
851
852 // $this->end_controls_tab();
853
854 // $this->end_controls_tabs();
855
856 // $this->end_controls_section();
857
858 // Separator Style Section
859 $this->start_controls_section(
860 'separator_style_section',
861 [
862 'label' => __('Separator', 'marqueex'),
863 'tab' => Controls_Manager::TAB_STYLE,
864 'condition' => [
865 'separator_type!' => 'none',
866 ],
867 ]
868 );
869
870 $this->add_control(
871 'separator_color',
872 [
873 'label' => __('Separator Color', 'marqueex'),
874 'type' => Controls_Manager::COLOR,
875 'default' => '#333',
876 'selectors' => [
877 '{{WRAPPER}} .marqueex-separator' => 'color: {{VALUE}};',
878 '{{WRAPPER}} .marqueex-separator svg' => 'fill: {{VALUE}};',
879 ],
880 ]
881 );
882
883 $this->add_group_control(
884 Group_Control_Typography::get_type(),
885 [
886 'name' => 'separator_typography',
887 'selector' => '{{WRAPPER}} .marqueex-separator',
888 'condition' => [
889 'separator_type' => ['text', 'date'],
890 ],
891 ]
892 );
893
894 $this->add_responsive_control(
895 'separator_size',
896 [
897 'label' => __('Icon Size', 'marqueex'),
898 'type' => Controls_Manager::SLIDER,
899 'size_units' => ['px', 'em'],
900 'range' => [
901 'px' => [
902 'min' => 8,
903 'max' => 50,
904 ],
905 ],
906 'selectors' => [
907 '{{WRAPPER}} .marqueex-separator-icon' => 'font-size: {{SIZE}}{{UNIT}};',
908 '{{WRAPPER}} .marqueex-separator-icon svg' => 'width: {{SIZE}}{{UNIT}}; height: {{SIZE}}{{UNIT}};',
909 ],
910 'condition' => [
911 'separator_type' => 'icon',
912 ],
913 ]
914 );
915
916 // $this->add_responsive_control(
917 // 'separator_spacing',
918 // [
919 // 'label' => __('Separator Spacing', 'marqueex'),
920 // 'type' => Controls_Manager::SLIDER,
921 // 'size_units' => ['px', 'em'],
922 // 'range' => [
923 // 'px' => [
924 // 'min' => 0,
925 // 'max' => 100,
926 // ],
927 // ],
928 // 'selectors' => [
929 // '{{WRAPPER}} .marqueex-separator' => 'margin: 0 {{SIZE}}{{UNIT}};',
930 // ],
931 // ]
932 // );
933
934 $this->add_group_control(
935 Group_Control_Background::get_type(),
936 [
937 'name' => 'separator_background',
938 'label' => __('Background', 'marqueex'),
939 'types' => ['classic', 'gradient'],
940 'selector' => '{{WRAPPER}} .marqueex-separator',
941 'fields_options' => [
942 'background' => [
943 'default' => 'classic',
944 ],
945 ],
946 ]
947 );
948
949 $this->add_group_control(
950 Group_Control_Border::get_type(),
951 [
952 'name' => 'separator_border',
953 'label' => __('Border', 'marqueex'),
954 'selector' => '{{WRAPPER}} .marqueex-separator',
955 ]
956 );
957
958 $this->add_responsive_control(
959 'separator_border_radius',
960 [
961 'label' => __('Border Radius', 'marqueex'),
962 'type' => Controls_Manager::DIMENSIONS,
963 'size_units' => ['px', '%', 'em'],
964 'selectors' => [
965 '{{WRAPPER}} .marqueex-separator' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
966 ],
967 ]
968 );
969
970 $this->add_group_control(
971 Group_Control_Box_Shadow::get_type(),
972 [
973 'name' => 'separator_box_shadow',
974 'label' => __('Box Shadow', 'marqueex'),
975 'selector' => '{{WRAPPER}} .marqueex-separator',
976 ]
977 );
978
979 $this->add_group_control(
980 Group_Control_Text_Shadow::get_type(),
981 [
982 'name' => 'separator_text_shadow',
983 'label' => __('Text Shadow', 'marqueex'),
984 'selector' => '{{WRAPPER}} .marqueex-separator',
985 'condition' => [
986 'separator_type' => ['text', 'date'],
987 ],
988 ]
989 );
990
991 $this->add_responsive_control(
992 'separator_padding',
993 [
994 'label' => __('Padding', 'marqueex'),
995 'type' => Controls_Manager::DIMENSIONS,
996 'size_units' => ['px', '%', 'em'],
997 'selectors' => [
998 '{{WRAPPER}} .marqueex-separator' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
999 ],
1000 ]
1001 );
1002
1003 $this->add_responsive_control(
1004 'separator_opacity',
1005 [
1006 'label' => __('Opacity', 'marqueex'),
1007 'type' => Controls_Manager::SLIDER,
1008 'range' => [
1009 'px' => [
1010 'max' => 1,
1011 'min' => 0.10,
1012 'step' => 0.01,
1013 ],
1014 ],
1015 'selectors' => [
1016 '{{WRAPPER}} .marqueex-separator' => 'opacity: {{SIZE}};',
1017 ],
1018 ]
1019 );
1020
1021 $this->add_control(
1022 'separator_backdrop_filter',
1023 [
1024 'label' => __('Backdrop Filter Blur', 'marqueex'),
1025 'type' => Controls_Manager::SLIDER,
1026 'size_units' => ['px'],
1027 'range' => [
1028 'px' => [
1029 'min' => 0,
1030 'max' => 20,
1031 ],
1032 ],
1033 'selectors' => [
1034 '{{WRAPPER}} .marqueex-separator' => 'backdrop-filter: blur({{SIZE}}{{UNIT}});',
1035 ],
1036 ]
1037 );
1038
1039 $this->add_control(
1040 'separator_transform',
1041 [
1042 'label' => __('Transform', 'marqueex'),
1043 'type' => Controls_Manager::TEXT,
1044 'placeholder' => __('scale(1.1) rotate(5deg)', 'marqueex'),
1045 'selectors' => [
1046 '{{WRAPPER}} .marqueex-separator' => 'transform: {{VALUE}};',
1047 ],
1048 ]
1049 );
1050
1051 // Separator Hover Effects
1052 $this->add_control(
1053 'separator_hover_heading',
1054 [
1055 'label' => __('Hover Effects', 'marqueex'),
1056 'type' => Controls_Manager::HEADING,
1057 'separator' => 'before',
1058 ]
1059 );
1060
1061 $this->add_control(
1062 'separator_hover_color',
1063 [
1064 'label' => __('Hover Color', 'marqueex'),
1065 'type' => Controls_Manager::COLOR,
1066 'selectors' => [
1067 '{{WRAPPER}} .marqueex-separator:hover' => 'color: {{VALUE}};',
1068 '{{WRAPPER}} .marqueex-separator:hover svg' => 'fill: {{VALUE}};',
1069 ],
1070 ]
1071 );
1072
1073 $this->add_group_control(
1074 Group_Control_Background::get_type(),
1075 [
1076 'name' => 'separator_hover_background',
1077 'label' => __('Hover Background', 'marqueex'),
1078 'types' => ['classic', 'gradient'],
1079 'selector' => '{{WRAPPER}} .marqueex-separator:hover',
1080 ]
1081 );
1082
1083 $this->add_group_control(
1084 Group_Control_Border::get_type(),
1085 [
1086 'name' => 'separator_hover_border',
1087 'label' => __('Hover Border', 'marqueex'),
1088 'selector' => '{{WRAPPER}} .marqueex-separator:hover',
1089 ]
1090 );
1091
1092 $this->add_group_control(
1093 Group_Control_Box_Shadow::get_type(),
1094 [
1095 'name' => 'separator_hover_box_shadow',
1096 'label' => __('Hover Box Shadow', 'marqueex'),
1097 'selector' => '{{WRAPPER}} .marqueex-separator:hover',
1098 ]
1099 );
1100
1101 $this->add_control(
1102 'separator_hover_transform',
1103 [
1104 'label' => __('Hover Transform', 'marqueex'),
1105 'type' => Controls_Manager::TEXT,
1106 'placeholder' => __('translateY(-2px) scale(1.05)', 'marqueex'),
1107 'selectors' => [
1108 '{{WRAPPER}} .marqueex-separator:hover' => 'transform: {{VALUE}};',
1109 ],
1110 ]
1111 );
1112
1113 $this->add_responsive_control(
1114 'separator_hover_opacity',
1115 [
1116 'label' => __('Hover Opacity', 'marqueex'),
1117 'type' => Controls_Manager::SLIDER,
1118 'range' => [
1119 'px' => [
1120 'max' => 1,
1121 'min' => 0.10,
1122 'step' => 0.01,
1123 ],
1124 ],
1125 'selectors' => [
1126 '{{WRAPPER}} .marqueex-separator:hover' => 'opacity: {{SIZE}};',
1127 ],
1128 ]
1129 );
1130
1131 $this->add_control(
1132 'separator_transition_duration',
1133 [
1134 'label' => __('Transition Duration', 'marqueex'),
1135 'type' => Controls_Manager::SLIDER,
1136 'size_units' => ['ms'],
1137 'range' => [
1138 'ms' => [
1139 'min' => 100,
1140 'max' => 2000,
1141 'step' => 50,
1142 ],
1143 ],
1144 'default' => [
1145 'size' => 300,
1146 'unit' => 'ms',
1147 ],
1148 'selectors' => [
1149 '{{WRAPPER}} .marqueex-separator' => 'transition: all {{SIZE}}{{UNIT}} ease;',
1150 ],
1151 ]
1152 );
1153
1154 $this->add_responsive_control(
1155 'separator_image_size',
1156 [
1157 'label' => __('Image Size', 'marqueex'),
1158 'type' => Controls_Manager::SLIDER,
1159 'size_units' => ['px'],
1160 'range' => [
1161 'px' => [
1162 'min' => 20,
1163 'max' => 100,
1164 ],
1165 ],
1166 'selectors' => [
1167 '{{WRAPPER}} .marqueex-separator-feature-image img' => 'width: {{SIZE}}{{UNIT}}; height: {{SIZE}}{{UNIT}}; object-fit: cover;',
1168 ],
1169 'condition' => [
1170 'separator_type' => 'featured_image',
1171 ],
1172 ]
1173 );
1174
1175 $this->add_responsive_control(
1176 'separator_image_border_radius',
1177 [
1178 'label' => __('Image Border Radius', 'marqueex'),
1179 'type' => Controls_Manager::DIMENSIONS,
1180 'size_units' => ['px', '%'],
1181 'selectors' => [
1182 '{{WRAPPER}} .marqueex-separator-feature-image img' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1183 ],
1184 'condition' => [
1185 'separator_type' => 'featured_image',
1186 ],
1187 ]
1188 );
1189
1190 $this->end_controls_section();
1191
1192
1193 // Label Style Section
1194 $this->start_controls_section(
1195 'label_style_section',
1196 [
1197 'label' => __('Label', 'marqueex'),
1198 'tab' => Controls_Manager::TAB_STYLE,
1199 'condition' => [
1200 'show_label' => 'yes',
1201 ],
1202 ]
1203 );
1204
1205 $this->add_group_control(
1206 Group_Control_Background::get_type(),
1207 [
1208 'name' => 'label_background',
1209 'label' => __('Background', 'marqueex'),
1210 'types' => ['classic', 'gradient'],
1211 'selector' => '{{WRAPPER}} .marqueex-ticker-label',
1212 ]
1213 );
1214
1215 $this->add_control(
1216 'label_text_color',
1217 [
1218 'label' => __('Text Color', 'marqueex'),
1219 'type' => Controls_Manager::COLOR,
1220 'selectors' => [
1221 '{{WRAPPER}} .marqueex-ticker-label' => 'color: {{VALUE}};',
1222 ],
1223 ]
1224 );
1225
1226 $this->add_group_control(
1227 Group_Control_Typography::get_type(),
1228 [
1229 'name' => 'label_typography',
1230 'selector' => '{{WRAPPER}} .marqueex-ticker-label',
1231 ]
1232 );
1233
1234 $this->add_group_control(
1235 Group_Control_Text_Shadow::get_type(),
1236 [
1237 'name' => 'label_text_shadow',
1238 'selector' => '{{WRAPPER}} .marqueex-ticker-label',
1239 ]
1240 );
1241
1242 $this->add_group_control(
1243 Group_Control_Border::get_type(),
1244 [
1245 'name' => 'label_border',
1246 'label' => __('Border', 'marqueex'),
1247 'selector' => '{{WRAPPER}} .marqueex-ticker-label',
1248 ]
1249 );
1250
1251 $this->add_responsive_control(
1252 'label_border_radius',
1253 [
1254 'label' => __('Border Radius', 'marqueex'),
1255 'type' => Controls_Manager::DIMENSIONS,
1256 'size_units' => ['px', '%', 'em'],
1257 'selectors' => [
1258 '{{WRAPPER}} .marqueex-ticker-label' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1259 ],
1260 ]
1261 );
1262
1263 $this->add_group_control(
1264 Group_Control_Box_Shadow::get_type(),
1265 [
1266 'name' => 'label_box_shadow',
1267 'label' => __('Box Shadow', 'marqueex'),
1268 'selector' => '{{WRAPPER}} .marqueex-ticker-label',
1269 ]
1270 );
1271
1272 $this->add_responsive_control(
1273 'label_padding',
1274 [
1275 'label' => __('Padding', 'marqueex'),
1276 'type' => Controls_Manager::DIMENSIONS,
1277 'size_units' => ['px', 'em', '%'],
1278 'selectors' => [
1279 '{{WRAPPER}} .marqueex-ticker-label' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1280 ],
1281 ]
1282 );
1283
1284 $this->add_responsive_control(
1285 'label_margin',
1286 [
1287 'label' => __('Margin', 'marqueex'),
1288 'type' => Controls_Manager::DIMENSIONS,
1289 'size_units' => ['px', 'em', '%'],
1290 'selectors' => [
1291 '{{WRAPPER}} .marqueex-ticker-label' => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1292 ],
1293 ]
1294 );
1295
1296 $this->add_responsive_control(
1297 'label_width',
1298 [
1299 'label' => __('Width', 'marqueex'),
1300 'type' => Controls_Manager::SLIDER,
1301 'size_units' => ['px', '%', 'em'],
1302 'range' => [
1303 'px' => [
1304 'min' => 50,
1305 'max' => 300,
1306 ],
1307 '%' => [
1308 'min' => 10,
1309 'max' => 50,
1310 ],
1311 ],
1312 'selectors' => [
1313 '{{WRAPPER}} .marqueex-ticker-label' => 'width: {{SIZE}}{{UNIT}};',
1314 ],
1315 ]
1316 );
1317
1318 $this->add_control(
1319 'label_position',
1320 [
1321 'label' => __('Position', 'marqueex'),
1322 'type' => Controls_Manager::SELECT,
1323 'default' => 'relative',
1324 'options' => [
1325 'relative' => __('Relative', 'marqueex'),
1326 'absolute' => __('Absolute', 'marqueex'),
1327 'sticky' => __('Sticky', 'marqueex'),
1328 ],
1329 'selectors' => [
1330 '{{WRAPPER}} .marqueex-ticker-label' => 'position: {{VALUE}};',
1331 ],
1332 ]
1333 );
1334
1335 $this->add_responsive_control(
1336 'label_z_index',
1337 [
1338 'label' => __('Z-Index', 'marqueex'),
1339 'type' => Controls_Manager::NUMBER,
1340 'min' => 0,
1341 'max' => 999,
1342 'selectors' => [
1343 '{{WRAPPER}} .marqueex-ticker-label' => 'z-index: {{VALUE}};',
1344 ],
1345 'condition' => [
1346 'label_position!' => 'relative',
1347 ],
1348 ]
1349 );
1350
1351 $this->end_controls_section();
1352 // Hover Effects Section
1353 // $this->start_controls_section(
1354 // 'hover_effects_section',
1355 // [
1356 // 'label' => __('Hover Effects', 'marqueex'),
1357 // 'tab' => Controls_Manager::TAB_STYLE,
1358 // ]
1359 // );
1360
1361 // $this->add_control(
1362 // 'container_hover_animation',
1363 // [
1364 // 'label' => __('Container Hover Animation', 'marqueex'),
1365 // 'type' => Controls_Manager::SELECT,
1366 // 'default' => 'none',
1367 // 'options' => [
1368 // 'none' => __('None', 'marqueex'),
1369 // 'scale' => __('Scale', 'marqueex'),
1370 // 'lift' => __('Lift', 'marqueex'),
1371 // 'glow' => __('Glow', 'marqueex'),
1372 // 'pulse' => __('Pulse', 'marqueex'),
1373 // ],
1374 // 'prefix_class' => 'marqueex-hover-',
1375 // ]
1376 // );
1377
1378 // $this->add_control(
1379 // 'hover_scale_intensity',
1380 // [
1381 // 'label' => __('Scale Intensity', 'marqueex'),
1382 // 'type' => Controls_Manager::SLIDER,
1383 // 'range' => [
1384 // 'px' => [
1385 // 'min' => 1,
1386 // 'max' => 1.2,
1387 // 'step' => 0.01,
1388 // ],
1389 // ],
1390 // 'default' => [
1391 // 'size' => 1.05,
1392 // ],
1393 // 'selectors' => [
1394 // '{{WRAPPER}}.marqueex-hover-scale .marqueex-marquee-block:hover' => 'transform: scale({{SIZE}});',
1395 // ],
1396 // 'condition' => [
1397 // 'container_hover_animation' => 'scale',
1398 // ],
1399 // ]
1400 // );
1401
1402 // $this->add_control(
1403 // 'hover_lift_distance',
1404 // [
1405 // 'label' => __('Lift Distance', 'marqueex'),
1406 // 'type' => Controls_Manager::SLIDER,
1407 // 'size_units' => ['px'],
1408 // 'range' => [
1409 // 'px' => [
1410 // 'min' => 1,
1411 // 'max' => 20,
1412 // ],
1413 // ],
1414 // 'default' => [
1415 // 'size' => 5,
1416 // ],
1417 // 'selectors' => [
1418 // '{{WRAPPER}}.marqueex-hover-lift .marqueex-marquee-block:hover' => 'transform: translateY(-{{SIZE}}{{UNIT}});',
1419 // ],
1420 // 'condition' => [
1421 // 'container_hover_animation' => 'lift',
1422 // ],
1423 // ]
1424 // );
1425
1426 // $this->add_control(
1427 // 'hover_glow_color',
1428 // [
1429 // 'label' => __('Glow Color', 'marqueex'),
1430 // 'type' => Controls_Manager::COLOR,
1431 // 'default' => '#007cba',
1432 // 'selectors' => [
1433 // '{{WRAPPER}}.marqueex-hover-glow .marqueex-marquee-block:hover' => 'box-shadow: 0 0 20px {{VALUE}};',
1434 // ],
1435 // 'condition' => [
1436 // 'container_hover_animation' => 'glow',
1437 // ],
1438 // ]
1439 // );
1440
1441 // $this->add_control(
1442 // 'hover_transition_duration',
1443 // [
1444 // 'label' => __('Transition Duration', 'marqueex'),
1445 // 'type' => Controls_Manager::SLIDER,
1446 // 'size_units' => ['ms', 's'],
1447 // 'range' => [
1448 // 'ms' => [
1449 // 'min' => 100,
1450 // 'max' => 1000,
1451 // 'step' => 50,
1452 // ],
1453 // 's' => [
1454 // 'min' => 0.1,
1455 // 'max' => 2,
1456 // 'step' => 0.1,
1457 // ],
1458 // ],
1459 // 'default' => [
1460 // 'unit' => 'ms',
1461 // 'size' => 300,
1462 // ],
1463 // 'selectors' => [
1464 // '{{WRAPPER}} .marqueex-marquee-block' => 'transition: all {{SIZE}}{{UNIT}} ease;',
1465 // ],
1466 // 'condition' => [
1467 // 'container_hover_animation!' => 'none',
1468 // ],
1469 // ]
1470 // );
1471
1472 // $this->end_controls_section();
1473
1474 // Animation & Performance Section
1475 // $this->start_controls_section(
1476 // 'animation_performance_section',
1477 // [
1478 // 'label' => __('Animation & Performance', 'marqueex'),
1479 // 'tab' => Controls_Manager::TAB_STYLE,
1480 // ]
1481 // );
1482
1483 // $this->add_control(
1484 // 'animation_easing',
1485 // [
1486 // 'label' => __('Animation Easing', 'marqueex'),
1487 // 'type' => Controls_Manager::SELECT,
1488 // 'default' => 'linear',
1489 // 'options' => [
1490 // 'linear' => __('Linear', 'marqueex'),
1491 // 'ease' => __('Ease', 'marqueex'),
1492 // 'ease-in' => __('Ease In', 'marqueex'),
1493 // 'ease-out' => __('Ease Out', 'marqueex'),
1494 // 'ease-in-out' => __('Ease In Out', 'marqueex'),
1495 // ],
1496 // 'selectors' => [
1497 // '{{WRAPPER}} .marqueex-marquee-track' => 'animation-timing-function: {{VALUE}};',
1498 // ],
1499 // ]
1500 // );
1501
1502 // $this->add_control(
1503 // 'gpu_acceleration',
1504 // [
1505 // 'label' => __('GPU Acceleration', 'marqueex'),
1506 // 'type' => Controls_Manager::SWITCHER,
1507 // 'label_on' => __('On', 'marqueex'),
1508 // 'label_off' => __('Off', 'marqueex'),
1509 // 'return_value' => 'yes',
1510 // 'default' => 'yes',
1511 // 'selectors' => [
1512 // '{{WRAPPER}} .marqueex-marquee-track' => 'transform: translateZ(0); will-change: transform;',
1513 // ],
1514 // ]
1515 // );
1516
1517 // $this->add_control(
1518 // 'smooth_scrolling',
1519 // [
1520 // 'label' => __('Smooth Scrolling', 'marqueex'),
1521 // 'type' => Controls_Manager::SWITCHER,
1522 // 'label_on' => __('On', 'marqueex'),
1523 // 'label_off' => __('Off', 'marqueex'),
1524 // 'return_value' => 'yes',
1525 // 'default' => 'yes',
1526 // 'selectors' => [
1527 // '{{WRAPPER}} .marqueex-marquee-track-wrapper' => 'overflow: hidden; -webkit-overflow-scrolling: touch;',
1528 // ],
1529 // ]
1530 // );
1531
1532 // $this->end_controls_section();
1533 }
1534
1535 /**
1536 * Prepare widget configuration from settings
1537 *
1538 * Extracts and processes all widget settings into a clean configuration array.
1539 *
1540 * @param array $settings Widget settings from Elementor
1541 * @return array Processed configuration array
1542 */
1543 private function prepare_widget_config($settings) {
1544 return [
1545 'unique_id' => 'marqueex-ticker-' . $this->get_id(),
1546 'speed' => max(10, min(100, intval($settings['speed']['size'] ?? 10))), // Security: Clamp speed values
1547 'direction' => $settings['direction'] ?? 'left',
1548 'pause_on_hover' => $settings['pause_on_hover'] === 'yes' ? 'true' : 'false',
1549 'reverse_direction' => $settings['reverse_direction'] === 'yes',
1550 'content_type' => in_array($settings['content_type'] ?? 'posts', ['posts', 'custom']) ? $settings['content_type'] : 'posts',
1551 ];
1552 }
1553
1554 /**
1555 * Render ticker content based on content type
1556 *
1557 * @param array $settings Widget settings
1558 * @param array $config Widget configuration
1559 */
1560 private function render_ticker_content_by_type($settings, $config) {
1561 if ($config['content_type'] === 'posts') {
1562 $this->render_dynamic_posts_content($settings);
1563 } else {
1564 $this->render_custom_text_content($settings);
1565 }
1566 }
1567
1568 /**
1569 * Render dynamic posts content
1570 *
1571 * @param array $settings Widget settings
1572 */
1573 private function render_dynamic_posts_content($settings) {
1574 $query_args = $this->get_query_args($settings);
1575 $posts = get_posts($query_args);
1576 $this->render_ticker_content($settings, $posts);
1577 }
1578
1579 /**
1580 * Render custom text content
1581 *
1582 * @param array $settings Widget settings
1583 */
1584 private function render_custom_text_content($settings) {
1585 $ticker_content = $settings['ticker_content_custom'] ?? [];
1586
1587 if (empty($ticker_content)) {
1588 return;
1589 }
1590
1591 echo '<div class="marqueex-marquee-wrapper">';
1592 $total_items = count($ticker_content);
1593
1594 foreach ($ticker_content as $index => $item) {
1595 $text = $item['text'] ?? '';
1596 $link = $item['link'] ?? [];
1597
1598 if (empty($text)) {
1599 continue;
1600 }
1601
1602 echo '<div class="marqueex-news-item">';
1603 echo '<span class="marqueex-news-text">';
1604
1605 if (!empty($link['url'])) {
1606 $rel_attrs = [];
1607
1608 if ($link['is_external']) {
1609 $rel_attrs[] = 'noopener noreferrer';
1610 }
1611 if ($link['nofollow']) {
1612 $rel_attrs[] = 'nofollow';
1613 }
1614
1615 $rel_value = implode(' ', $rel_attrs);
1616
1617 echo '<a href="' . esc_url($link['url']) . '"'
1618 . ($link['is_external'] ? ' target="_blank"' : '')
1619 . ($rel_value ? ' rel="' . esc_attr($rel_value) . '"' : '')
1620 . ' class="marqueex-title-link" aria-label="' . esc_attr($text) . '">'
1621 . esc_html($text) . '</a>';
1622 } else {
1623 echo esc_html($text);
1624 }
1625
1626 echo '</span>';
1627 echo '</div>';
1628
1629 // Only render separator if not the last item
1630 if ($index < $total_items - 1) {
1631 $this->render_separator_for_custom($settings);
1632 }
1633 }
1634 echo '</div>';
1635 }
1636
1637 /**
1638 * Render label section with icon and text
1639 *
1640 * Displays the ticker label with optional icon and customizable HTML tag.
1641 *
1642 * @param array $settings Widget settings
1643 */
1644 private function render_label($settings) {
1645 // Only render if label is enabled
1646 if ($settings['show_label'] !== 'yes') {
1647 return;
1648 }
1649
1650 $label_text = !empty($settings['label_text']) ? sanitize_text_field($settings['label_text']) : '';
1651 $label_icon = !empty($settings['label_icon']) ? $settings['label_icon'] : [];
1652 $label_tag = !empty($settings['label_heading_tag']) ? $settings['label_heading_tag'] : 'div';
1653
1654 // Security: Validate HTML tag
1655 $allowed_tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'span'];
1656 if (!in_array($label_tag, $allowed_tags)) {
1657 $label_tag = 'div';
1658 }
1659
1660 if (empty($label_text) && empty($label_icon['value'])) {
1661 return;
1662 }
1663
1664 echo '<div class="marqueex-ticker-label">';
1665 echo '<' . esc_attr($label_tag) . ' class="marqueex-label-heading">';
1666
1667 // Render icon if available
1668 if (!empty($label_icon['value']) && $settings['reverse_direction'] !== 'yes') {
1669 echo '<span class="marqueex-ticker-icon">';
1670 \Elementor\Icons_Manager::render_icon($label_icon, ['aria-hidden' => 'true']);
1671 echo '</span>';
1672 }
1673
1674 // Render text if available
1675 if (!empty($label_text)) {
1676 echo esc_html($label_text);
1677 }
1678
1679 if (!empty($label_icon['value']) && $settings['reverse_direction'] == 'yes') {
1680 echo '<span class="marqueex-ticker-icon">';
1681 \Elementor\Icons_Manager::render_icon($label_icon, ['aria-hidden' => 'true']);
1682 echo '</span>';
1683 }
1684
1685 echo '</' . esc_attr($label_tag) . '>';
1686 echo '</div>';
1687 }
1688
1689
1690 /**
1691 * Prepare link configuration
1692 *
1693 * @param array $settings Widget settings
1694 * @return array Link configuration
1695 */
1696 private function prepare_link_config($settings) {
1697 return [
1698 'enable_links' => $settings['enable_links'] ?? 'yes',
1699 'open_in_new_tab' => $settings['open_in_new_tab'] ?? 'no'
1700 ];
1701 }
1702
1703 /**
1704 * Render single news item
1705 *
1706 * @param object $post Post object
1707 * @param array $settings Widget settings
1708 * @param array $link_config Link configuration
1709 */
1710 private function render_single_news_item($post, $settings, $link_config) {
1711 if (!$post instanceof \WP_Post) {
1712 return;
1713 }
1714
1715 $title = get_the_title($post);
1716
1717 // Security: Trim title if enabled
1718 if ($settings['title_trim_enable'] === 'yes') {
1719 // $trim_length = max(10, min(200, intval($settings['title_trim_length'] ?? 50)));
1720 // if (mb_strlen($title) > $trim_length) {
1721 // $title = mb_substr($title, 0, $trim_length) . '...';
1722 // }
1723 $title = wp_trim_words($title, $settings['title_trim_length'], '...');
1724 }
1725
1726 $url = $this->get_post_url($post);
1727
1728 echo '<span class="marqueex-news-text">';
1729
1730 if ($link_config['enable_links'] === 'yes' && !empty($url)) {
1731 echo '<a href="' . esc_url($url) . '"'
1732 . ($link_config['open_in_new_tab'] === 'yes' ? ' target="_blank" rel="noopener noreferrer"' : '')
1733 . ' class="marqueex-title-link" aria-label="' . esc_attr($title) . '">'
1734 . esc_html($title) . '</a>';
1735 } else {
1736 echo esc_html($title);
1737 }
1738
1739 echo '</span>';
1740 }
1741
1742 /**
1743 * Render news ticker content with posts and separators
1744 *
1745 * Handles the rendering of news items with proper title trimming,
1746 * link generation, and separator insertion between items.
1747 *
1748 * @param array $settings Widget settings
1749 * @param array $posts Array of post objects
1750 */
1751 private function render_ticker_content($settings, $posts = []) {
1752 // Ensure we have posts to display
1753 $posts = $this->ensure_minimum_posts($posts);
1754
1755 // Prepare link configuration
1756 $link_config = $this->prepare_link_config($settings);
1757
1758 echo '<div class="marqueex-marquee-wrapper">';
1759
1760 // Render each post with separators
1761 $total_posts = count($posts);
1762 foreach ($posts as $index => $post) {
1763 echo '<div class="marqueex-news-item">';
1764 $this->render_single_news_item($post, $settings, $link_config);
1765 echo '</div>';
1766
1767 // Only render separator if not the last item
1768 if ($index < $total_posts - 1) {
1769 $this->render_separator($settings, $post);
1770 }
1771 }
1772
1773 echo '</div>';
1774 }
1775
1776 /**
1777 * Render separator based on type
1778 *
1779 * @param array $settings Widget settings
1780 * @param WP_Post $post Current post object
1781 */
1782 private function render_separator($settings, $post) {
1783 $separator_type = $settings['separator_type'] ?? 'none';
1784
1785 if ($separator_type === 'none') {
1786 return;
1787 }
1788
1789 $separator_preset = $settings['separator_style_preset'] ?? 'modern';
1790 $separator_classes = 'marqueex-separator marqueex-separator-' . esc_attr($separator_type) . ' marqueex-separator-preset-' . esc_attr($separator_preset);
1791
1792 echo '<span class="' . esc_attr($separator_classes) . '">';
1793
1794 switch ($separator_type) {
1795 case 'icon':
1796 $separator_icon = $settings['separator_icon'] ?? [];
1797 if (!empty($separator_icon['value'])) {
1798 echo '<span class="marqueex-separator-icon">';
1799 \Elementor\Icons_Manager::render_icon($separator_icon, ['aria-hidden' => 'true']);
1800 echo '</span>';
1801 }
1802 break;
1803
1804 case 'text':
1805 $separator_text = $settings['separator_text'] ?? '|';
1806 echo '<span class="marqueex-separator-text">' . esc_html($separator_text) . '</span>';
1807 break;
1808
1809 case 'date':
1810 echo '<span class="marqueex-separator-date">' . esc_html(get_the_date('', $post)) . '</span>';
1811 break;
1812
1813 case 'featured_image':
1814 if (has_post_thumbnail($post)) {
1815 echo '<span class="marqueex-separator-feature-image">';
1816 echo get_the_post_thumbnail($post, 'thumbnail');
1817 echo '</span>';
1818 }
1819 break;
1820 }
1821
1822 echo '</span>';
1823 }
1824
1825 /**
1826 * Render separator for custom content
1827 *
1828 * @param array $settings Widget settings
1829 */
1830 private function render_separator_for_custom($settings) {
1831 $separator_type = $settings['separator_type'] ?? 'none';
1832
1833 if ($separator_type === 'none') {
1834 return;
1835 }
1836
1837 $separator_preset = $settings['separator_style_preset'] ?? 'modern';
1838 $separator_classes = 'marqueex-separator marqueex-separator-' . esc_attr($separator_type) . ' marqueex-separator-preset-' . esc_attr($separator_preset);
1839
1840 echo '<span class="' . esc_attr($separator_classes) . '">';
1841
1842 switch ($separator_type) {
1843 case 'icon':
1844 $separator_icon = $settings['separator_icon'] ?? [];
1845 if (!empty($separator_icon['value'])) {
1846 echo '<span class="marqueex-separator-icon">';
1847 \Elementor\Icons_Manager::render_icon($separator_icon, ['aria-hidden' => 'true']);
1848 echo '</span>';
1849 }
1850 break;
1851
1852 case 'text':
1853 $separator_text = $settings['separator_text'] ?? '|';
1854 echo '<span class="marqueex-separator-text">' . esc_html($separator_text) . '</span>';
1855 break;
1856
1857 case 'date':
1858 echo '<span class="marqueex-separator-date">' . esc_html(current_time('F j, Y')) . '</span>';
1859 break;
1860
1861 case 'featured_image':
1862 // For custom content, we can show a default image or skip
1863 // Since there's no post context, we'll show a placeholder or skip
1864 break;
1865 }
1866
1867 echo '</span>';
1868 }
1869
1870 /**
1871 * Render widget output on the frontend
1872 *
1873 * This method handles the complete rendering of the news ticker widget,
1874 * including label positioning, content generation, and marquee setup.
1875 */
1876 protected function render() {
1877 $settings = $this->get_settings_for_display();
1878 // Extract and prepare widget configuration
1879 $widget_config = $this->prepare_widget_config($settings);
1880
1881 $this->add_render_attribute(
1882 [
1883 'marqueex-marquee-block' => [
1884 'class' => [
1885 'marqueex-marquee-block',
1886 'marqueex-news-ticker',
1887 $settings['pause_on_hover'] === 'yes' ? 'is-paused' : '',
1888 $settings['reverse_direction'] === 'yes' ? 'is-reversed' : '',
1889 ],
1890 'data-settings' => [
1891 wp_json_encode(array_filter([
1892 "marquee_speed" => max(10, min(100, intval($settings['speed']['size'] ?? 30))),
1893 "pause_on_hover" => $settings['pause_on_hover'] === 'yes',
1894 "reverse_direction" => $settings['reverse_direction'] === 'yes',
1895 ]))
1896 ],
1897 'role' => 'region',
1898 'aria-label' => __('News Ticker', 'marqueex'),
1899 ]
1900 ]
1901 );
1902
1903 // Render the track content once and reuse it for the aria-hidden clone,
1904 // avoiding a second identical post query per widget render.
1905 ob_start();
1906 $this->render_ticker_content_by_type($settings, $widget_config);
1907 $track_html = ob_get_clean();
1908
1909 ?>
1910 <?php // get_render_attribute_string() returns markup already escaped by Elementor. ?>
1911 <div <?php echo $this->get_render_attribute_string('marqueex-marquee-block'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
1912 <?php
1913
1914 if ($settings['reverse_direction'] !== 'yes') {
1915 $this->render_label($settings);
1916 }
1917 ?>
1918 <div class="marqueex-marquee-track-wrapper">
1919 <div class="marqueex-marquee-track">
1920 <?php echo $track_html; // phpcs:ignore WordPress.Security.EscapeOutput ?>
1921 </div>
1922 <div aria-hidden="true" class="marqueex-marquee-track">
1923 <?php echo $track_html; // phpcs:ignore WordPress.Security.EscapeOutput ?>
1924 </div>
1925 </div>
1926 <?php
1927
1928 if ($settings['reverse_direction'] === 'yes') {
1929 $this->render_label($settings);
1930 }
1931 ?>
1932 </div>
1933
1934 <?php
1935 }
1936 }
1937