MahdeenSky / 4chan Gallery

// ==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       MahdeenSky
// @match        https://boards.4chan.org/*/thread/*
// @match        https://boards.4chan.org/*/archive
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @license      MIT
// ==/UserScript==

// ==OpenUserJS==
// @author MahdeenSky
// ==/OpenUserJS==

(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!");
})();