// ==UserScript== // @name 8chan Spoiler Disabler // @namespace http://tampermonkey.net/ // @version 1.3 // @description Disables image and video spoilers on 8chan (moe/se/cc) by replacing both custom and default spoiler placeholders with thumbnails, including dynamically loaded posts. // @author impregnator // @match https://8chan.moe/* // @match https://8chan.se/* // @match https://8chan.cc/* // @grant none // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; // Function to process images and replace spoiler placeholders with thumbnails function processImages(images) { images.forEach(img => { // Check if the image is a spoiler placeholder (custom or default) if (img.src.includes('custom.spoiler') || img.src.includes('spoiler.png')) { // Get the parent element containing the full-sized file URL const link = img.closest('a.imgLink'); if (link) { // Extract the full-sized file URL const fullFileUrl = link.href; // Extract the file hash (everything after /.media/ up to the extension) const fileHash = fullFileUrl.match(/\/\.media\/([a-f0-9]+)\.[a-z0-9]+$/i); if (fileHash && fileHash[1]) { // Construct the thumbnail URL using the current domain const thumbnailUrl = `${window.location.origin}/.media/t_${fileHash[1]}`; // Replace the spoiler image with the thumbnail img.src = thumbnailUrl; } } } }); } // Process existing images on page load const initialImages = document.querySelectorAll('.uploadCell img'); processImages(initialImages); // Set up MutationObserver to handle dynamically added posts const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.addedNodes.length) { // Check each added node for new images mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { const newImages = node.querySelectorAll('.uploadCell img'); processImages(newImages); } }); } }); }); // Observe changes to the document body, including child nodes and subtrees observer.observe(document.body, { childList: true, subtree: true }); })();