4chan Gallery

4chan grid based Image Gallery for threads that can load images, images with sounds, webms with sounds (Button on the Bottom Right)

目前為 2024-03-30 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         4chan Gallery
// @namespace    http://tampermonkey.net/
// @version      2024-03-30 (1.6)
// @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        https://boards.4chan.org/*/thread/*
// @match        https://boards.4chan.org/*/archive
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @license      GNU GPLv3
// ==/UserScript==

(function () {
    "use strict";

    let threadURL = window.location.href;
    let lastScrollPosition = 0;
    let gallerySize = { width: 0, height: 0 };

    function setStyles(element, styles) {
        for (const property in styles) {
            element.style[property] = styles[property];
        }
    }

    const loadButton = () => {
        const isArchivePage = window.location.pathname.includes("/archive");

        const button = document.createElement("button");
        button.textContent = "Open Image Gallery";
        setStyles(button, {
            position: "fixed",
            bottom: "20px",
            right: "20px",
            zIndex: "1000",
            backgroundColor: "#1c1c1c",
            color: "#d9d9d9",
            padding: "10px 20px",
            borderRadius: "5px",
            border: "none",
            cursor: "pointer",
            boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
        });

        const openImageGallery = () => {
            const gallery = document.createElement("div");
            setStyles(gallery, {
                position: "fixed",
                top: "0",
                left: "0",
                width: "100%",
                height: "100%",
                backgroundColor: "rgba(0, 0, 0, 0.8)",
                display: "flex",
                justifyContent: "center",
                alignItems: "center",
                zIndex: "9999",
            });

            const gridContainer = document.createElement("div");
            setStyles(gridContainer, {
                display: "grid",
                gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
                gap: "10px",
                padding: "20px",
                backgroundColor: "#1c1c1c",
                color: "#d9d9d9",
                maxWidth: "80%",
                maxHeight: "80%",
                overflowY: "auto",
                resize: "both",
                overflow: "auto",
                border: "1px solid #d9d9d9",
            });

            // Restore the previous grid container size
            if (gallerySize.width > 0 && gallerySize.height > 0) {
                gridContainer.style.width = `${gallerySize.width}px`;
                gridContainer.style.height = `${gallerySize.height}px`;
            }

            let mode = "all"; // Default mode is "all"
            let autoPlayWebms = false; // Default auto play webms without sound is false

            // Toggle mode button
            const toggleModeButton = document.createElement("button");
            toggleModeButton.textContent = "Toggle Mode (All)";
            setStyles(toggleModeButton, {
                position: "absolute",
                top: "10px",
                left: "10px",
                backgroundColor: "#1c1c1c",
                color: "#d9d9d9",
                padding: "10px 20px",
                borderRadius: "5px",
                border: "none",
                cursor: "pointer",
                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
            });
            toggleModeButton.addEventListener("click", () => {
                mode = mode === "all" ? "webm" : "all";
                toggleModeButton.textContent = `Toggle Mode (${mode === "all" ? "All" : "Webm & Images with Sound"})`;
                gridContainer.innerHTML = ""; // Clear the grid
                loadPosts(mode); // Reload posts based on the new mode
            });
            gallery.appendChild(toggleModeButton);

            // Toggle auto play webms button
            const toggleAutoPlayButton = document.createElement("button");
            toggleAutoPlayButton.textContent = "Auto Play Webms without Sound";
            setStyles(toggleAutoPlayButton, {
                position: "absolute",
                top: "10px",
                left: "350px",
                backgroundColor: "#1c1c1c",
                color: "#d9d9d9",
                padding: "10px 20px",
                borderRadius: "5px",
                border: "none",
                cursor: "pointer",
                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
            });
            toggleAutoPlayButton.addEventListener("click", () => {
                autoPlayWebms = !autoPlayWebms;
                toggleAutoPlayButton.textContent = autoPlayWebms ? "Stop Auto Play Webms" : "Auto Play Webms without Sound";
                gridContainer.innerHTML = ""; // Clear the grid
                loadPosts(mode); // Reload posts based on the new mode and auto play setting
            });
            gallery.appendChild(toggleAutoPlayButton);

            const loadPosts = (mode) => {
                const checkedThreads = isArchivePage
                    ? Array.from(document.querySelectorAll(".flashListing input[type='checkbox']:checked")).map((checkbox) => checkbox.parentNode.parentNode)
                    : []; // Use an empty array for non-archive pages

                const loadPostsFromThread = (thread) => {
                    // thread number is the 2nd child of the parent node
                    const threadNo = thread.children[1].textContent;
                    // get current board
                    const board = window.location.pathname.split("/")[1];
                    const threadURL = `https://boards.4chan.org/${board}/thread/${threadNo}`;

                    fetch(threadURL)
                        .then((response) => response.text())
                        .then((html) => {
                            const parser = new DOMParser();
                            const doc = parser.parseFromString(html, "text/html");
                            const posts = doc.querySelectorAll(".postContainer");

                            posts.forEach((post) => {
                                let mediaLink = post.querySelector(".fileText a");
                                if (post.querySelector(".fileText-original")) {
                                    mediaLink = post.querySelector(".fileText-original a");
                                }

                                // thumbnailUrl is in the img element inside the mediaLink
                                let thumbnailUrl = post.querySelector(".fileThumb img")?.src;

                                const comment = post.querySelector(".postMessage");

                                if (mediaLink) {
                                    const isVideo = mediaLink.href.includes(".webm");
                                    const isImage = mediaLink.href.includes(".jpg") || mediaLink.href.includes(".png") || mediaLink.href.includes(".gif");
                                    const fileName = mediaLink.href.split("/").pop();
                                    const soundLink = mediaLink.title.match(/\[sound=(.+?)\]/);

                                    // Check if the post should be loaded based on the mode
                                    if (mode === "all" || (mode === "webm" && (isVideo || (isImage && soundLink)))) {
                                        const cell = document.createElement("div");
                                        setStyles(cell, {
                                            border: "1px solid #d9d9d9",
                                            position: "relative",
                                        });

                                        const buttonDiv = document.createElement("div");
                                        setStyles(buttonDiv, {
                                            display: "flex",
                                            justifyContent: "space-between",
                                            alignItems: "center",
                                            padding: "5px",
                                        });

                                        if (isVideo) {
                                            const videoContainer = document.createElement("div");
                                            setStyles(videoContainer, {
                                                position: "relative",
                                            });

                                            const videoThumbnail = document.createElement("img");
                                            videoThumbnail.src = thumbnailUrl;
                                            videoThumbnail.alt = "Video Thumbnail";
                                            setStyles(videoThumbnail, {
                                                width: "100%",
                                                maxHeight: "200px",
                                                objectFit: "contain",
                                                cursor: "pointer",
                                            });
                                            videoThumbnail.loading = "lazy";

                                            const video = document.createElement("video");
                                            video.src = mediaLink.href;
                                            video.muted = true;
                                            video.controls = true;
                                            video.title = comment.innerText;
                                            video.videothumbnailDisplayed = "true";
                                            setStyles(video, {
                                                maxWidth: "100%",
                                                maxHeight: "200px",
                                                objectFit: "contain",
                                                cursor: "pointer",
                                                display: "none",
                                            });

                                            videoThumbnail.addEventListener("click", () => {
                                                videoThumbnail.style.display = "none";
                                                video.style.display = "block";
                                                video.videothumbnailDisplayed = "false";
                                                video.load();
                                            });

                                            // hide the video thumbnail and show the video when hovered
                                            videoThumbnail.addEventListener("mouseenter", () => {
                                                videoThumbnail.style.display = "none";
                                                video.style.display = "block";
                                                video.videothumbnailDisplayed = "false";
                                                video.load();
                                            });

                                            // Play webms without sound automatically on hover or if autoPlayWebms is true
                                            if (!soundLink) {
                                                if (autoPlayWebms) {
                                                    video.addEventListener("canplaythrough", () => {
                                                        video.play();
                                                        video.loop = true; // Loop webms when autoPlayWebms is true
                                                    });
                                                } else {
                                                    video.addEventListener("mouseenter", () => {
                                                        video.play();
                                                    });
                                                    video.addEventListener("mouseleave", () => {
                                                        video.pause();
                                                    });
                                                }
                                            }

                                            video.addEventListener("click", () => {
                                                post.scrollIntoView({ behavior: "smooth" });
                                                gallerySize = {
                                                    width: gridContainer.offsetWidth,
                                                    height: gridContainer.offsetHeight,
                                                };
                                                document.body.removeChild(gallery);
                                            });

                                            videoContainer.appendChild(videoThumbnail);
                                            videoContainer.appendChild(video);

                                            if (soundLink) {
                                                video.preload = "none"; // Disable video preload for better performance

                                                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";
                                                setStyles(playPauseButton, {
                                                    backgroundColor: "#1c1c1c",
                                                    color: "#d9d9d9",
                                                    padding: "5px 10px",
                                                    borderRadius: "3px",
                                                    border: "none",
                                                    cursor: "pointer",
                                                    boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                                });

                                                playPauseButton.addEventListener("click", () => {
                                                    // hide the video thumbnail and show the video
                                                    if (video.videothumbnailDisplayed === "true") {
                                                        video.videothumbnailDisplayed = "false";
                                                        videoThumbnail.style.display = "none";
                                                        video.style.display = "block";
                                                        video.load();
                                                    }

                                                    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";
                                                setStyles(resetButton, {
                                                    backgroundColor: "#1c1c1c",
                                                    color: "#d9d9d9",
                                                    padding: "5px 10px",
                                                    borderRadius: "3px",
                                                    border: "none",
                                                    cursor: "pointer",
                                                    boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                                });
                                                resetButton.addEventListener("click", () => {
                                                    video.currentTime = 0;
                                                    audio.currentTime = 0;
                                                });
                                                buttonDiv.appendChild(resetButton);

                                                let lastVideoTime = 0;
                                                // Sync audio with video on timeupdate event only if the difference is 2 seconds or more
                                                video.addEventListener("timeupdate", () => {
                                                    if (Math.abs(video.currentTime - lastVideoTime) >= 2) {
                                                        audio.currentTime = video.currentTime;
                                                        lastVideoTime = video.currentTime;
                                                    }
                                                    lastVideoTime = video.currentTime;
                                                });
                                            }

                                            const cellButton = document.createElement("button");
                                            cellButton.textContent = "View Post";
                                            setStyles(cellButton, {
                                                backgroundColor: "#1c1c1c",
                                                color: "#d9d9d9",
                                                padding: "5px 10px",
                                                borderRadius: "3px",
                                                border: "none",
                                                cursor: "pointer",
                                                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                            });
                                            cellButton.addEventListener("click", () => {
                                                post.scrollIntoView({ behavior: "smooth" });
                                                gallerySize = {
                                                    width: gridContainer.offsetWidth,
                                                    height: gridContainer.offsetHeight,
                                                };
                                                document.body.removeChild(gallery);
                                            });

                                            buttonDiv.appendChild(cellButton);
                                            cell.appendChild(videoContainer);
                                        } else if (isImage) {
                                            const imageContainer = document.createElement("div");
                                            setStyles(imageContainer, {
                                                position: "relative",
                                            });

                                            const image = document.createElement("img");
                                            image.src = mediaLink.href;
                                            setStyles(image, {
                                                maxWidth: "100%",
                                                maxHeight: "200px",
                                                objectFit: "contain",
                                                cursor: "pointer",
                                            });
                                            image.addEventListener("click", () => {
                                                post.scrollIntoView({ behavior: "smooth" });
                                                gallerySize = {
                                                    width: gridContainer.offsetWidth,
                                                    height: gridContainer.offsetHeight,
                                                };
                                                document.body.removeChild(gallery);
                                            });
                                            image.title = comment.innerText;
                                            image.loading = "lazy";

                                            if (soundLink) {
                                                const audio = document.createElement("audio");
                                                audio.src = decodeURIComponent(soundLink[1].startsWith("http") ? soundLink[1] : `https://${soundLink[1]}`);
                                                imageContainer.appendChild(audio);

                                                image.addEventListener("mouseenter", () => {
                                                    audio.play();
                                                });
                                                image.addEventListener("mouseleave", () => {
                                                    audio.pause();
                                                });

                                                const playPauseButton = document.createElement("button");
                                                playPauseButton.textContent = "Play/Pause";
                                                setStyles(playPauseButton, {
                                                    backgroundColor: "#1c1c1c",
                                                    color: "#d9d9d9",
                                                    padding: "5px 10px",
                                                    borderRadius: "3px",
                                                    border: "none",
                                                    cursor: "pointer",
                                                    boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                                });
                                                playPauseButton.addEventListener("click", () => {
                                                    if (audio.paused) {
                                                        audio.play();
                                                    } else {
                                                        audio.pause();
                                                    }
                                                });
                                                buttonDiv.appendChild(playPauseButton);
                                            }
                                            imageContainer.appendChild(image);
                                            cell.appendChild(imageContainer);
                                        } else {
                                            return; // Skip non-video and non-image posts
                                        }

                                        cell.appendChild(buttonDiv);
                                        gridContainer.appendChild(cell);
                                    }
                                }
                            });
                        })
                        .catch((error) => console.error(error));
                };

                if (isArchivePage) {
                    checkedThreads.forEach(loadPostsFromThread);
                } else {
                    const posts = document.querySelectorAll(".postContainer");
                    posts.forEach((post) => {
                        let mediaLink = post.querySelector(".fileText a");
                        if (post.querySelector(".fileText-original")) {
                            mediaLink = post.querySelector(".fileText-original a");
                        }

                        let thumbnailUrl = post.querySelector(".fileThumb img")?.src;

                        const comment = post.querySelector(".postMessage");

                        if (mediaLink) {
                            const isVideo = mediaLink.href.includes(".webm");
                            const isImage = mediaLink.href.includes(".jpg") || mediaLink.href.includes(".png") || mediaLink.href.includes(".gif");
                            const fileName = mediaLink.href.split("/").pop();
                            const soundLink = mediaLink.title.match(/\[sound=(.+?)\]/);

                            // Check if the post should be loaded based on the mode
                            if (mode === "all" || (mode === "webm" && (isVideo || (isImage && soundLink)))) {
                                const cell = document.createElement("div");
                                setStyles(cell, {
                                    border: "1px solid #d9d9d9",
                                    position: "relative",
                                });

                                const buttonDiv = document.createElement("div");
                                setStyles(buttonDiv, {
                                    display: "flex",
                                    justifyContent: "space-between",
                                    alignItems: "center",
                                    padding: "5px",
                                });

                                if (isVideo) {
                                    const videoContainer = document.createElement("div");
                                    setStyles(videoContainer, {
                                        position: "relative",
                                    });

                                    const videoThumbnail = document.createElement("img");
                                    videoThumbnail.src = thumbnailUrl;
                                    videoThumbnail.alt = "Video Thumbnail";
                                    setStyles(videoThumbnail, {
                                        width: "100%",
                                        maxHeight: "200px",
                                        objectFit: "contain",
                                        cursor: "pointer",
                                    });
                                    videoThumbnail.loading = "lazy";

                                    const video = document.createElement("video");
                                    video.src = mediaLink.href;
                                    video.muted = true;
                                    video.controls = true;
                                    video.title = comment.innerText;
                                    video.videothumbnailDisplayed = "true";
                                    setStyles(video, {
                                        maxWidth: "100%",
                                        maxHeight: "200px",
                                        objectFit: "contain",
                                        cursor: "pointer",
                                        display: "none",
                                    });

                                    videoThumbnail.addEventListener("click", () => {
                                        videoThumbnail.style.display = "none";
                                        video.style.display = "block";
                                        video.videothumbnailDisplayed = "false";
                                        video.load();
                                    });

                                    // hide the video thumbnail and show the video when hovered
                                    videoThumbnail.addEventListener("mouseenter", () => {
                                        videoThumbnail.style.display = "none";
                                        video.style.display = "block";
                                        video.videothumbnailDisplayed = "false";
                                        video.load();
                                    });

                                    // Play webms without sound automatically on hover or if autoPlayWebms is true
                                    if (!soundLink) {
                                        if (autoPlayWebms) {
                                            video.addEventListener("canplaythrough", () => {
                                                video.play();
                                                video.loop = true; // Loop webms when autoPlayWebms is true
                                            });
                                        } else {
                                            video.addEventListener("mouseenter", () => {
                                                video.play();
                                            });
                                            video.addEventListener("mouseleave", () => {
                                                video.pause();
                                            });
                                        }
                                    }

                                    video.addEventListener("click", () => {
                                        post.scrollIntoView({ behavior: "smooth" });
                                        gallerySize = {
                                            width: gridContainer.offsetWidth,
                                            height: gridContainer.offsetHeight,
                                        };
                                        document.body.removeChild(gallery);
                                    });

                                    videoContainer.appendChild(videoThumbnail);
                                    videoContainer.appendChild(video);

                                    if (soundLink) {
                                        video.preload = "none"; // Disable video preload for better performance

                                        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";
                                        setStyles(playPauseButton, {
                                            backgroundColor: "#1c1c1c",
                                            color: "#d9d9d9",
                                            padding: "5px 10px",
                                            borderRadius: "3px",
                                            border: "none",
                                            cursor: "pointer",
                                            boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                        });
                                        playPauseButton.videothumbnailDisplayed = "true";

                                        playPauseButton.addEventListener("click", () => {
                                            // hide the video thumbnail and show the video
                                            if (video.videothumbnailDisplayed === "true") {
                                                video.videothumbnailDisplayed = "false";
                                                videoThumbnail.style.display = "none";
                                                video.style.display = "block";
                                                video.load();
                                            }

                                            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";
                                        setStyles(resetButton, {
                                            backgroundColor: "#1c1c1c",
                                            color: "#d9d9d9",
                                            padding: "5px 10px",
                                            borderRadius: "3px",
                                            border: "none",
                                            cursor: "pointer",
                                            boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                        });
                                        resetButton.addEventListener("click", () => {
                                            video.currentTime = 0;
                                            audio.currentTime = 0;
                                        });
                                        buttonDiv.appendChild(resetButton);

                                        let lastVideoTime = 0;
                                        // Sync audio with video on timeupdate event only if the difference is 2 seconds or more
                                        video.addEventListener("timeupdate", () => {
                                            if (Math.abs(video.currentTime - lastVideoTime) >= 2) {
                                                audio.currentTime = video.currentTime;
                                                lastVideoTime = video.currentTime;
                                            }
                                            lastVideoTime = video.currentTime;
                                        });
                                    }

                                    const cellButton = document.createElement("button");
                                    cellButton.textContent = "View Post";
                                    setStyles(cellButton, {
                                        backgroundColor: "#1c1c1c",
                                        color: "#d9d9d9",
                                        padding: "5px 10px",
                                        borderRadius: "3px",
                                        border: "none",
                                        cursor: "pointer",
                                        boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                    });
                                    cellButton.addEventListener("click", () => {
                                        post.scrollIntoView({ behavior: "smooth" });
                                        gallerySize = {
                                            width: gridContainer.offsetWidth,
                                            height: gridContainer.offsetHeight,
                                        };
                                        document.body.removeChild(gallery);
                                    });

                                    buttonDiv.appendChild(cellButton);
                                    cell.appendChild(videoContainer);
                                } else if (isImage) {
                                    const imageContainer = document.createElement("div");
                                    setStyles(imageContainer, {
                                        position: "relative",
                                    });

                                    const image = document.createElement("img");
                                    image.src = mediaLink.href;
                                    setStyles(image, {
                                        maxWidth: "100%",
                                        maxHeight: "200px",
                                        objectFit: "contain",
                                        cursor: "pointer",
                                    });
                                    image.addEventListener("click", () => {
                                        post.scrollIntoView({ behavior: "smooth" });
                                        gallerySize = {
                                            width: gridContainer.offsetWidth,
                                            height: gridContainer.offsetHeight,
                                        };
                                        document.body.removeChild(gallery);
                                    });
                                    image.title = comment.innerText;
                                    image.loading = "lazy";

                                    if (soundLink) {
                                        const audio = document.createElement("audio");
                                        audio.src = decodeURIComponent(soundLink[1].startsWith("http") ? soundLink[1] : `https://${soundLink[1]}`);
                                        imageContainer.appendChild(audio);

                                        image.addEventListener("mouseenter", () => {
                                            audio.play();
                                        });
                                        image.addEventListener("mouseleave", () => {
                                            audio.pause();
                                        });

                                        const playPauseButton = document.createElement("button");
                                        playPauseButton.textContent = "Play/Pause";
                                        setStyles(playPauseButton, {
                                            backgroundColor: "#1c1c1c",
                                            color: "#d9d9d9",
                                            padding: "5px 10px",
                                            borderRadius: "3px",
                                            border: "none",
                                            cursor: "pointer",
                                            boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                        });
                                        playPauseButton.addEventListener("click", () => {
                                            if (audio.paused) {
                                                audio.play();
                                            } else {
                                                audio.pause();
                                            }
                                        });
                                        buttonDiv.appendChild(playPauseButton);
                                    }

                                    imageContainer.appendChild(image);
                                    cell.appendChild(imageContainer);
                                } else {
                                    return; // Skip non-video and non-image posts
                                }

                                cell.appendChild(buttonDiv);
                                gridContainer.appendChild(cell);
                            }
                        }
                    });
                }
            };

            loadPosts(mode); // Load posts based on the initial mode

            gallery.appendChild(gridContainer);

            const closeButton = document.createElement("button");
            closeButton.textContent = "Close";
            setStyles(closeButton, {
                position: "absolute",
                top: "10px",
                right: "10px",
                zIndex: "10000",
                backgroundColor: "#1c1c1c",
                color: "#d9d9d9",
                padding: "10px 20px",
                borderRadius: "5px",
                border: "none",
                cursor: "pointer",
                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
            });
            closeButton.addEventListener("click", () => {
                gallerySize = {
                    width: gridContainer.offsetWidth,
                    height: gridContainer.offsetHeight,
                };
                document.body.removeChild(gallery);
            });
            gallery.appendChild(closeButton);

            document.body.appendChild(gallery);

            // Store the current scroll position and grid container size when closing the gallery
            // console.log(`Last scroll position: ${lastScrollPosition} px`);
            gridContainer.addEventListener("scroll", () => {
                lastScrollPosition = gridContainer.scrollTop;
                // console.log(`Current scroll position: ${lastScrollPosition} px`);
            });

            // Restore the last scroll position and grid container size when opening the gallery after a timeout if the url is the same
            if (window.location.href === threadURL) {
                setTimeout(() => {
                    gridContainer.scrollTop = lastScrollPosition;
                    // console.log(`Restored scroll position: ${lastScrollPosition} px`);
                    if (gallerySize.width > 0 && gallerySize.height > 0) {
                        gridContainer.style.width = `${gallerySize.width}px`;
                        gridContainer.style.height = `${gallerySize.height}px`;
                    }
                }, 200);
            } else {
                // Reset the last scroll position and grid container size if the url is different
                threadURL = window.location.href;
                lastScrollPosition = 0;
                gallerySize = { width: 0, height: 0 };
            }
        };

        button.addEventListener("click", openImageGallery);

        // Append the button to the body
        document.body.appendChild(button);

        if (isArchivePage) {
            // adds the category to thead
            const thead = document.querySelector(".flashListing thead tr");
            const checkboxCell = document.createElement("td");
            checkboxCell.className = "postblock";
            checkboxCell.textContent = "Selected";
            thead.insertBefore(checkboxCell, thead.firstChild);

            // Add checkboxes to each thread row
            const threadRows = document.querySelectorAll(".flashListing tbody tr");
            threadRows.forEach((row) => {
                const checkbox = document.createElement("input");
                checkbox.type = "checkbox";
                const checkboxCell = document.createElement("td");
                checkboxCell.appendChild(checkbox);
                row.insertBefore(checkboxCell, row.firstChild);
            });
        }
    };

    // Check if there are at least two posts before loading the button
    const posts = document.querySelectorAll(".postContainer");
    if (posts.length >= 2) {
        loadButton();
    } else {
        // If there are less than two posts, try again after 5 seconds
        setTimeout(loadButton, 1000);
    }

    console.log("4chan Gallery loaded successfully!");
})();