Uname: Linux webm012.cluster130.gra.hosting.ovh.net 5.15.167-ovh-vps-grsec-zfs-classid #1 SMP Tue Sep 17 08:14:20 UTC 2024 x86_64
Software: Apache
PHP version: 8.0.30 [ PHP INFO ] PHP os: Linux
Server Ip: 145.239.37.162
Your Ip: 216.73.216.190
User: dreampi (1009562) | Group: users (100)
Safe Mode: OFF
Disable Function:
_dyuweyrj4,_dyuweyrj4r,dl

name : timeline.js
(function ($) {
    "use strict";

    $(window).on('elementor/frontend/init', () => {
        elementorFrontend.hooks.addAction('frontend/element_ready/motiox-timeline.default', ($scope) => {

            checkScreenSize();
            window.addEventListener('resize', debounce(checkScreenSize, 200));

            function checkScreenSize() {
                const screenWidth = window.innerWidth;

                if (screenWidth > 768) {
                    initTimelineLargeScreen();
                } else {
                    initTimelineSmallScreen();
                }
            }

            function initTimelineLargeScreen() {
                setupTimeline();
                setupCircles();
                initializeItems(); // Đảm bảo gọi hàm này ngay sau khi load

                $(window).on('scroll.largeTimeline', throttle(() => {
                    updateLines();
                    updateInViewItems();
                    updateCircleColors();
                }, 100));
            }

            function initTimelineSmallScreen() {
                setupTimeline();
                setupCircles();
                initializeItems(); // Đảm bảo gọi hàm này ngay sau khi load

                $(window).on('scroll.smallTimeline', throttle(() => {
                    updateLines();
                    updateInViewItems();
                    updateCircleColors();
                }, 100));
            }

            function setupTimeline() {
                const wrapper = $('.elementor-timeline-wrapper');
                const greyLine = wrapper.find('.default-line');
                const lineToDraw = wrapper.find('.draw-line');

                if (!lineToDraw.length) return;

                const greyLineHeight = wrapper.height();

                greyLine.css({
                    height: greyLineHeight + 'px',
                    top: 15 + 'px',
                });

                lineToDraw.css({
                    maxHeight: greyLineHeight + 'px',
                    top: 15 + 'px',
                });

                // Gọi hàm khởi tạo trạng thái ẩn cho các mục
                initializeItems();
            }

            function initializeItems() {
                const items = $('.elementor-timeline-wrapper .item');
                // Ẩn tất cả các item
                items.css({
                    opacity: 0,
                }).removeClass('in-view');

                // Chỉ hiển thị item đầu tiên
                const firstItem = items.first();
                firstItem.css({
                    opacity: 1,
                    transform: 'translateY(0)',
                    transition: 'opacity 0.6s ease, transform 0.6s ease',
                }).addClass('in-view');
            }

            function setupCircles() {
                const wrapper = $('.elementor-timeline-wrapper');
                const items = wrapper.find('.item');
                const circleContainer = $('<div class="timeline-circles"></div>');

                // Remove old circles if any
                wrapper.find('.timeline-circles').remove();

                // Add a timeline circle for each item
                items.each(function () {
                    const circle = $('<div class="timeline-circle"></div>');
                    circleContainer.append(circle);
                });

                // Append the container to the wrapper
                wrapper.append(circleContainer);

                // Position circles based on items
                positionCircles(items);
            }

            function positionCircles(items) {
                const circles = $('.timeline-circle');
                items.each(function (index) {
                    const itemOffsetTop = $(this).position().top; // Lấy vị trí top của item

                    // Đặt vị trí top cho timeline-circle
                    $(circles[index]).css('top', itemOffsetTop + 'px');

                    // Tạo timeline-circle-dot nếu chưa có
                    const dot = $('<div class="timeline-circle-dot"></div>'); // Tạo dot mới
                    $(circles[index]).append(dot); // Thêm dot vào bên trong timeline-circle
                });
            }

            function updateLines() {
                const wrapper = $('.elementor-timeline-wrapper');
                const lineToDraw = wrapper.find('.draw-line');
                const items = wrapper.find('.item');

                const windowScrollTop = $(window).scrollTop();
                const windowHeightHalf = $(window).height() / 2;
                const timelineTop = wrapper.offset().top;
                const greyLineHeight = wrapper.height();

                if (windowScrollTop >= timelineTop - windowHeightHalf) {
                    let newHeight = windowScrollTop - timelineTop + windowHeightHalf;

                    lineToDraw.css({
                        height: Math.min(newHeight + 10, greyLineHeight) + 'px',
                    });
                }
            }

            function updateInViewItems() {
                const wrapper = $('.elementor-timeline-wrapper');
                const lineToDraw = wrapper.find('.draw-line');
                const items = wrapper.find('.item');

                const lineBottom = lineToDraw.offset().top + lineToDraw.outerHeight(); // Điểm cuối của đường draw-line

                items.each(function (index) {
                    const itemTop = $(this).offset().top; // Vị trí top của mục

                    // Kiểm tra nếu đường draw-line đã đi qua mục hiện tại
                    if (lineBottom >= itemTop && !$(this).hasClass('in-view')) {
                        // Chỉ hiện mục nếu draw-line vừa chạm tới và mục chưa được đánh dấu là in-view
                        $(this).addClass('in-view').css({
                            opacity: 1,
                            transform: 'translateY(0)',
                            transition: 'opacity 0.6s ease, transform 0.6s ease',
                        });
                    } else if (lineBottom < itemTop && $(this).hasClass('in-view')) {
                        // Ẩn mục nếu draw-line chưa tới hoặc đã vượt qua mốc
                        $(this).removeClass('in-view').css({
                            opacity: 0,
                            transform: 'translateY(20px)',
                            transition: 'opacity 0.6s ease, transform 0.6s ease',
                        });
                    }
                });
            }

            function updateCircleColors() {
                const wrapper = $('.elementor-timeline-wrapper');
                const lineToDraw = wrapper.find('.draw-line');
                const circles = wrapper.find('.timeline-circle');

                const lineBottom = lineToDraw.offset().top + lineToDraw.outerHeight();

                circles.each(function () {
                    const circleTop = $(this).offset().top;

                    if (lineBottom >= circleTop) {
                        $(this).addClass('active'); // Add active class to change color
                    } else {
                        $(this).removeClass('active'); // Remove active class
                    }
                });
            }

            // Utility functions: debounce and throttle
            function debounce(func, wait) {
                let timeout;
                return function (...args) {
                    clearTimeout(timeout);
                    timeout = setTimeout(() => func.apply(this, args), wait);
                };
            }

            function throttle(func, limit) {
                let lastFunc;
                let lastRan;
                return function (...args) {
                    const context = this;
                    if (!lastRan) {
                        func.apply(context, args);
                        lastRan = Date.now();
                    } else {
                        clearTimeout(lastFunc);
                        lastFunc = setTimeout(function () {
                            if (Date.now() - lastRan >= limit) {
                                func.apply(context, args);
                                lastRan = Date.now();
                            }
                        }, limit - (Date.now() - lastRan));
                    }
                };
            }

        });
    });

})(jQuery);
© 2026 GrazzMean-Shell