HEX
Server: LiteSpeed
System: Linux server.nevid-deploma.com 4.18.0-553.111.1.lve.el8.x86_64 #1 SMP Fri Mar 13 13:42:17 UTC 2026 x86_64
User: smilepac (1037)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: /home/smilepac/public_html/wp-content/plugins/codevz-plus/wpbakery/assets/js/360_degree.js
"use strict";

class ProductViewer {
    constructor(element) {
        this.element = element;
        this.handleContainer = element.querySelector(".cz_product-viewer-handle");
        this.handleFill = this.handleContainer?.querySelector(".fill");
        this.handle = this.handleContainer?.querySelector(".handle");
        this.imageWrapper = element.querySelector(".product-viewer");
        this.slideShow = this.imageWrapper?.querySelector(".product-sprite");
        
        this.frames = parseInt(element.dataset.frame) || 1;
        this.friction = parseFloat(element.dataset.friction) || 1;
        this.action = element.dataset.action;
        
        this.visibleFrame = 0;
        this.loaded = false;
        this.animating = false;
        this.xPosition = 0;

        this.loadFrames();
    }

    setTransform(el, value) {
        if (!el) return;
        el.style.transform = value;
    }

    loadFrames() {
        const imageUrl = this.slideShow?.dataset.image;
        if (!imageUrl) return;

        this.loading(0.5);
        const img = new Image();
        
        img.onload = () => {
            this.loaded = true;
        };
        
        setTimeout(() => {
            img.src = imageUrl;
        }, 50);
    }

    loading(pct) {
        this.setTransform(this.handleFill, `scaleX(${pct})`);

        setTimeout(() => {
            if (this.loaded) {
                this.element.classList.add("loaded");
                this.setTransform(this.handleFill, "scaleX(1)");
                this.dragImage();
                if (this.handle) this.dragHandle();
            } else {
                const nextPct = parseFloat(pct) + 0.1;
                if (nextPct < 1) this.loading(nextPct);
            }
        }, 500);
    }

    dragHandle() {
        const onStart = (e) => {
            e.preventDefault();
            this.handle.classList.add("cz_draggable");

            const rect = this.handleContainer.getBoundingClientRect();
            const handleWidth = this.handle.offsetWidth;
            const containerWidth = rect.width;
            const containerLeft = rect.left + window.scrollX;
            
            const minX = containerLeft - handleWidth / 2;
            const maxX = containerLeft + containerWidth - handleWidth / 2;
            const pageX = e.touches ? e.touches[0].pageX : e.pageX;

            this.xPosition = (this.handle.getBoundingClientRect().left + window.scrollX) + handleWidth - pageX;

            const onMove = (moveEvent) => {
                if (!this.animating) {
                    this.animating = true;
                    requestAnimationFrame(() => {
                        this.animateDraggedHandle(moveEvent, handleWidth, containerLeft, containerWidth, minX, maxX);
                    });
                }
            };

            const onEnd = () => {
                this.handle.classList.remove("cz_draggable");
                document.removeEventListener("mousemove", onMove);
                document.removeEventListener("touchmove", onMove);
            };

            document.addEventListener("mousemove", onMove);
            document.addEventListener("touchmove", onMove, { passive: false });
            document.addEventListener("mouseup", onEnd, { once: true });
            document.addEventListener("touchend", onEnd, { once: true });
        };

        this.handle.addEventListener("mousedown", onStart);
        this.handle.addEventListener("touchstart", onStart, { passive: false });
    }

    animateDraggedHandle(e, width, left, totalWidth, min, max) {
        const pageX = e.touches ? e.touches[0].pageX : e.pageX;
        let m = pageX + this.xPosition - width;

        m = Math.max(min, Math.min(m, max));

        const pct = Math.ceil(1000 * (m + width / 2 - left) / totalWidth) / 10;
        this.visibleFrame = Math.ceil(pct * (this.frames - 1) / 100);
        
        this.updateFrame();
        if (this.handle) this.handle.style.left = `${pct}%`;
        
        this.animating = false;
    }

    dragImage() {
        let startEvents = ["mousedown", "touchstart"];
        let endEvents = ["mouseup", "touchend"];

        if (this.action !== "drag") {
            startEvents = ["mouseenter", "touchstart"];
            endEvents = ["mouseleave", "touchend"];
        }

        const onStart = (e) => {
            this.slideShow.classList.add("cz_draggable");
            const rect = this.imageWrapper.getBoundingClientRect();
            const wrapperLeft = rect.left + window.scrollX;
            const wrapperWidth = rect.width;
            
            this.xPosition = e.touches ? e.touches[0].pageX : e.pageX;

            const onMove = (moveEvent) => {
                if (!this.animating) {
                    this.animating = true;
                    requestAnimationFrame(() => {
                        this.animateDraggedImage(moveEvent, wrapperLeft, wrapperWidth);
                    });
                }
            };

            const onEnd = () => {
                this.slideShow.classList.remove("cz_draggable");
                this.element.removeEventListener("mousemove", onMove);
                this.element.removeEventListener("touchmove", onMove);
                this.updateHandle();
            };

            this.element.addEventListener("mousemove", onMove);
            this.element.addEventListener("touchmove", onMove, { passive: false });
            
            endEvents.forEach(evt => {
                this.element.addEventListener(evt, onEnd, { once: true });
            });
        };

        startEvents.forEach(evt => {
            this.slideShow.addEventListener(evt, onStart, { passive: evt === 'touchstart' ? false : true });
        });
    }

    animateDraggedImage(e, left, width) {
        const pageX = e.touches ? e.touches[0].pageX : e.pageX;
        const deltaX = this.xPosition - pageX;
        
        let shift = Math.ceil((100 * deltaX) / (width * this.friction)) * (this.frames - 1) / 100;
        shift = shift > 0 ? Math.floor(shift) : Math.ceil(shift);

        let nextFrame = this.visibleFrame + shift;

        if (nextFrame < 0) nextFrame = this.frames - 1;
        else if (nextFrame > this.frames - 1) nextFrame = 0;

        if (nextFrame !== this.visibleFrame) {
            this.visibleFrame = nextFrame;
            this.updateFrame();
            this.xPosition = pageX;
        }
        this.animating = false;
    }

    updateHandle() {
        if (this.handle) {
            const pct = (100 * this.visibleFrame) / this.frames;
            this.handle.style.transition = "left 0.2s";
            this.handle.style.left = `${pct}%`;
            setTimeout(() => { this.handle.style.transition = ""; }, 200);
        }
    }

    updateFrame() {
        const pct = (-100 * this.visibleFrame) / this.frames;
        this.setTransform(this.slideShow, `translateX(${pct}%)`);
    }
}

Codevz_Plus.r360degree = function() {

    document.querySelectorAll(".cz_product-viewer-wrapper:not([data-init])").forEach( el => {
        el.dataset.init = "1";

        setTimeout(() => {
            new ProductViewer( el );
        }, 500);
    });

};

Codevz_Plus.r360degree();