PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.78
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.78
51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / widgets / Content_Ticker / script.js

script.js in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.78, at includes/widgets/Content_Ticker/script.js

279 lines 15.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 "use strict";
2 (function ($) {
3 $(window).on("elementor/frontend/init", function () {
4 elementorFrontend.hooks.addAction(
5 "frontend/element_ready/king-addons-content-ticker.default",
6 function ($scope) {
7 // Register a new handler extending Elementor’s Base handler
8 elementorFrontend.elementsHandler.addHandler(
9 elementorModules.frontend.handlers.Base.extend({
10 onInit() {
11 const $element = this.$element;
12
13 const $contentTickerSlider = $element.find(".king-addons-ticker-slider"),
14 $contentTickerMarquee = $element.find(".king-addons-ticker-marquee"),
15 marqueeData = $contentTickerMarquee.data("options"),
16 sliderClass = $element.attr("class"),
17 dataSlideEffect = $contentTickerSlider.attr("data-slide-effect");
18
19 // Helper to capture a single digit at the end of a matched pattern
20 const getClassNumber = (regex, fallback) => {
21 const match = sliderClass.match(regex);
22 return match ? parseInt(match[1], 10) : fallback;
23 };
24
25 // Determine how many columns to use in different breakpoints
26 const sliderColumnsDesktop = getClassNumber(/king-addons-ticker-slider-columns-(\d)/, 2);
27 const sliderColumnsWideScreen = getClassNumber(/columns--widescreen(\d)/, sliderColumnsDesktop);
28 const sliderColumnsLaptop = getClassNumber(/columns--laptop(\d)/, sliderColumnsDesktop);
29 const sliderColumnsTablet = getClassNumber(/columns--tablet(\d)/, 2);
30 const sliderColumnsTabletExtra = getClassNumber(/columns--tablet_extra(\d)/, sliderColumnsTablet);
31 const sliderColumnsMobile = getClassNumber(/columns--mobile(\d)/, 1);
32 // Mobile extra should fall back to mobile, not tablet.
33 const sliderColumnsMobileExtra = getClassNumber(/columns--mobile_extra(\d)/, sliderColumnsMobile);
34
35 // Slides to scroll logic if the slider effect is "hr-slide"
36 const sliderSlidesToScroll =
37 dataSlideEffect === "hr-slide"
38 ? getClassNumber(/king-addons-ticker-slides-to-scroll-(\d)/, 1)
39 : 1;
40
41 // Check if slide effect is typing or fade
42 const isTypingOrFade = dataSlideEffect === "typing" || dataSlideEffect === "fade";
43
44 // Short helper to decide slidesToShow and slidesToScroll
45 const getSlidesToShow = columns => (isTypingOrFade ? 1 : columns);
46 const getSlidesToScroll = columns => (sliderSlidesToScroll > columns ? 1 : sliderSlidesToScroll);
47
48 const isMeasurable = ($el) => {
49 if (!$el || !$el.length) {
50 return false;
51 }
52 // :visible can be true even with width 0 depending on CSS.
53 return $el.is(":visible") && $el.outerWidth() > 0;
54 };
55
56 // Slick settings with breakpoints
57 const normalizeBool = (value) => {
58 return value === true || value === 1 || value === "1" || value === "true" || value === "yes";
59 };
60
61 const slickOptionsFromAttr = (() => {
62 const raw = $contentTickerSlider.attr("data-slick-options");
63 if (!raw) {
64 return {};
65 }
66
67 try {
68 const parsed = JSON.parse(raw);
69 if (parsed && typeof parsed === "object") {
70 // Normalize Elementor switcher values.
71 if ("pauseOnHover" in parsed) {
72 parsed.pauseOnHover = normalizeBool(parsed.pauseOnHover);
73 }
74 if ("autoplay" in parsed) {
75 parsed.autoplay = normalizeBool(parsed.autoplay);
76 }
77 if ("infinite" in parsed) {
78 parsed.infinite = normalizeBool(parsed.infinite);
79 }
80 if ("rtl" in parsed) {
81 parsed.rtl = normalizeBool(parsed.rtl);
82 }
83 if ("arrows" in parsed) {
84 parsed.arrows = normalizeBool(parsed.arrows);
85 }
86 if ("vertical" in parsed) {
87 parsed.vertical = normalizeBool(parsed.vertical);
88 }
89
90 if ("autoplaySpeed" in parsed) {
91 parsed.autoplaySpeed = parseInt(parsed.autoplaySpeed, 10) || 0;
92 }
93 if ("speed" in parsed) {
94 parsed.speed = parseInt(parsed.speed, 10) || 0;
95 }
96
97 return parsed;
98 }
99 } catch (e) {
100 // Ignore invalid JSON.
101 }
102 return {};
103 })();
104
105 const initOrRefreshSlick = () => {
106 if (!$contentTickerSlider.length) {
107 return;
108 }
109
110 // If initialized while hidden, Slick can calculate wrong widths.
111 // Re-init only when element has a measurable width.
112 if (!isMeasurable($contentTickerSlider)) {
113 return;
114 }
115
116 // Prevent duplicate initialization when Elementor re-renders.
117 if ($contentTickerSlider.hasClass("slick-initialized")) {
118 try {
119 $contentTickerSlider.slick("setPosition");
120 } catch (e) {
121 // ignore
122 }
123 return;
124 }
125
126 $contentTickerSlider.slick({
127 ...slickOptionsFromAttr,
128 appendArrows: $element.find(".king-addons-ticker-slider-controls"),
129 // Base (>= 2400px) uses widescreen columns when provided.
130 slidesToShow: getSlidesToShow(sliderColumnsWideScreen),
131 slidesToScroll: getSlidesToScroll(sliderColumnsWideScreen),
132 fade: isTypingOrFade,
133 responsive: [
134 {
135 breakpoint: 2399,
136 settings: {
137 slidesToShow: getSlidesToShow(sliderColumnsDesktop),
138 slidesToScroll: getSlidesToScroll(sliderColumnsDesktop),
139 fade: isTypingOrFade,
140 },
141 },
142 {
143 breakpoint: 1221,
144 settings: {
145 slidesToShow: getSlidesToShow(sliderColumnsLaptop),
146 slidesToScroll: getSlidesToScroll(sliderColumnsLaptop),
147 fade: isTypingOrFade,
148 },
149 },
150 {
151 breakpoint: 1200,
152 settings: {
153 slidesToShow: getSlidesToShow(sliderColumnsTabletExtra),
154 slidesToScroll: getSlidesToScroll(sliderColumnsTabletExtra),
155 fade: isTypingOrFade,
156 },
157 },
158 {
159 breakpoint: 1024,
160 settings: {
161 slidesToShow: getSlidesToShow(sliderColumnsTablet),
162 slidesToScroll: getSlidesToScroll(sliderColumnsTablet),
163 fade: isTypingOrFade,
164 },
165 },
166 {
167 breakpoint: 880,
168 settings: {
169 slidesToShow: getSlidesToShow(sliderColumnsMobileExtra),
170 slidesToScroll: getSlidesToScroll(sliderColumnsMobileExtra),
171 fade: isTypingOrFade,
172 },
173 },
174 {
175 breakpoint: 768,
176 settings: {
177 slidesToShow: getSlidesToShow(sliderColumnsMobile),
178 slidesToScroll: getSlidesToScroll(sliderColumnsMobile),
179 fade: isTypingOrFade,
180 },
181 },
182 ],
183 });
184
185 // Force a layout pass right after init.
186 try {
187 $contentTickerSlider.slick("setPosition");
188 } catch (e) {
189 // ignore
190 }
191 };
192
193 const retryInit = (attempt = 0) => {
194 if (attempt > 40) {
195 return;
196 }
197 initOrRefreshSlick();
198 if (!$contentTickerSlider.hasClass("slick-initialized")) {
199 setTimeout(() => retryInit(attempt + 1), 50);
200 }
201 };
202
203 // If slider is already initialized (e.g. rendered by cached HTML), ensure it positions correctly.
204 if ($contentTickerSlider.hasClass("slick-initialized")) {
205 try {
206 $contentTickerSlider.slick("setPosition");
207 } catch (e) {
208 // ignore
209 }
210 } else {
211 // Try immediately, then retry while the widget becomes visible.
212 retryInit(0);
213 }
214
215 // Refresh on image load (common cause of wrong widths on frontend).
216 $contentTickerSlider.find("img").one("load", function () {
217 if ($contentTickerSlider.hasClass("slick-initialized")) {
218 try {
219 $contentTickerSlider.slick("setPosition");
220 } catch (e) {
221 // ignore
222 }
223 } else {
224 retryInit(0);
225 }
226 }).each(function () {
227 if (this.complete) {
228 $(this).trigger("load");
229 }
230 });
231
232 // Also refresh on global events.
233 $(window).on("load resize orientationchange", function () {
234 if ($contentTickerSlider.hasClass("slick-initialized")) {
235 try {
236 $contentTickerSlider.slick("setPosition");
237 } catch (e) {
238 // ignore
239 }
240 } else {
241 retryInit(0);
242 }
243 });
244
245 document.addEventListener("visibilitychange", function () {
246 if (!document.hidden) {
247 if ($contentTickerSlider.hasClass("slick-initialized")) {
248 try {
249 $contentTickerSlider.slick("setPosition");
250 } catch (e) {
251 // ignore
252 }
253 } else {
254 retryInit(0);
255 }
256 }
257 });
258
259 // Initialize marquee
260 if ($contentTickerMarquee.length && marqueeData) {
261 $contentTickerMarquee.marquee(marqueeData);
262 }
263
264 // If marquee is hidden, remove the hidden class
265 if ($element.find(".king-addons-marquee-hidden").length) {
266 $element
267 .find(".king-addons-ticker-marquee")
268 .removeClass("king-addons-marquee-hidden");
269 }
270 },
271 }),
272 {
273 $element: $scope,
274 }
275 );
276 }
277 );
278 });
279 })(jQuery);