MAL Quick Delete Button

Adds a delete button to entries of your anime list.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         MAL Quick Delete Button
// @namespace    https://tampermonkey.net/
// @version      1
// @description  Adds a delete button to entries of your anime list.
// @author       Rowel
// @match        https://myanimelist.net/animelist/*
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    const getCSRFToken = () => {
        const meta = document.querySelector('meta[name="csrf_token"]');
        return meta?.content || null;
    };

    const injectDeleteButtons = () => {
        const csrf = getCSRFToken();
        if (!csrf) {
            console.warn(' CSRF token not found. Are you logged in?');
            return;
        }

        const rows = document.querySelectorAll('td.data.progress');

        rows.forEach(cell => {
            const progressDiv = cell.querySelector('div[class^="progress-"]');
            if (!progressDiv || cell.querySelector('.delete-entry-button')) return;

            const match = progressDiv.className.match(/progress-(\d+)/);
            if (!match) return;

            const animeId = match[1];

            // Create the button
            const button = document.createElement('button');
            button.textContent = '❌';
            button.className = 'delete-entry-button';
            button.dataset.confirmed = 'false';
            Object.assign(button.style, {
                backgroundColor: '#e74c3c',
                color: 'white',
                border: 'none',
                borderRadius: '3px',
                padding: '2px 5px',
                fontSize: '10px',
                cursor: 'pointer'
            });

            button.addEventListener('click', () => {
                const confirmed = button.dataset.confirmed === 'true';

                if (!confirmed) {
                    // Change appearance for confirmation
                    button.textContent = '✅';
                    button.style.backgroundColor = '#27ae60';
                    button.dataset.confirmed = 'true';
                } else {
                    // Perform delete
                    GM_xmlhttpRequest({
                        method: 'POST',
                        url: `https://myanimelist.net/ownlist/anime/${animeId}/delete?hideLayout=1`,
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded',
                            'Origin': 'https://myanimelist.net',
                            'Referer': `https://myanimelist.net/ownlist/anime/${animeId}/edit?hideLayout=1`
                        },
                        data: `csrf_token=${encodeURIComponent(csrf)}`,
                        onload: res => {
                            if (res.status === 200) {
                                const fullRow = cell.closest('tr');
                                fullRow.style.opacity = '0.4';
                                setTimeout(() => fullRow.remove(), 500);
                            } else {
                                alert(`Failed to delete anime ID ${animeId}. Status: ${res.status}`);
                            }
                        }
                    });
                }
            });

            // Inline layout
            const progressText = progressDiv.textContent.trim();
            progressDiv.textContent = '';

            const inlineWrap = document.createElement('span');
            inlineWrap.style.display = 'inline-flex';
            inlineWrap.style.alignItems = 'center';
            inlineWrap.style.gap = '6px';

            const textNode = document.createElement('span');
            textNode.textContent = progressText;

            inlineWrap.appendChild(textNode);
            inlineWrap.appendChild(button);
            progressDiv.appendChild(inlineWrap);
        });
    };

    // Wait for table render
    const observer = new MutationObserver(() => {
        const loaded = document.querySelector('td.data.progress div[class^="progress-"]');
        if (loaded) {
            observer.disconnect();
            injectDeleteButtons();
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });
})();