Spotless for eBay

Highlights and hides sponsored content on eBay

当前为 2025-07-16 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Spotless for eBay
// @namespace    https://github.com/OsborneLabs
// @version      1.3.2
// @description  Highlights and hides sponsored content on eBay
// @author       Osborne Labs
// @license      GPL-3
// @homepageURL  https://github.com/OsborneLabs/Spotless
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNDcwLjYgNTEyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4KICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxLjQwNjU5MzQwNjU5MzQwMTYgMS40MDY1OTM0MDY1OTM0MDE2KSBzY2FsZSgyLjgxIDIuODEpIj4KICAgIDxwYXRoIGZpbGw9IiM4NDg0ODQiIGQ9Ik0xMjcuOCw0Ni4xSDM4LjdWNDJjMC0yMy40LDE5LjEtNDIuNSw0Mi41LTQyLjVoNC4xYzIzLjQsMCw0Mi41LDE5LjEsNDIuNSw0Mi41VjQ2LjF6IE00Mi43LDQyaDgxVjQyIGMwLTIxLjItMTcuMi0zOC40LTM4LjQtMzguNGgtNC4xQzYwLDMuNSw0Mi43LDIwLjgsNDIuNyw0Mkw0Mi43LDQyeiI+PC9wYXRoPgogICAgPHBhdGggZmlsbD0iI0U1MzIzOCIgZD0iTTM1LjEsMTgxLjdoLTkuNmMtMTQuMywwLTI1LjktMTEuNi0yNS45LTI1LjlWNDkuNGMwLTUuMiw0LjMtOS41LDkuNS05LjVoMjYuMUwzNS4xLDE4MS43TDM1LjEsMTgxLjd6Ij48L3BhdGg+CiAgICA8cmVjdCB4PSIzNS4xIiB5PSIzOS45IiBmaWxsPSIjMDA2NEQyIiB3aWR0aD0iNDguMSIgaGVpZ2h0PSIxNDEuOCI+PC9yZWN0PgogICAgPHJlY3QgeD0iODMuMiIgeT0iMzkuOSIgZmlsbD0iI0Y1QUYwMiIgd2lkdGg9IjQ4LjEiIGhlaWdodD0iMTQxLjgiPjwvcmVjdD4KICAgIDxwYXRoIGZpbGw9IiM4NkI4MTciIGQ9Ik0xNDEsMTgxLjdoLTkuNlYzOS45aDI2LjFjNS4yLDAsOS41LDQuMyw5LjUsOS41djEwNi40QzE2NywxNzAuMSwxNTUuMywxODEuNywxNDEsMTgxLjd6Ij48L3BhdGg+CiAgPC9nPgo8L3N2Zz4K
// @match        https://www.ebay.com/sch/*
// @match        https://www.ebay.at/sch/*
// @match        https://www.ebay.ca/sch/*
// @match        https://www.ebay.ch/sch/*
// @match        https://www.ebay.com.au/sch/*
// @match        https://www.ebay.com.hk/sch/*
// @match        https://www.ebay.com.my/sch/*
// @match        https://www.ebay.com.sg/sch/*
// @match        https://www.ebay.co.uk/sch/*
// @match        https://www.ebay.de/sch/*
// @match        https://www.ebay.es/sch/*
// @match        https://www.ebay.fr/sch/*
// @match        https://www.ebay.ie/sch/*
// @match        https://www.ebay.it/sch/*
// @match        https://www.ebay.nl/sch/*
// @match        https://www.ebay.pl/sch/*
// @run-at       document-start
// @grant        none
// ==/UserScript==

/* jshint esversion: 11 */

(function() {
    "use strict";

    const APP_TITLE = "Spotless";
    const SPONSORED_CONTENT_KEY = "hideSponsoredContent";

    let hidingEnabled = localStorage.getItem(SPONSORED_CONTENT_KEY);
    hidingEnabled = hidingEnabled !== "false";
    let highlightedSponsoredContent = [];

    let isProcessing = false;
    let updateScheduled = false;
    let observerInitialized = false;

    const APP_ICONS = {
        locked: `
            <svg class="lock-icon lock-icon-animation" id="lockedIcon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                <path d="M12 2C9.79 2 8 3.79 8 6v4H7c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2v-8c0-1.1-.9-2-2-2h-1V6c0-2.21-1.79-4-4-4zm-2 8V6c0-1.1.9-2 2-2s2 .9 2 2v4h-4z"/>
            </svg>`,
        unlocked: `
            <svg class="lock-icon lock-icon-animation" id="unlockedIcon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                <path d="M17 8V6c0-2.76-2.24-5-5-5S7 3.24 7 6h2c0-1.66 1.34-3 3-3s3 1.34 3 3v2H7c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2h-1z"/>
            </svg>`,
        arrow: `
            <svg id="arrowIcon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                <path d="M7 10l5 5 5-5z"/>
            </svg>`,
        heart: `
            <svg class="heart-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41 0.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
            </svg>`
    };

    function init() {
        initializeUI();
        processSponsoredContent();
    }

    function initializeUI() {
        createStyles();
        buildPanel();
    }

    function createStyles() {
        const style = document.createElement("style");
        style.textContent = `
            :root {

                --size-font-title: 18px;
                --size-font-body: 14px;
                --size-font-body-error: 16px;
                --size-font-footer: 12px;

                --color-bubble: #e74c3c;
                --color-divider-border: rgba(255, 255, 255, 0.1);
                --color-font-text: white;
                --color-font-link-hover: lightblue;
                --color-font-link-visited: lightblue;
                --color-highlight-background: #ffe6e6;
                --color-highlight-border: red;
                --color-panel: rgba(34, 50, 70, 0.85);
                --color-panel-shadow: 0 8px 20px rgba(0, 0, 0, 0.20);
                --color-row: rgba(20, 30, 45, 0.5);
                --color-svg-fill: white;
                --color-svg-fill-heart-hover: red;
                --color-switch-knob: white;
                --color-switch-off: #ccc;
                --color-switch-on: #2AA866;
                --color-switch-on-shadow: 0 0 4px rgba(39, 174, 96, 0.3);

                --thickness-highlight-border: 2px;
            }

            #panelWrapper {
                position: fixed;
                bottom: 10px;
                right: 5px;
                z-index: 2147483647;
                width: 100%;
                max-width: 320px;
                padding: 0 16px;
                box-sizing: border-box;
                font-family: "Segoe UI", sans-serif;
            }

            @media (max-width: 768px) {
                #panelWrapper {
                    bottom: 5px;
                    left: 50%;
                    transform: translateX(-50%);
                    width: 90% !important;
                    right: unset;
                    padding: 0 16px;
                }
            }

            #panelBox {
                display: flex;
                flex-direction: column;
                gap: 0px;
                background: var(--color-panel);
                backdrop-filter: blur(10px);
                color: var(--color-font-text);
                padding: 16px;
                border-radius: 12px;
                width: 100%;
                box-shadow: var(--color-panel-shadow);
                transition: transform 0.2s ease;
            }

            #panelBox:hover {
                transform: translateY(-2px);
            }

            #panelHeader {
                display: flex;
                align-items: center;
                gap: 8px;
            }

            #panelHeader h2.panel-title {
                font-size: var(--size-font-title);
                font-weight: 600;
                margin: 0;
                color: var(--color-font-text);
            }

            .panel-body-row {
                margin: 0;
                font-size: var(--size-font-body);
                display: flex;
                align-items: center;
                justify-content: space-between;
                background: var(--color-row);
                backdrop-filter: blur(12px);
                padding: 12px 16px;
                border-radius: 8px;
            }

            .panel-body-row + .panel-body-row {
                margin-top: 5px;
            }

            .panelFooter {
                display: flex;
                align-items: center;
                justify-content: flex-end;
                gap: 6px;
                font-size: var(--size-font-footer);
                color: var(--color-font-text);
            }

            .panelPageContainer {
                position: relative;
                width: 100%;
            }

            hr.section-divider {
                flex-grow: 1;
                border: none;
                border-top: 1px solid var(--color-divider-border);
                margin: 12px 0;
            }

            #minimizePanelButton {
                width: 28px;
                height: 28px;
                margin-left: auto;
                padding: 2px;
                border: none;
                background: none;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                box-sizing: content-box;
            }

            #panelBox.minimized #arrowIcon {
                transform: rotate(180deg);
            }

            #panelBox.minimized {
                padding: 12px;
                overflow: hidden;
            }

            .lock-icon {
                width: 24px;
                height: 24px;
                padding: 4px;
                border-radius: 50%;
                fill: var(--color-svg-fill);
            }

            .lock-icon-animation {
                position: absolute;
                top: 0;
                left: 0;
                width: 28px;
                height: 28px;
                opacity: 0;
                transition: opacity 0.4s ease, transform 0.4s ease;
                transform: rotate(0deg);
            }

            .lock-icon-animation.active {
                opacity: 1;
                transform: rotate(360deg);
            }

            #lockIconContainer {
                position: relative;
                width: 28px;
                height: 28px;
            }

            #arrowIcon {
                width: 28px;
                height: 28px;
                fill: var(--color-svg-fill);
                transition: transform 0.3s ease;
            }

            .heart-icon {
                width: 10px;
                height: 10px;
                vertical-align: middle;
                fill: var(--color-svg-fill);
            }

            .heart-icon:hover {
                fill: var(--color-svg-fill-heart-hover);
            }

            #countBubble {
                background-color: var(--color-bubble);
                color: var(--color-font-text);
                font-size: 12px;
                font-weight: bold;
                padding: 3px 8px;
                border-radius: 999px;
                min-width: 20px;
                text-align: center;
            }

            .switch {
                position: relative;
                display: inline-block;
                width: 42px;
                height: 22px;
            }

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

            .switch-label {
                margin-right: 10px;
            }

            .slider {
                position: absolute;
                cursor: pointer;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background-color: var(--color-switch-off);
                transition: 0.3s;
                border-radius: 34px;
            }

            .slider:before {
                position: absolute;
                content: "";
                height: 18px;
                width: 18px;
                top: 2px;
                left: 2px;
                background-color: var(--color-switch-knob);
                transition: 0.3s;
                border-radius: 50%;
            }

            input:checked + .slider {
                background-color: var(--color-switch-on);
                box-shadow: var(--color-switch-on-shadow);
            }

            input:checked + .slider:before {
                transform: translateX(20px);
            }

            #creator-page {
                color: var(--color-font-text);
                transition: color 0.3s ease;
            }

            #creator-page:hover, .outbound-status-page:hover, .retry-link:hover{
                color: var(--color-font-link-hover);
            }

            .error-page {
                text-align: center;
                font-size: var(--size-font-body-error);
            }

            .outbound-status-page, .retry-link {
                text-decoration: underline;
                color: var(--color-font-text);
            }

            .outbound-status-page:visited, .retry-link:visited {
                color: var(--color-font-link-visited);
            }

            .sponsored-highlight {
            border: var(--thickness-highlight-border) solid var(--color-highlight-border) !important;
            background-color: var(--color-highlight-background);
            }

            .sponsored-hidden {
                display: none !important;
            }

        `;
        document.head.appendChild(style);
    }

    function determineCurrentPage(sponsoredCount) {
        if (sponsoredCount === 0) {
            return buildPanelErrorPage();
        }
        return buildPanelHomePage();
    }

    function initializeObserver() {
        if (observerInitialized) return;
        observer.observe(document.body, {
            childList: true,
            subtree: true,
        });
        observerInitialized = true;
    }

    async function buildPanel() {
        const wrapper = document.createElement("div");
        wrapper.id = "panelWrapper";

        const box = document.createElement("div");
        box.id = "panelBox";

        const header = buildPanelHeader();
        const sponsoredCount = await processSponsoredContent();
        const body = determineCurrentPage(sponsoredCount, hidingEnabled);
        const footer = buildPanelFooter();

        const topDivider = document.createElement("hr");
        topDivider.className = "section-divider";
        const bottomDivider = document.createElement("hr");
        bottomDivider.className = "section-divider";

        box.appendChild(header);
        box.appendChild(topDivider);
        box.appendChild(body);
        box.appendChild(bottomDivider);
        box.appendChild(footer);

        wrapper.appendChild(box);
        document.body.appendChild(wrapper);

        const minimizePanelButton = document.getElementById("minimizePanelButton");
        minimizePanelButton.addEventListener("click", () => {

            const panelBox = document.getElementById("panelBox");
            const isCurrentlyMinimized = panelBox.classList.contains("minimized");
            const newState = !isCurrentlyMinimized;

            localStorage.setItem("panelMinimized", newState);
            setPanelMinimized(newState);
        });

        const isPanelMinimized = localStorage.getItem("panelMinimized") === "true";
        setPanelMinimized(isPanelMinimized);

        const toggleSponsoredContentSwitchInput = document.getElementById("toggleSponsoredContentSwitch");

        if (!toggleSponsoredContentSwitchInput) {
            updateLockIcons();
            return;
        }

        toggleSponsoredContentSwitchInput.addEventListener("change", (e) => {
            hidingEnabled = e.target.checked;
            localStorage.setItem(SPONSORED_CONTENT_KEY, hidingEnabled);
            updateLockIcons();
            debounceHighlighting();
        });
        updateLockIcons();
    }

    function buildPanelHeader() {
        const header = document.createElement("div");
        header.id = "panelHeader";

        header.innerHTML = `
            <div id="lockIconContainer">
                ${APP_ICONS.locked}
                ${APP_ICONS.unlocked}
            </div>
            <h2 class="panel-title" aria-level="1">${APP_TITLE}</h2>
            <button id="minimizePanelButton" aria-label="Expand or minimize the panel">
                ${APP_ICONS.arrow}
            </button>
        `;
        return header;
    }

    function buildPanelFooter() {
        const footer = document.createElement("div");
        footer.className = "panelFooter";

        const creatorPage = document.createElement("a");
        creatorPage.href = "https://github.com/OsborneLabs/Spotless";
        creatorPage.target = "_blank";
        creatorPage.style.textDecoration = "none";
        creatorPage.textContent = "Osborne";
        creatorPage.id = "creator-page";

        const separator = document.createElement("span");
        separator.textContent = " · ";

        const donatePage = document.createElement("a");
        donatePage.href = "https://ko-fi.com/osbornelabs";
        donatePage.target = "_blank";
        donatePage.innerHTML = APP_ICONS.heart;
        donatePage.style.display = "inline-flex";
        donatePage.style.alignItems = "center";
        donatePage.style.justifyContent = "center";

        footer.appendChild(creatorPage);
        footer.appendChild(separator);
        footer.appendChild(donatePage);
        return footer;
    }

    function buildPanelRow(innerHTML = "") {
        const row = document.createElement("div");
        row.className = "panel-body-row";
        row.innerHTML = innerHTML;
        return row;
    }

    function buildCountSponsoredContentRow() {
        const row = buildPanelRow(`
            <span>Content found</span>
            <span id="countBubble">0</span>
        `);
        row.id = "count-sponsored-content-row";
        return row;
    }

    function buildToggleSponsoredContentRow() {
        const row = buildPanelRow(`
            <span class="switch-label">Hide sponsored content</span>
            <label class="switch" aria-label="Toggle visibility of sponsored content">
                <input type="checkbox" id="toggleSponsoredContentSwitch" ${hidingEnabled ? "checked" : ""}>
                <span class="slider"></span>
            </label>
        `);
        row.id = "toggle-sponsored-content-row";
        return row;
    }

    function buildPanelHomePage() {
        const pageContainer = document.createElement("div");
        pageContainer.id = "panelPageContainer";
        pageContainer.classList.add("panelPageContainer");

        const homePage = document.createElement("div");
        homePage.id = "homePage";
        homePage.className = "panel-page";
        homePage.style.display = "block";

        const countSponsoredContentRow = buildCountSponsoredContentRow();
        const toggleSponsoredContentRow = buildToggleSponsoredContentRow();

        homePage.appendChild(countSponsoredContentRow);
        homePage.appendChild(toggleSponsoredContentRow);

        pageContainer.appendChild(homePage);
        return pageContainer;
    }

    function buildPanelErrorPage() {
        const errorPage = document.createElement("div");
        errorPage.classList.add("error-page", "panel-page");

        const errorMessage = document.createElement("p");
        errorMessage.textContent = "No sponsored content found. ";
        errorMessage.appendChild(document.createElement("br"));

        const outboundRetryPage = document.createElement("a");
        outboundRetryPage.textContent = "Retry";
        outboundRetryPage.href = "#";
        outboundRetryPage.addEventListener("click", function(event) {
            event.preventDefault();
            location.reload();
        });
        outboundRetryPage.classList.add("retry-link");

        const outboundStatusPage = document.createElement("a");
        outboundStatusPage.textContent = "check status";
        outboundStatusPage.href = "https://github.com/OsborneLabs/Spotless";
        outboundStatusPage.target = "_blank";
        outboundStatusPage.classList.add("outbound-status-page");

        errorMessage.appendChild(outboundRetryPage);
        errorMessage.appendChild(document.createTextNode(" or "));
        errorMessage.appendChild(outboundStatusPage);

        const endText = document.createTextNode(".");
        errorMessage.appendChild(endText);
        errorPage.appendChild(errorMessage);
        return errorPage;
    }

    function setPanelMinimized(minimized) {
        const panelBox = document.getElementById("panelBox");
        if (!panelBox) return;

        const panelPage = panelBox.querySelector(".panel-page");
        const sectionDividers = panelBox.querySelectorAll(".section-divider");
        const panelFooter = panelBox.querySelector(".panelFooter");

        panelBox.classList.toggle("minimized", minimized);

        if (panelPage) panelPage.style.display = minimized ? "none" : "block";
        sectionDividers.forEach(el => {
            el.style.display = minimized ? "none" : "";
        });
        if (panelFooter) panelFooter.style.display = minimized ? "none" : "";
    }

    function updateLockIcons() {
        const locked = document.getElementById("lockedIcon");
        const unlocked = document.getElementById("unlockedIcon");
        locked.classList.toggle("active", hidingEnabled);
        unlocked.classList.toggle("active", !hidingEnabled);
    }

    function getListingElements() {
        return Array.from(document.querySelectorAll("li[class*='s-']")).filter(
            (el) => el.className.split(/\s+/).some((cls) => /^s-[\w-]+$/.test(cls))
        );
    }

    function detectSponsoredListingByBase64(batchSize = 5) {
        return new Promise((resolve) => {
            const listings = getListingElements();
            const sponsoredElements = [];
            let index = 0;

            function processBatch() {
                const end = Math.min(index + batchSize, listings.length);
                const batch = listings.slice(index, end);
                let processedInBatch = 0;

                if (batch.length === 0) {
                    resolve(sponsoredElements);
                    return;
                }

                batch.forEach((listing) => {
                    const svgImage = listing.querySelector(".s-item__sep span[aria-hidden='true']");
                    if (!svgImage) return done();

                    const backgroundImage = getComputedStyle(svgImage.parentElement).backgroundImage;
                    const match = backgroundImage.match(/url\("data:image\/svg\+xml;base64,([^"]+)"\)/);

                    if (!match || !match[1]) return done();

                    const base64 = match[1];
                    const svgString = atob(base64);
                    const img = new Image();
                    const canvas = document.createElement("canvas");
                    const ctx = canvas.getContext("2d");

                    img.src = "data:image/svg+xml;base64," + btoa(svgString);

                    img.onload = () => {
                        canvas.width = img.naturalWidth || 25;
                        canvas.height = img.naturalHeight || 25;
                        ctx.drawImage(img, 0, 0);
                        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;

                        for (let i = 0; i < imageData.length; i += 4) {
                            const r = imageData[i];
                            const g = imageData[i + 1];
                            const b = imageData[i + 2];
                            const a = imageData[i + 3];

                            if (a > 0 && r === 112 && g === 112 && b === 112) {
                                sponsoredElements.push(listing);
                                break;
                            }
                        }
                        done();
                    };

                    img.onerror = () => {
                        done();
                    };

                    function done() {
                        processedInBatch++;
                        if (processedInBatch === batch.length) {
                            index += batchSize;
                            setTimeout(processBatch, 0);
                        }
                    }
                });
                if (batch.length === 0) resolve(sponsoredElements);
            }
            if (listings.length === 0) {
                resolve([]);
            } else {
                processBatch();
            }
        });
    }

    function detectSponsoredListingByAriaID(listings = getListingElements()) {
        const groupMap = {};
        const ariaLabelToGroup = {};
        let groupCounter = 0;

        listings.forEach(listing => {
            const labelSpans = Array.from(listing.querySelectorAll('span[aria-labelledby]'));
            if (labelSpans.length === 0) return;

            for (const spanElement of labelSpans) {
                const ariaLabel = spanElement.getAttribute('aria-labelledby');
                if (!ariaLabel || !ariaLabel.includes("s-")) continue;

                if (!ariaLabelToGroup[ariaLabel]) {
                    ariaLabelToGroup[ariaLabel] = `Group ${numberToLettersForAriaID(groupCounter)}`;
                    groupCounter++;
                }
                const group = ariaLabelToGroup[ariaLabel];
                if (!groupMap[group]) {
                    groupMap[group] = [];
                }
                groupMap[group].push(listing);
                break;
            }
        });

        let sponsoredGroup = null;
        let minCount = Infinity;

        for (const [group, groupListings] of Object.entries(groupMap)) {
            if (groupListings.length < minCount) {
                sponsoredGroup = group;
                minCount = groupListings.length;
            }
        }
        return sponsoredGroup ? groupMap[sponsoredGroup] : [];
    }

    function numberToLettersForAriaID(num) {
        let result = '';
        while (num >= 0) {
            result = String.fromCharCode((num % 26) + 65) + result;
            num = Math.floor(num / 26) - 1;
        }
        return result;
    }

    function detectSponsoredListingByInvertFilter() {
        const INVERT_REGEX = /div\.([a-zA-Z0-9_-]+)(?:\s+div)?\s*\{[^}]*color:\s*(black|white);[^}]*filter:\s*invert\(([-\d.]+)\)/g;

        const sponsoredGroups = {};
        const classToInvertMap = {};

        const styleTags = Array.from(document.querySelectorAll("style"));
        styleTags.forEach(styleTag => {
            const css = styleTag.textContent;
            let match;
            while ((match = INVERT_REGEX.exec(css)) !== null) {
                const [_, className, color, invertValue] = match;
                if (!classToInvertMap[className]) {
                    classToInvertMap[className] = [];
                }
                classToInvertMap[className].push({
                    color,
                    invert: parseFloat(invertValue)
                });
            }
        });
        const containers = Array.from(document.querySelectorAll('div[role="text"]')).filter(container => {
            return container.querySelector('div[aria-hidden="true"]');
        });

        containers.forEach(container => {
            const targetDiv = container.querySelector('div[aria-hidden="true"]');
            if (!targetDiv) return;

            const ancestorDiv = container.closest("div[class*='_']");
            if (!ancestorDiv) return;

            const classList = Array.from(ancestorDiv.classList);
            const dynamicClass = classList.find(cls => classToInvertMap[cls]);
            if (!dynamicClass) return;

            const candidates = classToInvertMap[dynamicClass];
            const invertEntry = candidates?.[0];
            if (!invertEntry) return;

            const key = invertEntry.invert;
            if (!sponsoredGroups[key]) {
                sponsoredGroups[key] = [];
            }
            sponsoredGroups[key].push(container);
        });

        const groupEntries = Object.entries(sponsoredGroups);
        if (groupEntries.length === 0) {
            return {
                allGroups: []
            };
        }

        const sortedGroups = groupEntries.sort((a, b) => a[1].length - b[1].length);
        const [sponsoredInvert, sponsoredList] = sortedGroups[0];

        return {
            invert: parseFloat(sponsoredInvert),
            elements: sponsoredList,
            allGroups: groupEntries
        };
    }

    function detectSponsoredBanner() {
        const tld = getEffectiveTLD();
        if (tld !== "co.uk" && tld !== "com.au" && tld !== "de") return [];

        return new Promise((resolve) => {
            setTimeout(() => {
                const banners = Array.from(
                    document.querySelectorAll(".s-answer-region-center-top.s-answer-region > div")
                ).filter((el) => {
                    return !el.className.includes("srp-controls") && el.offsetHeight >= 100;
                });
                resolve(banners);
            }, 525);
        });
    }

    async function processSponsoredContent() {
        if (isProcessing) return 0;
        isProcessing = true;

        try {
            observer.disconnect();
            clearDesignateSponsoredContent();

            const listings = getListingElements();
            const unprocessedListings = listings.filter(el => !el.hasAttribute("data-sponsored-processed"));
            const detectedSponsoredElements = new Set();

            const base64Results = await detectSponsoredListingByBase64();
            base64Results.forEach(el => {
                const li = el.closest("li");
                if (li) detectedSponsoredElements.add(li);
            });

            if (detectedSponsoredElements.size === 0) {
                const ariaMethod = detectSponsoredListingByAriaID(unprocessedListings);
                ariaMethod.forEach(listing => {
                    const li = listing.closest("li");
                    if (li) detectedSponsoredElements.add(li);
                });
            }
            if (detectedSponsoredElements.size === 0) {
                const invertMethod = detectSponsoredListingByInvertFilter();
                invertMethod.elements?.forEach(container => {
                    const li = container.closest("li");
                    if (li) detectedSponsoredElements.add(li);
                });
            }

            const banners = await detectSponsoredBanner();
            banners.forEach(banner => {
                detectedSponsoredElements.add(banner);
            });

            requestAnimationFrame(() => {
                for (const el of detectedSponsoredElements) {
                    if (!el.hasAttribute("data-sponsored-processed")) {
                        designateSponsoredContent(el);
                        highlightSponsoredContent(el);
                        hideShowSponsoredContent(el, hidingEnabled);
                    }
                }

                const count = detectedSponsoredElements.size;
                countSponsoredContent(count);

                initializeObserver();
                isProcessing = false;
            });
            return detectedSponsoredElements.size;
        } catch (err) {
            console.error("Error processing sponsored content:", err);
            isProcessing = false;
            initializeObserver();
            return 0;
        }
    }

    function designateSponsoredContent(el) {
        el.setAttribute("data-sponsored", "true");
        el.setAttribute("data-sponsored-processed", "true");
        highlightedSponsoredContent.push(el);
    }

    function clearDesignateSponsoredContent() {
        highlightedSponsoredContent.forEach(el => {
            el.classList.remove("sponsored-hidden");
            el.removeAttribute("data-sponsored");
            el.removeAttribute("data-sponsored-processed");
            el.style.border = "";
            el.style.backgroundColor = "";
        });
        highlightedSponsoredContent.length = 0;
    }

    function highlightSponsoredContent(element) {
        element.setAttribute("data-sponsored", "true");
        element.classList.add("sponsored-highlight");
    }

    function hideShowSponsoredContent(element, hide) {
        element.classList.toggle("sponsored-hidden", hide);
    }

    function countSponsoredContent(count) {
        const countBubble = document.getElementById("countBubble");
        if (countBubble) countBubble.textContent = count;
    }

    function debounceHighlighting() {
        if (updateScheduled || isProcessing) return;
        updateScheduled = true;
        requestAnimationFrame(() => {
            processSponsoredContent().finally(() => {
                updateScheduled = false;
            });
        });
    }

    function getEffectiveTLD() {
        const host = window.location.hostname;
        const tldMap = {
            "ebay.com.au": "com.au",
            "ebay.com.hk": "com.hk",
            "ebay.com.my": "com.my",
            "ebay.com.sg": "com.sg",
            "ebay.co.uk": "co.uk",
            "ebay.at": "at",
            "ebay.ca": "ca",
            "ebay.ch": "ch",
            "ebay.de": "de",
            "ebay.es": "es",
            "ebay.fr": "fr",
            "ebay.ie": "ie",
            "ebay.it": "it",
            "ebay.nl": "nl",
            "ebay.pl": "pl",
            "ebay.com": "com",
        };
        return Object.entries(tldMap).find(([domain]) => host.endsWith(domain))?.[1] || "Unknown";
    }

    const observer = new MutationObserver(() => {
        debounceHighlighting();
    });

    (async function() {
        if (document.readyState === "complete" || document.readyState === "interactive") {
            await new Promise(r => setTimeout(r, 200));
            init();
        } else {
            window.addEventListener("DOMContentLoaded", async () => {
                await new Promise(r => setTimeout(r, 200));
                init();
            });
        }
    })();

    window.addEventListener("storage", (event) => {
        if (event.key === SPONSORED_CONTENT_KEY) {
            const newValue = event.newValue === "true";
            if (newValue !== hidingEnabled) {
                hidingEnabled = newValue;
                const toggleSponsoredContentSwitchInput = document.getElementById("toggleSponsoredContentSwitch");
                if (toggleSponsoredContentSwitchInput) toggleSponsoredContentSwitchInput.checked = hidingEnabled;
                updateLockIcons();
                debounceHighlighting();
            }
        } else if (event.key === "panelMinimized") {
            setPanelMinimized(event.newValue === "true");
        }
    });
})();