// ==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);
})();