OLX Premium Redesign with Background Images

Completely transforms OLX listings with beautiful background images and modern design

// ==UserScript==
// @name         OLX Premium Redesign with Background Images
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  Completely transforms OLX listings with beautiful background images and modern design
// @author       im tired of this
// @match        https://www.olx.bg/*
// @grant        GM_xmlhttpRequest
// @connect      olx.bg
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // Cache for fetched data and processed pages/cards
    const dataCache = {};
    const processedPages = new Set();
    const processedCards = new Set();

    // Track current page and max page
    let currentPage = 1;
    let maxPage = 1;
    let isLoadingMorePages = false;

    // Placeholder image URL
    const PLACEHOLDER_IMAGE = "https://www.olx.bg/app/static/media/no_thumbnail.15f456ec5.svg";

    // Settings stored in localStorage
    const getSettings = () => {
        const defaultSettings = {
            hidePromoted: false
        };

        try {
            const stored = localStorage.getItem('olx-enhanced-settings');
            return stored ? JSON.parse(stored) : defaultSettings;
        } catch (e) {
            console.error('Error loading settings:', e);
            return defaultSettings;
        }
    };

    const saveSettings = (settings) => {
        try {
            localStorage.setItem('olx-enhanced-settings', JSON.stringify(settings));
        } catch (e) {
            console.error('Error saving settings:', e);
        }
    };

    // Add our stylesheet
    const addStyles = () => {
        if (document.getElementById('olx-premium-styles')) return;

        const style = document.createElement('style');
        style.id = 'olx-premium-styles';
        style.textContent = `
            /* Enhanced card with background image */
            .olx-card {
                padding: 20px;
                border-radius: 12px;
                margin-bottom: 20px;
                position: relative;
                overflow: hidden;
                box-shadow: 0 4px 12px rgba(0, 40, 44, 0.15);
                transition: transform 0.2s, box-shadow 0.2s;
                background-color: white;
                z-index: 1;
                min-height: 200px;
            }

            .olx-card:hover {
                transform: translateY(-3px);
                box-shadow: 0 6px 16px rgba(0, 40, 44, 0.2);
            }

            /* Background image overlay */
            .olx-bg-image {
                position: absolute;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                object-fit: cover;
                opacity: 0.12;
                z-index: -1;
                filter: blur(3px);
                transform: scale(1.1);
            }

            /* Main card content */
            .olx-content {
                position: relative;
                display: flex;
                flex-direction: column;
                height: 100%;
                z-index: 2;
            }

            /* Top section with tags, title, date and price */
            .olx-top {
                display: flex;
                flex-wrap: wrap;
                justify-content: space-between;
                margin-bottom: 15px;
            }

            /* Tags and title container */
            .olx-title-section {
                flex: 1;
                padding-right: 20px;
            }

            /* Tags row */
            .olx-tags {
                display: flex;
                gap: 8px;
                margin-bottom: 8px;
                align-items: center;
            }

            /* Individual tags */
            .olx-tag {
                display: inline-flex;
                align-items: center;
                height: 24px;
                padding: 0 12px;
                border-radius: 20px;
                font-size: 13px;
                font-weight: bold;
                text-transform: uppercase;
            }

            .olx-condition-tag {
                background: #23e5db;
                color: white;
            }

            .olx-promoted-tag {
                background: #ff9800;
                color: white;
            }

            .olx-delivery-tag {
                background: #4caf50;
                color: white;
                display: inline-flex;
                align-items: center;
                gap: 5px;
            }

            /* Title */
            .olx-title {
                font-size: 25px;
                font-weight: bold;
                color: #002f34;
                line-height: 1.3;
            }

            /* Price and date section */
            .olx-price-section {
                display: flex;
                flex-direction: column;
                align-items: flex-end;
            }

            /* Date above price */
            .olx-date {
                font-size: 16px;
                color: #406367;
                margin-bottom: 6px;
            }

            /* Price */
            .olx-price {
                font-size: 40px;
                font-weight: bold;
                color: #005f64;
            }

            /* Middle section with thumbnail gallery */
            .olx-middle {
                display: flex;
                margin: 15px 0;
            }

            /* Gallery */
            .olx-gallery {
                display: flex;
                flex-wrap: wrap;
                gap: 10px;
                margin-bottom: 15px;
                justify-content: flex-start;
            }

            .olx-thumb {
                width: 100px;
                height: 100px;
                border-radius: 8px;
                border: 2px solid #e0e0e0;
                cursor: pointer;
                object-fit: cover;
                transition: transform 0.15s, border-color 0.15s;
                box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
            }

            .olx-thumb:hover {
                transform: scale(1.05);
                border-color: #23e5db;
            }

            /* Description */
            .olx-desc {
                font-size: 16px;
                line-height: 1.5;
                color: #406367;
                padding: 15px;
                background: rgba(248, 251, 251, 0.5);
                border-radius: 8px;
                margin-bottom: 15px;
                overflow: hidden;
                max-height: 145px;
                position: relative;
                transition: max-height 0.3s;
                border: 1px solid rgba(238, 242, 242, 0.8);
            }

            .olx-desc.expanded {
                max-height: 1000px;
            }

            .olx-desc.short {
                max-height: none;
            }

            .olx-desc:not(.expanded):not(.short)::after {
                content: '';
                position: absolute;
                bottom: 0;
                left: 0;
                width: 100%;
                height: 25px;
                background: linear-gradient(transparent, rgba(248, 251, 251, 0.9));
            }

            .olx-toggle {
                color: #23e5db;
                cursor: pointer;
                font-size: 15px;
                margin-bottom: 15px;
                display: inline-block;
                user-select: none;
                font-weight: bold;
            }

            .olx-toggle:hover {
                text-decoration: underline;
            }

            /* Bottom section with actions and location */
            .olx-bottom {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-top: auto;
            }

            /* Action buttons */
            .olx-actions {
                display: flex;
                gap: 10px;
            }

            .olx-follow {
                display: flex;
                align-items: center;
                justify-content: center;
                gap: 8px;
                padding: 8px 15px;
                background: #f0f4f4;
                color: #406367;
                border-radius: 6px;
                cursor: pointer;
                font-size: 14px;
                font-weight: bold;
                transition: background 0.2s;
                border: none;
            }

            .olx-follow:hover {
                background: #e6eaea;
            }

            .olx-original {
                padding: 8px 15px;
                background: #002f34;
                color: white;
                border-radius: 6px;
                text-decoration: none;
                font-size: 14px;
                display: inline-flex;
                align-items: center;
                gap: 8px;
                font-weight: bold;
                transition: background 0.2s;
            }

            .olx-original:hover {
                background: #00484e;
            }

            /* Location */
            .olx-location {
                font-size: 18px;
                color: #23e5db;
                text-decoration: none;
                display: inline-flex;
                align-items: center;
                gap: 6px;
            }

            .olx-location:hover {
                text-decoration: underline;
            }

            /* Infinite scroll loader */
            .olx-loader {
                text-align: center;
                padding: 20px;
                font-size: 14px;
                color: #406367;
            }

            .olx-spinner {
                display: inline-block;
                width: 20px;
                height: 20px;
                border: 3px solid rgba(0,0,0,0.1);
                border-radius: 50%;
                border-top-color: #23e5db;
                animation: olx-spin 1s linear infinite;
                margin-right: 10px;
            }

            @keyframes olx-spin {
                to { transform: rotate(360deg); }
            }

            /* Image modal */
            .olx-modal {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0,0,0,0.9);
                z-index: 9999999;
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;
            }

            .olx-modal-img {
                max-width: 90%;
                max-height: 80vh;
                object-fit: contain;
            }

            .olx-modal-controls {
                display: flex;
                gap: 30px;
                margin-top: 20px;
            }

            .olx-modal-btn {
                background: rgba(255,255,255,0.2);
                color: white;
                border: none;
                padding: 8px 16px;
                border-radius: 4px;
                cursor: pointer;
                font-size: 14px;
            }

            .olx-modal-btn:hover {
                background: rgba(255,255,255,0.3);
            }

            .olx-modal-close {
                position: absolute;
                top: 20px;
                right: 20px;
                color: white;
                background: none;
                border: none;
                font-size: 30px;
                cursor: pointer;
            }

            /* Filter controls */
            .olx-filter-control {
                display: flex;
                align-items: center;
                gap: 10px;
                font-size: 14px;
                color: #406367;
                cursor: pointer;
                user-select: none;
            }

            .olx-filter-control:hover {
                color: #002f34;
            }

            .olx-switch {
                position: relative;
                display: inline-block;
                width: 36px;
                height: 20px;
            }

            .olx-switch input {
                opacity: 0;
                width: 0;
                height: 0;
            }

            .olx-slider {
                position: absolute;
                cursor: pointer;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background-color: #ccc;
                transition: .3s;
                border-radius: 20px;
            }

            .olx-slider:before {
                position: absolute;
                content: "";
                height: 16px;
                width: 16px;
                left: 2px;
                bottom: 2px;
                background-color: white;
                transition: .3s;
                border-radius: 50%;
            }

            input:checked + .olx-slider {
                background-color: #23e5db;
            }

            input:checked + .olx-slider:before {
                transform: translateX(16px);
            }

            /* Hide promoted cards if filter is on */
            div[data-olx-promoted="true"].olx-hide-promoted {
                display: none !important;
            }

            /* Media query for smaller screens */
            @media (max-width: 768px) {
                .olx-top {
                    flex-direction: column;
                }

                .olx-price-section {
                    align-items: flex-start;
                    margin-top: 10px;
                }

                .olx-price {
                    font-size: 30px;
                }

                .olx-bottom {
                    flex-direction: column;
                    align-items: flex-start;
                    gap: 15px;
                }

                .olx-location {
                    font-size: 16px;
                }
            }
        `;
        document.head.appendChild(style);
    };

    // Create an image modal
    const setupImageModal = () => {
        if (document.getElementById('olx-modal')) return;

        const modal = document.createElement('div');
        modal.id = 'olx-modal';
        modal.className = 'olx-modal';
        modal.style.display = 'none';

        modal.innerHTML = `
            <button class="olx-modal-close" aria-label="Close">&times;</button>
            <img src="" class="olx-modal-img" alt="Product image">
            <div class="olx-modal-controls">
                <button class="olx-modal-btn olx-prev">&lt; Previous</button>
                <button class="olx-modal-btn olx-next">Next &gt;</button>
            </div>
        `;

        document.body.appendChild(modal);

        let currentImages = [];
        let currentIndex = 0;

        const img = modal.querySelector('.olx-modal-img');
        const prevBtn = modal.querySelector('.olx-prev');
        const nextBtn = modal.querySelector('.olx-next');
        const closeBtn = modal.querySelector('.olx-modal-close');

        // Update image
        const updateImage = () => {
            img.src = currentImages[currentIndex];
            img.alt = `Image ${currentIndex + 1} of ${currentImages.length}`;
        };

        // Close button
        closeBtn.addEventListener('click', () => {
            modal.style.display = 'none';
            document.body.style.overflow = '';
        });

        // Background click
        modal.addEventListener('click', (e) => {
            if (e.target === modal) {
                modal.style.display = 'none';
                document.body.style.overflow = '';
            }
        });

        // Previous button
        prevBtn.addEventListener('click', (e) => {
            e.stopPropagation();
            currentIndex = (currentIndex - 1 + currentImages.length) % currentImages.length;
            updateImage();
        });

        // Next button
        nextBtn.addEventListener('click', (e) => {
            e.stopPropagation();
            currentIndex = (currentIndex + 1) % currentImages.length;
            updateImage();
        });

        // Keyboard navigation
        document.addEventListener('keydown', (e) => {
            if (modal.style.display === 'none') return;

            if (e.key === 'Escape') {
                modal.style.display = 'none';
                document.body.style.overflow = '';
            } else if (e.key === 'ArrowLeft') {
                prevBtn.click();
            } else if (e.key === 'ArrowRight') {
                nextBtn.click();
            }
        });

        // Expose function to show images
        window.showOlxModal = (images, index = 0) => {
            currentImages = images;
            currentIndex = index;
            updateImage();
            modal.style.display = 'flex';
            document.body.style.overflow = 'hidden';
        };
    };

    // Extract listing data from a card element
    const extractCardData = (card) => {
        const data = {
            title: '',
            price: '',
            url: '',
            location: '',
            date: '',
            condition: '',
            promoted: false,
            delivery: false,
            image: '',
            images: []
        };

        // Get URL
        const link = card.querySelector('a.css-1tqlkj0');
        if (link) {
            data.url = link.getAttribute('href');
        }

        // Get title
        const title = card.querySelector('h4.css-1g61gc2');
        if (title) {
            data.title = title.textContent.trim();
        }

        // Get price
        const price = card.querySelector('p[data-testid="ad-price"]');
        if (price) {
            // Get the raw price text
            const rawPriceText = price.textContent.trim();

            // Check for "Безплатно" (Free) and replace with "0 лв."
            if (rawPriceText.includes('Безплатно')) {
                data.price = '0 лв.';
            } else {
                // Regular price processing
                let priceText = rawPriceText;

                // Clean up any CSS classes that might be in the text
                priceText = priceText.replace(/\.css-.*$/, '');

                // Remove any other non-standard content
                priceText = priceText.replace(/[^\d\s.,лв€$£¥]+/g, '');

                // Remove any extra "в" character that sometimes appears
                priceText = priceText.replace(' в', '');

                data.price = priceText;
            }
        }

        // Get location and date
        const locationDate = card.querySelector('p[data-testid="location-date"]');
        if (locationDate) {
            const locationDateText = locationDate.textContent.trim();
            const parts = locationDateText.split(' - ');

            if (parts.length > 1) {
                data.location = parts[0].trim();
                data.date = parts[1].trim();
            } else {
                data.location = locationDateText;
            }
        }

        // Get condition
        const condition = card.querySelector('span.css-iudov9');
        if (condition) {
            data.condition = condition.textContent.trim();
        }

        // Check if promoted
        const promotedBadge = card.querySelector('div.css-5jyke2 div.css-s3yjnp');
        if (promotedBadge) {
            data.promoted = true;
        }

        // Check if delivery is available
        const deliveryBadge = card.querySelector('div[data-testid="card-delivery-badge"]');
        if (deliveryBadge) {
            data.delivery = true;
        }

        // Get main image
        const img = card.querySelector('img.css-8wsg1m');
        if (img) {
            data.image = img.src;

            // Try to get higher quality from srcset
            if (img.srcset) {
                const srcset = img.srcset;
                const srcsetParts = srcset.split(',');
                if (srcsetParts.length > 0) {
                    const bestPart = srcsetParts[srcsetParts.length - 1].trim();
                    const src = bestPart.split(' ')[0];
                    if (src) data.image = src;
                }
            }

            // Add to images array
            data.images.push(data.image);
        }

        return data;
    };

    // Extract all images and description from a listing page
    const extractDetailedData = (html, basicData) => {
        const data = { ...basicData };

        // Create parser
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'text/html');

        // Get description
        const descriptionElement = doc.querySelector('div[data-cy="ad_description"] div');
        if (descriptionElement) {
            data.description = descriptionElement.innerHTML.trim();
        } else {
            data.description = '';
        }

        // Get images from swiper
        const swiper = doc.querySelector('.swiper-wrapper');
        if (swiper) {
            // Create a temporary array for new images
            const newImages = [];

            // Process swiper images first
            const slides = swiper.querySelectorAll('.swiper-slide');
            slides.forEach(slide => {
                const slideImg = slide.querySelector('img');
                if (slideImg) {
                    let imgSrc = slideImg.src;

                    // Try to get higher quality from srcset
                    if (slideImg.srcset) {
                        const srcset = slideImg.srcset;
                        const srcsetParts = srcset.split(',');
                        if (srcsetParts.length > 0) {
                            const bestPart = srcsetParts[srcsetParts.length - 1].trim();
                            const src = bestPart.split(' ')[0];
                            if (src) imgSrc = src;
                        }
                    }

                    // Add to images array if not already there and not a placeholder
                    if (imgSrc && !newImages.includes(imgSrc) && imgSrc !== PLACEHOLDER_IMAGE) {
                        newImages.push(imgSrc);
                    }
                }
            });

            // If we have the main image and it's not a placeholder, add it if not already in the array
            if (data.image && data.image !== PLACEHOLDER_IMAGE && !newImages.includes(data.image)) {
                newImages.unshift(data.image); // Add at the beginning
            }

            // Now update the data.images with our deduplicated list
            data.images = newImages;

            // If we don't have any real images, but we have a placeholder, keep that
            if (data.images.length === 0 && data.image) {
                data.images.push(data.image);
            }
        } else if (data.image) {
            // If no swiper but we have a main image, use it
            data.images = [data.image];
        }

        return data;
    };

    // Create a new card with our premium layout
    const createPremiumCard = (data) => {
        const card = document.createElement('div');
        card.className = 'olx-card';

        // If promoted, add attribute for filtering
        if (data.promoted) {
            card.setAttribute('data-olx-promoted', 'true');

            // Apply hide class if filter is on
            const settings = getSettings();
            if (settings.hidePromoted) {
                card.classList.add('olx-hide-promoted');
            }
        }

        // Determine which image to use as background
        let backgroundImage = data.image;

        // If the main image is a placeholder and we have other images, use the first non-placeholder
        if (backgroundImage === PLACEHOLDER_IMAGE && data.images && data.images.length > 0) {
            // Find first non-placeholder image
            for (let i = 0; i < data.images.length; i++) {
                if (data.images[i] !== PLACEHOLDER_IMAGE) {
                    backgroundImage = data.images[i];
                    break;
                }
            }
        }

        // Add background image
        if (backgroundImage) {
            card.innerHTML = `<img src="${backgroundImage}" class="olx-bg-image" alt="Background">`;
        }

        // Main content container
        const content = document.createElement('div');
        content.className = 'olx-content';

        // Top section: Tags, Title, Price, Date
        let html = '<div class="olx-top">';

        // Title section with tags
        html += '<div class="olx-title-section">';

        // Tags row
        html += '<div class="olx-tags">';
        if (data.condition) {
            html += `<span class="olx-tag olx-condition-tag">${data.condition}</span>`;
        }
        if (data.promoted) {
            html += `<span class="olx-tag olx-promoted-tag">Promoted</span>`;
        }
        if (data.delivery) {
            html += `
                <span class="olx-tag olx-delivery-tag">
                    <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none" viewBox="0 0 24 24">
                        <path fill="currentColor" fill-rule="evenodd" d="m2 4 1-1h10l2 2h3.5L22 9.667V17l-1 1h-2a3 3 0 1 1-6 0h-3a3 3 0 1 1-6 0H3l-1-1V4Z"/>
                    </svg>
                    Delivery
                </span>
            `;
        }
        html += '</div>'; // Close tags

        // Title
        html += `<div class="olx-title">${data.title || ''}</div>`;
        html += '</div>'; // Close title section

        // Price and date section
        html += '<div class="olx-price-section">';
        if (data.date) {
            html += `<div class="olx-date">${data.date}</div>`;
        }
        html += `<div class="olx-price">${data.price || ''}</div>`;
        html += '</div>'; // Close price section

        html += '</div>'; // Close top section

        // Always create the gallery, even with just one image
        if (data.images && data.images.length > 0) {
            html += '<div class="olx-gallery">';

            // Create an array of images for the gallery that doesn't include the background
            const galleryImages = data.images.filter(img => img !== backgroundImage);

            // If we filtered out all images (because they were the same as background),
            // just show the background image in the gallery too
            if (galleryImages.length === 0) {
                html += `<img src="${backgroundImage}" class="olx-thumb" alt="Image 1" data-index="0">`;
            } else {
                // Otherwise show all images except the background
                for (let idx = 0; idx < galleryImages.length; idx++) {
                    // Find the original index in the full images array for the data-index attribute
                    const originalIndex = data.images.indexOf(galleryImages[idx]);
                    html += `<img src="${galleryImages[idx]}" class="olx-thumb" alt="Image ${idx + 1}" data-index="${originalIndex}">`;
                }
            }

            html += '</div>';
        }

        // Description if available
        if (data.description) {
            // Estimate if description is short
            const tempDiv = document.createElement('div');
            tempDiv.innerHTML = data.description;
            const textLength = tempDiv.textContent.length;
            const isShort = textLength < 100;

            html += `<div class="olx-desc ${isShort ? 'short' : ''}">${data.description}</div>`;

            // Only add toggle if not short
            if (!isShort) {
                html += '<div class="olx-toggle">+ Show more</div>';
            }
        }

        // Bottom section with actions and location
        html += '<div class="olx-bottom">';

        // Action buttons
        html += '<div class="olx-actions">';
        html += `
            <button class="olx-follow">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
                    <path fill="currentColor" fill-rule="evenodd" d="M20.219 10.367 12 20.419 3.806 10.4A3.96 3.96 0 0 1 3 8c0-2.206 1.795-4 4-4a4.004 4.004 0 0 1 3.868 3h2.264A4.003 4.003 0 0 1 17 4c2.206 0 4 1.794 4 4 0 .868-.279 1.698-.781 2.367M17 2a5.999 5.999 0 0 0-5 2.686A5.999 5.999 0 0 0 7 2C3.692 2 1 4.691 1 8a5.97 5.97 0 0 0 1.232 3.633L10.71 22h2.582l8.501-10.399A5.943 5.943 0 0 0 23 8c0-3.309-2.692-6-6-6"></path>
                </svg>
                <span>Последвай</span>
            </button>
            <a href="${data.url}" class="olx-original" target="_blank">
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                    <path d="M19 19H5V5h7V3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/>
                </svg>
                View Original
            </a>
        `;
        html += '</div>'; // Close actions

        // Location with Google Maps link
        if (data.location) {
            const mapsUrl = `https://www.google.com/maps/search/${encodeURIComponent(data.location)}`;
            html += `
                <a href="${mapsUrl}" class="olx-location" target="_blank">
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
                        <path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>
                    </svg>
                    ${data.location}
                </a>
            `;
        }

        html += '</div>'; // Close bottom section

        content.innerHTML = html;
        card.appendChild(content);

        // Add event listeners

        // Thumbnail clicks
        const thumbs = card.querySelectorAll('.olx-thumb');
        thumbs.forEach(thumb => {
            thumb.addEventListener('click', () => {
                const index = parseInt(thumb.getAttribute('data-index'));
                window.showOlxModal(data.images, index);
            });
        });

        // Background image click should show gallery
        const bgImage = card.querySelector('.olx-bg-image');
        if (bgImage && data.images && data.images.length > 0) {
            bgImage.addEventListener('click', () => {
                // Find the index of the background image in the images array
                const bgIndex = data.images.indexOf(backgroundImage);
                window.showOlxModal(data.images, bgIndex >= 0 ? bgIndex : 0);
            });
        }

        // Toggle description
        const desc = card.querySelector('.olx-desc:not(.short)');
        const toggle = card.querySelector('.olx-toggle');
        if (desc && toggle) {
            toggle.addEventListener('click', () => {
                if (desc.classList.contains('expanded')) {
                    desc.classList.remove('expanded');
                    toggle.textContent = '+ Show more';
                } else {
                    desc.classList.add('expanded');
                    toggle.textContent = '- Show less';
                }
            });
        }

        return card;
    };

    // Fetch additional data for a listing and update the card
    const fetchAndUpdateCard = (card, basicData) => {
        // Check if we already have this data cached
        if (dataCache[basicData.url]) {
            const fullData = dataCache[basicData.url];
            replaceCardContent(card, fullData);
            return;
        }

        // Make sure URL is absolute
        const fullUrl = basicData.url.startsWith('/') ? 'https://www.olx.bg' + basicData.url : basicData.url;

        // Create temporary content while loading
        replaceCardContent(card, basicData);

        // Fetch the listing page
        GM_xmlhttpRequest({
            method: 'GET',
            url: fullUrl,
            timeout: 10000,
            onload: (response) => {
                if (response.status === 200) {
                    try {
                        // Extract full data
                        const fullData = extractDetailedData(response.responseText, basicData);

                        // Cache the data
                        dataCache[basicData.url] = fullData;

                        // Update card with full data
                        replaceCardContent(card, fullData);
                    } catch (error) {
                        console.error('OLX Detail Error:', error);
                    }
                }
            },
            onerror: (error) => {
                console.error('OLX Fetch Error:', error);
            }
        });
    };

    // Replace a card's content with our premium layout
    const replaceCardContent = (card, data) => {
        // Create our custom card
        const premiumCard = createPremiumCard(data);

        // Replace all content in the original card
        card.innerHTML = '';

        // Copy all children from premium card to original card
        while (premiumCard.firstChild) {
            card.appendChild(premiumCard.firstChild);
        }

        // Copy relevant attributes from premium card
        if (premiumCard.hasAttribute('data-olx-promoted')) {
            card.setAttribute('data-olx-promoted', premiumCard.getAttribute('data-olx-promoted'));
        }

        if (premiumCard.classList.contains('olx-hide-promoted')) {
            card.classList.add('olx-hide-promoted');
        }

        // Copy class names
        card.className = `css-l9drzq ${premiumCard.className}`;

        // Mark as processed
        card.setAttribute('data-olx-processed', 'true');
        processedCards.add(card.id || Math.random().toString(36).substring(2, 9));
    };

    // Filter grid to only show cards with data-testid="l-card"
    const filterGrid = () => {
        const grid = document.querySelector('div[data-testid="listing-grid"]');
        if (!grid) return;

        // Get all direct children
        const children = Array.from(grid.children);

        // Remove any that don't have data-testid="l-card"
        children.forEach(child => {
            if (!child.matches('div[data-testid="l-card"]') &&
                !child.classList.contains('olx-loader')) {
                child.remove();
            }
        });
    };

    // Process all listing cards on the page
    const processCards = () => {
        const grid = document.querySelector('div[data-testid="listing-grid"]');
        if (!grid) return;

        // Filter grid first
        filterGrid();

        // Find all unprocessed cards
        const cards = grid.querySelectorAll('div[data-testid="l-card"]:not([data-olx-processed="true"])');

        cards.forEach(card => {
            // Skip if already processed
            if (processedCards.has(card.id || '')) return;

            // Extract basic data from the card
            const basicData = extractCardData(card);

            // Skip if no URL found
            if (!basicData.url) return;

            // Fetch additional data and update the card
            fetchAndUpdateCard(card, basicData);
        });
    };

    // Add promoted filter toggle
    const addFilterToggle = () => {
        // Find the pagination control area
        const filterArea = document.querySelector('div.css-k5itnz');
        if (!filterArea) return;

        // Load settings
        const settings = getSettings();

        // Create toggle element
        const filterToggle = document.createElement('div');
        filterToggle.className = 'olx-filter-control';
        filterToggle.innerHTML = `
            <label class="olx-switch">
                <input type="checkbox" id="olx-hide-promoted" ${settings.hidePromoted ? 'checked' : ''}>
                <span class="olx-slider"></span>
            </label>
            <span>Hide promoted listings</span>
        `;

        // Replace the "How are listings ordered?" link
        filterArea.innerHTML = '';
        filterArea.appendChild(filterToggle);

        // Add event listener for toggle
        const checkbox = filterToggle.querySelector('#olx-hide-promoted');
        checkbox.addEventListener('change', () => {
            // Update settings
            settings.hidePromoted = checkbox.checked;
            saveSettings(settings);

            // Update visibility of promoted cards
            const promotedCards = document.querySelectorAll('div[data-olx-promoted="true"]');
            promotedCards.forEach(card => {
                if (checkbox.checked) {
                    card.classList.add('olx-hide-promoted');
                } else {
                    card.classList.remove('olx-hide-promoted');
                }
            });
        });
    };

    // Function to detect the current page number and max page from pagination
    const detectPagination = () => {
        const pagination = document.querySelector('div[data-testid="pagination-wrapper"]');
        if (!pagination) return;

        // Find active page
        const activePage = pagination.querySelector('.pagination-item__active');
        if (activePage) {
            const pageLink = activePage.querySelector('a');
            if (pageLink) {
                const pageNum = parseInt(pageLink.textContent);
                if (!isNaN(pageNum)) {
                    currentPage = pageNum;
                }
            }
        }

        // Find last page
        const lastPageLink = pagination.querySelector('li:nth-last-child(2) a');
        if (lastPageLink) {
            const lastNum = parseInt(lastPageLink.textContent);
            if (!isNaN(lastNum)) {
                maxPage = lastNum;
            }
        }

        console.log(`OLX Pagination: Current page ${currentPage}, Max page ${maxPage}`);
    };

    // Infinite scroll - load next page
    const loadNextPage = () => {
        if (isLoadingMorePages || currentPage >= maxPage) return;

        // Mark as loading
        isLoadingMorePages = true;

        // Create loader element
        const loader = document.createElement('div');
        loader.className = 'olx-loader';
        loader.innerHTML = '<div class="olx-spinner"></div> Loading more listings...';

        // Add to page
        const grid = document.querySelector('div[data-testid="listing-grid"]');
        if (grid) {
            grid.appendChild(loader);
        }

        // Get next page URL
        const nextPage = currentPage + 1;

        // Check if already processed
        if (processedPages.has(nextPage)) {
            isLoadingMorePages = false;
            if (loader.parentNode) {
                loader.parentNode.removeChild(loader);
            }
            return;
        }

        // Build URL for next page
        let nextUrl = window.location.href;
        if (nextUrl.includes('page=')) {
            nextUrl = nextUrl.replace(/page=\d+/, `page=${nextPage}`);
        } else if (nextUrl.includes('?')) {
            nextUrl += `&page=${nextPage}`;
        } else {
            nextUrl += `?page=${nextPage}`;
        }

        // Fetch next page
        GM_xmlhttpRequest({
            method: 'GET',
            url: nextUrl,
            timeout: 15000,
            onload: (response) => {
                // Remove loader
                if (loader.parentNode) {
                    loader.parentNode.removeChild(loader);
                }

                if (response.status === 200) {
                    try {
                        // Parse HTML
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(response.responseText, 'text/html');

                        // Get cards from next page
                        const nextPageCards = Array.from(doc.querySelectorAll('div[data-testid="l-card"]'));

                        // Process and add each card to current page
                        nextPageCards.forEach(nextPageCard => {
                            // Extract basic data
                            const basicData = extractCardData(nextPageCard);

                            // Skip if no URL
                            if (!basicData.url) return;

                            // Create a new card element
                            const newCard = document.createElement('div');
                            newCard.setAttribute('data-testid', 'l-card');
                            newCard.setAttribute('data-cy', 'l-card');
                            newCard.className = 'css-l9drzq';
                            newCard.id = 'card_' + Math.random().toString(36).substring(2, 9);

                            // Add to grid
                            if (grid) {
                                grid.appendChild(newCard);

                                // Fetch details and update
                                fetchAndUpdateCard(newCard, basicData);
                            }
                        });

                        // Mark page as processed
                        processedPages.add(nextPage);

                        // Update current page
                        currentPage = nextPage;

                        // Reset loading flag
                        isLoadingMorePages = false;

                    } catch (error) {
                        console.error('OLX Next Page Error:', error);
                        isLoadingMorePages = false;
                    }
                } else {
                    console.error('OLX Next Page Failed:', response.status);
                    isLoadingMorePages = false;
                }
            },
            onerror: (error) => {
                console.error('OLX Next Page Request Error:', error);
                if (loader.parentNode) {
                    loader.parentNode.removeChild(loader);
                }
                isLoadingMorePages = false;
            }
        });
    };

    // Check scroll position and load more if needed
    const checkScroll = () => {
        if (isLoadingMorePages || currentPage >= maxPage) return;

        const scrollY = window.scrollY;
        const visibleHeight = window.innerHeight;
        const pageHeight = document.documentElement.scrollHeight;
        const bottomOfPage = scrollY + visibleHeight >= pageHeight - 800; // Load earlier (800px before bottom)

        if (bottomOfPage) {
            loadNextPage();
        }
    };

    // Setup scroll detection
    const setupInfiniteScroll = () => {
        // Detect pagination first
        detectPagination();

        // Check scroll position periodically
        setInterval(checkScroll, 500);

        // Also check on scroll with throttling
        let scrollTimeout;
        window.addEventListener('scroll', () => {
            if (scrollTimeout) return;

            scrollTimeout = setTimeout(() => {
                checkScroll();
                scrollTimeout = null;
            }, 200);
        }, { passive: true });
    };

    // Watch for changes in the DOM to process new cards and pagination
    const setupObserver = () => {
        const observer = new MutationObserver((mutations) => {
            let shouldProcess = false;
            let checkPagination = false;
            let checkFilter = false;

            for (const mutation of mutations) {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            // Check for listing cards
                            if (node.matches('div[data-testid="l-card"]') ||
                                node.querySelector('div[data-testid="l-card"]')) {
                                shouldProcess = true;
                            }

                            // Check for pagination
                            if (node.matches('div[data-testid="pagination-wrapper"]') ||
                                node.querySelector('div[data-testid="pagination-wrapper"]')) {
                                checkPagination = true;
                            }

                            // Check for filter area
                            if (node.matches('div.css-k5itnz') ||
                                node.querySelector('div.css-k5itnz')) {
                                checkFilter = true;
                            }
                        }
                    }
                }

                if (shouldProcess && checkPagination && checkFilter) break;
            }

            if (shouldProcess) {
                setTimeout(processCards, 100);
            }

            if (checkPagination) {
                setTimeout(detectPagination, 100);
            }

            if (checkFilter) {
                setTimeout(addFilterToggle, 100);
            }
        });

        observer.observe(document.body, { childList: true, subtree: true });
    };

    // Initialize script
    const init = () => {
        // Add styles
        addStyles();

        // Setup image modal
        setupImageModal();

        // Add filter toggle
        setTimeout(addFilterToggle, 1000);

        // Process cards
        setTimeout(processCards, 1000);

        // Setup infinite scroll
        setTimeout(setupInfiniteScroll, 1500);

        // Setup observer
        setupObserver();

        // Periodic card processing and filtering
        setInterval(() => {
            processCards();
            filterGrid();
        }, 2000);

        // Handle URL changes (SPA navigation)
        let lastUrl = location.href;
        setInterval(() => {
            const url = location.href;
            if (url !== lastUrl) {
                lastUrl = url;

                // Reset page tracking
                currentPage = 1;
                maxPage = 1;
                processedPages.clear();
                processedCards.clear();

                // Detect pagination and process cards after a delay
                setTimeout(() => {
                    detectPagination();
                    addFilterToggle();
                    processCards();
                }, 1000);
            }
        }, 1000);
    };

    // Start the script
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();