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.

  1. // ==UserScript==
  2. // @name Advanced Responsive Ethical Data Gathering Tool v3.2
  3. // @namespace http://tampermonkey.net/
  4. // @version 2025-02-11
  5. // @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.
  6. // @author LittleLooney
  7. // @license Copyright (C) Littlelooney All rights reserved.
  8. // @match *://*/*
  9. // @icon https://www.google.com
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. /**********************************
  17. * Utility Functions & Logging *
  18. **********************************/
  19.  
  20. const DEBUG = true;
  21.  
  22. // Debounce: Ensures that a function isn't called too frequently.
  23. function debounce(func, wait) {
  24. let timeout;
  25. return function(...args) {
  26. clearTimeout(timeout);
  27. timeout = setTimeout(() => func.apply(this, args), wait);
  28. };
  29. }
  30.  
  31. // Logging function: Logs messages with timestamps to console and to the sidebar log area.
  32. function logMessage(level, message) {
  33. const timestamp = new Date().toISOString();
  34. const fullMessage = `[${timestamp}] [${level}] ${message}`;
  35. if (DEBUG) {
  36. console[level](fullMessage);
  37. }
  38. const logArea = document.getElementById('logArea');
  39. if (logArea) {
  40. const logEntry = document.createElement('div');
  41. logEntry.textContent = fullMessage;
  42. logEntry.style.borderBottom = "1px solid #444";
  43. logEntry.style.padding = "2px 0";
  44. logArea.appendChild(logEntry);
  45. // Auto-scroll to the bottom.
  46. logArea.scrollTop = logArea.scrollHeight;
  47. }
  48. }
  49.  
  50. /**********************************
  51. * Force Media Loading Function *
  52. **********************************/
  53.  
  54. function forceLoadMedia() {
  55. try {
  56. // Process images.
  57. const images = document.querySelectorAll('img');
  58. images.forEach(img => {
  59. if (img.hasAttribute('loading')) img.removeAttribute('loading');
  60. // If there's a high-res version in a data attribute, use it.
  61. if (img.dataset && img.dataset.fullsrc) {
  62. img.src = img.dataset.fullsrc;
  63. }
  64. if (img.dataset && img.dataset.src) {
  65. img.src = img.dataset.src;
  66. }
  67. img.classList.remove('lazy', 'lazyload');
  68. // Force reload if not complete.
  69. if (!img.complete || img.naturalWidth === 0) {
  70. img.src = img.src;
  71. }
  72. });
  73. logMessage('info', `Force loaded ${images.length} image(s).`);
  74.  
  75. // Process videos.
  76. const videos = document.querySelectorAll('video');
  77. videos.forEach(video => {
  78. video.setAttribute('preload', 'auto');
  79. video.load();
  80. });
  81. logMessage('info', `Force loaded ${videos.length} video(s).`);
  82.  
  83. // Process iframes.
  84. const iframes = document.querySelectorAll('iframe');
  85. iframes.forEach(iframe => {
  86. let src = iframe.getAttribute('src');
  87. if (src) iframe.src = src;
  88. });
  89. logMessage('info', `Force loaded ${iframes.length} iframe(s).`);
  90. } catch (error) {
  91. logMessage('error', `Error in forceLoadMedia: ${error}`);
  92. }
  93. }
  94.  
  95. /**********************************
  96. * Unblurring Methods *
  97. **********************************/
  98.  
  99. // Remove CSS blur filters.
  100. function unblurElements() {
  101. try {
  102. const blurredElements = document.querySelectorAll('[style*="filter: blur"], .blurred');
  103. blurredElements.forEach(el => {
  104. el.style.filter = 'none';
  105. el.classList.remove('blurred');
  106. });
  107. logMessage('info', `Removed CSS blur filters from ${blurredElements.length} element(s).`);
  108. } catch (error) {
  109. logMessage('error', `Error in unblurElements: ${error}`);
  110. }
  111. }
  112.  
  113. /**********************************
  114. * Canvas-Based Image Unblurring *
  115. **********************************/
  116.  
  117. function applyConvolution(imageData, kernel, kernelSize) {
  118. const width = imageData.width;
  119. const height = imageData.height;
  120. const inputData = imageData.data;
  121. const outputData = new Uint8ClampedArray(inputData.length);
  122. const half = Math.floor(kernelSize / 2);
  123.  
  124. for (let y = 0; y < height; y++) {
  125. for (let x = 0; x < width; x++) {
  126. let r = 0, g = 0, b = 0, a = 0;
  127. for (let ky = -half; ky <= half; ky++) {
  128. for (let kx = -half; kx <= half; kx++) {
  129. const ix = x + kx;
  130. const iy = y + ky;
  131. if (ix >= 0 && ix < width && iy >= 0 && iy < height) {
  132. const idx = (iy * width + ix) * 4;
  133. const weight = kernel[(ky + half) * kernelSize + (kx + half)];
  134. r += inputData[idx] * weight;
  135. g += inputData[idx + 1] * weight;
  136. b += inputData[idx + 2] * weight;
  137. a += inputData[idx + 3] * weight;
  138. }
  139. }
  140. }
  141. const outIdx = (y * width + x) * 4;
  142. outputData[outIdx] = Math.min(255, Math.max(0, r));
  143. outputData[outIdx + 1] = Math.min(255, Math.max(0, g));
  144. outputData[outIdx + 2] = Math.min(255, Math.max(0, b));
  145. outputData[outIdx + 3] = Math.min(255, Math.max(0, a));
  146. }
  147. }
  148. return new ImageData(outputData, width, height);
  149. }
  150.  
  151. function sharpenImageData(imageData) {
  152. // Use a basic 3x3 sharpening kernel.
  153. const kernel = [
  154. -1, -1, -1,
  155. -1, 9, -1,
  156. -1, -1, -1
  157. ];
  158. return applyConvolution(imageData, kernel, 3);
  159. }
  160.  
  161. // Canvas-based unblur for images.
  162. function advancedUnblurImages_Canvas() {
  163. try {
  164. const imgs = document.querySelectorAll('img');
  165. imgs.forEach(img => {
  166. try {
  167. img.style.filter = 'none'; // Remove any CSS blur.
  168. if (!img.complete || img.naturalWidth === 0) {
  169. logMessage('warn', 'Skipping an image because it is not fully loaded.');
  170. return;
  171. }
  172. const canvas = document.createElement('canvas');
  173. const context = canvas.getContext('2d');
  174. canvas.width = img.naturalWidth;
  175. canvas.height = img.naturalHeight;
  176. context.drawImage(img, 0, 0);
  177. let imageData = context.getImageData(0, 0, canvas.width, canvas.height);
  178. let sharpenedData = sharpenImageData(imageData);
  179. context.putImageData(sharpenedData, 0, 0);
  180. img.src = canvas.toDataURL();
  181. logMessage('info', 'Canvas-based advanced unblurring applied to an image.');
  182. } catch (innerError) {
  183. logMessage('error', 'Error in canvas-based unblur: ' + innerError);
  184. }
  185. });
  186. } catch (error) {
  187. logMessage('error', 'Error in advancedUnblurImages_Canvas: ' + error);
  188. }
  189. }
  190.  
  191. /**********************************
  192. * OpenCV.js-Based Image Unblurring *
  193. **********************************/
  194.  
  195. function loadOpenCVJS(callback) {
  196. if (window.cv) {
  197. callback();
  198. return;
  199. }
  200. const script = document.createElement('script');
  201. script.src = "https://docs.opencv.org/4.x/opencv.js";
  202. script.async = true;
  203. script.onload = function () {
  204. cv['onRuntimeInitialized'] = function() {
  205. logMessage('info', 'OpenCV.js runtime initialized.');
  206. callback();
  207. };
  208. };
  209. script.onerror = function() {
  210. logMessage('error', 'Failed to load OpenCV.js.');
  211. };
  212. document.body.appendChild(script);
  213. }
  214.  
  215. function advancedUnblurImages_OpenCV() {
  216. loadOpenCVJS(() => {
  217. const imgs = document.querySelectorAll('img');
  218. imgs.forEach(img => {
  219. try {
  220. img.style.filter = 'none';
  221. if (!img.complete || img.naturalWidth === 0) {
  222. logMessage('warn', 'Skipping an image (OpenCV) because it is not fully loaded.');
  223. return;
  224. }
  225. const canvas = document.createElement('canvas');
  226. canvas.width = img.naturalWidth;
  227. canvas.height = img.naturalHeight;
  228. const ctx = canvas.getContext('2d');
  229. ctx.drawImage(img, 0, 0);
  230. let src = cv.imread(canvas);
  231. let dst = new cv.Mat();
  232. let kernel = cv.matFromArray(3, 3, cv.CV_32F,
  233. [-1, -1, -1,
  234. -1, 9, -1,
  235. -1, -1, -1]);
  236. cv.filter2D(src, dst, cv.CV_8U, kernel);
  237. cv.imshow(canvas, dst);
  238. src.delete(); dst.delete(); kernel.delete();
  239. img.src = canvas.toDataURL();
  240. logMessage('info', 'OpenCV-based advanced unblurring applied to an image.');
  241. } catch (e) {
  242. logMessage('error', 'Error in advancedUnblurImages_OpenCV: ' + e);
  243. }
  244. });
  245. });
  246. }
  247.  
  248. // Combined unblur function.
  249. function advancedUnblurContent() {
  250. try {
  251. unblurElements();
  252. forceLoadMedia();
  253. advancedUnblurImages_Canvas(); // Optionally, also run advancedUnblurImages_OpenCV();
  254. logMessage('info', 'Advanced unblur content executed.');
  255. } catch (error) {
  256. logMessage('error', 'Error in advancedUnblurContent: ' + error);
  257. }
  258. }
  259.  
  260. /**********************************
  261. * Reveal Hidden Text Function *
  262. **********************************/
  263.  
  264. /**
  265. * Scans elements (e.g., within .text_layer) and if the computed text color is transparent
  266. * or a heavy text-shadow is applied, sets the text color to black, removes/reduces the shadow,
  267. * and ensures full opacity.
  268. */
  269. function revealHiddenText() {
  270. const textElements = document.querySelectorAll('.text_layer *');
  271. textElements.forEach(el => {
  272. try {
  273. const cs = window.getComputedStyle(el);
  274. if (cs.color === "rgba(0, 0, 0, 0)" || cs.color === "transparent") {
  275. el.style.color = "black";
  276. }
  277. if (cs.textShadow && cs.textShadow !== "none") {
  278. // Option: reduce the shadow blur (e.g., to 5px) instead of removing entirely.
  279. el.style.textShadow = "none";
  280. }
  281. if (cs.opacity < 1) {
  282. el.style.opacity = "1";
  283. }
  284. } catch (e) {
  285. logMessage('error', 'Error in revealHiddenText on an element: ' + e);
  286. }
  287. });
  288. logMessage('info', 'Completed revealHiddenText() processing.');
  289. }
  290.  
  291. /**********************************
  292. * Advanced Hidden Data Recovery *
  293. **********************************/
  294.  
  295. function advancedRecoverHiddenContent() {
  296. let recoveredCount = 0;
  297.  
  298. // Process media elements.
  299. const mediaSelectors = ['img', 'video', 'iframe', 'embed', 'object'];
  300. const mediaElements = document.querySelectorAll(mediaSelectors.join(','));
  301. mediaElements.forEach(el => {
  302. try {
  303. const cs = window.getComputedStyle(el);
  304. const zIndex = parseInt(cs.zIndex) || 0;
  305. if (zIndex > 1000) return;
  306.  
  307. let isHidden = (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0');
  308. const rect = el.getBoundingClientRect();
  309. if (rect.width === 0 || rect.height === 0) isHidden = true;
  310. if (isHidden) {
  311. el.style.display = 'block';
  312. el.style.visibility = 'visible';
  313. el.style.opacity = '1';
  314. if (el.tagName.toLowerCase() === 'img' && (!el.complete || el.naturalWidth === 0)) {
  315. el.src = el.src;
  316. }
  317. recoveredCount++;
  318. }
  319. } catch (e) {
  320. logMessage('error', 'Error processing media element in advancedRecoverHiddenContent: ' + e);
  321. }
  322. });
  323.  
  324. // Process text elements.
  325. const textSelectors = ['p', 'span', 'div'];
  326. const textElements = document.querySelectorAll(textSelectors.join(','));
  327. textElements.forEach(el => {
  328. try {
  329. const cs = window.getComputedStyle(el);
  330. const zIndex = parseInt(cs.zIndex) || 0;
  331. if (zIndex > 1000) return;
  332.  
  333. let isHidden = (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0');
  334. const rect = el.getBoundingClientRect();
  335. if (rect.width < 5 || rect.height < 5) isHidden = true;
  336. const text = el.innerText.trim();
  337. if (text.length > 5 && isHidden) {
  338. el.style.display = 'block';
  339. el.style.visibility = 'visible';
  340. el.style.opacity = '1';
  341. recoveredCount++;
  342. }
  343. } catch (e) {
  344. logMessage('error', 'Error processing text element in advancedRecoverHiddenContent: ' + e);
  345. }
  346. });
  347.  
  348. logMessage('info', `Advanced recovered ${recoveredCount} content element(s).`);
  349. }
  350.  
  351. /**********************************
  352. * Fix Elements for Accessibility *
  353. **********************************/
  354.  
  355. /**
  356. * 1. Removes any unselectable="on" attributes so that text can be selected.
  357. * 2. Hides known overlay elements (for example, with class "promo_div")
  358. * so that the blank/blurred box does not obscure the page.
  359. */
  360. function fixElementsForAccessibility() {
  361. try {
  362. // Remove unselectable attributes.
  363. const unselectableElements = document.querySelectorAll('[unselectable="on"]');
  364. unselectableElements.forEach(el => {
  365. el.removeAttribute('unselectable');
  366. });
  367. logMessage('info', `Removed unselectable attribute from ${unselectableElements.length} element(s).`);
  368.  
  369. // Hide problematic overlays.
  370. const promoElements = document.querySelectorAll('.promo_div');
  371. promoElements.forEach(el => {
  372. el.style.display = 'none';
  373. });
  374. logMessage('info', `Hid ${promoElements.length} promo element(s).`);
  375. } catch (error) {
  376. logMessage('error', 'Error in fixElementsForAccessibility: ' + error);
  377. }
  378. }
  379.  
  380. /**********************************
  381. * Data Extraction Functions *
  382. **********************************/
  383.  
  384. function extractData() {
  385. let data = { texts: [], images: [], videos: [], docs: [] };
  386. try {
  387. document.querySelectorAll('p, h1, h2, h3, h4, h5, h6').forEach(el => {
  388. const txt = el.innerText.trim();
  389. if (txt) data.texts.push(txt);
  390. });
  391. // For images, return an object with more details.
  392. data.images = Array.from(document.querySelectorAll('img')).map(img => ({
  393. src: (img.dataset && img.dataset.fullsrc) ? img.dataset.fullsrc : img.src,
  394. naturalWidth: img.naturalWidth,
  395. naturalHeight: img.naturalHeight,
  396. displayedWidth: img.width,
  397. displayedHeight: img.height,
  398. alt: img.alt || ""
  399. })).filter(obj => obj.src);
  400. data.videos = Array.from(document.querySelectorAll('video, iframe'))
  401. .map(el => (el.tagName.toLowerCase() === 'video' ? (el.currentSrc || el.src) : el.src))
  402. .filter(src => src);
  403. data.docs = Array.from(document.querySelectorAll('a'))
  404. .map(a => a.href)
  405. .filter(href => /\.(pdf|docx?|xlsx?|pptx?)($|\?)/i.test(href));
  406. logMessage('info', 'Basic data extraction complete.');
  407. } catch (error) {
  408. logMessage('error', `Error in extractData: ${error}`);
  409. }
  410. return data;
  411. }
  412.  
  413. function extractMetaTags() {
  414. const metaData = {};
  415. try {
  416. const metaTags = document.querySelectorAll(
  417. 'meta[property^="og:"], meta[name="description"], meta[name="keywords"], meta[name^="twitter:"]'
  418. );
  419. metaTags.forEach(meta => {
  420. const key = meta.getAttribute('property') || meta.getAttribute('name');
  421. const content = meta.getAttribute('content');
  422. if (key && content) metaData[key] = content;
  423. });
  424. logMessage('info', `Extracted ${Object.keys(metaData).length} meta tag(s).`);
  425. } catch (error) {
  426. logMessage('error', `Error in extractMetaTags: ${error}`);
  427. }
  428. return metaData;
  429. }
  430.  
  431. function extractBackgroundImages() {
  432. const backgroundImages = [];
  433. try {
  434. const elements = document.querySelectorAll('[style*="background-image"]');
  435. elements.forEach(el => {
  436. const style = el.getAttribute('style');
  437. const regex = /background-image:\s*url\((['"]?)(.*?)\1\)/i;
  438. const match = regex.exec(style);
  439. if (match && match[2]) backgroundImages.push(match[2]);
  440. });
  441. logMessage('info', `Extracted ${backgroundImages.length} background image(s).`);
  442. } catch (error) {
  443. logMessage('error', `Error in extractBackgroundImages: ${error}`);
  444. }
  445. return backgroundImages;
  446. }
  447.  
  448. function extractHiddenForms() {
  449. const formsData = [];
  450. try {
  451. const hiddenInputs = document.querySelectorAll('form input[type="hidden"]');
  452. hiddenInputs.forEach(input => {
  453. formsData.push({
  454. name: input.name,
  455. value: input.value,
  456. form: input.form ? (input.form.action || 'N/A') : 'N/A'
  457. });
  458. });
  459. logMessage('info', `Extracted ${formsData.length} hidden form field(s).`);
  460. } catch (error) {
  461. logMessage('error', `Error in extractHiddenForms: ${error}`);
  462. }
  463. return formsData;
  464. }
  465.  
  466. function extractShadowDOMData() {
  467. const shadowData = [];
  468. try {
  469. function traverseShadow(root) {
  470. let collectedText = '';
  471. root.childNodes.forEach(node => {
  472. if (node.nodeType === Node.TEXT_NODE) {
  473. collectedText += node.textContent.trim() + ' ';
  474. } else if (node.nodeType === Node.ELEMENT_NODE) {
  475. collectedText += node.innerText.trim() + ' ';
  476. if (node.shadowRoot) {
  477. collectedText += traverseShadow(node.shadowRoot);
  478. }
  479. }
  480. });
  481. return collectedText;
  482. }
  483. const allElements = document.querySelectorAll('*');
  484. allElements.forEach(el => {
  485. if (el.shadowRoot) {
  486. const text = traverseShadow(el.shadowRoot);
  487. shadowData.push({ host: el.tagName, text: text.trim() });
  488. }
  489. });
  490. logMessage('info', `Extracted shadow DOM data from ${shadowData.length} element(s).`);
  491. } catch (error) {
  492. logMessage('error', `Error in extractShadowDOMData: ${error}`);
  493. }
  494. return shadowData;
  495. }
  496.  
  497. function advancedExtractData() {
  498. const basicData = extractData();
  499. const meta = extractMetaTags();
  500. const backgrounds = extractBackgroundImages();
  501. const hiddenForms = extractHiddenForms();
  502. const shadow = extractShadowDOMData();
  503. return { ...basicData, meta, backgrounds, hiddenForms, shadow };
  504. }
  505.  
  506. /**********************************
  507. * OCR Functionality (Tesseract) *
  508. **********************************/
  509.  
  510. function loadTesseractJS(callback) {
  511. if (window.Tesseract) {
  512. callback();
  513. return;
  514. }
  515. const script = document.createElement('script');
  516. script.src = "https://cdn.jsdelivr.net/npm/tesseract.js@v2.1.5/dist/tesseract.min.js";
  517. script.onload = () => {
  518. logMessage('info', 'Tesseract.js loaded.');
  519. callback();
  520. };
  521. script.onerror = () => {
  522. logMessage('error', 'Failed to load Tesseract.js.');
  523. };
  524. document.body.appendChild(script);
  525. }
  526.  
  527. function performOCR() {
  528. try {
  529. const outputDiv = document.getElementById('dataOutput');
  530. outputDiv.innerHTML += `<p>Starting OCR processing...</p>`;
  531. loadTesseractJS(() => {
  532. const imgs = Array.from(document.querySelectorAll('img')).filter(img => img.src);
  533. if (imgs.length === 0) {
  534. outputDiv.innerHTML += `<p>No images found for OCR.</p>`;
  535. logMessage('warn', 'No images available for OCR.');
  536. return;
  537. }
  538. let ocrResults = [];
  539. let current = 0;
  540. function processNext() {
  541. if (current >= imgs.length) {
  542. outputDiv.innerHTML += `<p><strong>OCR Completed.</strong></p>`;
  543. outputDiv.innerHTML += `<details open>
  544. <summary>OCR Results (first 3)</summary>
  545. <pre style="white-space: pre-wrap;">${ocrResults.slice(0, 3).join('\n\n')}</pre>
  546. </details>`;
  547. logMessage('info', 'OCR processing completed.');
  548. return;
  549. }
  550. const img = imgs[current];
  551. outputDiv.innerHTML += `<p>Processing image ${current + 1} of ${imgs.length}...</p>`;
  552. window.Tesseract.recognize(img.src, 'eng', { logger: m => console.log(m) })
  553. .then(result => {
  554. ocrResults.push(result.data.text.trim());
  555. })
  556. .catch(err => {
  557. ocrResults.push(`Error processing image: ${err}`);
  558. logMessage('error', `OCR error on image ${current + 1}: ${err}`);
  559. })
  560. .finally(() => {
  561. current++;
  562. processNext();
  563. });
  564. }
  565. processNext();
  566. });
  567. } catch (error) {
  568. logMessage('error', `Error in performOCR: ${error}`);
  569. }
  570. }
  571.  
  572. /**********************************
  573. * Sidebar Interface & Controls *
  574. **********************************/
  575.  
  576. function createSidebar() {
  577. let sidebar = document.getElementById('ethicalDataSidebar');
  578. if (sidebar) return sidebar;
  579.  
  580. sidebar = document.createElement('div');
  581. sidebar.id = 'ethicalDataSidebar';
  582. Object.assign(sidebar.style, {
  583. position: 'fixed',
  584. top: '0',
  585. right: '0',
  586. width: '400px',
  587. height: '100vh',
  588. backgroundColor: 'rgba(0,0,0,0.9)',
  589. color: '#fff',
  590. overflowY: 'auto',
  591. zIndex: '9999',
  592. padding: '10px',
  593. fontFamily: 'Arial, sans-serif',
  594. fontSize: '14px',
  595. lineHeight: '1.4'
  596. });
  597.  
  598. sidebar.innerHTML = `
  599. <h2 style="margin-top:0;">Data Extraction</h2>
  600. <button id="refreshDataBtn" style="margin:5px 0;">Refresh Data</button>
  601. <button id="forceLoadMediaBtn" style="margin:5px 0;">Force Load Media</button>
  602. <button id="unblurContentBtn" style="margin:5px 0;">Advanced Unblur (Canvas)</button>
  603. <button id="unblurContentOpenCVBtn" style="margin:5px 0;">Advanced Unblur (OpenCV)</button>
  604. <button id="revealTextBtn" style="margin:5px 0;">Reveal Hidden Text</button>
  605. <button id="recoverHiddenBtn" style="margin:5px 0;">Recover Hidden Data</button>
  606. <button id="fixElementsBtn" style="margin:5px 0;">Enable Text Selection & Remove Overlays</button>
  607. <button id="ocrImagesBtn" style="margin:5px 0;">Perform OCR on Images</button>
  608. <button id="exportDataBtn" style="margin:5px 0;">Export JSON</button>
  609. <button id="clearLogsBtn" style="margin:5px 0;">Clear Logs</button>
  610. <div id="dataOutput" style="margin-top:10px;"></div>
  611. <hr>
  612. <h3>Logs</h3>
  613. <div id="logArea" style="max-height:150px; overflow-y:auto; background:#222; padding:5px; font-size:12px;"></div>
  614. `;
  615. document.body.appendChild(sidebar);
  616.  
  617. document.getElementById('refreshDataBtn').addEventListener('click', updateSidebarData);
  618. document.getElementById('forceLoadMediaBtn').addEventListener('click', () => { forceLoadMedia(); updateSidebarData(); });
  619. document.getElementById('unblurContentBtn').addEventListener('click', advancedUnblurContent);
  620. document.getElementById('unblurContentOpenCVBtn').addEventListener('click', advancedUnblurImages_OpenCV);
  621. document.getElementById('revealTextBtn').addEventListener('click', () => { revealHiddenText(); updateSidebarData(); });
  622. document.getElementById('recoverHiddenBtn').addEventListener('click', () => { advancedRecoverHiddenContent(); updateSidebarData(); });
  623. document.getElementById('fixElementsBtn').addEventListener('click', () => { fixElementsForAccessibility(); updateSidebarData(); });
  624. document.getElementById('ocrImagesBtn').addEventListener('click', performOCR);
  625. document.getElementById('exportDataBtn').addEventListener('click', exportData);
  626. document.getElementById('clearLogsBtn').addEventListener('click', () => {
  627. const logArea = document.getElementById('logArea');
  628. if (logArea) { logArea.innerHTML = ""; }
  629. });
  630.  
  631. logMessage('info', 'Sidebar created and event listeners attached.');
  632. return sidebar;
  633. }
  634.  
  635. // Update the sidebar with extracted data while preserving open <details> states.
  636. function updateSidebarData() {
  637. try {
  638. // Preserve current open state of details elements.
  639. const detailsStates = {};
  640. document.querySelectorAll('#dataOutput details').forEach(d => {
  641. const summaryText = d.querySelector('summary')?.innerText || "";
  642. detailsStates[summaryText] = d.hasAttribute("open");
  643. });
  644.  
  645. const data = advancedExtractData();
  646. const outputDiv = document.getElementById('dataOutput');
  647. if (!outputDiv) return;
  648.  
  649. // Update innerHTML with advanced image data.
  650. outputDiv.innerHTML = `
  651. <p><strong>Text Blocks:</strong> ${data.texts.length}</p>
  652. <p><strong>Images:</strong> ${data.images.length}</p>
  653. <p><strong>Videos:</strong> ${data.videos.length}</p>
  654. <p><strong>Document Links:</strong> ${data.docs.length}</p>
  655. <hr>
  656. <details>
  657. <summary>View Text (first 5)</summary>
  658. <pre style="white-space: pre-wrap;">${data.texts.slice(0, 5).join('\n\n')}</pre>
  659. </details>
  660. <details>
  661. <summary>View Image Data (first 5)</summary>
  662. <pre style="white-space: pre-wrap;">${data.images.slice(0, 5).map(img =>
  663. `src: ${img.src}\nnatural: ${img.naturalWidth}x${img.naturalHeight}\ndisplayed: ${img.displayedWidth}x${img.displayedHeight}\nalt: ${img.alt}`
  664. ).join('\n\n')}</pre>
  665. </details>
  666. <details>
  667. <summary>View Video URLs (first 5)</summary>
  668. <pre style="white-space: pre-wrap;">${data.videos.slice(0, 5).join('\n')}</pre>
  669. </details>
  670. <details>
  671. <summary>View Document Links (first 5)</summary>
  672. <pre style="white-space: pre-wrap;">${data.docs.slice(0, 5).join('\n')}</pre>
  673. </details>
  674. <hr>
  675. <details>
  676. <summary>Meta Tags (${Object.keys(data.meta).length})</summary>
  677. <pre style="white-space: pre-wrap;">${JSON.stringify(data.meta, null, 2)}</pre>
  678. </details>
  679. <details>
  680. <summary>Background Images (${data.backgrounds.length})</summary>
  681. <pre style="white-space: pre-wrap;">${data.backgrounds.slice(0, 5).join('\n')}</pre>
  682. </details>
  683. <details>
  684. <summary>Hidden Form Fields (${data.hiddenForms.length})</summary>
  685. <pre style="white-space: pre-wrap;">${JSON.stringify(data.hiddenForms.slice(0, 5), null, 2)}</pre>
  686. </details>
  687. <details>
  688. <summary>Shadow DOM Data (${data.shadow.length})</summary>
  689. <pre style="white-space: pre-wrap;">${data.shadow.slice(0, 3).map(item => item.host + ': ' + item.text).join('\n\n')}</pre>
  690. </details>
  691. `;
  692.  
  693. // Reapply previous details open states.
  694. document.querySelectorAll('#dataOutput details').forEach(d => {
  695. const summaryText = d.querySelector('summary')?.innerText || "";
  696. if (detailsStates[summaryText]) {
  697. d.setAttribute("open", "");
  698. }
  699. });
  700.  
  701. logMessage('info', 'Sidebar data updated.');
  702. } catch (error) {
  703. logMessage('error', `Error in updateSidebarData: ${error}`);
  704. }
  705. }
  706.  
  707. function exportData() {
  708. try {
  709. const data = advancedExtractData();
  710. const dataStr = JSON.stringify(data, null, 2);
  711. const blob = new Blob([dataStr], { type: "application/json" });
  712. const url = URL.createObjectURL(blob);
  713. const a = document.createElement('a');
  714. a.href = url;
  715. a.download = "extractedData.json";
  716. a.click();
  717. URL.revokeObjectURL(url);
  718. logMessage('info', 'Data exported as JSON.');
  719. } catch (error) {
  720. logMessage('error', `Error in exportData: ${error}`);
  721. }
  722. }
  723.  
  724. /**********************************
  725. * Other Core Functionalities *
  726. **********************************/
  727.  
  728. // Re-enable right-click and copy/paste.
  729. function enableRightClickAndCopyPaste() {
  730. try {
  731. const events = ['contextmenu', 'copy', 'cut', 'paste'];
  732. events.forEach(eventName => {
  733. document.addEventListener(eventName, function(e) {
  734. try { e.stopPropagation(); } catch (err) { logMessage('error', `Error in ${eventName} event: ${err}`); }
  735. }, true);
  736. });
  737. logMessage('info', 'Right-click and copy-paste events re-enabled.');
  738. } catch (error) {
  739. logMessage('error', `Error in enableRightClickAndCopyPaste: ${error}`);
  740. }
  741. }
  742.  
  743. // Auto-expand "show more" / "read more" sections.
  744. function expandHiddenSections() {
  745. try {
  746. const buttons = document.querySelectorAll('button, a');
  747. let clickCount = 0;
  748. buttons.forEach(btn => {
  749. try {
  750. const txt = btn.textContent.toLowerCase();
  751. if (txt.includes('show more') || txt.includes('read more')) {
  752. btn.click();
  753. clickCount++;
  754. }
  755. } catch (err) {
  756. logMessage('error', `Error processing button: ${err}`);
  757. }
  758. });
  759. logMessage('info', `Clicked ${clickCount} "show more/read more" button(s).`);
  760. } catch (error) {
  761. logMessage('error', `Error in expandHiddenSections: ${error}`);
  762. }
  763. }
  764.  
  765. /**********************************
  766. * Mutation Observer (Debounced) *
  767. **********************************/
  768.  
  769. const debouncedUpdate = debounce(() => {
  770. try {
  771. unblurElements();
  772. expandHiddenSections();
  773. updateSidebarData();
  774. } catch (error) {
  775. logMessage('error', `Error in debounced DOM update: ${error}`);
  776. }
  777. }, 500);
  778.  
  779. function observeDomChanges() {
  780. try {
  781. const observer = new MutationObserver(debouncedUpdate);
  782. observer.observe(document.body, { childList: true, subtree: true });
  783. logMessage('info', 'DOM observer initialized.');
  784. } catch (error) {
  785. logMessage('error', `Error in observeDomChanges: ${error}`);
  786. }
  787. }
  788.  
  789. /**********************************
  790. * Initialization *
  791. **********************************/
  792.  
  793. function init() {
  794. try {
  795. enableRightClickAndCopyPaste();
  796. unblurElements();
  797. expandHiddenSections();
  798. createSidebar();
  799. updateSidebarData();
  800. observeDomChanges();
  801. logMessage('info', 'Advanced Responsive Ethical Data Gathering Tool initialized.');
  802. } catch (error) {
  803. logMessage('error', `Error during init: ${error}`);
  804. }
  805. }
  806.  
  807. window.addEventListener('load', init);
  808.  
  809. })();