// ==UserScript== // @name 8chan media // @namespace http://tampermonkey.net/ // @version 1.0 // @description Displays the full original filename as a clickable view link and uses an SVG icon for the download link. Uses the original filename for downloads. // @author alleyesonus // @match https://8chan.se/* // @match https://8chan.moe/* // @match https://8chan.cc/* // @run-at document-idle // @icon https://www.google.com/s2/favicons?sz=64&domain=8chan.se // @grant none // ==/UserScript== (function () { "use strict"; const DownloadSvg = `M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32V274.7l-73.4-73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l128 128c12.5 12.5 32.8 12.5 45.3 0l128-128c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L288 274.7V32z M64 352c-35.3 0-64 28.7-64 64v32c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V416c0-35.3-28.7-64-64-64H346.5l-45.3 45.3c-25 25-65.5 25-90.5 0L165.5 352H64z m368 56a24 24 0 1 1 0 48 24 24 0 1 1 0-48z`; const DownloadW = 512, DownloadH = 512; const DownloadSvgHtml = ``; const processUploadCell = (cell) => { const details = cell.querySelector(".uploadDetails"); const originalNameLink = details?.querySelector(".originalNameLink"); const sizeLabel = details?.querySelector(".sizeLabel"); const dimensionLabel = details?.querySelector(".dimensionLabel"); const imgLink = cell.querySelector("a.imgLink"); // Skip if essential elements are missing if (!details || !originalNameLink || !sizeLabel || !dimensionLabel || !imgLink) return; const filename = originalNameLink.textContent.trim(); const fileUrl = imgLink.getAttribute("href"); // Create DocumentFragment for batched DOM updates const fragment = document.createDocumentFragment(); const label = document.createElement("span"); label.textContent = "File: "; const viewLink = document.createElement("a"); viewLink.href = fileUrl; viewLink.target = "_blank"; viewLink.textContent = filename; viewLink.title = "Open in new tab"; const fileInfoText = document.createTextNode(`${sizeLabel.textContent.trim()}, ${dimensionLabel.textContent.trim()}, `); const downloadLink = document.createElement("a"); downloadLink.href = fileUrl; downloadLink.download = filename; downloadLink.title = "Download"; downloadLink.innerHTML = DownloadSvgHtml; downloadLink.className = "downloadLink"; fragment.append(label, fileInfoText, viewLink, downloadLink); // Hide unnecessary elements with CSS details.querySelectorAll(".hideFileButton, .hideMobile, .sizeLabel, .dimensionLabel").forEach(e => { e.style.display = "none"; }); // Clear and append new content details.textContent = ""; details.appendChild(fragment); const nameTrigger = details.querySelector(".nameLink"); if (nameTrigger) { nameTrigger.classList.add("hiddenNameLink"); details.appendChild(nameTrigger); } }; /** * Iterates over all upload cells on the page and processes them in chunks. */ const migrateUploadDetails = () => { const cells = Array.from(document.querySelectorAll(".uploadCell")); const processChunk = () => { const chunk = cells.splice(0, 10); // Process 10 cells at a time chunk.forEach(cell => processUploadCell(cell)); if (cells.length > 0) { requestAnimationFrame(processChunk); } }; requestAnimationFrame(processChunk); }; // --- Styling --- const insertCustomStyles = () => { const style = document.createElement("style"); style.textContent = ` /* Compact layout for upload cells */ .uploadCell { display: inline-flex; flex-direction: column; align-items: flex-start; } /* Minimal styling for file details */ .uploadDetails { font-size: 0.95em; line-height: 1.5; white-space: normal; overflow-wrap: break-word; padding: 0 !important; margin: 0 !important; } .uploadCell br { display: none; } .downloadLink { display: inline-flex; align-items: center; justify-content: center; margin-left: 5px; } a { color: inherit; text-decoration: none; } a:hover { text-decoration: underline; } .hiddenNameLink { position: absolute; width: 0; height:0; opacity: 0; pointer-events: none; } .hideFileButton, .hideMobile, .sizeLabel, .dimensionLabel { display: none; } /* SVG icon styling */ .icon { width: 10px; height: 10px; vertical-align: middle; } `; document.head.appendChild(style); }; // --- Dynamic Content Handling --- const observeDynamicContent = () => { const observer = new MutationObserver((mutations) => { if (mutations.some(m => m.addedNodes.length && m.addedNodes[0]?.querySelector?.(".uploadCell"))) { migrateUploadDetails(); } }); observer.observe(document.body, { childList: true, subtree: true }); }; // --- Initialization --- const init = () => { insertCustomStyles(); migrateUploadDetails(); observeDynamicContent(); }; init(); })();