Advanced Responsive Ethical Data Gathering Tool v3.2

Massive integrated tool for ethical data research: forces media load; unblurs content (via CSS, canvas, OpenCV.js); reveals hidden text; recovers hidden data; fixes problematic overlays and unselectable text; extracts rich data (including high-detail image info), meta, and more; plus OCR, state-preserving UI, clearable logs, and live logging.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Advanced Responsive Ethical Data Gathering Tool v3.2
// @namespace    http://tampermonkey.net/
// @version      2025-02-11
// @description  Massive integrated tool for ethical data research: forces media load; unblurs content (via CSS, canvas, OpenCV.js); reveals hidden text; recovers hidden data; fixes problematic overlays and unselectable text; extracts rich data (including high-detail image info), meta, and more; plus OCR, state-preserving UI, clearable logs, and live logging.
// @author       LittleLooney
// @license Copyright (C) Littlelooney All rights reserved.
// @match        *://*/*
// @icon         https://www.google.com
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    /**********************************
     * Utility Functions & Logging  *
     **********************************/

    const DEBUG = true;

    // Debounce: Ensures that a function isn't called too frequently.
    function debounce(func, wait) {
        let timeout;
        return function(...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    }

    // Logging function: Logs messages with timestamps to console and to the sidebar log area.
    function logMessage(level, message) {
        const timestamp = new Date().toISOString();
        const fullMessage = `[${timestamp}] [${level}] ${message}`;
        if (DEBUG) {
            console[level](fullMessage);
        }
        const logArea = document.getElementById('logArea');
        if (logArea) {
            const logEntry = document.createElement('div');
            logEntry.textContent = fullMessage;
            logEntry.style.borderBottom = "1px solid #444";
            logEntry.style.padding = "2px 0";
            logArea.appendChild(logEntry);
            // Auto-scroll to the bottom.
            logArea.scrollTop = logArea.scrollHeight;
        }
    }

    /**********************************
     * Force Media Loading Function   *
     **********************************/

    function forceLoadMedia() {
        try {
            // Process images.
            const images = document.querySelectorAll('img');
            images.forEach(img => {
                if (img.hasAttribute('loading')) img.removeAttribute('loading');
                // If there's a high-res version in a data attribute, use it.
                if (img.dataset && img.dataset.fullsrc) {
                    img.src = img.dataset.fullsrc;
                }
                if (img.dataset && img.dataset.src) {
                    img.src = img.dataset.src;
                }
                img.classList.remove('lazy', 'lazyload');
                // Force reload if not complete.
                if (!img.complete || img.naturalWidth === 0) {
                    img.src = img.src;
                }
            });
            logMessage('info', `Force loaded ${images.length} image(s).`);

            // Process videos.
            const videos = document.querySelectorAll('video');
            videos.forEach(video => {
                video.setAttribute('preload', 'auto');
                video.load();
            });
            logMessage('info', `Force loaded ${videos.length} video(s).`);

            // Process iframes.
            const iframes = document.querySelectorAll('iframe');
            iframes.forEach(iframe => {
                let src = iframe.getAttribute('src');
                if (src) iframe.src = src;
            });
            logMessage('info', `Force loaded ${iframes.length} iframe(s).`);
        } catch (error) {
            logMessage('error', `Error in forceLoadMedia: ${error}`);
        }
    }

    /**********************************
     * Unblurring Methods             *
     **********************************/

    // Remove CSS blur filters.
    function unblurElements() {
        try {
            const blurredElements = document.querySelectorAll('[style*="filter: blur"], .blurred');
            blurredElements.forEach(el => {
                el.style.filter = 'none';
                el.classList.remove('blurred');
            });
            logMessage('info', `Removed CSS blur filters from ${blurredElements.length} element(s).`);
        } catch (error) {
            logMessage('error', `Error in unblurElements: ${error}`);
        }
    }

    /**********************************
     * Canvas-Based Image Unblurring  *
     **********************************/

    function applyConvolution(imageData, kernel, kernelSize) {
        const width = imageData.width;
        const height = imageData.height;
        const inputData = imageData.data;
        const outputData = new Uint8ClampedArray(inputData.length);
        const half = Math.floor(kernelSize / 2);

        for (let y = 0; y < height; y++) {
            for (let x = 0; x < width; x++) {
                let r = 0, g = 0, b = 0, a = 0;
                for (let ky = -half; ky <= half; ky++) {
                    for (let kx = -half; kx <= half; kx++) {
                        const ix = x + kx;
                        const iy = y + ky;
                        if (ix >= 0 && ix < width && iy >= 0 && iy < height) {
                            const idx = (iy * width + ix) * 4;
                            const weight = kernel[(ky + half) * kernelSize + (kx + half)];
                            r += inputData[idx] * weight;
                            g += inputData[idx + 1] * weight;
                            b += inputData[idx + 2] * weight;
                            a += inputData[idx + 3] * weight;
                        }
                    }
                }
                const outIdx = (y * width + x) * 4;
                outputData[outIdx] = Math.min(255, Math.max(0, r));
                outputData[outIdx + 1] = Math.min(255, Math.max(0, g));
                outputData[outIdx + 2] = Math.min(255, Math.max(0, b));
                outputData[outIdx + 3] = Math.min(255, Math.max(0, a));
            }
        }
        return new ImageData(outputData, width, height);
    }

    function sharpenImageData(imageData) {
        // Use a basic 3x3 sharpening kernel.
        const kernel = [
            -1, -1, -1,
            -1,  9, -1,
            -1, -1, -1
        ];
        return applyConvolution(imageData, kernel, 3);
    }

    // Canvas-based unblur for images.
    function advancedUnblurImages_Canvas() {
        try {
            const imgs = document.querySelectorAll('img');
            imgs.forEach(img => {
                try {
                    img.style.filter = 'none'; // Remove any CSS blur.
                    if (!img.complete || img.naturalWidth === 0) {
                        logMessage('warn', 'Skipping an image because it is not fully loaded.');
                        return;
                    }
                    const canvas = document.createElement('canvas');
                    const context = canvas.getContext('2d');
                    canvas.width = img.naturalWidth;
                    canvas.height = img.naturalHeight;
                    context.drawImage(img, 0, 0);
                    let imageData = context.getImageData(0, 0, canvas.width, canvas.height);
                    let sharpenedData = sharpenImageData(imageData);
                    context.putImageData(sharpenedData, 0, 0);
                    img.src = canvas.toDataURL();
                    logMessage('info', 'Canvas-based advanced unblurring applied to an image.');
                } catch (innerError) {
                    logMessage('error', 'Error in canvas-based unblur: ' + innerError);
                }
            });
        } catch (error) {
            logMessage('error', 'Error in advancedUnblurImages_Canvas: ' + error);
        }
    }

    /**********************************
     * OpenCV.js-Based Image Unblurring *
     **********************************/

    function loadOpenCVJS(callback) {
        if (window.cv) {
            callback();
            return;
        }
        const script = document.createElement('script');
        script.src = "https://docs.opencv.org/4.x/opencv.js";
        script.async = true;
        script.onload = function () {
            cv['onRuntimeInitialized'] = function() {
                logMessage('info', 'OpenCV.js runtime initialized.');
                callback();
            };
        };
        script.onerror = function() {
            logMessage('error', 'Failed to load OpenCV.js.');
        };
        document.body.appendChild(script);
    }

    function advancedUnblurImages_OpenCV() {
        loadOpenCVJS(() => {
            const imgs = document.querySelectorAll('img');
            imgs.forEach(img => {
                try {
                    img.style.filter = 'none';
                    if (!img.complete || img.naturalWidth === 0) {
                        logMessage('warn', 'Skipping an image (OpenCV) because it is not fully loaded.');
                        return;
                    }
                    const canvas = document.createElement('canvas');
                    canvas.width = img.naturalWidth;
                    canvas.height = img.naturalHeight;
                    const ctx = canvas.getContext('2d');
                    ctx.drawImage(img, 0, 0);
                    let src = cv.imread(canvas);
                    let dst = new cv.Mat();
                    let kernel = cv.matFromArray(3, 3, cv.CV_32F,
                        [-1, -1, -1,
                         -1,  9, -1,
                         -1, -1, -1]);
                    cv.filter2D(src, dst, cv.CV_8U, kernel);
                    cv.imshow(canvas, dst);
                    src.delete(); dst.delete(); kernel.delete();
                    img.src = canvas.toDataURL();
                    logMessage('info', 'OpenCV-based advanced unblurring applied to an image.');
                } catch (e) {
                    logMessage('error', 'Error in advancedUnblurImages_OpenCV: ' + e);
                }
            });
        });
    }

    // Combined unblur function.
    function advancedUnblurContent() {
        try {
            unblurElements();
            forceLoadMedia();
            advancedUnblurImages_Canvas(); // Optionally, also run advancedUnblurImages_OpenCV();
            logMessage('info', 'Advanced unblur content executed.');
        } catch (error) {
            logMessage('error', 'Error in advancedUnblurContent: ' + error);
        }
    }

    /**********************************
     * Reveal Hidden Text Function    *
     **********************************/

    /**
     * Scans elements (e.g., within .text_layer) and if the computed text color is transparent
     * or a heavy text-shadow is applied, sets the text color to black, removes/reduces the shadow,
     * and ensures full opacity.
     */
    function revealHiddenText() {
        const textElements = document.querySelectorAll('.text_layer *');
        textElements.forEach(el => {
            try {
                const cs = window.getComputedStyle(el);
                if (cs.color === "rgba(0, 0, 0, 0)" || cs.color === "transparent") {
                    el.style.color = "black";
                }
                if (cs.textShadow && cs.textShadow !== "none") {
                    // Option: reduce the shadow blur (e.g., to 5px) instead of removing entirely.
                    el.style.textShadow = "none";
                }
                if (cs.opacity < 1) {
                    el.style.opacity = "1";
                }
            } catch (e) {
                logMessage('error', 'Error in revealHiddenText on an element: ' + e);
            }
        });
        logMessage('info', 'Completed revealHiddenText() processing.');
    }

    /**********************************
     * Advanced Hidden Data Recovery  *
     **********************************/

    function advancedRecoverHiddenContent() {
        let recoveredCount = 0;

        // Process media elements.
        const mediaSelectors = ['img', 'video', 'iframe', 'embed', 'object'];
        const mediaElements = document.querySelectorAll(mediaSelectors.join(','));
        mediaElements.forEach(el => {
            try {
                const cs = window.getComputedStyle(el);
                const zIndex = parseInt(cs.zIndex) || 0;
                if (zIndex > 1000) return;

                let isHidden = (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0');
                const rect = el.getBoundingClientRect();
                if (rect.width === 0 || rect.height === 0) isHidden = true;
                if (isHidden) {
                    el.style.display = 'block';
                    el.style.visibility = 'visible';
                    el.style.opacity = '1';
                    if (el.tagName.toLowerCase() === 'img' && (!el.complete || el.naturalWidth === 0)) {
                        el.src = el.src;
                    }
                    recoveredCount++;
                }
            } catch (e) {
                logMessage('error', 'Error processing media element in advancedRecoverHiddenContent: ' + e);
            }
        });

        // Process text elements.
        const textSelectors = ['p', 'span', 'div'];
        const textElements = document.querySelectorAll(textSelectors.join(','));
        textElements.forEach(el => {
            try {
                const cs = window.getComputedStyle(el);
                const zIndex = parseInt(cs.zIndex) || 0;
                if (zIndex > 1000) return;

                let isHidden = (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0');
                const rect = el.getBoundingClientRect();
                if (rect.width < 5 || rect.height < 5) isHidden = true;
                const text = el.innerText.trim();
                if (text.length > 5 && isHidden) {
                    el.style.display = 'block';
                    el.style.visibility = 'visible';
                    el.style.opacity = '1';
                    recoveredCount++;
                }
            } catch (e) {
                logMessage('error', 'Error processing text element in advancedRecoverHiddenContent: ' + e);
            }
        });

        logMessage('info', `Advanced recovered ${recoveredCount} content element(s).`);
    }

    /**********************************
     * Fix Elements for Accessibility *
     **********************************/

    /**
     * 1. Removes any unselectable="on" attributes so that text can be selected.
     * 2. Hides known overlay elements (for example, with class "promo_div")
     *    so that the blank/blurred box does not obscure the page.
     */
    function fixElementsForAccessibility() {
        try {
            // Remove unselectable attributes.
            const unselectableElements = document.querySelectorAll('[unselectable="on"]');
            unselectableElements.forEach(el => {
                el.removeAttribute('unselectable');
            });
            logMessage('info', `Removed unselectable attribute from ${unselectableElements.length} element(s).`);

            // Hide problematic overlays.
            const promoElements = document.querySelectorAll('.promo_div');
            promoElements.forEach(el => {
                el.style.display = 'none';
            });
            logMessage('info', `Hid ${promoElements.length} promo element(s).`);
        } catch (error) {
            logMessage('error', 'Error in fixElementsForAccessibility: ' + error);
        }
    }

    /**********************************
     * Data Extraction Functions      *
     **********************************/

    function extractData() {
        let data = { texts: [], images: [], videos: [], docs: [] };
        try {
            document.querySelectorAll('p, h1, h2, h3, h4, h5, h6').forEach(el => {
                const txt = el.innerText.trim();
                if (txt) data.texts.push(txt);
            });
            // For images, return an object with more details.
            data.images = Array.from(document.querySelectorAll('img')).map(img => ({
                src: (img.dataset && img.dataset.fullsrc) ? img.dataset.fullsrc : img.src,
                naturalWidth: img.naturalWidth,
                naturalHeight: img.naturalHeight,
                displayedWidth: img.width,
                displayedHeight: img.height,
                alt: img.alt || ""
            })).filter(obj => obj.src);
            data.videos = Array.from(document.querySelectorAll('video, iframe'))
                .map(el => (el.tagName.toLowerCase() === 'video' ? (el.currentSrc || el.src) : el.src))
                .filter(src => src);
            data.docs = Array.from(document.querySelectorAll('a'))
                .map(a => a.href)
                .filter(href => /\.(pdf|docx?|xlsx?|pptx?)($|\?)/i.test(href));
            logMessage('info', 'Basic data extraction complete.');
        } catch (error) {
            logMessage('error', `Error in extractData: ${error}`);
        }
        return data;
    }

    function extractMetaTags() {
        const metaData = {};
        try {
            const metaTags = document.querySelectorAll(
                'meta[property^="og:"], meta[name="description"], meta[name="keywords"], meta[name^="twitter:"]'
            );
            metaTags.forEach(meta => {
                const key = meta.getAttribute('property') || meta.getAttribute('name');
                const content = meta.getAttribute('content');
                if (key && content) metaData[key] = content;
            });
            logMessage('info', `Extracted ${Object.keys(metaData).length} meta tag(s).`);
        } catch (error) {
            logMessage('error', `Error in extractMetaTags: ${error}`);
        }
        return metaData;
    }

    function extractBackgroundImages() {
        const backgroundImages = [];
        try {
            const elements = document.querySelectorAll('[style*="background-image"]');
            elements.forEach(el => {
                const style = el.getAttribute('style');
                const regex = /background-image:\s*url\((['"]?)(.*?)\1\)/i;
                const match = regex.exec(style);
                if (match && match[2]) backgroundImages.push(match[2]);
            });
            logMessage('info', `Extracted ${backgroundImages.length} background image(s).`);
        } catch (error) {
            logMessage('error', `Error in extractBackgroundImages: ${error}`);
        }
        return backgroundImages;
    }

    function extractHiddenForms() {
        const formsData = [];
        try {
            const hiddenInputs = document.querySelectorAll('form input[type="hidden"]');
            hiddenInputs.forEach(input => {
                formsData.push({
                    name: input.name,
                    value: input.value,
                    form: input.form ? (input.form.action || 'N/A') : 'N/A'
                });
            });
            logMessage('info', `Extracted ${formsData.length} hidden form field(s).`);
        } catch (error) {
            logMessage('error', `Error in extractHiddenForms: ${error}`);
        }
        return formsData;
    }

    function extractShadowDOMData() {
        const shadowData = [];
        try {
            function traverseShadow(root) {
                let collectedText = '';
                root.childNodes.forEach(node => {
                    if (node.nodeType === Node.TEXT_NODE) {
                        collectedText += node.textContent.trim() + ' ';
                    } else if (node.nodeType === Node.ELEMENT_NODE) {
                        collectedText += node.innerText.trim() + ' ';
                        if (node.shadowRoot) {
                            collectedText += traverseShadow(node.shadowRoot);
                        }
                    }
                });
                return collectedText;
            }
            const allElements = document.querySelectorAll('*');
            allElements.forEach(el => {
                if (el.shadowRoot) {
                    const text = traverseShadow(el.shadowRoot);
                    shadowData.push({ host: el.tagName, text: text.trim() });
                }
            });
            logMessage('info', `Extracted shadow DOM data from ${shadowData.length} element(s).`);
        } catch (error) {
            logMessage('error', `Error in extractShadowDOMData: ${error}`);
        }
        return shadowData;
    }

    function advancedExtractData() {
        const basicData = extractData();
        const meta = extractMetaTags();
        const backgrounds = extractBackgroundImages();
        const hiddenForms = extractHiddenForms();
        const shadow = extractShadowDOMData();
        return { ...basicData, meta, backgrounds, hiddenForms, shadow };
    }

    /**********************************
     * OCR Functionality (Tesseract)  *
     **********************************/

    function loadTesseractJS(callback) {
        if (window.Tesseract) {
            callback();
            return;
        }
        const script = document.createElement('script');
        script.src = "https://cdn.jsdelivr.net/npm/[email protected]/dist/tesseract.min.js";
        script.onload = () => {
            logMessage('info', 'Tesseract.js loaded.');
            callback();
        };
        script.onerror = () => {
            logMessage('error', 'Failed to load Tesseract.js.');
        };
        document.body.appendChild(script);
    }

    function performOCR() {
        try {
            const outputDiv = document.getElementById('dataOutput');
            outputDiv.innerHTML += `<p>Starting OCR processing...</p>`;
            loadTesseractJS(() => {
                const imgs = Array.from(document.querySelectorAll('img')).filter(img => img.src);
                if (imgs.length === 0) {
                    outputDiv.innerHTML += `<p>No images found for OCR.</p>`;
                    logMessage('warn', 'No images available for OCR.');
                    return;
                }
                let ocrResults = [];
                let current = 0;
                function processNext() {
                    if (current >= imgs.length) {
                        outputDiv.innerHTML += `<p><strong>OCR Completed.</strong></p>`;
                        outputDiv.innerHTML += `<details open>
                            <summary>OCR Results (first 3)</summary>
                            <pre style="white-space: pre-wrap;">${ocrResults.slice(0, 3).join('\n\n')}</pre>
                        </details>`;
                        logMessage('info', 'OCR processing completed.');
                        return;
                    }
                    const img = imgs[current];
                    outputDiv.innerHTML += `<p>Processing image ${current + 1} of ${imgs.length}...</p>`;
                    window.Tesseract.recognize(img.src, 'eng', { logger: m => console.log(m) })
                        .then(result => {
                            ocrResults.push(result.data.text.trim());
                        })
                        .catch(err => {
                            ocrResults.push(`Error processing image: ${err}`);
                            logMessage('error', `OCR error on image ${current + 1}: ${err}`);
                        })
                        .finally(() => {
                            current++;
                            processNext();
                        });
                }
                processNext();
            });
        } catch (error) {
            logMessage('error', `Error in performOCR: ${error}`);
        }
    }

    /**********************************
     * Sidebar Interface & Controls   *
     **********************************/

    function createSidebar() {
        let sidebar = document.getElementById('ethicalDataSidebar');
        if (sidebar) return sidebar;

        sidebar = document.createElement('div');
        sidebar.id = 'ethicalDataSidebar';
        Object.assign(sidebar.style, {
            position: 'fixed',
            top: '0',
            right: '0',
            width: '400px',
            height: '100vh',
            backgroundColor: 'rgba(0,0,0,0.9)',
            color: '#fff',
            overflowY: 'auto',
            zIndex: '9999',
            padding: '10px',
            fontFamily: 'Arial, sans-serif',
            fontSize: '14px',
            lineHeight: '1.4'
        });

        sidebar.innerHTML = `
            <h2 style="margin-top:0;">Data Extraction</h2>
            <button id="refreshDataBtn" style="margin:5px 0;">Refresh Data</button>
            <button id="forceLoadMediaBtn" style="margin:5px 0;">Force Load Media</button>
            <button id="unblurContentBtn" style="margin:5px 0;">Advanced Unblur (Canvas)</button>
            <button id="unblurContentOpenCVBtn" style="margin:5px 0;">Advanced Unblur (OpenCV)</button>
            <button id="revealTextBtn" style="margin:5px 0;">Reveal Hidden Text</button>
            <button id="recoverHiddenBtn" style="margin:5px 0;">Recover Hidden Data</button>
            <button id="fixElementsBtn" style="margin:5px 0;">Enable Text Selection & Remove Overlays</button>
            <button id="ocrImagesBtn" style="margin:5px 0;">Perform OCR on Images</button>
            <button id="exportDataBtn" style="margin:5px 0;">Export JSON</button>
            <button id="clearLogsBtn" style="margin:5px 0;">Clear Logs</button>
            <div id="dataOutput" style="margin-top:10px;"></div>
            <hr>
            <h3>Logs</h3>
            <div id="logArea" style="max-height:150px; overflow-y:auto; background:#222; padding:5px; font-size:12px;"></div>
        `;
        document.body.appendChild(sidebar);

        document.getElementById('refreshDataBtn').addEventListener('click', updateSidebarData);
        document.getElementById('forceLoadMediaBtn').addEventListener('click', () => { forceLoadMedia(); updateSidebarData(); });
        document.getElementById('unblurContentBtn').addEventListener('click', advancedUnblurContent);
        document.getElementById('unblurContentOpenCVBtn').addEventListener('click', advancedUnblurImages_OpenCV);
        document.getElementById('revealTextBtn').addEventListener('click', () => { revealHiddenText(); updateSidebarData(); });
        document.getElementById('recoverHiddenBtn').addEventListener('click', () => { advancedRecoverHiddenContent(); updateSidebarData(); });
        document.getElementById('fixElementsBtn').addEventListener('click', () => { fixElementsForAccessibility(); updateSidebarData(); });
        document.getElementById('ocrImagesBtn').addEventListener('click', performOCR);
        document.getElementById('exportDataBtn').addEventListener('click', exportData);
        document.getElementById('clearLogsBtn').addEventListener('click', () => {
            const logArea = document.getElementById('logArea');
            if (logArea) { logArea.innerHTML = ""; }
        });

        logMessage('info', 'Sidebar created and event listeners attached.');
        return sidebar;
    }

    // Update the sidebar with extracted data while preserving open <details> states.
    function updateSidebarData() {
        try {
            // Preserve current open state of details elements.
            const detailsStates = {};
            document.querySelectorAll('#dataOutput details').forEach(d => {
                const summaryText = d.querySelector('summary')?.innerText || "";
                detailsStates[summaryText] = d.hasAttribute("open");
            });

            const data = advancedExtractData();
            const outputDiv = document.getElementById('dataOutput');
            if (!outputDiv) return;

            // Update innerHTML with advanced image data.
            outputDiv.innerHTML = `
                <p><strong>Text Blocks:</strong> ${data.texts.length}</p>
                <p><strong>Images:</strong> ${data.images.length}</p>
                <p><strong>Videos:</strong> ${data.videos.length}</p>
                <p><strong>Document Links:</strong> ${data.docs.length}</p>
                <hr>
                <details>
                  <summary>View Text (first 5)</summary>
                  <pre style="white-space: pre-wrap;">${data.texts.slice(0, 5).join('\n\n')}</pre>
                </details>
                <details>
                  <summary>View Image Data (first 5)</summary>
                  <pre style="white-space: pre-wrap;">${data.images.slice(0, 5).map(img =>
                    `src: ${img.src}\nnatural: ${img.naturalWidth}x${img.naturalHeight}\ndisplayed: ${img.displayedWidth}x${img.displayedHeight}\nalt: ${img.alt}`
                  ).join('\n\n')}</pre>
                </details>
                <details>
                  <summary>View Video URLs (first 5)</summary>
                  <pre style="white-space: pre-wrap;">${data.videos.slice(0, 5).join('\n')}</pre>
                </details>
                <details>
                  <summary>View Document Links (first 5)</summary>
                  <pre style="white-space: pre-wrap;">${data.docs.slice(0, 5).join('\n')}</pre>
                </details>
                <hr>
                <details>
                  <summary>Meta Tags (${Object.keys(data.meta).length})</summary>
                  <pre style="white-space: pre-wrap;">${JSON.stringify(data.meta, null, 2)}</pre>
                </details>
                <details>
                  <summary>Background Images (${data.backgrounds.length})</summary>
                  <pre style="white-space: pre-wrap;">${data.backgrounds.slice(0, 5).join('\n')}</pre>
                </details>
                <details>
                  <summary>Hidden Form Fields (${data.hiddenForms.length})</summary>
                  <pre style="white-space: pre-wrap;">${JSON.stringify(data.hiddenForms.slice(0, 5), null, 2)}</pre>
                </details>
                <details>
                  <summary>Shadow DOM Data (${data.shadow.length})</summary>
                  <pre style="white-space: pre-wrap;">${data.shadow.slice(0, 3).map(item => item.host + ': ' + item.text).join('\n\n')}</pre>
                </details>
            `;

            // Reapply previous details open states.
            document.querySelectorAll('#dataOutput details').forEach(d => {
                const summaryText = d.querySelector('summary')?.innerText || "";
                if (detailsStates[summaryText]) {
                    d.setAttribute("open", "");
                }
            });

            logMessage('info', 'Sidebar data updated.');
        } catch (error) {
            logMessage('error', `Error in updateSidebarData: ${error}`);
        }
    }

    function exportData() {
        try {
            const data = advancedExtractData();
            const dataStr = JSON.stringify(data, null, 2);
            const blob = new Blob([dataStr], { type: "application/json" });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = "extractedData.json";
            a.click();
            URL.revokeObjectURL(url);
            logMessage('info', 'Data exported as JSON.');
        } catch (error) {
            logMessage('error', `Error in exportData: ${error}`);
        }
    }

    /**********************************
     * Other Core Functionalities     *
     **********************************/

    // Re-enable right-click and copy/paste.
    function enableRightClickAndCopyPaste() {
        try {
            const events = ['contextmenu', 'copy', 'cut', 'paste'];
            events.forEach(eventName => {
                document.addEventListener(eventName, function(e) {
                    try { e.stopPropagation(); } catch (err) { logMessage('error', `Error in ${eventName} event: ${err}`); }
                }, true);
            });
            logMessage('info', 'Right-click and copy-paste events re-enabled.');
        } catch (error) {
            logMessage('error', `Error in enableRightClickAndCopyPaste: ${error}`);
        }
    }

    // Auto-expand "show more" / "read more" sections.
    function expandHiddenSections() {
        try {
            const buttons = document.querySelectorAll('button, a');
            let clickCount = 0;
            buttons.forEach(btn => {
                try {
                    const txt = btn.textContent.toLowerCase();
                    if (txt.includes('show more') || txt.includes('read more')) {
                        btn.click();
                        clickCount++;
                    }
                } catch (err) {
                    logMessage('error', `Error processing button: ${err}`);
                }
            });
            logMessage('info', `Clicked ${clickCount} "show more/read more" button(s).`);
        } catch (error) {
            logMessage('error', `Error in expandHiddenSections: ${error}`);
        }
    }

    /**********************************
     * Mutation Observer (Debounced)  *
     **********************************/

    const debouncedUpdate = debounce(() => {
        try {
            unblurElements();
            expandHiddenSections();
            updateSidebarData();
        } catch (error) {
            logMessage('error', `Error in debounced DOM update: ${error}`);
        }
    }, 500);

    function observeDomChanges() {
        try {
            const observer = new MutationObserver(debouncedUpdate);
            observer.observe(document.body, { childList: true, subtree: true });
            logMessage('info', 'DOM observer initialized.');
        } catch (error) {
            logMessage('error', `Error in observeDomChanges: ${error}`);
        }
    }

    /**********************************
     * Initialization                 *
     **********************************/

    function init() {
        try {
            enableRightClickAndCopyPaste();
            unblurElements();
            expandHiddenSections();
            createSidebar();
            updateSidebarData();
            observeDomChanges();
            logMessage('info', 'Advanced Responsive Ethical Data Gathering Tool initialized.');
        } catch (error) {
            logMessage('error', `Error during init: ${error}`);
        }
    }

    window.addEventListener('load', init);

})();