Greasy Fork

8chan.moe Bigger Deletion Checkbox with Toggle

Adds a larger checkbox to the right of each post's deletion checkbox on 8chan.moe mod.js and thread pages, hidden by default with a toggle button in the OP title bar.

目前为 2025-04-23 提交的版本。查看 最新版本

// ==UserScript==
// @name        8chan.moe Bigger Deletion Checkbox with Toggle
// @namespace   Violentmonkey Scripts
// @match       https://8chan.moe/mod.js?boardUri=*&threadId=*
// @match       https://8chan.moe/*/res/*
// @grant       none
// @version     1.4
// @author      Anonymous
// @license     MIT
// @description Adds a larger checkbox to the right of each post's deletion checkbox on 8chan.moe mod.js and thread pages, hidden by default with a toggle button in the OP title bar.
// ==/UserScript==

(function() {
    'use strict';

    // Track toggle state (hidden by default)
    let areLargeCheckboxesVisible = false;

    // Function to add a larger checkbox to a post
    function addLargeCheckbox(post) {
        const deletionCheckbox = post.querySelector('input.deletionCheckBox');
        if (!deletionCheckbox) return; // Skip if no deletion checkbox found

        // Find the postInfo or opHead div to append the new checkbox
        const postInfo = post.querySelector('.postInfo.title, .opHead.title');
        if (!postInfo) return;

        // Create a container for the large checkbox
        const largeCheckboxContainer = document.createElement('span');
        largeCheckboxContainer.className = 'large-checkbox-container';
        largeCheckboxContainer.style.marginLeft = '12px'; // Spacing for larger checkbox
        largeCheckboxContainer.style.display = areLargeCheckboxesVisible ? 'inline-block' : 'none';
        largeCheckboxContainer.style.verticalAlign = 'middle';

        // Create the large checkbox
        const largeCheckbox = document.createElement('input');
        largeCheckbox.type = 'checkbox';
        largeCheckbox.style.width = '48px'; // 2x larger (24px * 2 = 48px)
        largeCheckbox.style.height = '48px';
        largeCheckbox.style.border = '4px solid black'; // Thicker border
        largeCheckbox.style.cursor = 'pointer';

        // Sync the large checkbox with the original
        largeCheckbox.checked = deletionCheckbox.checked;
        largeCheckbox.addEventListener('change', () => {
            deletionCheckbox.checked = largeCheckbox.checked;
            // Trigger change event on original checkbox to ensure form functionality
            const event = new Event('change', { bubbles: true });
            deletionCheckbox.dispatchEvent(event);
        });

        // Sync the original checkbox with the large one
        deletionCheckbox.addEventListener('change', () => {
            largeCheckbox.checked = deletionCheckbox.checked;
        });

        // Append the large checkbox to the container
        largeCheckboxContainer.appendChild(largeCheckbox);

        // Append the container to the postInfo div
        postInfo.appendChild(largeCheckboxContainer);
    }

    // Function to add toggle button to OP post
    function addToggleButton() {
        const opPost = document.querySelector('.innerOP');
        if (!opPost) {
            console.log('No .innerOP found for toggle button');
            return;
        }

        const opTitle = opPost.querySelector('.opHead.title, .postInfo.title');
        if (!opTitle) {
            console.log('No .opHead.title or .postInfo.title found in .innerOP');
            return;
        }

        // Remove existing toggle button to prevent duplicates
        const existingButton = opTitle.querySelector('.toggle-checkboxes-button');
        if (existingButton) {
            existingButton.remove();
        }

        // Create toggle button
        const toggleButton = document.createElement('button');
        toggleButton.type = 'button'; // Prevent form submission
        toggleButton.className = 'toggle-checkboxes-button glowOnHover';
        toggleButton.textContent = areLargeCheckboxesVisible ? 'Hide Big Checkboxes' : 'Show Big Checkboxes';
        toggleButton.style.cursor = 'pointer';
        toggleButton.style.marginLeft = '10px';
        toggleButton.style.padding = '2px 6px';
        toggleButton.style.border = '1px solid #ccc';
        toggleButton.style.backgroundColor = areLargeCheckboxesVisible ? '#e0e0e0' : '#f0f0f0';
        toggleButton.style.borderRadius = '3px';
        toggleButton.style.verticalAlign = 'middle';
        toggleButton.style.fontSize = '12px';

        // Toggle functionality
        toggleButton.addEventListener('click', (event) => {
            event.preventDefault(); // Prevent any default behavior
            event.stopPropagation(); // Prevent bubbling to parent elements
            areLargeCheckboxesVisible = !areLargeCheckboxesVisible;
            const containers = document.querySelectorAll('.large-checkbox-container');
            containers.forEach(container => {
                container.style.display = areLargeCheckboxesVisible ? 'inline-block' : 'none';
            });
            toggleButton.textContent = areLargeCheckboxesVisible ? 'Hide Big Checkboxes' : 'Show Big Checkboxes';
            toggleButton.style.backgroundColor = areLargeCheckboxesVisible ? '#e0e0e0' : '#f0f0f0';
        });

        // Append button to OP title bar
        opTitle.appendChild(toggleButton);
        console.log('Toggle button added to OP title bar');
    }

    // Process all posts (OP and replies)
    function processPosts() {
        // Handle OP
        const opPost = document.querySelector('.innerOP');
        if (opPost) {
            addLargeCheckbox(opPost);
        }

        // Handle replies
        const replyPosts = document.querySelectorAll('.innerPost');
        replyPosts.forEach(post => {
            addLargeCheckbox(post);
        });
    }

    // Initial processing
    processPosts();
    addToggleButton();

    // Observe for dynamically added posts (e.g., via auto-refresh or new replies)
    const observer = new MutationObserver((mutations) => {
        mutations.forEach(mutation => {
            if (mutation.addedNodes.length) {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        // Check if the added node is a post
                        if (node.classList.contains('innerPost') || node.classList.contains('innerOP')) {
                            addLargeCheckbox(node);
                            // Add toggle button if OP is added dynamically
                            if (node.classList.contains('innerOP')) {
                                addToggleButton();
                            }
                        }
                        // Check for posts within the added node
                        node.querySelectorAll('.innerPost, .innerOP').forEach(post => {
                            addLargeCheckbox(post);
                            // Add toggle button if OP is found
                            if (post.classList.contains('innerOP')) {
                                addToggleButton();
                            }
                        });
                    }
                });
            }
        });
    });

    // Observe changes in the thread list
    const threadList = document.getElementById('threadList');
    if (threadList) {
        observer.observe(threadList, {
            childList: true,
            subtree: true
        });
    } else {
        console.log('No #threadList found for MutationObserver');
    }
})();