Dynamic Inline Quick View Grid

Google Pixel 8 pro

Original price was: $960.00.Current price is: $580.00.

Apple iPhone 13 pro

$75.00

SAMSUNG S24 Ultra

Price range: $250.00 through $1,350.00

Nothing Phone 2

Original price was: $960.00.Current price is: $580.00.

LG Velvet

$10.99

OnePlus 12R

$679.00

Sony Xperia 1 V 5G

Original price was: $50.00.Current price is: $25.00.

Huawei P30 Pro

Original price was: $960.00.Current price is: $580.00.

Xiaomi Redmi Note 13

Original price was: $960.00.Current price is: $580.00.

/* active style */
.quick-view-grid--active .dwc-quickview-grid__card.active{
    border: solid var(--active-border-color) var(--active-border-width);
  border-bottom: var(--active-border-bottom);
  box-shadow: var(--active-box-shadow);
  background-color: var(--active-bg);
}

.dwc-quickview-grid__card{
  border: solid var(--qv-card-border-color) var(--qv-card-border-width);
  background-color: var(--qv-card-bg)
}

.dwc-quick-view__inner-wrap--grid {
    height: 100%;
  /*  max-height: 95dvb;
    overflow-y: scroll;*/
  overflow-x: hidden;
  overscroll-behavior: contain;
  background-color: var(--fetch-bg)
}


/* caret */
.dwc-quickview-grid__card .caret {
  inline-size: 0;
  block-size: 0;
  border-inline-start: var(--qv-caret-size) solid transparent;
  border-inline-end: var(--qv-caret-size) solid transparent;
  border-block-start: var(--qv-caret-size) solid var(--qv-caret-color);
  position: absolute;
  inset-block-end: calc(var(--qv-caret-size) * -0.8);
  inset-inline-start: 50%;
  transform: translateX(-50%);
}

/*quick view*/
.dwc-expanded-content{
    border-inline: solid var(--active-border-color) var(--active-border-width)
}


/*SCROLLBARS*/
.dwc-quick-view__inner-wrap::-webkit-scrollbar-track {
    border-radius: 5px;
    background-color: transparent;
}

.dwc-quick-view__inner-wrap::-webkit-scrollbar {
    width: 5px;
    background-color: transparent;
}

.dwc-quick-view__inner-wrap::-webkit-scrollbar-thumb {
    border-radius: 5px;
    background-color: gray;
}

/* SCROLLBARS END */

/**
 * Default configuration (internal settings)
 * This contains all the technical configuration that rarely changes
 */
const quickViewConfig = {
    // Selectors
    selectors: {
        card: '.dwc-quickview-grid__card',
        quickViewBtn: '.dwc-quickview-grid-btn',
        expandedContainer: '.dwc-expanded-container',
        expandedContent: '.dwc-expanded-content',
        closeBtn: '.dwc-quickview-grid__close-btn',
        nextBtn: '.dwc-quickview-grid__next-button',
        prevBtn: '.dwc-quickview-grid__prev-button',
        nextPrevContainer: '.dwc-quickview-grid__next-prev-buttons',
        miniCart: '.brxe-woocommerce-mini-cart',
        cartDetail: '.cart-detail',
        wooGallery: '.woocommerce-product-gallery',
        wooGalleryImage: '.woocommerce-product-gallery__image img',
        variationsForm: '.variations_form',
        variationId: 'input.variation_id',
        qtyInput: 'input.qty',
        plusBtn: '.plus',
        minusBtn: '.minus',
        addToCartBtn: '.brx_ajax_add_to_cart, .single_add_to_cart_button',
        wcTabs: '.wc-tabs, .tabs',
        wcTabsWrapper: '.wc-tabs-wrapper, .woocommerce-tabs',
        addedToCartLink: 'a.added_to_cart',
        bricksStyleLinks: '[id^="bricks-post-"]',
        bricksFrontendStyle: '#bricks-frontend-inline-inline-css',
        brxQueryTrail: '.brx-query-trail'
    },
    
    // Timeouts
    timeouts: {
        focus: 500,
        heightUpdate: 50,
        scroll: 100,
        resetOrder: 500,
        wooInit: 100,
        bricksInit: 150,
        urlChange: 1500,
        cartRevert: 3000,
        resizeDebounce: 300,
        styleInjection: 50
    },
    
    // CSS Classes
    classes: {
        active: 'active',
        open: 'open',
        inactive: 'inactive',
        caret: 'caret',
        quickViewActive: 'quick-view-grid--active',
        macOS: 'dwc-macOS',
        loading: 'loading',
        showCartDetails: 'show-cart-details'
    },
    
    // CSS Custom Properties
    cssVars: {
        contentHeight: '--dwc-grid-content-height'
    },
    
    // Callbacks (optional, for extensibility)
    callbacks: {
        onOpen: null,
        onClose: null,
        onNavigate: null,
        onAddToCart: null
    }
};

// ============================================
// UTILITY FUNCTIONS
// ============================================
const Utils = {
    /**
     * Debounce function to limit execution rate
     * @param {Function} func - Function to debounce
     * @param {number} timeout - Delay in milliseconds
     * @returns {Function} Debounced function
     */
    debounce(func, timeout) {
        let timer;
        return function(...args) {
            const context = this;
            clearTimeout(timer);
            timer = setTimeout(() => func.apply(context, args), timeout);
        };
    },

    /**
     * Safely get element with null check
     * @param {string} selector - CSS selector
     * @param {Element} context - Context element (default: document)
     * @returns {Element|null} Element or null
     */
    getElementSafely(selector, context = document) {
        try {
            return context.querySelector(selector);
        } catch (error) {
            console.error(`Error selecting element: ${selector}`, error);
            return null;
        }
    },

    /**
     * Safely get all elements with null check
     * @param {string} selector - CSS selector
     * @param {Element} context - Context element (default: document)
     * @returns {NodeList} NodeList or empty array
     */
    getAllElementsSafely(selector, context = document) {
        try {
            return context.querySelectorAll(selector) || [];
        } catch (error) {
            console.error(`Error selecting elements: ${selector}`, error);
            return [];
        }
    },

    /**
     * Deep merge configuration objects
     * @param {Object} defaults - Default configuration
     * @param {Object} userConfig - User configuration
     * @returns {Object} Merged configuration
     */
    mergeConfig(defaults, userConfig) {
        const result = { ...defaults };
        
        for (const key in userConfig) {
            if (userConfig.hasOwnProperty(key)) {
                if (typeof userConfig[key] === 'object' && !Array.isArray(userConfig[key]) && userConfig[key] !== null) {
                    result[key] = this.mergeConfig(defaults[key] || {}, userConfig[key]);
                } else {
                    result[key] = userConfig[key];
                }
            }
        }
        
        return result;
    },

    /**
     * Check if running on macOS
     * @returns {boolean} True if macOS
     */
    isMacOS() {
        return navigator.platform.toUpperCase().indexOf('MAC') >= 0;
    }
};

// ============================================
// CONTENT MANAGER CLASS
// ============================================
/**
 * Manages content fetching, caching, and replacement
 * Uses Map for in-memory caching instead of localStorage
 * @class ContentManager
 */
class ContentManager {
    constructor(config) {
        this.config = config;
        this.cache = new Map(); // Replace localStorage with Map
        this.tempDiv = document.createElement('div');
        this.tempStyle = document.createElement('div');
        this.currentQuickView = null;
        this.isInitialFetchComplete = false;
    }

    /**
     * Fetch initial content from source URL
     * @returns {Promise<void>}
     */
    fetchInitialContent() {
        return new Promise((resolve, reject) => {
            try {
                const xhr = new XMLHttpRequest();
                xhr.open('GET', this.config.sourceUrl);
                
                xhr.onreadystatechange = () => {
                    if (xhr.readyState === 4) {
                        if (xhr.status === 200) {
                            this._processInitialResponse(xhr.responseText);
                            resolve();
                        } else {
                            reject(new Error(`XHR failed with status: ${xhr.status}`));
                        }
                    }
                };
                
                xhr.onerror = () => reject(new Error('XHR request failed'));
                xhr.send();
            } catch (error) {
                console.error('Error fetching initial content:', error);
                reject(error);
            }
        });
    }

    /**
     * Process initial XHR response
     * @param {string} responseText - Response HTML
     * @private
     */
    _processInitialResponse(responseText) {
        try {
            this.tempStyle.innerHTML = responseText;
            
            // Inject styles
            this._injectBricksStyles();
            
            // Parse and sanitize
            const sanitizedContent = this._sanitizeContent(responseText);
            
            // Cache in Map instead of localStorage
            this.cache.set(this.config.sourceUrl, sanitizedContent);
            this.isInitialFetchComplete = true;
            
        } catch (error) {
            console.error('Error processing initial response:', error);
        }
    }

    /**
     * Inject Bricks styles into document head
     * @private
     */
    _injectBricksStyles() {
        try {
            // Import frontend style links
            const bricksStyleLinks = this.tempStyle.querySelectorAll(this.config.selectors.bricksStyleLinks);
            const tenthHeadElement = document.head.children[9];
            
            if (tenthHeadElement) {
                bricksStyleLinks.forEach(link => {
                    document.head.insertBefore(link.cloneNode(true), tenthHeadElement.nextSibling);
                });
            }
            
            // Import frontend stylesheet
            const quickViewStyles = this.tempStyle.querySelector(this.config.selectors.bricksFrontendStyle);
            if (quickViewStyles) {
                quickViewStyles.id += '-2';
                document.head.appendChild(quickViewStyles);
            }
        } catch (error) {
            console.error('Error injecting Bricks styles:', error);
        }
    }

    /**
     * Sanitize HTML content
     * @param {string} html - Raw HTML
     * @returns {string} Sanitized HTML
     * @private
     */
    _sanitizeContent(html) {
        try {
            const parser = new DOMParser();
            const doc = parser.parseFromString(html, 'text/html');
            
            // Remove unwanted elements
            const brxQueryTrailDiv = doc.querySelector(this.config.selectors.brxQueryTrail);
            if (brxQueryTrailDiv && brxQueryTrailDiv.parentNode) {
                brxQueryTrailDiv.parentNode.removeChild(brxQueryTrailDiv);
            }
            
            return doc.documentElement.innerHTML;
        } catch (error) {
            console.error('Error sanitizing content:', error);
            return html;
        }
    }

    /**
     * Get cached content from Map
     * @param {string} key - Cache key
     * @returns {string|null} Cached content
     */
    getCachedContent(key) {
        return this.cache.get(key) || null;
    }

    /**
     * Replace content in expanded container
     * @param {string} dataId - Data ID of content to display
     * @param {Element} clickedButton - Button that was clicked
     * @returns {boolean} Success status
     */
    replaceContent(dataId, clickedButton) {
        try {
            const cachedContent = this.getCachedContent(this.config.sourceUrl);
            if (!cachedContent) {
                console.error('No cached content available');
                return false;
            }
            
            this.tempDiv.innerHTML = cachedContent;
            
            // Find content with matching data-id
            const fetchedQuickView = this.tempDiv.querySelector(`[data-id="${dataId}"]`);
            if (!fetchedQuickView) {
                console.error(`Content with data-id="${dataId}" not found`);
                return false;
            }
            
            // Make focusable
            fetchedQuickView.setAttribute('tabindex', '0');
            
            // Get container
            const container = Utils.getElementSafely(this.config.selectors.expandedContainer);
            if (!container) {
                console.error('Expanded container not found');
                return false;
            }
            
            // Remove previous content
            if (this.currentQuickView && this.currentQuickView.parentNode) {
                container.removeChild(this.currentQuickView);
            }
            
            // Append new content
            container.appendChild(fetchedQuickView);
            this.currentQuickView = fetchedQuickView;
            
            // Add active class to body
            if (!document.body.classList.contains(this.config.classes.quickViewActive)) {
                document.body.classList.add(this.config.classes.quickViewActive);
            }
            
            return true;
            
        } catch (error) {
            console.error('Error replacing content:', error);
            return false;
        }
    }

    /**
     * Get current quick view element
     * @returns {Element|null}
     */
    getCurrentQuickView() {
        return this.currentQuickView;
    }

    /**
     * Clear cache
     */
    clearCache() {
        this.cache.clear();
    }

    /**
     * Cleanup
     */
    destroy() {
        this.clearCache();
        this.currentQuickView = null;
        this.tempDiv = null;
        this.tempStyle = null;
    }
}

// ============================================
// NAVIGATION CONTROLLER CLASS
// ============================================
/**
 * Manages navigation (next/prev), button states, keyboard controls
 * @class NavigationController
 */
class NavigationController {
    constructor(config, contentManager, gridController) {
        this.config = config;
        this.contentManager = contentManager;
        this.gridController = gridController;
        
        // Cache DOM elements
        this.nextButton = null;
        this.prevButton = null;
        this.expandedContainer = null;
        this.quickViewButtons = [];
        
        // Store bound handlers for cleanup
        this.handlers = {
            next: this._handleNext.bind(this),
            prev: this._handlePrev.bind(this),
            keyboard: this._handleKeyboard.bind(this),
            escape: this._handleEscape.bind(this)
        };
    }

    /**
     * Initialize navigation
     */
    init() {
        try {
            // Cache elements
            this.nextButton = Utils.getElementSafely(this.config.selectors.nextBtn);
            this.prevButton = Utils.getElementSafely(this.config.selectors.prevBtn);
            this.expandedContainer = Utils.getElementSafely(this.config.selectors.expandedContainer);
            this.quickViewButtons = Array.from(Utils.getAllElementsSafely(this.config.selectors.quickViewBtn));
            
            if (!this.nextButton || !this.prevButton || !this.expandedContainer) {
                console.warn('Navigation buttons or container not found');
                return;
            }
            
            // Attach event listeners
            this.nextButton.addEventListener('click', this.handlers.next);
            this.prevButton.addEventListener('click', this.handlers.prev);
            this.expandedContainer.addEventListener('keydown', this.handlers.keyboard);
            document.addEventListener('keydown', this.handlers.escape);
            
        } catch (error) {
            console.error('Error initializing navigation:', error);
        }
    }

    /**
     * Handle next button click
     * @private
     */
    _handleNext() {
        this._navigateQuickView('next');
    }

    /**
     * Handle previous button click
     * @private
     */
    _handlePrev() {
        this._navigateQuickView('prev');
    }

    /**
     * Handle keyboard navigation (arrow keys)
     * @param {KeyboardEvent} event
     * @private
     */
    _handleKeyboard(event) {
        if (event.key === 'ArrowLeft' && this.prevButton) {
            this.prevButton.click();
        } else if (event.key === 'ArrowRight' && this.nextButton) {
            this.nextButton.click();
        }
    }

    /**
     * Handle escape key
     * @param {KeyboardEvent} event
     * @private
     */
    _handleEscape(event) {
        if (event.key === 'Escape') {
            this._closeQuickView(event);
        }
    }

    /**
     * Navigate to next or previous quick view
     * @param {string} direction - 'next' or 'prev'
     * @private
     */
    _navigateQuickView(direction) {
        try {
            const expandedContent = Utils.getElementSafely(this.config.selectors.expandedContent);
            if (!expandedContent) return;
            
            const currentDataId = expandedContent.dataset.id;
            const currentIndex = this.quickViewButtons.findIndex(
                btn => btn.getAttribute('data-id') === currentDataId
            );
            
            if (currentIndex === -1) return;
            
            // Determine target index
            let targetIndex;
            if (direction === 'next') {
                targetIndex = currentIndex + 1;
                if (targetIndex >= this.quickViewButtons.length) return;
            } else if (direction === 'prev') {
                targetIndex = currentIndex - 1;
                if (targetIndex < 0) return;
            }
            
            // Update button states BEFORE navigation
            this.updateButtonStates(targetIndex, this.quickViewButtons.length);
            
            // Navigate
            this.quickViewButtons[targetIndex].click();
            
            // Call callback if provided
            if (this.config.callbacks.onNavigate) {
                this.config.callbacks.onNavigate(targetIndex, direction);
            }
            
        } catch (error) {
            console.error('Error navigating quick view:', error);
        }
    }

    /**
     * Update button states based on current position
     * DRY solution - single reusable method for all button state logic
     * @param {number} currentIndex - Current item index
     * @param {number} totalItems - Total number of items
     */
    updateButtonStates(currentIndex, totalItems) {
        if (!this.nextButton || !this.prevButton) return;
        
        const isFirstItem = currentIndex <= 0;
        const isLastItem = currentIndex >= totalItems - 1;
        
        if (isLastItem) {
            this.nextButton.classList.add(this.config.classes.inactive);
            this.prevButton.classList.remove(this.config.classes.inactive);
        } else if (isFirstItem) {
            this.prevButton.classList.add(this.config.classes.inactive);
            this.nextButton.classList.remove(this.config.classes.inactive);
        } else {
            this.nextButton.classList.remove(this.config.classes.inactive);
            this.prevButton.classList.remove(this.config.classes.inactive);
        }
    }

    /**
     * Get current quick view index
     * @returns {number} Current index or -1 if not found
     */
    getCurrentIndex() {
        try {
            const expandedContent = Utils.getElementSafely(this.config.selectors.expandedContent);
            if (!expandedContent) return -1;
            
            const currentDataId = expandedContent.dataset.id;
            return this.quickViewButtons.findIndex(
                btn => btn.getAttribute('data-id') === currentDataId
            );
        } catch (error) {
            console.error('Error getting current index:', error);
            return -1;
        }
    }

    /**
     * Focus target button
     * @param {Event} event
     */
    focusTargetButton(event) {
        try {
            const currentDiv = Utils.getElementSafely(this.config.selectors.expandedContent);
            if (!currentDiv) return;
            
            const currentId = currentDiv.getAttribute('data-id');
            const targetBtn = document.querySelector(`[data-id='${currentId}']`);
            
            if (targetBtn && event.relatedTarget !== targetBtn) {
                targetBtn.focus();
            }
        } catch (error) {
            console.error('Error focusing target button:', error);
        }
    }

    /**
     * Close quick view and remove active class
     * @param {Event} event
     * @private
     */
    _closeQuickView(event) {
        try {
            // Reset button states
            if (this.prevButton) this.prevButton.classList.remove(this.config.classes.inactive);
            if (this.nextButton) this.nextButton.classList.remove(this.config.classes.inactive);
            
            // Remove active class from body
            if (document.body.classList.contains(this.config.classes.quickViewActive)) {
                document.body.classList.remove(this.config.classes.quickViewActive);
                
                // Return focus
                const currentDiv = Utils.getElementSafely(this.config.selectors.expandedContent);
                if (currentDiv) {
                    const currentId = currentDiv.getAttribute('data-id');
                    const targetBtn = document.querySelector(`[data-id='${currentId}']`);
                    const closeButton = Utils.getElementSafely(this.config.selectors.closeBtn);
                    
                    if (targetBtn && event.relatedTarget !== targetBtn) {
                        targetBtn.focus();
                    } else if (closeButton && event.relatedTarget !== closeButton) {
                        closeButton.focus();
                    }
                }
            }
            
            // Close container via grid controller
            if (this.gridController) {
                this.gridController.closeContainer();
            }
            
        } catch (error) {
            console.error('Error closing quick view:', error);
        }
    }

    /**
     * Refresh button references (called after URL changes)
     */
    refreshButtons() {
        this.quickViewButtons = Array.from(Utils.getAllElementsSafely(this.config.selectors.quickViewBtn));
    }

    /**
     * Cleanup
     */
    destroy() {
        try {
            if (this.nextButton) this.nextButton.removeEventListener('click', this.handlers.next);
            if (this.prevButton) this.prevButton.removeEventListener('click', this.handlers.prev);
            if (this.expandedContainer) this.expandedContainer.removeEventListener('keydown', this.handlers.keyboard);
            document.removeEventListener('keydown', this.handlers.escape);
            
            this.nextButton = null;
            this.prevButton = null;
            this.expandedContainer = null;
            this.quickViewButtons = [];
        } catch (error) {
            console.error('Error destroying navigation controller:', error);
        }
    }
}

// ============================================
// GRID CONTROLLER CLASS
// ============================================
/**
 * Manages grid cards, container open/close, and layout
 * @class GridController
 */
class GridController {
    constructor(config) {
        this.config = config;
        this.cards = [];
        this.expandedContainer = null;
        this.resizeHandler = null;
        this.resetOrderTimeout = null;
    }

    /**
     * Initialize grid
     */
    init() {
        try {
            // Setup cards
            this.cards = Array.from(Utils.getAllElementsSafely(this.config.selectors.card));
            this.cards.forEach((card, index) => {
                card.id = `card${index}`;
                card.style.order = index;
            });
            
            // Setup expanded container
            this.expandedContainer = Utils.getElementSafely(this.config.selectors.expandedContainer);
            if (this.expandedContainer) {
                this.expandedContainer.inert = true;
                this.expandedContainer.setAttribute('aria-hidden', 'true');
                this.expandedContainer.style.order = this.cards.length;
            }
            
            // Setup next/prev buttons container position
            this._setupNavButtonsPosition();
            
            // Setup resize handler
            this.resizeHandler = Utils.debounce(() => {
                if (this.expandedContainer && this.expandedContainer.classList.contains(this.config.classes.open)) {
                    this.updateContentHeight();
                }
            }, this.config.timeouts.resizeDebounce);
            
            window.addEventListener('resize', this.resizeHandler);
            
        } catch (error) {
            console.error('Error initializing grid:', error);
        }
    }

    /**
     * Setup navigation buttons position
     * @private
     */
    _setupNavButtonsPosition() {
        const nextPrevContainer = Utils.getElementSafely(this.config.selectors.nextPrevContainer);
        if (nextPrevContainer) {
            nextPrevContainer.classList.add(this.config.navArrowsPosition);
        }
    }

    /**
     * Show container for clicked card
     * @param {Element} clickedCard
     */
    showContainer(clickedCard) {
        try {
            if (!this.expandedContainer || !clickedCard) return;
            
            // Update active card and caret
            this.updateActiveCard(clickedCard);
            
            // Get clicked card ID
            const clickedCardId = parseInt(clickedCard.id.replace('card', ''), 10);
            
            // Toggle or open container
            if (this.expandedContainer.style.order == `${clickedCardId}`) {
                this.expandedContainer.classList.toggle(this.config.classes.open);
            } else {
                this.expandedContainer.classList.add(this.config.classes.open);
                this.expandedContainer.style.order = `${clickedCardId}`;
            }
            
            // Update aria and inert
            const isOpen = this.expandedContainer.classList.contains(this.config.classes.open);
            
            if (isOpen) {
                this.expandedContainer.inert = false;
                this.expandedContainer.setAttribute('aria-hidden', 'false');
                this.expandedContainer.focus({ preventScroll: true });
                this.updateContentHeight();
                
                // Scroll into view
                setTimeout(() => {
                    const scrollY = this.expandedContainer.getBoundingClientRect().top + 
                                  window.pageYOffset - this.config.scrollOffset;
                    window.scrollTo({
                        top: scrollY,
                        behavior: 'smooth'
                    });
                }, this.config.timeouts.scroll);
                
                // Call callback
                if (this.config.callbacks.onOpen) {
                    this.config.callbacks.onOpen(clickedCard);
                }
            } else {
                this.expandedContainer.inert = true;
                this.expandedContainer.setAttribute('aria-hidden', 'true');
                document.body.style.setProperty(this.config.cssVars.contentHeight, '0');
            }
            
            this.resetOrder();
            
        } catch (error) {
            console.error('Error showing container:', error);
        }
    }

    /**
     * Close container
     */
    closeContainer() {
        try {
            if (!this.expandedContainer) return;
            
            this.removeActiveClass();
            this.resetOrder();
            this.expandedContainer.inert = true;
            this.expandedContainer.classList.remove(this.config.classes.open);
            this.expandedContainer.setAttribute('aria-hidden', 'true');
            document.body.style.setProperty(this.config.cssVars.contentHeight, '0');
            
            // Call callback
            if (this.config.callbacks.onClose) {
                this.config.callbacks.onClose();
            }
            
        } catch (error) {
            console.error('Error closing container:', error);
        }
    }

    /**
     * Update active card and add caret
     * @param {Element} clickedCard
     */
    updateActiveCard(clickedCard) {
        try {
            const isActive = clickedCard.classList.contains(this.config.classes.active);
            
            // Remove active class from all cards
            this.removeActiveClass();
            
            // Add active class and caret if not already active
            if (!isActive) {
                clickedCard.classList.add(this.config.classes.active);
                
                const caret = document.createElement('div');
                caret.classList.add(this.config.classes.caret);
                clickedCard.style.position = 'relative';
                clickedCard.appendChild(caret);
            }
        } catch (error) {
            console.error('Error updating active card:', error);
        }
    }

    /**
     * Remove active class and caret from all cards
     */
    removeActiveClass() {
        try {
            this.cards.forEach(card => {
                card.classList.remove(this.config.classes.active);
                const existingCaret = card.querySelector(`.${this.config.classes.caret}`);
                if (existingCaret) {
                    existingCaret.remove();
                }
            });
        } catch (error) {
            console.error('Error removing active class:', error);
        }
    }

    /**
     * Reset container order when all cards are inactive
     */
    resetOrder() {
        // Clear any existing timeout
        if (this.resetOrderTimeout) {
            clearTimeout(this.resetOrderTimeout);
        }
        
        this.resetOrderTimeout = setTimeout(() => {
            try {
                const noActiveCard = !this.cards.some(card => 
                    card.classList.contains(this.config.classes.active)
                );
                
                if (noActiveCard && this.expandedContainer) {
                    this.expandedContainer.style.order = this.cards.length;
                    document.body.classList.remove(this.config.classes.quickViewActive);
                }
            } catch (error) {
                console.error('Error resetting order:', error);
            }
        }, this.config.timeouts.resetOrder);
    }

    /**
     * Update content height CSS variable
     */
    updateContentHeight() {
        setTimeout(() => {
            try {
                if (this.expandedContainer) {
                    const contentHeight = this.expandedContainer.scrollHeight;
                    document.body.style.setProperty(
                        this.config.cssVars.contentHeight, 
                        `${contentHeight}px`
                    );
                }
            } catch (error) {
                console.error('Error updating content height:', error);
            }
        }, this.config.timeouts.heightUpdate);
    }

    /**
     * Cleanup
     */
    destroy() {
        try {
            if (this.resizeHandler) {
                window.removeEventListener('resize', this.resizeHandler);
            }
            if (this.resetOrderTimeout) {
                clearTimeout(this.resetOrderTimeout);
            }
            this.cards = [];
            this.expandedContainer = null;
        } catch (error) {
            console.error('Error destroying grid controller:', error);
        }
    }
}

// ============================================
// WOOCOMMERCE INTEGRATION CLASS
// ============================================
/**
 * Handles all WooCommerce functionality using jQuery
 * @class WooCommerceIntegration
 */
class WooCommerceIntegration {
    constructor(config, gridController) {
        this.config = config;
        this.gridController = gridController;
        this.initialized = false;
    }

    /**
     * Initialize WooCommerce components
     * @param {number} timeout - Initialization delay
     */
    init(timeout = 100) {
        if (!this.config.initializeWooScripts) return;
        
        setTimeout(() => {
            try {
                this.setupQuantityControls();
                this.setupAddToCart();
                this.setupVariationChange();
                this.initializeProductTabs();
                this.initializeVariationForms();
                this.initializeProductGallery();
                jQuery(document.body).trigger('wc_fragment_refresh');
                this.initialized = true;
            } catch (error) {
                console.error('Error initializing WooCommerce:', error);
            }
        }, timeout);
    }

    /**
     * Setup quantity increment/decrement controls
     */
    setupQuantityControls() {
        try {
            jQuery(document).off('click', this.config.selectors.plusBtn)
                .on('click', this.config.selectors.plusBtn, () => {
                    const $input = jQuery(this.config.selectors.expandedContainer + ' ' + this.config.selectors.qtyInput);
                    const currentVal = parseInt($input.val());
                    if (!isNaN(currentVal)) {
                        $input.val(currentVal + 1);
                        $input.attr('value', currentVal + 1);
                    }
                });
            
            jQuery(document).off('click', this.config.selectors.minusBtn)
                .on('click', this.config.selectors.minusBtn, () => {
                    const $input = jQuery(this.config.selectors.expandedContainer + ' ' + this.config.selectors.qtyInput);
                    const currentVal = parseInt($input.val());
                    if (!isNaN(currentVal) && currentVal > 1) {
                        $input.val(currentVal - 1);
                        $input.attr('value', currentVal - 1);
                    }
                });
            
            jQuery(document).on('input', this.config.selectors.qtyInput, function() {
                const $input = jQuery(this);
                $input.attr('value', $input.val());
            });
        } catch (error) {
            console.error('Error setting up quantity controls:', error);
        }
    }

    /**
     * Setup AJAX add to cart functionality
     */
    setupAddToCart() {
        try {
            jQuery(document).off('click', this.config.selectors.addToCartBtn)
                .on('click', this.config.selectors.addToCartBtn, (e) => {
                    e.preventDefault();
                    
                    const $button = jQuery(e.currentTarget);
                    const $form = $button.closest(this.config.selectors.variationsForm);
                    const $span = $button.find('span');
                    const originalText = 'Add again';
                    
                    const isVariable = $form.length > 0;
                    const quantity = isVariable ? 
                        $form.find(this.config.selectors.qtyInput).val() || 1 : 
                        $button.closest('.brx-loop-product-form').find(this.config.selectors.qtyInput).val() || 1;
                    
                    const data = {
                        action: 'woocommerce_add_to_cart',
                        product_id: $button.data('product_id'),
                        quantity: quantity,
                        product_type: isVariable ? 'variable' : 'simple'
                    };
                    
                    if (isVariable) {
                        const variation_id = $form.find(this.config.selectors.variationId).val();
                        if (!variation_id || variation_id === "0") {
                            return;
                        }
                        data.variation_id = variation_id;
                        jQuery.each($form.serializeArray(), (index, item) => {
                            if (item.name !== 'product_id') {
                                data[item.name] = item.value;
                            }
                        });
                    }
                    
                    // Update button text
                    this._updateButtonText($button, $span, 'Adding...');
                    
                    // AJAX request
                    jQuery.ajax({
                        type: 'POST',
                        url: wc_add_to_cart_params.ajax_url,
                        data: data,
                        success: (response) => {
                            this._handleAddToCartSuccess(response, $button, $span, originalText);
                        },
                        error: () => {
                            console.error('Failed to add to cart');
                            this._revertButtonText($button, $span, originalText);
                        }
                    });
                });
        } catch (error) {
            console.error('Error setting up add to cart:', error);
        }
    }

    /**
     * Update button text during add to cart
     * @private
     */
    _updateButtonText($button, $span, text) {
        $button.addClass(this.config.classes.loading);
        if ($span.length) {
            $span.text(text);
        } else {
            $button.text(text);
        }
    }

    /**
     * Handle successful add to cart
     * @private
     */
    _handleAddToCartSuccess(response, $button, $span, originalText) {
        try {
            if (response.fragments && response.cart_hash) {
                jQuery(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $button]);
                this._updateButtonText($button, $span, 'Added');
                $button.removeClass(this.config.classes.loading);
                
                if (typeof bricksWooRefreshCartFragments === 'function') {
                    bricksWooRefreshCartFragments();
                }
            } else {
                this._updateButtonText($button, $span, 'Added');
                $button.removeClass(this.config.classes.loading);
                
                if (typeof bricksWooRefreshCartFragments === 'function') {
                    bricksWooRefreshCartFragments();
                }
            }
            
            setTimeout(() => {
                this._revertButtonText($button, $span, originalText);
            }, this.config.timeouts.cartRevert);
            
            this._cartToggle();
            
            // Call callback
            if (this.config.callbacks.onAddToCart) {
                this.config.callbacks.onAddToCart(response);
            }
        } catch (error) {
            console.error('Error handling add to cart success:', error);
        }
    }

    /**
     * Revert button text after add to cart
     * @private
     */
    _revertButtonText($button, $span, originalText) {
        if ($span.length) {
            $span.remove();
            $button.text(originalText);
        } else {
            $button.text(originalText);
        }
        
        this._appendViewCartLink($button);
    }

    /**
     * Append view cart link
     * @private
     */
    _appendViewCartLink($button) {
        try {
            if (!$button.next(this.config.selectors.addedToCartLink).length) {
                const currentSiteUrl = window.location.origin + '/cart/';
                const viewCartLink = `<a href="${currentSiteUrl}" class="added_to_cart wc-forward" title="View cart">View cart</a>`;
                $button.after(viewCartLink);
            }
        } catch (error) {
            console.error('Error appending view cart link:', error);
        }
    }

    /**
     * Toggle mini cart display
     * @private
     */
    _cartToggle() {
        try {
            const miniCart = Utils.getElementSafely(this.config.selectors.miniCart);
            if (miniCart) {
                miniCart.classList.add(this.config.classes.showCartDetails);
                const cartDetail = miniCart.querySelector(this.config.selectors.cartDetail);
                if (cartDetail) {
                    cartDetail.classList.add(this.config.classes.active);
                }
            }
        } catch (error) {
            console.error('Error toggling cart:', error);
        }
    }

    /**
     * Setup variation change handlers
     */
    setupVariationChange() {
        try {
            jQuery(document).on('woocommerce_variation_select_change', () => {
                this._updateVariationImage();
            });
            
            jQuery(document).on('found_variation', (event, variation) => {
                if (variation && variation.image && variation.image.src) {
                    const newImageSrc = variation.image.src;
                    const newImageSrcset = variation.image.srcset;
                    
                    // Update main product image
                    const $mainImage = jQuery(this.config.selectors.wooGalleryImage);
                    $mainImage.attr('src', newImageSrc);
                    $mainImage.attr('srcset', newImageSrcset);
                    
                    // Reinitialize gallery
                    if (typeof jQuery.fn.wc_product_gallery === 'function') {
                        jQuery(this.config.selectors.wooGallery).wc_product_gallery();
                    }
                    
                    // Update container height
                    if (this.gridController) {
                        this.gridController.updateContentHeight();
                    }
                }
            });
        } catch (error) {
            console.error('Error setting up variation change:', error);
        }
    }

    /**
     * Update variation image
     * @private
     */
    _updateVariationImage() {
        jQuery(this.config.selectors.variationsForm).each(function() {
            const $form = jQuery(this);
            if (typeof $form.wc_variation_form === 'function') {
                $form.wc_variation_form().trigger('check_variations');
            }
        });
    }

    /**
     * Initialize WooCommerce product tabs
     */
    initializeProductTabs() {
        try {
            jQuery(this.config.selectors.wcTabs).trigger('init');
            
            jQuery(this.config.selectors.wcTabsWrapper).on('init', function() {
                const $activeTab = jQuery(this).find('ul.wc-tabs li.active');
                if ($activeTab.length) {
                    jQuery('#' + $activeTab.find('a').attr('href').replace('#', '')).show();
                }
            }).trigger('init');
        } catch (error) {
            console.error('Error initializing product tabs:', error);
        }
    }

    /**
     * Initialize variation forms
     */
    initializeVariationForms() {
        try {
            jQuery(this.config.selectors.variationsForm).each(function() {
                if (typeof jQuery(this).wc_variation_form === 'function') {
                    jQuery(this).wc_variation_form().trigger('check_variations');
                }
            });
        } catch (error) {
            console.error('Error initializing variation forms:', error);
        }
    }

    /**
     * Initialize product gallery
     */
    initializeProductGallery() {
        try {
            jQuery(this.config.selectors.wooGallery).each(function() {
                if (typeof jQuery(this).wc_product_gallery === 'function') {
                    jQuery(this).wc_product_gallery();
                }
            });
        } catch (error) {
            console.error('Error initializing product gallery:', error);
        }
    }

    /**
     * Cleanup
     */
    destroy() {
        try {
            // Remove jQuery event handlers
            jQuery(document).off('click', this.config.selectors.plusBtn);
            jQuery(document).off('click', this.config.selectors.minusBtn);
            jQuery(document).off('click', this.config.selectors.addToCartBtn);
            jQuery(document).off('input', this.config.selectors.qtyInput);
            jQuery(document).off('woocommerce_variation_select_change');
            jQuery(document).off('found_variation');
            
            this.initialized = false;
        } catch (error) {
            console.error('Error destroying WooCommerce integration:', error);
        }
    }
}

// ============================================
// BRICKS INTEGRATION CLASS
// ============================================
/**
 * Handles Bricks Builder element initialization
 * @class BricksIntegration
 */
class BricksIntegration {
    constructor(config) {
        this.config = config;
        this.initialized = false;
    }

    /**
     * Initialize Bricks elements
     * @param {number} timeout - Initialization delay
     */
    init(timeout = 100) {
        if (!this.config.initializeBricksScripts) return;
        
        setTimeout(() => {
            try {
                this._initializeElements();
                this.initialized = true;
            } catch (error) {
                console.error('Error initializing Bricks elements:', error);
            }
        }, timeout);
    }

    /**
     * Initialize all Bricks elements
     * @private
     */
    _initializeElements() {
        const bricksFunctions = [
            'bricksAccordion',
            'bricksTabs',
            'bricksSplide',
            'bricksSwiper',
            'bricksVideo',
            'bricksToggle',
            'bricksWooProductGallery',
            'bricksWooRefreshCartFragments'
        ];
        
        bricksFunctions.forEach(funcName => {
            try {
                if (typeof window[funcName] === 'function') {
                    window[funcName]();
                }
            } catch (error) {
                console.error(`Error calling ${funcName}:`, error);
            }
        });
    }

    /**
     * Cleanup
     */
    destroy() {
        this.initialized = false;
    }
}

// ============================================
// MAIN ORCHESTRATOR CLASS
// ============================================
/**
 * Main QuickView Grid orchestrator class
 * Coordinates all subcomponents and manages lifecycle
 * @class QuickViewGrid
 */
class QuickViewGrid {
    constructor(userConfig = {}) {
        // Merge user config with defaults
        this.config = Utils.mergeConfig(quickViewConfig, userConfig);
        
        // Initialize components
        this.contentManager = null;
        this.navigationController = null;
        this.gridController = null;
        this.wooCommerceIntegration = null;
        this.bricksIntegration = null;
        
        // State
        this.isInitialized = false;
        this.quickViewButtons = [];
        
        // Bound handlers for cleanup
        this.handlers = {
            quickViewClick: this._handleQuickViewClick.bind(this),
            closeClick: this._handleCloseClick.bind(this),
            closeFocusOut: this._handleCloseFocusOut.bind(this),
            urlChange: this._handleUrlChange.bind(this),
            resize: null // Will be set with debounced function
        };
        
        // URL change tracking
        this._setupHistoryTracking();
    }

    /**
     * Initialize QuickView Grid
     * @returns {Promise<void>}
     */
    async init() {
        if (this.isInitialized) {
            console.warn('QuickView Grid already initialized');
            return;
        }
        
        try {
            // Detect and add macOS class
            if (Utils.isMacOS()) {
                document.body.classList.add(this.config.classes.macOS);
            }
            
            // Initialize content manager and fetch initial content
            this.contentManager = new ContentManager(this.config);
            await this.contentManager.fetchInitialContent();
            
            // Initialize grid controller
            this.gridController = new GridController(this.config);
            this.gridController.init();
            
            // Initialize navigation controller
            this.navigationController = new NavigationController(
                this.config, 
                this.contentManager,
                this.gridController
            );
            this.navigationController.init();
            
            // Initialize WooCommerce integration
            this.wooCommerceIntegration = new WooCommerceIntegration(
                this.config,
                this.gridController
            );
            this.wooCommerceIntegration.init(this.config.timeouts.wooInit);
            
            // Initialize Bricks integration
            this.bricksIntegration = new BricksIntegration(this.config);
            this.bricksIntegration.init(this.config.timeouts.bricksInit);
            
            // Setup quick view button click handlers
            this._setupQuickViewButtons();
            
            // Setup resize handler with debounce
            this.handlers.resize = Utils.debounce(() => {
                this.wooCommerceIntegration.init(this.config.timeouts.wooInit);
                this.bricksIntegration.init(this.config.timeouts.bricksInit);
            }, this.config.timeouts.resizeDebounce);
            
            window.addEventListener('resize', this.handlers.resize);
            
            // Setup URL change listeners
            this._setupUrlChangeListeners();
            
            this.isInitialized = true;
            console.log('QuickView Grid initialized successfully');
            
        } catch (error) {
            console.error('Error initializing QuickView Grid:', error);
            throw error;
        }
    }

    /**
     * Setup quick view button click handlers
     * @private
     */
    _setupQuickViewButtons() {
        this.quickViewButtons = Array.from(
            Utils.getAllElementsSafely(this.config.selectors.quickViewBtn)
        );
        
        this.quickViewButtons.forEach((btn, index) => {
            // Store index on button for easy reference
            btn.dataset.index = index;
            
            // Remove existing listener if any (prevent duplicates)
            btn.removeEventListener('click', this.handlers.quickViewClick);
            
            // Add click listener
            btn.addEventListener('click', this.handlers.quickViewClick);
        });
    }

    /**
     * Handle quick view button click
     * @param {Event} event
     * @private
     */
    _handleQuickViewClick(event) {
        try {
            const btn = event.currentTarget;
            const dataId = btn.getAttribute('data-id');
            const index = parseInt(btn.dataset.index);
            
            // Replace content
            const success = this.contentManager.replaceContent(dataId, btn);
            if (!success) return;
            
            // Show container
            const card = btn.closest(this.config.selectors.card);
            if (card) {
                this.gridController.showContainer(card);
            }
            
            // Reinitialize WooCommerce and Bricks
            this.wooCommerceIntegration.init(this.config.timeouts.wooInit);
            this.bricksIntegration.init(this.config.timeouts.bricksInit);
            
            // Focus on quick view
            setTimeout(() => {
                const quickView = this.contentManager.getCurrentQuickView();
                if (quickView) {
                    quickView.focus({ preventScroll: true });
                }
            }, this.config.timeouts.focus);
            
            // Setup close button
            this._setupCloseButton();
            
            // Update navigation button states
            this.navigationController.updateButtonStates(
                index, 
                this.quickViewButtons.length
            );
            
        } catch (error) {
            console.error('Error handling quick view click:', error);
        }
    }

    /**
     * Setup close button handlers
     * @private
     */
    _setupCloseButton() {
        const closeButton = Utils.getElementSafely(this.config.selectors.closeBtn);
        if (!closeButton) return;
        
        // Remove existing listeners
        closeButton.removeEventListener('click', this.handlers.closeClick);
        closeButton.removeEventListener('focusout', this.handlers.closeFocusOut);
        
        // Add new listeners
        closeButton.addEventListener('click', this.handlers.closeClick);
        closeButton.addEventListener('focusout', this.handlers.closeFocusOut);
    }

    /**
     * Handle close button click
     * @param {Event} event
     * @private
     */
    _handleCloseClick(event) {
        this.gridController.closeContainer();
        this.navigationController._closeQuickView(event);
    }

    /**
     * Handle close button focus out
     * @param {Event} event
     * @private
     */
    _handleCloseFocusOut(event) {
        this.navigationController.focusTargetButton(event);
    }

    /**
     * Setup history tracking for URL changes
     * @private
     */
    _setupHistoryTracking() {
        const originalPushState = history.pushState;
        const originalReplaceState = history.replaceState;
        
        history.pushState = (...args) => {
            if (typeof history.onpushstate === "function") {
                history.onpushstate({ state: args[0] });
            }
            const result = originalPushState.apply(history, args);
            this._handleUrlChange();
            return result;
        };
        
        history.replaceState = (...args) => {
            if (typeof history.onreplacestate === "function") {
                history.onreplacestate({ state: args[0] });
            }
            const result = originalReplaceState.apply(history, args);
            this._handleUrlChange();
            return result;
        };
    }

    /**
     * Setup URL change listeners
     * @private
     */
    _setupUrlChangeListeners() {
        window.addEventListener('popstate', this.handlers.urlChange);
        window.addEventListener('hashchange', this.handlers.urlChange);
    }

    /**
     * Handle URL change - refresh buttons without adding duplicate listeners
     * @private
     */
    _handleUrlChange() {
        setTimeout(() => {
            try {
                // Refresh quick view buttons
                this._setupQuickViewButtons();
                
                // Refresh navigation controller button references
                this.navigationController.refreshButtons();
                
            } catch (error) {
                console.error('Error handling URL change:', error);
            }
        }, this.config.timeouts.urlChange);
    }

    /**
     * Get current configuration
     * @returns {Object} Current configuration
     */
    getConfig() {
        return { ...this.config };
    }

    /**
     * Update configuration
     * @param {Object} newConfig - New configuration values
     */
    updateConfig(newConfig) {
        this.config = Utils.mergeConfig(this.config, newConfig);
        console.log('Configuration updated');
    }

    /**
     * Destroy and cleanup QuickView Grid
     */
    destroy() {
        try {
            // Remove quick view button listeners
            this.quickViewButtons.forEach(btn => {
                btn.removeEventListener('click', this.handlers.quickViewClick);
            });
            
            // Remove URL change listeners
            window.removeEventListener('popstate', this.handlers.urlChange);
            window.removeEventListener('hashchange', this.handlers.urlChange);
            window.removeEventListener('resize', this.handlers.resize);
            
            // Destroy components
            if (this.contentManager) this.contentManager.destroy();
            if (this.navigationController) this.navigationController.destroy();
            if (this.gridController) this.gridController.destroy();
            if (this.wooCommerceIntegration) this.wooCommerceIntegration.destroy();
            if (this.bricksIntegration) this.bricksIntegration.destroy();
            
            // Clear references
            this.contentManager = null;
            this.navigationController = null;
            this.gridController = null;
            this.wooCommerceIntegration = null;
            this.bricksIntegration = null;
            this.quickViewButtons = [];
            
            this.isInitialized = false;
            console.log('QuickView Grid destroyed');
            
        } catch (error) {
            console.error('Error destroying QuickView Grid:', error);
        }
    }
}

// ============================================
// INITIALIZATION
// ============================================
document.addEventListener('DOMContentLoaded', () => {
    try {
        // Map external variables to user settings
        // These variables should be declared elsewhere in your WordPress theme
        if (typeof url !== 'undefined') {
            quickViewUserSettings.sourceUrl = url;
        }
        if (typeof scrollOffset !== 'undefined') {
            quickViewUserSettings.scrollOffset = scrollOffset;
        }
        if (typeof navArrowsPosition !== 'undefined') {
            quickViewUserSettings.navArrowsPosition = navArrowsPosition;
        }
        if (typeof initializeWooScripts !== 'undefined') {
            quickViewUserSettings.initializeWooScripts = Boolean(initializeWooScripts);
        }
        if (typeof initializeBricksScripts !== 'undefined') {
            quickViewUserSettings.initializeBricksScripts = Boolean(initializeBricksScripts);
        }
        
        // Merge user settings with default config
        const finalConfig = Utils.mergeConfig(quickViewConfig, quickViewUserSettings);
        
        // Initialize QuickView Grid
        const quickViewGrid = new QuickViewGrid(finalConfig);
        quickViewGrid.init().catch(error => {
            console.error('Failed to initialize QuickView Grid:', error);
        });
        
        // Expose to window for debugging and external access
        window.quickViewGrid = quickViewGrid;
        
    } catch (error) {
        console.error('Error in QuickView Grid initialization:', error);
    }
});
0