PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.83
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.83
51.1.84 51.1.85 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 All 39 releases
← All changes | includes/widgets/Timeline/script.js +310 -0 51.1.251.1.83 View file →
@@ -1,0 +1,310 @@
1 +"use strict";
2 +(function ($) {
3 + $(window).on('elementor/frontend/init', () => {
4 + elementorFrontend.hooks.addAction(
5 + 'frontend/element_ready/king-addons-timeline.default',
6 + ($scope) => {
7 + elementorFrontend.elementsHandler.addHandler(
8 + elementorModules.frontend.handlers.Base.extend({
9 + onInit() {
10 + const $el = this.$element;
11 +
12 + // Helper to safely find elements or return an empty jQuery object
13 + const findOrEmpty = (sel) => {
14 + const found = $el.find(sel);
15 + return found.length ? found : $();
16 + };
17 +
18 + let iScrollTarget = findOrEmpty('.king-addons-timeline-centered'),
19 + element = iScrollTarget,
20 + pagination = findOrEmpty('.king-addons-grid-pagination'),
21 + middleLine = findOrEmpty('.king-addons-middle-line'),
22 + timelineFill = findOrEmpty('.king-addons-timeline-fill'),
23 + lastIcon = findOrEmpty('.king-addons-main-line-icon.king-addons-icon:last'),
24 + firstIcon = findOrEmpty('.king-addons-main-line-icon.king-addons-icon').first(),
25 + scopeClass = '.elementor-element-' + $el.attr('data-id'),
26 + aosOffset = +findOrEmpty('.king-addons-story-info-vertical').attr('data-animation-offset') || 0,
27 + aosDuration = +findOrEmpty('.king-addons-story-info-vertical').attr('data-animation-duration') || 0;
28 +
29 + // Remove "left aligned" class on smaller screens
30 + const removeLeftAlignedClass = () => {
31 + if ($el.find('.king-addons-centered').length) {
32 + if (window.innerWidth <= 767) {
33 + $el.find('.king-addons-wrapper .king-addons-timeline-centered')
34 + .removeClass('king-addons-both-sided-timeline')
35 + .addClass('king-addons-one-sided-timeline king-addons-remove-one-sided-later');
36 + $el.find('.king-addons-wrapper .king-addons-left-aligned')
37 + .removeClass('king-addons-left-aligned')
38 + .addClass('king-addons-right-aligned king-addons-remove-right-aligned-later');
39 + } else {
40 + $el.find('.king-addons-wrapper .king-addons-timeline-centered.king-addons-remove-one-sided-later')
41 + .removeClass('king-addons-one-sided-timeline king-addons-remove-one-sided-later')
42 + .addClass('king-addons-both-sided-timeline');
43 + $el.find('.king-addons-wrapper .king-addons-remove-right-aligned-later')
44 + .removeClass('king-addons-right-aligned king-addons-remove-right-aligned-later')
45 + .addClass('king-addons-left-aligned');
46 + }
47 + }
48 + };
49 +
50 + // Fill the timeline line as the user scrolls
51 + const postsTimelineFill = (last, first) => {
52 + if (!timelineFill.length) return;
53 +
54 + // If first item is preceded by a year label, use that label instead
55 + if ($el.find('.king-addons-timeline-entry:eq(0)').prev('.king-addons-year-wrap').length) {
56 + first = $el.find('.king-addons-year-label').eq(0);
57 + }
58 +
59 + const fillHeight = parseFloat(timelineFill.css('height')),
60 + docScrollTop = document.documentElement.scrollTop,
61 + clientHeightHalf = document.documentElement.clientHeight / 2,
62 + offsetFirst = first.offset().top,
63 + offsetLast = last.offset().top,
64 + lastHeight = parseFloat(last.css('height'));
65 +
66 + // Only update fill if we haven't scrolled past the last icon
67 + if ((docScrollTop + clientHeightHalf - offsetFirst) < (offsetLast - offsetFirst + lastHeight)) {
68 + timelineFill.css(
69 + 'height',
70 + (docScrollTop + clientHeightHalf - offsetFirst) + 'px'
71 + );
72 + }
73 +
74 + // Highlight icons if they are "behind" the fill
75 + $el.find('.king-addons-main-line-icon.king-addons-icon').each(function () {
76 + const $icon = $(this);
77 + $icon.toggleClass(
78 + 'king-addons-change-border-color',
79 + $icon.offset().top < offsetFirst + fillHeight
80 + );
81 + });
82 + };
83 +
84 + // Adjust the vertical line in the center (and its fill) to match icons' positions
85 + const adjustMiddleLineHeight = (midLine, tFill, last, first, el) => {
86 + if (
87 + !$el.find('.king-addons-both-sided-timeline').length &&
88 + !$el.find('.king-addons-one-sided-timeline').length &&
89 + !$el.find('.king-addons-one-sided-timeline-left').length
90 + ) {
91 + return;
92 + }
93 + if ($el.find('.king-addons-timeline-entry:eq(0)').prev('.king-addons-year-wrap').length) {
94 + first = $el.find('.king-addons-year-label').eq(0);
95 + }
96 +
97 + const offsetEl = el.offset().top,
98 + offsetFirst = first.offset().top,
99 + offsetLast = last.offset().top,
100 + lastHeight = parseFloat(last.css('height')),
101 + topPos = (offsetFirst - offsetEl) + 'px',
102 + heightVal = (offsetLast - offsetFirst + lastHeight);
103 +
104 + midLine.css({
105 + top: topPos,
106 + height: heightVal
107 + });
108 + if (tFill.length) tFill.css('top', topPos);
109 + };
110 +
111 + // Only proceed if we have a timeline
112 + if (iScrollTarget.length) {
113 + // Combine redundant window resize bindings
114 + $(window).on('resize smartresize', () => {
115 + removeLeftAlignedClass();
116 + adjustMiddleLineHeight(middleLine, timelineFill, lastIcon, firstIcon, element);
117 + });
118 +
119 + // Fire alignment and size adjustments with a short delay
120 + setTimeout(() => {
121 + removeLeftAlignedClass();
122 + $(window).trigger('resize');
123 + }, 500);
124 +
125 + setTimeout(() => {
126 + adjustMiddleLineHeight(middleLine, timelineFill, lastIcon, firstIcon, element);
127 + $(window).trigger('resize');
128 + }, 500);
129 +
130 + // Hide pagination if not "load-more"
131 + if (iScrollTarget.attr('data-pagination') !== 'load-more') {
132 + pagination.css('visibility', 'hidden');
133 + }
134 +
135 + // Initialize AOS
136 + // noinspection JSUnresolvedReference
137 + AOS.init({
138 + offset: aosOffset,
139 + duration: aosDuration,
140 + once: true,
141 + });
142 +
143 + // Fill the timeline on scroll
144 + postsTimelineFill(lastIcon, firstIcon);
145 + $(window).on('scroll', () => postsTimelineFill(lastIcon, firstIcon));
146 +
147 + // Infinite scroll / load-more logic
148 + // noinspection JSJQueryEfficiency
149 + if (
150 + !$el.find('.elementor-repeater-items').length &&
151 + !$('body').hasClass('elementor-editor-active') &&
152 + (iScrollTarget.data('pagination') === 'load-more' ||
153 + iScrollTarget.data('pagination') === 'infinite-scroll')
154 + ) {
155 + const threshold =
156 + iScrollTarget.data('pagination') === 'load-more' ? false : 10;
157 +
158 + // noinspection JSUnresolvedReference
159 + iScrollTarget.infiniteScroll({
160 + path: scopeClass + ' .king-addons-grid-pagination a',
161 + hideNav: false,
162 + append: scopeClass + '.king-addons-timeline-entry',
163 + history: false,
164 + scrollThreshold: threshold,
165 + status: scopeClass + ' .page-load-status',
166 + });
167 +
168 + iScrollTarget.on('request.infiniteScroll', () => {
169 + $el.find('.king-addons-load-more-btn').hide();
170 + $el.find('.king-addons-pagination-loading').css('display', 'inline-block');
171 + });
172 +
173 + let pagesLoaded = 0;
174 + iScrollTarget.on('load.infiniteScroll', (event, response) => {
175 + pagesLoaded++;
176 + const items = $(response).find(scopeClass).find('.king-addons-timeline-entry');
177 + // noinspection JSUnresolvedReference
178 + iScrollTarget.infiniteScroll('appendItems', items);
179 +
180 + // Re-align new items
181 + if (
182 + !$el.find('.king-addons-one-sided-timeline').length &&
183 + !$el.find('.king-addons-one-sided-timeline-left').length
184 + ) {
185 + $el.find('.king-addons-timeline-entry').each(function (idx) {
186 + const $entry = $(this);
187 + $entry.removeClass('king-addons-right-aligned king-addons-left-aligned');
188 + if (idx % 2 === 0) {
189 + $entry.addClass('king-addons-left-aligned');
190 + $entry
191 + .find('.king-addons-story-info-vertical')
192 + .attr('data-aos', $entry.find('.king-addons-story-info-vertical').attr('data-aos-left'));
193 + } else {
194 + $entry.addClass('king-addons-right-aligned');
195 + $entry
196 + .find('.king-addons-story-info-vertical')
197 + .attr('data-aos', $entry.find('.king-addons-story-info-vertical').attr('data-aos-right'));
198 + }
199 + });
200 + }
201 +
202 + // Re-init AOS for newly added items
203 + // noinspection JSUnresolvedReference
204 + AOS.init({ offset: aosOffset, duration: aosDuration, once: true });
205 + $(window).scroll();
206 + $el.find('.king-addons-pagination-loading').hide();
207 +
208 + // Show load more button or finish message
209 + if (iScrollTarget.data('max-pages') - 1 !== pagesLoaded) {
210 + if (iScrollTarget.attr('data-pagination') === 'load-more') {
211 + $el.find('.king-addons-load-more-btn').fadeIn();
212 + }
213 + } else {
214 + $el.find('.king-addons-pagination-finish').fadeIn(1000);
215 + pagination.delay(2000).fadeOut(1000);
216 + }
217 +
218 + // Update references after new items are appended
219 + middleLine = $el.find('.king-addons-middle-line');
220 + timelineFill = $el.find(".king-addons-timeline-fill");
221 + lastIcon = $el.find('.king-addons-main-line-icon.king-addons-icon:last');
222 + firstIcon = $el.find('.king-addons-main-line-icon.king-addons-icon').first();
223 + element = $el.find('.king-addons-timeline-centered');
224 +
225 + adjustMiddleLineHeight(middleLine, timelineFill, lastIcon, firstIcon, element);
226 + $(window).trigger('resize');
227 + postsTimelineFill(lastIcon, firstIcon);
228 + });
229 +
230 + // Manual load more
231 + if (!$('body').hasClass('elementor-editor-active')) {
232 + $el.find('.king-addons-load-more-btn').on('click', () => {
233 + // noinspection JSUnresolvedReference
234 + iScrollTarget.infiniteScroll('loadNextPage');
235 + return false;
236 + });
237 + if (iScrollTarget.attr('data-pagination') === 'infinite-scroll') {
238 + // noinspection JSUnresolvedReference
239 + iScrollTarget.infiniteScroll('loadNextPage');
240 + }
241 + }
242 + }
243 + }
244 +
245 + // Swiper logic (horizontal timeline)
246 + if ($el.find('.swiper-wrapper').length) {
247 + const swiperLoader = (swiperElement, swiperConfig) => {
248 + const asyncSwiper = elementorFrontend.utils.swiper;
249 + return new asyncSwiper(swiperElement, swiperConfig).then(
250 + (instance) => instance
251 + );
252 + };
253 +
254 + // Identify correct horizontal selector
255 + const horizontal = $el.find('.king-addons-horizontal-bottom').length
256 + ? '.king-addons-horizontal-bottom'
257 + : '.king-addons-horizontal';
258 + const swiperSlider = $el.find(horizontal + ".swiper");
259 + const slidesToShow = swiperSlider.data("slidestoshow");
260 +
261 + swiperLoader(swiperSlider, {
262 + spaceBetween: +swiperSlider.data('swiper-space-between'),
263 + loop: swiperSlider.data('loop') === 'yes',
264 + autoplay:
265 + swiperSlider.data("autoplay") === 'yes'
266 + ? {
267 + delay: +swiperSlider.attr('data-swiper-delay'),
268 + disableOnInteraction: false,
269 + pauseOnMouseEnter:
270 + swiperSlider.data('swiper-poh') === 'yes',
271 + }
272 + : false,
273 + on: {
274 + init: () => {
275 + const timelineOuter = $el.find('.king-addons-timeline-outer-container');
276 + if (timelineOuter.length) timelineOuter.css('opacity', 1);
277 + },
278 + },
279 + speed: +swiperSlider.attr('data-swiper-speed'),
280 + slidesPerView: slidesToShow,
281 + direction: 'horizontal',
282 + pagination: {
283 + el: '.king-addons-swiper-pagination',
284 + type: 'progressbar',
285 + },
286 + navigation: {
287 + nextEl: '.king-addons-button-next',
288 + prevEl: '.king-addons-button-prev',
289 + },
290 + breakpoints: {
291 + 320: { slidesPerView: 1 },
292 + 480: { slidesPerView: 2 },
293 + 769: { slidesPerView: slidesToShow },
294 + },
295 + });
296 + } else {
297 + // If no slider, just make timeline visible
298 + $(document).ready(() => {
299 + const timelineOuter = $el.find('.king-addons-timeline-outer-container');
300 + if (timelineOuter.length) timelineOuter.css('opacity', 1);
301 + });
302 + }
303 + },
304 + }),
305 + { $element: $scope }
306 + );
307 + }
308 + );
309 + });
310 +})(jQuery);