4chan Gallery

4chan grid-based image gallery with zoom mode support for threads that allows you to browse images, and soundposts (images with sounds, webms with sounds) along with other utility features.

当前为 2024-07-26 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         4chan Gallery
// @namespace    http://tampermonkey.net/
// @version      2024-07-26 (2.6)
// @description  4chan grid-based image gallery with zoom mode support for threads that allows you to browse images, and soundposts (images with sounds, webms with sounds) along with other utility features.
// @author       TheDarkEnjoyer
// @match        https://boards.4chan.org/*/thread/*
// @match        https://boards.4chan.org/*/archive
// @match        https://boards.4channel.org/*/thread/*
// @match        https://boards.4channel.org/*/archive
// @match        https://warosu.org/*/thread/*
// @match        https://warosu.org/*/
// @match        https://archived.moe/*/thread/*
// @match        https://archived.moe/*/
// @match        https://archive.palanq.win/*/
// @match        https://archive.palanq.win/*/thread/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @license      GNU GPLv3
// ==/UserScript==

(function () {
    "use strict";
    // injectVideoJS();
    const defaultSettings = {
        Load_High_Res_Images_By_Default: {
            value: false,
            info: "When opening the gallery, load high quality images by default (no thumbnails)",
        },
    };

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

    // store settings in local storage
    if (!localStorage.getItem("gallerySettings")) {
        localStorage.setItem("gallerySettings", JSON.stringify(defaultSettings));
    }
    let settings = JSON.parse(localStorage.getItem("gallerySettings"));

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

    function getPosts(websiteUrl, doc) {
        switch (websiteUrl) {
            case "warosu.org":
                return doc.querySelectorAll(".comment");
            case "archived.moe":
            case "archive.palanq.win":
                return doc.querySelectorAll(".has_image");
            case "boards.4chan.org":
            case "boards.4channel.org":
            default:
                return doc.querySelectorAll(".postContainer");
        }
    }

    function getDocument(thread, threadURL) {
        return new Promise((resolve, reject) => {
            if (thread === threadURL) {
                resolve(document);
            } else {
                fetch(thread)
                    .then((response) => response.text())
                    .then((html) => {
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(html, "text/html");
                        resolve(doc);
                    })
                    .catch((error) => {
                        reject(error);
                    });
            }
        });
    }

    function injectVideoJS() {
        const link = document.createElement("link");
        link.href = "https://vjs.zencdn.net/8.10.0/video-js.css";
        link.rel = "stylesheet";
        document.head.appendChild(link);

        // theme
        const theme = document.createElement("link");
        theme.href = "https://unpkg.com/@videojs/themes@1/dist/city/index.css";
        theme.rel = "stylesheet";
        document.head.appendChild(theme);

        const script = document.createElement("script");
        script.src = "https://vjs.zencdn.net/8.10.0/video.min.js";
        document.body.appendChild(script);
        ("VideoJS injected successfully!");
    }

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

        const button = document.createElement("button");
        button.textContent = "Open Image Gallery";
        button.id = "openImageGallery";
        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");
            gallery.id = "imageGallery";
            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(3, 1fr)`,
                gridTemplateRows: `repeat(2, 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

            // top left corner of the screen
            const mediaTypeButtonContainer = document.createElement("div");
            setStyles(mediaTypeButtonContainer, {
                position: "absolute",
                top: "10px",
                left: "10px",
                display: "flex",
                gap: "10px",
            });

            // Toggle mode button
            const toggleModeButton = document.createElement("button");
            toggleModeButton.textContent = "Toggle Mode (All)";
            setStyles(toggleModeButton, {
                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
            });

            // Toggle auto play webms button
            const toggleAutoPlayButton = document.createElement("button");
            toggleAutoPlayButton.textContent = "Auto Play Webms without Sound";
            setStyles(toggleAutoPlayButton, {
                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
            });
            mediaTypeButtonContainer.appendChild(toggleModeButton);
            mediaTypeButtonContainer.appendChild(toggleAutoPlayButton);
            gallery.appendChild(mediaTypeButtonContainer);

            // settings button on the top right corner of the screen
            const settingsButton = document.createElement("button");
            settingsButton.id = "settingsButton";
            settingsButton.textContent = "Settings";
            setStyles(settingsButton, {
                position: "absolute",
                top: "20px",
                right: "20px",
                backgroundColor: "#007bff", // Primary color
                color: "#fff",
                padding: "10px 20px",
                borderRadius: "5px",
                border: "none",
                cursor: "pointer",
                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                transition: "background-color 0.3s ease",
            });
            settingsButton.addEventListener("click", () => {
                const settingsContainer = document.createElement("div");
                settingsContainer.id = "settingsContainer";
                setStyles(settingsContainer, {
                    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",
                    animation: "fadeIn 0.3s ease",
                });

                const settingsBox = document.createElement("div");
                setStyles(settingsBox, {
                    backgroundColor: "#000000", // Background color
                    color: "#ffffff", // Text color
                    padding: "30px",
                    borderRadius: "10px",
                    border: "1px solid #6c757d", // Secondary color
                    maxWidth: "80%",
                    maxHeight: "80%",
                    overflowY: "auto",
                    boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",
                });

                const settingsTitle = document.createElement("h2");
                settingsTitle.id = "settingsTitle";
                settingsTitle.textContent = "Settings";
                setStyles(settingsTitle, {
                    textAlign: "center",
                    marginBottom: "20px",
                });

                const settingsList = document.createElement("ul");
                settingsList.id = "settingsList";
                setStyles(settingsList, {
                    listStyleType: "none",
                    padding: "0",
                    margin: "0",
                });

                // include default settings as existing settings inside the input fields
                // have an icon next to the setting that explains what the setting does
                for (const setting in settings) {
                    const settingItem = document.createElement("li");
                    setStyles(settingItem, {
                        display: "flex",
                        alignItems: "center",
                        marginBottom: "15px",
                    });

                    const settingLabel = document.createElement("label");
                    settingLabel.textContent = setting.replace(/_/g, " ");
                    settingLabel.title = settings[setting].info;
                    setStyles(settingLabel, {
                        flex: "1",
                        display: "flex",
                        alignItems: "center",
                    });

                    const settingIcon = document.createElement("span");
                    settingIcon.className = "material-icons-outlined";
                    settingIcon.textContent = settings[setting].icon;
                    settingIcon.style.marginRight = "10px";
                    settingLabel.prepend(settingIcon);

                    settingItem.appendChild(settingLabel);

                    const settingInput = document.createElement("input");
                    const settingValueType = typeof defaultSettings[setting].value;
                    if (settingValueType === "boolean") {
                        settingInput.type = "checkbox";
                        settingInput.checked = settings[setting].value;
                    } else if (settingValueType === "number") {
                        settingInput.type = "number";
                        settingInput.value = settings[setting].value;
                    } else {
                        settingInput.type = "text";
                        settingInput.value = settings[setting].value;
                    }
                    setStyles(settingInput, {
                        padding: "8px 12px",
                        borderRadius: "5px",
                        border: "1px solid #6c757d", // Secondary color
                        flex: "2",
                    });
                    settingInput.addEventListener("focus", () => {
                        setStyles(settingInput, {
                            borderColor: "#007bff", // Primary color
                            boxShadow: "0 0 0 2px rgba(0, 123, 255, 0.25)",
                            outline: "none",
                        });
                    });
                    settingInput.addEventListener("blur", () => {
                        setStyles(settingInput, {
                            borderColor: "#6c757d", // Secondary color
                            boxShadow: "none",
                        });
                    });

                    if (settingValueType === "boolean") {
                        settingInput.style.marginRight = "10px";
                    }

                    settingItem.appendChild(settingInput);
                    settingsList.appendChild(settingItem);
                }

                const saveButton = document.createElement("button");
                saveButton.id = "saveButton";
                saveButton.textContent = "Save";
                setStyles(saveButton, {
                    backgroundColor: "#007bff", // Primary color
                    color: "#fff",
                    padding: "10px 20px",
                    borderRadius: "5px",
                    border: "none",
                    cursor: "pointer",
                    boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                    transition: "background-color 0.3s ease",
                    marginRight: "10px",
                });
                saveButton.addEventListener("click", () => {
                    const newSettings = defaultSettings;
                    const inputs = document.querySelectorAll("#settingsList input");
                    inputs.forEach((input) => {
                        const settingName = input.previousSibling.textContent.replace(
                            / /g,
                            "_"
                        );
                        const settingValue =
                            typeof defaultSettings[settingName].value === "boolean"
                                ? input.checked
                                : input.value;
                        newSettings[settingName].value = settingValue;
                    });
                    localStorage.setItem("gallerySettings", JSON.stringify(newSettings));
                    settings = newSettings;
                    settingsContainer.remove();
                    gridContainer.innerHTML = ""; // Clear the grid
                    loadPosts(mode); // Reload posts based on the new settings
                });

                // Close button
                const closeButton = document.createElement("button");
                closeButton.id = "closeButton";
                closeButton.textContent = "Close";
                setStyles(closeButton, {
                    backgroundColor: "#007bff", // Primary color
                    color: "#fff",
                    padding: "10px 20px",
                    borderRadius: "5px",
                    border: "none",
                    cursor: "pointer",
                    boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                    transition: "background-color 0.3s ease",
                });
                closeButton.addEventListener("click", () => {
                    settingsContainer.remove();
                });

                settingsBox.appendChild(settingsTitle);
                settingsBox.appendChild(settingsList);
                settingsBox.appendChild(saveButton);
                settingsBox.appendChild(closeButton);
                settingsContainer.appendChild(settingsBox);
                gallery.appendChild(settingsContainer);
            });

            // Hover effect for settings button
            settingsButton.addEventListener("mouseenter", () => {
                settingsButton.style.backgroundColor = "#0056b3";
            });
            settingsButton.addEventListener("mouseleave", () => {
                settingsButton.style.backgroundColor = "#007bff";
            });

            gallery.appendChild(settingsButton);

            const loadPosts = (mode) => {
                const checkedThreads = isArchivePage
                    ? // Get all checked threads in the archive page or the current link if it's not an archive page
                    Array.from(
                        document.querySelectorAll(
                            ".flashListing input[type='checkbox']:checked"
                        )
                    ).map((checkbox) => {
                        let archiveSite =
                            checkbox.parentNode.parentNode.querySelector("a").href;
                        return archiveSite;
                    })
                    : [threadURL];

                const loadPostsFromThread = (thread) => {
                    // get the website url without the protocol and next slash
                    const websiteUrl = thread.replace(/(^\w+:|^)\/\//, "").split("/")[0];

                    // const board = thread.split("/thread/")[0].split("/").pop();
                    // const threadNo = `${parseInt(thread.split("thread/").pop())}`
                    getDocument(thread, threadURL).then((doc) => {
                        let posts;

                        // use a case statement to deal with different websites
                        posts = getPosts(websiteUrl, doc);

                        posts.forEach((post) => {
                            let mediaLinkFlag = false;
                            let board;
                            let threadID;
                            let postID;
                            let postURL;
                            let thumbnailUrl;
                            let mediaLink;
                            let fileName;
                            let comment;

                            let isVideo;
                            let isImage;
                            let soundLink;
                            let encodedSoundPostLink;
                            let temp;

                            // case statement for different websites
                            switch (websiteUrl) {
                                case "warosu.org":
                                    let thumbnailElement = post.querySelector("img");

                                    fileName = post
                                        .querySelector(".fileinfo")
                                        ?.innerText.split(", ")[2];
                                    thumbnailUrl = thumbnailElement?.src;
                                    mediaLink = thumbnailElement?.parentNode.href;
                                    comment = post.querySelector("blockquote");

                                    threadID = post.querySelector(".js").href.match(/thread\/(\d+)/)[1];
                                    postID = post.id.replace("pc", "").replace("p", "");
                                    break;
                                case "archived.moe":
                                case "archive.palanq.win":
                                    thumbnailUrl = post.querySelector(".post_image").src;
                                    mediaLink = post.querySelector(".thread_image_link").href;
                                    fileName = post.querySelector(
                                        ".post_file_filename"
                                    ).title;
                                    comment = post.querySelector(".text");
                                    threadID = post.querySelector(".post_data > a").href.match(
                                        /thread\/(\d+)/
                                    )[1];
                                    postID = post.id
                                    break;
                                case "boards.4chan.org":
                                case "boards.4channel.org":
                                default:
                                    mediaLink = post.querySelector(".fileText a");
                                    if (post.querySelector(".fileText-original a")) {
                                        mediaLink = post.querySelector(".fileText-original a");
                                    }
                                    if (!mediaLink) {
                                        return;
                                    }

                                    if (
                                        mediaLink.href.includes("4cdn") ||
                                        mediaLink.href.includes("4chan.org")
                                    ) {
                                        if (mediaLink.title) {
                                            fileName = mediaLink.title;
                                        } else {
                                            fileName = mediaLink.innerText;
                                        }
                                    } else {
                                        fileName = mediaLink.innerText;
                                    }
                                    mediaLink = mediaLink.href;

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

                                    threadID = thread.match(/thread\/(\d+)/)[1];
                                    postID = post.id.replace("pc", "").replace("p", "");
                            }

                            if (mediaLink) {
                                isVideo = mediaLink.includes(".webm");
                                isImage =
                                    mediaLink.includes(".jpg") ||
                                    mediaLink.includes(".png") ||
                                    mediaLink.includes(".gif");
                                soundLink = fileName.match(/\[sound=(.+?)\]/);
                                mediaLinkFlag = true;
                            } else {
                                return; // Skip posts without media links
                            }

                            // replace the "#pcXXXXXXX" or "#pXXXXXXX" with an empty string to get the actual thread url
                            if (thread.includes("#")) {
                                postURL = thread.replace(/#p\d+/, "");
                                postURL = postURL.replace(/#pc\d+/, "");
                            } else {
                                postURL = thread;
                            }

                            // post info (constant)
                            board = thread.match(/\/\/[^\/]+\/([^\/]+)/)[1];
                            if (soundLink) {
                                encodedSoundPostLink = `https://4chan.mahdeensky.top/${board}/thread/${threadID}/${postID}`;
                            }

                            if (mediaLinkFlag) {
                                // 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",
                                            display: "flex",
                                            justifyContent: "center",
                                        });

                                        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;
                                        video.muted = true;
                                        video.controls = true;
                                        video.title = comment.innerText;
                                        video.videothumbnailDisplayed = "true";
                                        video.setAttribute("fileName", fileName);
                                        video.setAttribute("board", board);
                                        video.setAttribute("threadID", threadID);
                                        video.setAttribute("postID", postID);
                                        setStyles(video, {
                                            maxWidth: "100%",
                                            maxHeight: "200px",
                                            objectFit: "contain",
                                            cursor: "pointer",
                                            display: "none",
                                        });

                                        // videoJS stuff (not working for some reason)
                                        // video.className = "video-js";
                                        // video.setAttribute("data-setup", "{}");
                                        // const source = document.createElement("source");
                                        // source.src = mediaLink;
                                        // source.type = "video/webm";
                                        // video.appendChild(source);

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

                                        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]}`
                                            );
                                            // add attribute to the audio element with the encoded soundpost link
                                            audio.setAttribute(
                                                "encodedSoundPostLink",
                                                encodedSoundPostLink
                                            );
                                            videoContainer.appendChild(audio);

                                            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);

                                            // html5 video play
                                            video.onplay = (event) => {
                                                audio.play();
                                            };

                                            video.onpause = (event) => {
                                                audio.pause();
                                            };

                                            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;
                                            });
                                        }

                                        cell.appendChild(videoContainer);
                                    } else if (isImage) {
                                        const imageContainer = document.createElement("div");
                                        setStyles(imageContainer, {
                                            position: "relative",
                                            display: "flex",
                                            justifyContent: "center",
                                            alignItems: "center",
                                        });

                                        const image = document.createElement("img");
                                        image.src = thumbnailUrl;
                                        if (settings.Load_High_Res_Images_By_Default.value) {
                                            image.src = mediaLink;
                                        }
                                        if (mediaLink.includes(".gif")) {
                                            image.src = mediaLink;
                                        }
                                        image.setAttribute("fileName", fileName);
                                        image.setAttribute("actualSrc", mediaLink);
                                        image.setAttribute("thumbnailUrl", thumbnailUrl);
                                        image.setAttribute("board", board);
                                        image.setAttribute("threadID", threadID);
                                        image.setAttribute("postID", postID);
                                        setStyles(image, {
                                            maxWidth: "100%",
                                            maxHeight: "200px",
                                            objectFit: "contain",
                                            cursor: "pointer",
                                        });

                                        let createDarkenBackground = () => {
                                            const background = document.createElement("div");
                                            background.id = "darkenBackground";
                                            setStyles(background, {
                                                position: "fixed",
                                                top: "0",
                                                left: "0",
                                                width: "100%",
                                                height: "100%",
                                                backgroundColor: "rgba(0, 0, 0, 0.3)",
                                                backdropFilter: "blur(5px)",
                                                zIndex: "9999",
                                            });
                                            return background;
                                        };

                                        let zoomImage = () => {
                                            // have the image pop up centered in front of the screen so that it fills about 80% of the screen
                                            image.style = "";
                                            image.src = mediaLink;
                                            setStyles(image, {
                                                position: "fixed",
                                                top: "50%",
                                                left: "50%",
                                                transform: "translate(-50%, -50%)",
                                                zIndex: "10000",
                                                height: "80%",
                                                width: "80%",
                                                objectFit: "contain",
                                                cursor: "pointer",
                                            });

                                            // darken and blur the background behind the image without affecting the image
                                            const background = createDarkenBackground();
                                            gallery.appendChild(background);

                                            // create a container for the buttons, number, and download buttons (even space between them)
                                            // position: fixed; bottom: 10px; display: flex; flex-direction: row; justify-content: space-around; z-index: 10000; width: 100%; margin:auto;
                                            const bottomContainer = document.createElement("div");
                                            setStyles(bottomContainer, {
                                                position: "fixed",
                                                bottom: "10px",
                                                display: "flex",
                                                flexDirection: "row",
                                                justifyContent: "space-around",
                                                zIndex: "10000",
                                                width: "100%",
                                                margin: "auto",
                                            });
                                            background.appendChild(bottomContainer);

                                            // buttons on the bottom left of the screen for reverse image search (SauceNAO, Google Lens, Yandex)
                                            const buttonContainer = document.createElement("div");
                                            setStyles(buttonContainer, {
                                                display: "flex",
                                                gap: "10px",
                                            });
                                            buttonContainer.setAttribute("mediaLink", mediaLink);

                                            const sauceNAOButton = document.createElement("button");
                                            sauceNAOButton.textContent = "SauceNAO";
                                            setStyles(sauceNAOButton, {
                                                backgroundColor: "#1c1c1c",
                                                color: "#d9d9d9",
                                                padding: "5px 10px",
                                                borderRadius: "3px",
                                                border: "none",
                                                cursor: "pointer",
                                            });
                                            sauceNAOButton.addEventListener("click", () => {
                                                window.open(
                                                    `https://saucenao.com/search.php?url=${encodeURIComponent(
                                                        buttonContainer.getAttribute("mediaLink")
                                                    )}`
                                                );
                                            });
                                            buttonContainer.appendChild(sauceNAOButton);

                                            const googleLensButton = document.createElement("button");
                                            googleLensButton.textContent = "Google Lens";
                                            setStyles(googleLensButton, {
                                                backgroundColor: "#1c1c1c",
                                                color: "#d9d9d9",
                                                padding: "5px 10px",
                                                borderRadius: "3px",
                                                border: "none",
                                                cursor: "pointer",
                                                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                            });
                                            googleLensButton.addEventListener("click", () => {
                                                window.open(
                                                    `https://lens.google.com/uploadbyurl?url=${encodeURIComponent(
                                                        buttonContainer.getAttribute("mediaLink")
                                                    )}`
                                                );
                                            });
                                            buttonContainer.appendChild(googleLensButton);

                                            const yandexButton = document.createElement("button");
                                            yandexButton.textContent = "Yandex";
                                            setStyles(yandexButton, {
                                                backgroundColor: "#1c1c1c",
                                                color: "#d9d9d9",
                                                padding: "5px 10px",
                                                borderRadius: "3px",
                                                border: "none",
                                                cursor: "pointer",
                                                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                            });
                                            yandexButton.addEventListener("click", () => {
                                                window.open(
                                                    `https://yandex.com/images/search?rpt=imageview&url=${encodeURIComponent(
                                                        buttonContainer.getAttribute("mediaLink")
                                                    )}`
                                                );
                                            });
                                            buttonContainer.appendChild(yandexButton);

                                            bottomContainer.appendChild(buttonContainer);

                                            // download container for video/img and audio
                                            const downloadButtonContainer =
                                                document.createElement("div");
                                            setStyles(downloadButtonContainer, {
                                                display: "flex",
                                                gap: "10px",
                                            });
                                            bottomContainer.appendChild(downloadButtonContainer);

                                            const viewPostButton = document.createElement("a");
                                            viewPostButton.textContent = "View Post";
                                            viewPostButton.href = `https://boards.4chan.org/${board}/thread/${threadID}#p${postID}`;
                                            setStyles(viewPostButton, {
                                                backgroundColor: "#1c1c1c",
                                                color: "#d9d9d9",
                                                padding: "5px 10px",
                                                borderRadius: "3px",
                                                border: "none",
                                                cursor: "pointer",
                                                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                            });
                                            downloadButtonContainer.appendChild(viewPostButton);

                                            const downloadButton = document.createElement("a");
                                            downloadButton.textContent = "Download Video/Image";
                                            downloadButton.href = mediaLink;
                                            downloadButton.download = fileName;
                                            downloadButton.target = "_blank";
                                            setStyles(downloadButton, {
                                                backgroundColor: "#1c1c1c",
                                                color: "#d9d9d9",
                                                padding: "5px 10px",
                                                borderRadius: "3px",
                                                border: "none",
                                                cursor: "pointer",
                                                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                            });
                                            downloadButtonContainer.appendChild(downloadButton);

                                            const audioDownloadButton = document.createElement("a");
                                            audioDownloadButton.textContent = "Download Audio";
                                            audioDownloadButton.target = "_blank";
                                            setStyles(audioDownloadButton, {
                                                backgroundColor: "#1c1c1c",
                                                color: "#d9d9d9",
                                                padding: "5px 10px",
                                                borderRadius: "3px",
                                                border: "none",
                                                cursor: "pointer",
                                                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                            });
                                            if (soundLink) {
                                                audioDownloadButton.href = decodeURIComponent(
                                                    soundLink[1].startsWith("http")
                                                        ? soundLink[1]
                                                        : `https://${soundLink[1]}`
                                                );
                                                audioDownloadButton.download = soundLink[1]
                                                    .split("/")
                                                    .pop();
                                            } else {
                                                audioDownloadButton.style.display = "none";
                                            }
                                            downloadButtonContainer.appendChild(audioDownloadButton);

                                            // a button beside the download video and download audio button that says download encoded soundpost which links to the following url in a new tab "https://4chan.mahdeensky.top/<board>/thread/<thread>/<post>" where things between the <>, are variables to be replaced
                                            const encodedSoundPostButton =
                                                document.createElement("a");
                                            encodedSoundPostButton.textContent =
                                                "Download Encoded Soundpost";
                                            encodedSoundPostButton.target = "_blank";
                                            setStyles(encodedSoundPostButton, {
                                                backgroundColor: "#1c1c1c",
                                                color: "#d9d9d9",
                                                padding: "5px 10px",
                                                borderRadius: "3px",
                                                border: "none",
                                                cursor: "pointer",
                                                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                            });
                                            if (soundLink) {
                                                encodedSoundPostButton.href = `https://4chan.mahdeensky.top/${board}/thread/${threadID}/${postID}`;
                                            } else {
                                                encodedSoundPostButton.style.display = "none";
                                            }
                                            downloadButtonContainer.appendChild(
                                                encodedSoundPostButton
                                            );

                                            // number on the bottom right of the screen to show which image is currently being viewed
                                            const imageNumber = document.createElement("div");
                                            let currentImageNumber =
                                                Array.from(cell.parentNode.children).indexOf(cell) + 1;
                                            let imageTotal = cell.parentNode.children.length;
                                            imageNumber.textContent = `${currentImageNumber}/${imageTotal}`;
                                            setStyles(imageNumber, {
                                                backgroundColor: "#1c1c1c",
                                                color: "#d9d9d9",
                                                padding: "5px 10px",
                                                borderRadius: "3px",
                                                border: "none",
                                                cursor: "pointer",
                                                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                                zIndex: "10000",
                                            });
                                            bottomContainer.appendChild(imageNumber);

                                            // title of the image/video on the top left of the screen
                                            const imageTitle = document.createElement("div");
                                            imageTitle.textContent = fileName;
                                            setStyles(imageTitle, {
                                                position: "fixed",
                                                top: "10px",
                                                left: "10px",
                                                backgroundColor: "#1c1c1c",
                                                color: "#d9d9d9",
                                                padding: "5px 10px",
                                                borderRadius: "3px",
                                                border: "none",
                                                cursor: "pointer",
                                                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                                zIndex: "10000",
                                            });
                                            background.appendChild(imageTitle);

                                            let currentCell = cell;
                                            // use left and right arrow keys to navigate between images/videos
                                            let keybindHandler = (event) => {
                                                if (event.key === "ArrowLeft") {
                                                    // get the previous cell in the grid
                                                    const previousCell =
                                                        currentCell.previousElementSibling;
                                                    if (previousCell) {
                                                        if (gallery.querySelector("#zoomedVideo")) {
                                                            if (
                                                                gallery
                                                                    .querySelector("#zoomedVideo")
                                                                    .querySelector("audio")
                                                            ) {
                                                                gallery
                                                                    .querySelector("#zoomedVideo")
                                                                    .querySelector("audio")
                                                                    .pause();
                                                            }
                                                            gallery.removeChild(
                                                                gallery.querySelector("#zoomedVideo")
                                                            );
                                                        } else if (gallery.querySelector("#zoomedImage")) {
                                                            gallery.removeChild(
                                                                gallery.querySelector("#zoomedImage")
                                                            );
                                                        } else {
                                                            image.style = "";
                                                            // image.src = thumbnailUrl;
                                                            setStyles(image, {
                                                                maxWidth: "100%",
                                                                maxHeight: "200px",
                                                                objectFit: "contain",
                                                            });
                                                        }

                                                        // check if it has a video
                                                        const video = previousCell?.querySelector("video");
                                                        if (video) {
                                                            const video = previousCell
                                                                .querySelector("video")
                                                                .cloneNode(true);
                                                            video.id = "zoomedVideo";
                                                            video.style = "";
                                                            setStyles(video, {
                                                                position: "fixed",
                                                                top: "50%",
                                                                left: "50%",
                                                                transform: "translate(-50%, -50%)",
                                                                zIndex: "10000",
                                                                height: "80%",
                                                                width: "80%",
                                                                objectFit: "contain",
                                                                cursor: "pointer",
                                                                preload: "auto",
                                                            });
                                                            gallery.appendChild(video);

                                                            // check if there is an audio element
                                                            let audio = previousCell.querySelector("audio");
                                                            if (audio) {
                                                                audio = audio.cloneNode(true);

                                                                // same event listeners as the video
                                                                video.onplay = (event) => {
                                                                    audio.play();
                                                                };

                                                                video.onpause = (event) => {
                                                                    audio.pause();
                                                                };

                                                                let lastVideoTime = 0;
                                                                video.addEventListener("timeupdate", () => {
                                                                    if (
                                                                        Math.abs(
                                                                            video.currentTime - lastVideoTime
                                                                        ) >= 2
                                                                    ) {
                                                                        audio.currentTime = video.currentTime;
                                                                        lastVideoTime = video.currentTime;
                                                                    }
                                                                    lastVideoTime = video.currentTime;
                                                                });
                                                                video.appendChild(audio);
                                                            }
                                                        } else {
                                                            // if it doesn't have a video, it must have an image
                                                            const originalImage =
                                                                previousCell.querySelector("img");
                                                            const currentImage =
                                                                originalImage.cloneNode(true);
                                                            currentImage.id = "zoomedImage";
                                                            currentImage.style = "";
                                                            currentImage.src =
                                                                currentImage.getAttribute("actualSrc");
                                                            originalImage.src =
                                                                originalImage.getAttribute("actualSrc");
                                                            setStyles(currentImage, {
                                                                position: "fixed",
                                                                top: "50%",
                                                                left: "50%",
                                                                transform: "translate(-50%, -50%)",
                                                                zIndex: "10000",
                                                                height: "80%",
                                                                width: "80%",
                                                                objectFit: "contain",
                                                                cursor: "pointer",
                                                            });
                                                            gallery.appendChild(currentImage);
                                                            currentImage.addEventListener("click", () => {
                                                                gallery.removeChild(currentImage);
                                                                gallery.removeChild(background);
                                                                document.removeEventListener(
                                                                    "keydown",
                                                                    keybindHandler
                                                                );
                                                            });

                                                            let audio = previousCell.querySelector("audio");
                                                            if (audio) {
                                                                audio = audio.cloneNode(true);
                                                                currentImage.appendChild(audio);

                                                                // event listeners when hovering over the image
                                                                currentImage.addEventListener(
                                                                    "mouseenter",
                                                                    () => {
                                                                        audio.play();
                                                                    }
                                                                );
                                                                currentImage.addEventListener(
                                                                    "mouseleave",
                                                                    () => {
                                                                        audio.pause();
                                                                    }
                                                                );
                                                            }
                                                        }

                                                        if (previousCell) {
                                                            currentCell = previousCell;
                                                            buttonContainer.setAttribute(
                                                                "mediaLink",
                                                                previousCell.querySelector("img").src
                                                            );

                                                            currentImageNumber -= 1;
                                                            imageNumber.textContent = `${currentImageNumber}/${imageTotal}`;

                                                            // filename of the video if it has one, otherwise the filename of the image
                                                            imageTitle.textContent = video
                                                                ? video.getAttribute("fileName")
                                                                : previousCell
                                                                    .querySelector("img")
                                                                    .getAttribute("fileName");

                                                            // update view post button link
                                                            let previousMedia = video || previousCell.querySelector("img");
                                                            let previousBoard = previousMedia.getAttribute("board");
                                                            let previousThreadID = previousMedia.getAttribute("threadID");
                                                            let previousPostID = previousMedia.getAttribute("postID");
                                                            viewPostButton.href = `https://boards.4chan.org/${previousBoard}/thread/${previousThreadID}#p${previousPostID}`;

                                                            // update the download button links
                                                            downloadButton.href = previousMedia.src;
                                                            if (previousCell.querySelector("audio")) {
                                                                // updating audio button download link
                                                                audioDownloadButton.href =
                                                                    previousCell.querySelector("audio").src;
                                                                audioDownloadButton.download = previousCell
                                                                    .querySelector("audio")
                                                                    .src.split("/")
                                                                    .pop();
                                                                audioDownloadButton.style.display = "block";

                                                                // updating encoded soundpost button link
                                                                encodedSoundPostButton.href = previousCell.querySelector("audio")
                                                                    .getAttribute("encodedSoundPostLink");
                                                                encodedSoundPostButton.style.display = "block";

                                                            } else {
                                                                audioDownloadButton.style.display = "none";
                                                                encodedSoundPostButton.style.display = "none";
                                                            }
                                                        }
                                                    }
                                                } else if (event.key === "ArrowRight") {
                                                    // get the next cell in the grid
                                                    const nextCell = currentCell.nextElementSibling;
                                                    if (nextCell) {
                                                        if (gallery.querySelector("#zoomedVideo")) {
                                                            if (
                                                                gallery
                                                                    .querySelector("#zoomedVideo")
                                                                    .querySelector("audio")
                                                            ) {
                                                                gallery
                                                                    .querySelector("#zoomedVideo")
                                                                    .querySelector("audio")
                                                                    .pause();
                                                            }
                                                            gallery.removeChild(
                                                                gallery.querySelector("#zoomedVideo")
                                                            );
                                                            // ("removed video");
                                                        } else if (gallery.querySelector("#zoomedImage")) {
                                                            gallery.removeChild(
                                                                gallery.querySelector("#zoomedImage")
                                                            );
                                                            // ("removed image");
                                                        } else {
                                                            image.style = "";
                                                            setStyles(image, {
                                                                maxWidth: "100%",
                                                                maxHeight: "200px",
                                                                objectFit: "contain",
                                                            });
                                                        }

                                                        // check if it has a video
                                                        const video = nextCell?.querySelector("video");
                                                        if (video) {
                                                            const video = nextCell
                                                                .querySelector("video")
                                                                .cloneNode(true);
                                                            video.id = "zoomedVideo";
                                                            video.style = "";
                                                            setStyles(video, {
                                                                position: "fixed",
                                                                top: "50%",
                                                                left: "50%",
                                                                transform: "translate(-50%, -50%)",
                                                                zIndex: "10000",
                                                                height: "80%",
                                                                width: "80%",
                                                                objectFit: "contain",
                                                                cursor: "pointer",
                                                                preload: "auto",
                                                            });

                                                            // check if there is an audio element
                                                            let audio = nextCell.querySelector("audio");
                                                            if (audio) {
                                                                audio = audio.cloneNode(true);

                                                                // same event listeners as the video
                                                                video.onplay = (event) => {
                                                                    audio.play();
                                                                };

                                                                video.onpause = (event) => {
                                                                    audio.pause();
                                                                };

                                                                let lastVideoTime = 0;
                                                                video.addEventListener("timeupdate", () => {
                                                                    if (
                                                                        Math.abs(
                                                                            video.currentTime - lastVideoTime
                                                                        ) >= 2
                                                                    ) {
                                                                        audio.currentTime = video.currentTime;
                                                                        lastVideoTime = video.currentTime;
                                                                    }
                                                                    lastVideoTime = video.currentTime;
                                                                });
                                                                video.appendChild(audio);
                                                            }
                                                            gallery.appendChild(video);
                                                        } else {
                                                            const originalImage =
                                                                nextCell.querySelector("img");
                                                            const currentImage =
                                                                originalImage.cloneNode(true);
                                                            currentImage.id = "zoomedImage";
                                                            currentImage.style = "";
                                                            currentImage.src =
                                                                currentImage.getAttribute("actualSrc");
                                                            originalImage.src =
                                                                originalImage.getAttribute("actualSrc");
                                                            setStyles(currentImage, {
                                                                position: "fixed",
                                                                top: "50%",
                                                                left: "50%",
                                                                transform: "translate(-50%, -50%)",
                                                                zIndex: "10000",
                                                                height: "80%",
                                                                width: "80%",
                                                                objectFit: "contain",
                                                                cursor: "pointer",
                                                            });
                                                            gallery.appendChild(currentImage);
                                                            currentImage.addEventListener("click", () => {
                                                                gallery.removeChild(currentImage);
                                                                gallery.removeChild(background);
                                                                document.removeEventListener(
                                                                    "keydown",
                                                                    keybindHandler
                                                                );
                                                            });

                                                            let audio = nextCell.querySelector("audio");
                                                            if (audio) {
                                                                audio = nextCell
                                                                    .querySelector("audio")
                                                                    .cloneNode(true);
                                                                currentImage.appendChild(audio);

                                                                currentImage.addEventListener(
                                                                    "mouseenter",
                                                                    () => {
                                                                        audio.play();
                                                                    }
                                                                );
                                                                currentImage.addEventListener(
                                                                    "mouseleave",
                                                                    () => {
                                                                        audio.pause();
                                                                    }
                                                                );
                                                            }
                                                        }
                                                        if (nextCell) {
                                                            currentCell = nextCell;
                                                            buttonContainer.setAttribute(
                                                                "mediaLink",
                                                                nextCell.querySelector("img").src
                                                            );

                                                            currentImageNumber += 1;
                                                            imageNumber.textContent = `${currentImageNumber}/${imageTotal}`;

                                                            // filename of the video if it has one, otherwise the filename of the image
                                                            imageTitle.textContent = video
                                                                ? video.getAttribute("fileName")
                                                                : nextCell
                                                                    .querySelector("img")
                                                                    .getAttribute("fileName");

                                                            // update view post button link
                                                            let nextMedia = video || nextCell.querySelector("img");
                                                            let nextBoard = nextMedia.getAttribute("board");
                                                            let nextThreadID = nextMedia.getAttribute("threadID");
                                                            let nextPostID = nextMedia.getAttribute("postID");
                                                            viewPostButton.href = `https://boards.4chan.org/${nextBoard}/thread/${nextThreadID}#p${nextPostID}`;

                                                            // update the download button links
                                                            downloadButton.href = nextMedia.src;
                                                            if (nextCell.querySelector("audio")) {
                                                                audioDownloadButton.href =
                                                                    nextCell.querySelector("audio").src;
                                                                audioDownloadButton.download = nextCell
                                                                    .querySelector("audio")
                                                                    .src.split("/")
                                                                    .pop();
                                                                audioDownloadButton.style.display = "block";

                                                                encodedSoundPostButton.href = nextCell.querySelector("audio")
                                                                    .getAttribute("encodedSoundPostLink");
                                                                encodedSoundPostButton.style.display = "block";
                                                            } else {
                                                                audioDownloadButton.style.display = "none";
                                                                encodedSoundPostButton.style.display = "none";
                                                            }
                                                        }
                                                    }
                                                }
                                            };
                                            document.addEventListener("keydown", keybindHandler);

                                            image.addEventListener(
                                                "click",
                                                () => {
                                                    image.style = "";
                                                    // image.src = thumbnailUrl;
                                                    setStyles(image, {
                                                        maxWidth: "99%",
                                                        maxHeight: "199px",
                                                        objectFit: "contain",
                                                    });

                                                    if (gallery.querySelector("#darkenBackground")) {
                                                        gallery.removeChild(background);
                                                    }
                                                    document.removeEventListener(
                                                        "keydown",
                                                        keybindHandler
                                                    );

                                                    image.addEventListener("click", zoomImage, {
                                                        once: true,
                                                    });
                                                },
                                                { once: true }
                                            );
                                        };

                                        image.addEventListener("click", zoomImage, { once: true });
                                        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]}`
                                            );
                                            audio.loop = true;
                                            // set the attribute to the audio element with the encoded soundpost link
                                            audio.setAttribute(
                                                "encodedSoundPostLink",
                                                encodedSoundPostLink
                                            );
                                            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
                                    }

                                    // Add button that scrolls to the post in the thread
                                    const viewPostButton = document.createElement("button");
                                    viewPostButton.textContent = "View Post";
                                    setStyles(viewPostButton, {
                                        backgroundColor: "#1c1c1c",
                                        color: "#d9d9d9",
                                        padding: "5px 10px",
                                        borderRadius: "3px",
                                        border: "none",
                                        cursor: "pointer",
                                        boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
                                    });

                                    viewPostButton.addEventListener("click", () => {
                                        // post id example: "pc77515440"
                                        window.location.href = postURL + "#" + post.id;
                                        document.body.removeChild(gallery);
                                    });
                                    buttonDiv.appendChild(viewPostButton);

                                    cell.appendChild(buttonDiv);
                                    gridContainer.appendChild(cell);
                                }
                            }
                        });
                    });
                };
                checkedThreads.forEach(loadPostsFromThread);
            };

            loadPosts(mode);

            gallery.appendChild(gridContainer);

            const closeButton = document.createElement("button");
            closeButton.textContent = "Close";
            closeButton.id = "closeGallery";
            setStyles(closeButton, {
                position: "absolute",
                bottom: "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
            // (`Last scroll position: ${lastScrollPosition} px`);
            gridContainer.addEventListener("scroll", () => {
                lastScrollPosition = gridContainer.scrollTop;
                // (`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(() => {
                    if (gallerySize.width > 0 && gallerySize.height > 0) {
                        gridContainer.style.width = `${gallerySize.width}px`;
                        gridContainer.style.height = `${gallerySize.height}px`;
                    }
                    // (`Restored scroll position: ${lastScrollPosition} px`);
                    gridContainer.scrollTop = lastScrollPosition;
                }, 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);
            });
        }
    };

    // Use the "i" key to open and close the gallery/grid
    document.addEventListener("keydown", (event) => {
        if (event.key === "i") {
            // Prevent the gallery from opening when typing in an input or textarea
            if (
                event.target.tagName == "INPUT" ||
                event.target.tagName == "TEXTAREA"
            ) {
                return;
            }

            if (document.querySelector("#imageGallery")) {
                document.body.removeChild(document.querySelector("#imageGallery"));
            } else {
                if (document.querySelector("#openImageGallery")) {
                    document.querySelector("#openImageGallery").click();
                }
            }
        }
    });

    loadButton();
    ("4chan Gallery loaded successfully!");
})();