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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

})();