您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a delete button to all items on lists
当前为
- // ==UserScript==
- // @name AniList Delete Button on List Items
- // @license MIT
- // @namespace rtonne
- // @match https://anilist.co/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=anilist.co
- // @version 1.1
- // @author Rtonne
- // @description Adds a delete button to all items on lists
- // @grant GM.addStyle
- // @grant GM.registerMenuCommand
- // @grant GM.unregisterMenuCommand
- // @grant GM.getValue
- // @grant GM.setValue
- // ==/UserScript==
- (async () => {
- const inplace = "autoconfirm" === GM.registerMenuCommand(
- (await GM.getValue("autoconfirm", false)) ? "☑ Auto-confirm Deletion" : "☐ Auto-confirm Deletion",
- toggleAutoConfirm,
- {id: "autoconfirm", autoClose: false}
- );
- async function toggleAutoConfirm() {
- const new_value = !(await GM.getValue("autoconfirm", false));
- await GM.setValue("autoconfirm", new_value);
- if (!inplace) {
- GM.unregisterMenuCommand("autoconfirm");
- }
- GM.registerMenuCommand(
- new_value ? "☑ Auto-confirm Deletion" : "☐ Auto-confirm Deletion",
- toggleAutoConfirm,
- {id: "autoconfirm", autoClose: false}
- );
- }
- })();
- GM.addStyle(`
- .rtonne-anilist-listitem-delete-button {
- background: rgb(var(--color-red)) !important;
- }
- .entry-card .rtonne-anilist-listitem-delete-button {
- top: 42px !important;
- }
- .medialist.table.compact .entry .cover {
- display: flex !important;
- max-width: 82px !important;
- min-width: 82px;
- gap: 2px;
- margin-inline: -41px;
- }
- .medialist.table.compact .entry:not(:hover) .cover .image {
- display: none;
- }
- .medialist.table:not(.compact) .entry:hover .cover {
- display: flex !important;
- max-width: 102px !important;
- min-width: 102px;
- gap: 2px;
- }
- @media (max-width: 760px) {
- .medialist.table:not(.compact) .entry:hover .cover {
- max-width: 82px !important;
- min-width: 82px;
- }
- .medialist.table:not(.compact) .entry:hover {
- padding-left: 97px;
- }
- }
- body.rtonne-anilist-modal-hidden .list-editor-wrap,
- body.rtonne-anilist-messagebox-hidden .el-message-box {
- display: none !important;
- }
- `);
- const trash_svg = `
- <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="ellipsis-h" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-ellipsis-h fa-w-16 fa-lg">
- <!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
- <path fill="currentColor" d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/>
- </svg>
- `;
- let current_url = null;
- const url_regex = /^https:\/\/anilist.co\/user\/.+\/((animelist)|(mangalist))(\/.*)?$/;
- // Using observer to run script whenever the body changes
- // because anilist doesn't reload when changing page
- const observer = new MutationObserver(async () => {
- try {
- let new_url = window.location.href;
- // Because anilist doesn't reload on changing url
- // we have to allow the whole website and check here if we are in a list
- if (!url_regex.test(new_url)) {
- return;
- }
- const elements = await waitForElements(
- document,
- ".entry, .entry-card"
- );
- // If the url is different we are in a different playlist
- // Or if the playlist length is different, we loaded more of the same playlist
- if (
- current_url === new_url &&
- elements.length ===
- document.querySelectorAll(".rtonne-anilist-listitem-delete-button")
- .length
- ) {
- return;
- }
- current_url = new_url;
- // If we have actions in the banner, it's not our list and can't edit it
- if (
- document.querySelector(
- ".banner-content .actions"
- ).children.length > 0
- ) {
- return;
- }
- elements.forEach((parent) => {
- const element = parent.querySelector(".cover");
- const is_card = parent.classList.contains("entry-card");
- // We return if the item already has a delete button so
- // there isn't an infinite loop where adding a button triggers
- // the observer which adds more buttons
- if (element.querySelector(".rtonne-anilist-listitem-delete-button"))
- return;
- const button = document.createElement("div");
- button.className = "rtonne-anilist-listitem-delete-button edit";
- button.innerHTML = trash_svg;
- element.querySelector(".edit").after(button);
- button.onclick = async () => {
- const autoconfirm = await GM.getValue("autoconfirm", false);
- if (autoconfirm) {
- document.body.classList.add("rtonne-anilist-messagebox-hidden")
- }
- document.body.classList.add("rtonne-anilist-modal-hidden")
- const edit_button = element.querySelector(".edit:not(.rtonne-anilist-listitem-delete-button)");
- edit_button.click();
- const [dialog_delete_button] = await waitForElements(
- document,
- ".list-editor-wrap .delete-btn"
- );
- dialog_delete_button.click();
- const [confirm_ok_button] = await waitForElements(
- document,
- ".el-message-box .el-button--small.el-button--primary"
- );
- // I need to wait for the confirm cancel button as well so it all load properly, somehow
- await waitForElements(
- document,
- ".el-message-box .el-button--small:not(.el-button--primary)"
- );
- if (autoconfirm) {
- const fading_in_confirm_message_container = document.querySelector(".el-message-box__wrapper.msgbox-fad-enter-active");
- // Wait until message container finished fading in
- await waitForElementToBeRemovedOrHidden(fading_in_confirm_message_container);
- confirm_ok_button.click()
- const new_confirm_cancel_button = document.querySelector(".list-editor-wrap .delete-btn");
- await waitForElementToBeRemovedOrHidden(new_confirm_cancel_button);
- document.body.classList.remove("rtonne-anilist-messagebox-hidden")
- document.body.classList.remove("rtonne-anilist-modal-hidden")
- } else {
- const confirm_message_container = document.querySelector(".el-message-box__wrapper");
- await waitForElementToBeRemovedOrHidden(confirm_message_container);
- const dialog_close_button = document.querySelector(".list-editor-wrap button:has(> i.el-icon-close)");
- dialog_close_button.click()
- document.body.classList.remove("rtonne-anilist-modal-hidden")
- }
- };
- });
- } catch (err) {
- console.log(err);
- }
- });
- observer.observe(document.body, {
- childList: true,
- subtree: true,
- });
- // https://stackoverflow.com/questions/5525071/how-to-wait-until-an-element-exists
- // This function is required because elements take a while to appear sometimes
- function waitForElements(node, selector) {
- return new Promise((resolve) => {
- if (node.querySelector(selector)) {
- return resolve(node.querySelectorAll(selector));
- }
- const observer = new MutationObserver(() => {
- if (node.querySelector(selector)) {
- observer.disconnect();
- resolve(node.querySelectorAll(selector));
- }
- });
- observer.observe(document.body, {
- childList: true,
- subtree: true,
- });
- });
- }
- function waitForElementToBeRemovedOrHidden(element) {
- return new Promise((resolve) => {
- if (!document.contains(element) || element.style.display === "none") {
- return resolve(null);
- }
- const observer = new MutationObserver(() => {
- if (!document.contains(element) || element.style.display === "none") {
- observer.disconnect();
- resolve(null);
- }
- });
- observer.observe(document.body, {
- childList: true,
- subtree: true,
- attributeOldValue: true,
- attributeFilter: ["style"],
- });
- });
- }