# blockenberg/2.0.7/blocks/testimonials/frontend.js

Blockenberg — 600+ Advanced Gutenberg Blocks &amp; AI Agent for WordPress Block Editor, version 2.0.7. 319 lines.

- Page: https://pluginprobe.com/plugins/blockenberg/2.0.7/code/blocks/testimonials/frontend.js
- Raw: https://pluginprobe.com/plugins/blockenberg/2.0.7/raw/blocks/testimonials/frontend.js
- Modified: 2026-03-06T12:32:40+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/blockenberg/2.0.7/code/blocks/testimonials/frontend.js#L10-L20`.

```javascript
(function () {
    function init(root) {
        if (!root) return;
        var track = root.querySelector('.bkbg-ts-carousel-track');
        if (!track) return;

        var infiniteEnabled = root.getAttribute('data-bkbg-infinite') !== '0';
        var autoScrollEnabled = root.getAttribute('data-bkbg-autoscroll') === '1';
        var autoScrollSpeed = parseFloat(root.getAttribute('data-bkbg-autoscroll-speed') || '40');
        if (!isFinite(autoScrollSpeed) || autoScrollSpeed < 0) autoScrollSpeed = 40;
        var autoScrollPauseOnHover = root.getAttribute('data-bkbg-autoscroll-hover') !== '0';

        var dots = root.querySelectorAll('[data-bkbg-dot]');
        var progressBar = root.querySelector('.bkbg-ts-progress-bar');
        var fractionCurrent = root.querySelector('[data-bkbg-fraction="current"]');
        var fractionTotal = root.querySelector('[data-bkbg-fraction="total"]');

        var baseCount = 0;
        var didClone = false;
        var isNormalizing = false;
        var segmentStart = 0;
        var segmentWidth = 0;

        function disableTabbing(el) {
            try {
                var focusables = el.querySelectorAll('a, button, input, textarea, select');
                for (var i = 0; i < focusables.length; i++) {
                    focusables[i].setAttribute('tabindex', '-1');
                }
            } catch (e) { }
        }

        function setupInfinite() {
            if (!infiniteEnabled) return;
            var slides = getSlides();
            baseCount = slides.length;
            if (baseCount < 2) return;
            if (didClone) return;

            var originals = Array.prototype.slice.call(slides);
            var before = [];
            var after = [];
            for (var i = 0; i < originals.length; i++) {
                var c1 = originals[i].cloneNode(true);
                c1.classList.add('is-clone');
                c1.setAttribute('aria-hidden', 'true');
                disableTabbing(c1);
                after.push(c1);

                var c2 = originals[i].cloneNode(true);
                c2.classList.add('is-clone');
                c2.setAttribute('aria-hidden', 'true');
                disableTabbing(c2);
                before.push(c2);
            }

            for (var j = before.length - 1; j >= 0; j--) {
                track.insertBefore(before[j], track.firstChild);
            }
            for (var k = 0; k < after.length; k++) {
                track.appendChild(after[k]);
            }

            didClone = true;
            window.requestAnimationFrame(function () {
                measureInfinite(true);
                syncIndicators();
            });
        }

        function measureInfinite(recenter) {
            if (!infiniteEnabled || !didClone || !baseCount) return;
            var slides = getSlides();
            if (!slides || slides.length < baseCount * 3) return;
            var middleFirst = slides[baseCount];
            var afterFirst = slides[baseCount * 2];
            if (!middleFirst || !afterFirst) return;

            var nextStart = middleFirst.offsetLeft;
            var nextWidth = afterFirst.offsetLeft - middleFirst.offsetLeft;
            if (!isFinite(nextStart) || !isFinite(nextWidth) || nextWidth <= 0) return;

            if (recenter) {
                var local = track.scrollLeft - segmentStart;
                if (segmentWidth > 0) {
                    local = local % segmentWidth;
                    if (local < 0) local += segmentWidth;
                } else {
                    local = 0;
                }
                segmentStart = nextStart;
                segmentWidth = nextWidth;
                track.scrollLeft = segmentStart + local;
            } else {
                segmentStart = nextStart;
                segmentWidth = nextWidth;
            }
        }

        function normalizeInfiniteScroll() {
            if (!infiniteEnabled || !didClone) return;
            if (!segmentWidth || !isFinite(segmentWidth)) {
                measureInfinite(false);
            }
            if (!segmentWidth) return;

            if (track.scrollLeft < (segmentStart - 1) || track.scrollLeft >= (segmentStart + segmentWidth + 1)) {
                var local = track.scrollLeft - segmentStart;
                local = local % segmentWidth;
                if (local < 0) local += segmentWidth;
                isNormalizing = true;
                track.scrollLeft = segmentStart + local;
                isNormalizing = false;
            }
        }

        function getSlides() {
            return track.querySelectorAll('.bkbg-ts-slide');
        }

        function getCurrentIndex() {
            var slides = getSlides();
            if (!slides.length) return 0;

            var trackRect = track.getBoundingClientRect();
            var currentIndex = 0;
            for (var i = 0; i < slides.length; i++) {
                var r = slides[i].getBoundingClientRect();
                if (r.left >= trackRect.left - 10) {
                    currentIndex = i;
                    break;
                }
            }
            return currentIndex;
        }

        function scrollToIndex(index) {
            var slides = getSlides();
            if (!slides.length) return;
            var i = Math.max(0, Math.min(slides.length - 1, index));
            slides[i].scrollIntoView({ behavior: 'smooth', inline: 'start', block: 'nearest' });
        }

        function scrollToBaseIndex(index) {
            if (!infiniteEnabled || !didClone || !baseCount) {
                scrollToIndex(index);
                return;
            }
            var slides = getSlides();
            if (!slides.length) return;
            var idx = index % baseCount;
            if (idx < 0) idx += baseCount;
            var target = baseCount + idx;
            if (target < 0) target = 0;
            if (target > slides.length - 1) target = slides.length - 1;
            slides[target].scrollIntoView({ behavior: 'smooth', inline: 'start', block: 'nearest' });
        }

        function syncIndicators() {
            var slides = getSlides();
            if (!slides.length) return;

            var idx = getCurrentIndex();
            var total = slides.length;
            if (infiniteEnabled && didClone && baseCount) {
                total = baseCount;
                idx = idx % baseCount;
                if (idx < 0) idx += baseCount;
            }

            if (dots && dots.length) {
                for (var i = 0; i < dots.length; i++) {
                    if (i === idx) dots[i].classList.add('is-active');
                    else dots[i].classList.remove('is-active');
                }
            }

            if (progressBar) {
                var pct = 0;
                if (infiniteEnabled && didClone) {
                    if (!segmentWidth) measureInfinite(false);
                    var local = track.scrollLeft - segmentStart;
                    if (segmentWidth > 0) {
                        local = local % segmentWidth;
                        if (local < 0) local += segmentWidth;
                    }
                    var maxLocal = segmentWidth - track.clientWidth;
                    pct = maxLocal > 0 ? (local / maxLocal) * 100 : 0;
                } else {
                    var max = track.scrollWidth - track.clientWidth;
                    pct = max > 0 ? (track.scrollLeft / max) * 100 : 0;
                }
                if (pct < 0) pct = 0;
                if (pct > 100) pct = 100;
                progressBar.style.width = pct + '%';
            }

            if (fractionCurrent) fractionCurrent.textContent = String(idx + 1);
            if (fractionTotal) fractionTotal.textContent = String(total);
        }

        function scrollByOne(dir) {
            var slides = getSlides();
            if (!slides.length) return;

            var currentIndex = getCurrentIndex();
            scrollToIndex(currentIndex + dir);
        }

        var prev = root.querySelector('[data-bkbg-nav="prev"], [data-ts-nav="prev"]');
        var next = root.querySelector('[data-bkbg-nav="next"], [data-ts-nav="next"]');
        if (prev) prev.addEventListener('click', function (e) { e.preventDefault(); scrollByOne(-1); });
        if (next) next.addEventListener('click', function (e) { e.preventDefault(); scrollByOne(1); });

        if (dots && dots.length) {
            for (var i = 0; i < dots.length; i++) {
                (function (btn) {
                    btn.addEventListener('click', function (e) {
                        e.preventDefault();
                        var idx = parseInt(btn.getAttribute('data-bkbg-dot') || '0', 10);
                        scrollToBaseIndex(idx);
                    });
                })(dots[i]);
            }
        }

        // Auto-scroll (continuous)
        var rafId = null;
        var lastTs = 0;
        var autoPaused = false;
        var originalSnap = track.style.scrollSnapType;

        function tick(ts) {
            if (!autoScrollEnabled) return;
            if (!lastTs) lastTs = ts;
            var dt = ts - lastTs;
            lastTs = ts;
            if (!document.hidden && !autoPaused) {
                track.scrollLeft += (autoScrollSpeed * dt) / 1000;
                normalizeInfiniteScroll();
                syncIndicators();
            }
            rafId = window.requestAnimationFrame(tick);
        }

        function startAutoScroll() {
            if (!autoScrollEnabled) return;
            if (rafId) return;
            track.style.scrollSnapType = 'none';
            lastTs = 0;
            rafId = window.requestAnimationFrame(tick);
        }

        function stopAutoScroll() {
            if (rafId) {
                window.cancelAnimationFrame(rafId);
                rafId = null;
            }
            track.style.scrollSnapType = originalSnap;
            lastTs = 0;
        }

        if (autoScrollEnabled) {
            startAutoScroll();
            if (autoScrollPauseOnHover) {
                root.addEventListener('mouseenter', function () { autoPaused = true; });
                root.addEventListener('mouseleave', function () { autoPaused = false; });
            }
            document.addEventListener('visibilitychange', function () {
                if (document.hidden) stopAutoScroll();
                else startAutoScroll();
            });
        }

        setupInfinite();

        try {
            var imgs = track.querySelectorAll('img');
            for (var ii = 0; ii < imgs.length; ii++) {
                (function (img) {
                    if (!img) return;
                    if (img.complete) return;
                    img.addEventListener('load', function () {
                        window.requestAnimationFrame(function () {
                            measureInfinite(true);
                            syncIndicators();
                        });
                    }, { passive: true });
                })(imgs[ii]);
            }
        } catch (e) { }

        window.addEventListener('resize', function () {
            window.requestAnimationFrame(function () {
                measureInfinite(true);
                syncIndicators();
            });
        }, { passive: true });

        syncIndicators();
        track.addEventListener('scroll', function () {
            normalizeInfiniteScroll();
            if (isNormalizing) return;
            syncIndicators();
        }, { passive: true });
    }

    function boot() {
        var blocks = document.querySelectorAll('.wp-block-blockenberg-testimonials');
        for (var i = 0; i < blocks.length; i++) init(blocks[i]);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', boot);
    } else {
        boot();
    }
})();

```
