您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Avatar checklist for Grundo's Cafe, visit https://www.grundos.cafe/~Tyco
当前为
// ==UserScript== // @name [GC] Avatar Checklist // @namespace https://www.grundos.cafe/ // @version 1.0 // @description Avatar checklist for Grundo's Cafe, visit https://www.grundos.cafe/~Tyco // @author soupfaerie, supercow64, arithmancer // @match https://www.grundos.cafe/~Tyco // @match https://www.grundos.cafe/~tyco // @grant none // @license MIT // ==/UserScript== const textToHTML = (text) => new DOMParser().parseFromString(text, "text/html"); /** * Analyse the HTML select element for a list of avatars the user has collected. * * @param {Node} node The root node (default: document) * @returns {string[]} the list of avatars as an array of basenames */ const getCollectedAvatars = (node = document) => { // The list of avatars is partitioned into default avatars // and collected secret avatars. The option with the text --- // (6 dashes) is the inclusive cutoff. All avatars at and below // the cutoff are collected secret avatars const allAvatars = Array.from( node.querySelectorAll(`[name="new_avatar"] option`) ); const i = allAvatars.findIndex((e) => e.textContent.includes("---")); return allAvatars.slice(i).map((e) => e.value); }; /** * Returns a Promise that resolves to a list of avatars * the user has collected. * * @returns {string[]} list of collected avatars */ const getCollectedAvatarsAsync = () => fetch("/neoboards/preferences/") .then((res) => res.text()) .then(textToHTML) .then(getCollectedAvatars); /** * For static assets, returns the basename of the asset indicated * in the url. * * ```js * basename("https://example.com/foo/bar/baz.gif") == "baz.gif" * ``` * * @param {string} url path to the file with slashes * @returns {string} the basename */ const basename = (url) => url.split("/").slice(-1)[0]; /** * Move collected avatar cards into their section's <details> element. * * The tracker page groups avatars by section. Each section is a <div> * directly under the #avatars container. Within each section there are one or * more `.avatar-grid` containers followed by a <details> element containing an * empty `.avatar-grid`. Collected avatars should be appended to that grid. * * @param {string[]} collectedAvatars basenames of the user's collected avatars */ function moveCollectedAvatars(collectedAvatars) { const sections = document.querySelectorAll('#avatars > div'); sections.forEach((section) => { const foundGrid = section.querySelector('details .avatar-grid'); if (!foundGrid) return; const cards = section.querySelectorAll(':scope > .avatar-grid > .avatar-card'); cards.forEach((card) => { const img = card.querySelector('img'); if (!img) return; // site theme cards currently lack images if (collectedAvatars.includes(basename(img.src))) { card.classList.add('check'); foundGrid.appendChild(card); } }); }); } getCollectedAvatarsAsync().then(moveCollectedAvatars);