// ==UserScript== // @name VoidVerified // @namespace http://tampermonkey.net/ // @version 0.2.0 // @description Display a verified sign next to user's name in AniList. // @author voidnyan // @match https://anilist.co/* // @grant none // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; const version = "0.2.0"; const localStorageConfigKey = "VoidVerificationConfig"; const usernameSelector = ["a.name"]; const displaySaveButton = false; const config = JSON.parse(window.localStorage.getItem(localStorageConfigKey)); const verifiedDefaults = { enableForProfile: true, username: { enabled: true, enabledForReplies: true, color: "white", sign: "✔", title: "Verified", }, highlight: { enabled: true, enabledForReplies: true, color: undefined, size: "5px", } }; const verifiedUsersDefault = [ { username: "voidnyan", color: "green", sign: "💻", title: "devnyan" }, // "anotheruser" ].map(u => typeof u === "string" ? {username: u} : u); displaySaveButton && renderSaveButton(); const verified = config?.verified ?? verifiedDefaults; const verifiedUsers = config?.verifiedUsers ?? verifiedUsersDefault; const verifiedUsersArray = [ ...verifiedUsers.map(user => user.username) ]; const observer = new MutationObserver(observeMutations); observer.observe(document.body, {childList: true, subtree: true}); function observeMutations(mutations) { for (const mutation of mutations) { if (mutation.addedNodes.length > 0) { mutation.addedNodes.forEach(handleVerified); } } } function handleVerified(node){ if (!(node instanceof HTMLElement)) { return; } switch (true){ case node.matches("div.reply"): handleReply(node); break; case node.matches("div.activity-anime_list"): case node.matches("div.activity-manga_list"): case node.matches("div.container"): handleListActivity(node); break; case node.matches("div.activity-text"): case node.matches("div.activity-message"): handleTextActivity(node); break; case node.matches("div.user"): handleProfile(node); break; } } function handleReply(node) { const username = node.querySelector(usernameSelector); addVerifiedMarkToReply(username); highlightReply(node, username); } function handleListActivity(node) { const isProfileActivity = node.querySelector(".small") !== null; if (isProfileActivity) { return; } const username = node.querySelector(usernameSelector); addVerifiedMark(username); highlightActivity(node, username); } function handleTextActivity(node) { const username = node.querySelector(usernameSelector); addVerifiedMark(username); highlightActivity(node, username); } function handleProfile(){ if (!verified.enableForProfile){ return; } const username = document.querySelector("h1.name"); addVerifiedMarkToProfile(username); } function addVerifiedMark(username) { if (!isVerifiedUser(username) || !verified.username.enabled) { return; } const span = createMarkElement(username); span.style.width = "min-content"; username.after(span); } function addVerifiedMarkToReply(username) { if (!isVerifiedUser(username) || !verified.username.enabledForReplies) { return; } const span = createMarkElement(username); span.style.display = "inline-block"; span.style.verticalAlign = "top"; span.style.lineHeight = "25px"; span.style.height = "25px"; username.after(span); } function addVerifiedMarkToProfile(username) { if (!isVerifiedUser(username)) { return; } const span = document.createElement("span"); span.innerHTML = verified.username.sign; span.title = verified.username.title; span.style.marginLeft = "6px"; username.after(span); } function getLinkColor(username){ var linkColor = getComputedStyle(username).getPropertyValue("--color-blue"); return `rgb(${linkColor})`; } function createMarkElement(username){ const span = document.createElement("span"); const dataset = username.dataset; const id = Object.keys(dataset)[0]; span.setAttribute(`data-${id}`, ""); span.style.color = verified.username.color ?? getLinkColor(username); span.style.marginLeft = "6px"; span.innerHTML = getSign(username); span.title = getTitle(username); return span; } function highlightActivity(node, username) { if (!verified.highlight.enabled || !isVerifiedUser(username)) { return; }; const wrapper = node.querySelector("div.wrap"); wrapper.style.marginRight = `-${verified.highlight.size}`; wrapper.style.borderRight = `${verified.highlight.size} solid ${getColor(username)}`; } function highlightReply(node, username) { if (!verified.highlight.enabledForReplies || !isVerifiedUser(username)) { return; } node.style.marginRight = `-${verified.highlight.size}`; node.style.borderRight = `${verified.highlight.size} solid ${getColor(username)}`; } function isVerifiedUser(username) { const isVerifiedUser = verifiedUsersArray.includes(username?.innerHTML.trim()); return isVerifiedUser; } function getColor(username) { const user = getUserObject(username); const color = user?.color ?? verified.highlight.color ?? getLinkColor(username); return color; } function getSign(username){ const user = getUserObject(username); const sign = user?.sign ?? verified.username.sign; return sign; } function getTitle(username) { const user = getUserObject(username); const title = user?.title ?? verified.username.title; return title; } function getUserObject(username){ var usernameAsString = username.innerHTML.trim(); const user = verifiedUsers.find(u => u.username === usernameAsString); return user; } function overrideLocalStorageConfig(verifiedOverride, verifiedUsersOverride) { const config = { verified: verifiedOverride, verifiedUsers: verifiedUsersOverride } window.localStorage.setItem(localStorageConfigKey, JSON.stringify(config)); } function saveConfigToLocalStorage(){ const config = { verified, verifiedUsers }; window.localStorage.setItem(localStorageConfigKey, JSON.stringify(config)); } function renderSaveButton() { const footerLinks = document.querySelector("div.footer div.links"); const button = document.createElement("button"); button.onClick = overrideLocalStorageConfig(verifiedDefaults, verifiedUsersDefault); button.innerHTML = "Save Configuration"; footerLinks.lastChild.append(button); } console.log(`VoidVerified ${version} loaded.`); })();