// ==UserScript== // @name 4chan Gallery // @namespace http://tampermonkey.net/ // @version 2024-03-26 // @description 4chan grid based Image Gallery for threads that can load images, images with sounds, webms with sounds (Button on the Bottom Right) // @author TheDarkEnjoyer // @match http://boards.4chan.org/vt/thread/* // @match https://boards.4chan.org/vt/thread/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant none // @license GNU GPLv3 // @downloadURL none // ==/UserScript== (function () { "use strict"; // Function to load the button const loadButton = () => { const posts = document.querySelectorAll(".postContainer"); // Check if there are at least two posts if (posts.length >= 2) { const button = document.createElement("button"); button.textContent = "Open Image Gallery"; button.style.position = "fixed"; button.style.bottom = "20px"; button.style.right = "20px"; button.style.zIndex = "1000"; button.style.backgroundColor = "var(--main-color)"; // Dark mode compatible background button.style.color = "var(--text-color)"; // Dark mode compatible text color // Append the button to the body after 2 seconds setTimeout(() => document.body.appendChild(button), 2000); const openImageGallery = () => { const gallery = document.createElement("div"); gallery.style.position = "fixed"; gallery.style.top = "0"; gallery.style.left = "0"; gallery.style.width = "100%"; gallery.style.height = "100%"; gallery.style.backgroundColor = "rgba(0, 0, 0, 0.8)"; gallery.style.display = "flex"; gallery.style.justifyContent = "center"; gallery.style.alignItems = "center"; gallery.style.zIndex = "9999"; const gridContainer = document.createElement("div"); gridContainer.style.display = "grid"; gridContainer.style.gridTemplateColumns = "repeat(auto-fit, minmax(200px, 1fr))"; gridContainer.style.gridGap = "10px"; gridContainer.style.padding = "20px"; gridContainer.style.backgroundColor = "var(--main-color)"; // Dark mode compatible background gridContainer.style.color = "var(--text-color)"; // Dark mode compatible text color gridContainer.style.maxWidth = "80%"; gridContainer.style.maxHeight = "80%"; gridContainer.style.overflowY = "auto"; gridContainer.style.resize = "both"; // Make the grid resizable gridContainer.style.overflow = "auto"; // Add scrollbars when resized gridContainer.style.border = "1px solid var(--text-color)"; // Add border to the grid container // Loop through each post and add its image/video to the grid posts.forEach((post) => { const mediaLink = post.querySelector(".fileText a"); const comment = post.querySelector(".postMessage"); if (mediaLink) { const isVideo = mediaLink.href.includes(".webm"); const fileName = mediaLink.href.split("/").pop(); const soundLink = mediaLink.title.match(/\[sound=(.+?)\]/); const cell = document.createElement("div"); cell.style.border = "1px solid var(--text-color)"; // Add border to each cell cell.style.position = "relative"; // Make the cell position relative const buttonDiv = document.createElement("div"); buttonDiv.style.display = "flex"; buttonDiv.style.justifyContent = "space-between"; buttonDiv.style.alignItems = "center"; buttonDiv.style.padding = "5px"; if (isVideo) { const videoContainer = document.createElement("div"); videoContainer.style.position = "relative"; const video = document.createElement("video"); video.src = mediaLink.href; video.style.maxWidth = "100%"; video.style.maxHeight = "200px"; video.style.objectFit = "contain"; video.muted = true; video.style.cursor = "pointer"; video.addEventListener("click", () => { post.scrollIntoView({ behavior: "smooth" }); document.body.removeChild(gallery); }); video.controls = true; video.title = comment.textContent; videoContainer.appendChild(video); if (soundLink) { const audio = document.createElement("audio"); audio.src = decodeURIComponent(soundLink[1].startsWith("http") ? soundLink[1] : `https://${soundLink[1]}`); videoContainer.appendChild(audio); const playPauseButton = document.createElement("button"); playPauseButton.textContent = "Play/Pause"; playPauseButton.addEventListener("click", () => { if (video.paused && audio.paused) { video.play(); audio.play(); } else { video.pause(); audio.pause(); } }); buttonDiv.appendChild(playPauseButton); const resetButton = document.createElement("button"); resetButton.textContent = "Reset"; resetButton.addEventListener("click", () => { video.currentTime = 0; audio.currentTime = 0; }); buttonDiv.appendChild(resetButton); } const cellButton = document.createElement("button"); cellButton.textContent = "View Post"; cellButton.style.backgroundColor = "var(--main-color)"; cellButton.style.color = "var(--text-color)"; cellButton.addEventListener("click", () => { post.scrollIntoView({ behavior: "smooth" }); document.body.removeChild(gallery); }); buttonDiv.appendChild(cellButton); cell.appendChild(videoContainer); cell.appendChild(buttonDiv); } else { const image = document.createElement("img"); image.src = mediaLink.href; image.style.maxWidth = "100%"; image.style.maxHeight = "200px"; image.style.objectFit = "contain"; image.style.cursor = "pointer"; image.addEventListener("click", () => { post.scrollIntoView({ behavior: "smooth" }); document.body.removeChild(gallery); }); image.title = comment.textContent; if (soundLink) { const audio = document.createElement("audio"); audio.src = decodeURIComponent(soundLink[1].startsWith("http") ? soundLink[1] : `https://${soundLink[1]}`); const playPauseButton = document.createElement("button"); playPauseButton.textContent = "Play/Pause"; playPauseButton.style.position = "absolute"; playPauseButton.style.bottom = "10px"; playPauseButton.style.left = "10px"; playPauseButton.addEventListener("click", () => { if (audio.paused) { audio.play(); } else { audio.pause(); } }); buttonDiv.appendChild(playPauseButton); } cell.appendChild(image); cell.appendChild(buttonDiv); } gridContainer.appendChild(cell); } }); gallery.appendChild(gridContainer); const closeButton = document.createElement("button"); closeButton.textContent = "Close"; closeButton.style.position = "absolute"; closeButton.style.top = "10px"; closeButton.style.right = "10px"; closeButton.style.zIndex = "10000"; closeButton.style.backgroundColor = "var(--main-color)"; closeButton.style.color = "var(--text-color)"; closeButton.addEventListener("click", () => { document.body.removeChild(gallery); }); gallery.appendChild(closeButton); document.body.appendChild(gallery); }; button.addEventListener("click", openImageGallery); } else { // If there are less than two posts, try again after 5 seconds setTimeout(loadButton, 5000); } }; loadButton(); console.log("4chan Gallery loaded successfully!"); })();