8chan.moe Delete/Trash No Refresh

Prevents page refresh when clicking Delete or Trash buttons on 8chan.moe moderation pages and handles deletions via AJAX

// ==UserScript==
// @name         8chan.moe Delete/Trash No Refresh
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  Prevents page refresh when clicking Delete or Trash buttons on 8chan.moe moderation pages and handles deletions via AJAX
// @author       Anonymous
// @match        https://8chan.moe/*/res/*.html*
// @match        https://8chan.moe/mod.js?*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    console.log('8chan.moe Delete/Trash No Refresh script v2.1 loaded');

    // Function to handle button clicks and send AJAX request
    function handleButtonClick(event) {
        event.preventDefault(); // Prevent default button behavior
        event.stopPropagation(); // Stop event bubbling
        event.stopImmediatePropagation(); // Stop other handlers

        const button = event.target;
        console.log(`Clicked button: ${button.id} with value: ${button.value}`);

        // Try to find the form
        let form = button.closest('form');
        if (!form) {
            // Fallback: search for a form with deletion checkboxes
            form = document.querySelector('form input[name*="-"][name*="-"]:checked')?.closest('form') || document.querySelector('form');
            if (!form) {
                console.error('No form found in the document');
                alert('Error: No form found. Please report this issue.');
                return;
            }
        }
        console.log(`Form found: ${form.outerHTML}`);

        // Prevent form submission
        form.addEventListener('submit', (e) => {
            e.preventDefault();
            e.stopPropagation();
            console.log('Form submission prevented for form:', form);
        }, { capture: true, once: true });

        // Disable button to prevent multiple clicks
        button.disabled = true;
        setTimeout(() => { button.disabled = false; }, 500); // Re-enable after 500ms

        // Collect form data
        const formData = new FormData();
        formData.append('action', button.value); // Add the button's value (delete or trash)

        // Add checked post checkboxes (e.g., a-9-12)
        const checkedCheckboxes = form.querySelectorAll('input[name*="-"][name*="-"]:checked');
        if (checkedCheckboxes.length === 0) {
            console.error('No checked checkboxes found');
            alert('Error: No posts selected. Please check at least one post.');
            return;
        }
        checkedCheckboxes.forEach(checkbox => {
            formData.append(checkbox.name, 'true');
            console.log(`Added post checkbox: ${checkbox.name}=true`);
        });

        // Log form data for debugging
        for (const [key, value] of formData.entries()) {
            console.log(`FormData: ${key}=${value}`);
        }

        // Log form details
        console.log(`Form action attribute: ${form.getAttribute('action') || 'undefined'}`);
        console.log(`Form method: ${form.getAttribute('method') || 'undefined'}`);

        // Use the correct deletion endpoint
        const actionUrl = 'https://8chan.moe/contentActions.js?json=1';
        console.log(`Final action URL: ${actionUrl}`);

        // Send AJAX request
        fetch(actionUrl, {
            method: 'POST',
            body: formData, // Use multipart/form-data
            credentials: 'same-origin', // Include cookies for authentication
            headers: {
                'Accept': 'application/json',
                'X-Requested-With': 'XMLHttpRequest'
                // Omit Content-Type to let browser set multipart/form-data with boundary
            }
        })
        .then(response => {
            console.log(`Response status: ${response.status}, ok: ${response.ok}`);
            return response.json().catch(() => response.text()).then(data => ({ status: response.status, ok: response.ok, data }));
        })
        .then(({ status, ok, data }) => {
            if (ok) {
                console.log(`${button.value} action successful`, data);
                // Provide visual feedback for each checked post
                checkedCheckboxes.forEach(checkbox => {
                    const postElement = checkbox.closest('.post') || checkbox.closest('.post-container') || checkbox.closest('article');
                    if (postElement) {
                        postElement.style.opacity = '0.5'; // Visual indication
                        postElement.style.pointerEvents = 'none'; // Disable interactions
                    }
                });
            } else {
                throw new Error(`Failed to ${button.value}: ${status} ${JSON.stringify(data)}`);
            }
        })
        .catch(error => {
            console.error('Error:', error);
            alert(`Error performing ${button.value} action: ${error.message}`);
            // Revert visual changes on failure
            checkedCheckboxes.forEach(checkbox => {
                const postElement = checkbox.closest('.post') || checkbox.closest('.post-container') || checkbox.closest('article');
                if (postElement) {
                    postElement.style.opacity = '';
                    postElement.style.pointerEvents = '';
                }
            });
        });
    }

    // Add event listeners to Delete and Trash buttons
    document.addEventListener('click', function(event) {
        if (event.target.matches('#deleteFormButton') || event.target.matches('#trashFormButton')) {
            console.log('Button click detected:', event.target.id);
            handleButtonClick(event);
        }
    }, { capture: true });

    // Prevent form submission for all forms with delete/trash buttons or checkboxes
    document.querySelectorAll('form').forEach(form => {
        if (form.querySelector('#deleteFormButton') || form.querySelector('#trashFormButton') || form.querySelector('input[name*="-"][name*="-"]')) {
            form.addEventListener('submit', (e) => {
                e.preventDefault();
                e.stopPropagation();
                console.log('Form submission blocked for form:', form);
            }, { capture: true });
        }
    });

    // Global submit prevention for safety
    document.addEventListener('submit', (e) => {
        if (e.target.querySelector('#deleteFormButton') || e.target.querySelector('#trashFormButton') || e.target.querySelector('input[name*="-"][name*="-"]')) {
            e.preventDefault();
            console.log('Global form submission blocked');
        }
    }, { capture: true });

})();