// ==UserScript== // @name Anilist Rating Tier Indicator // @namespace http://tampermonkey.net/ // @version 1.9 // @description Adds a tier badge next to ratings on Anilist, including Mean Score. // @author hiddenhokage, Claude, ChatGPT // @match *://anilist.co/* // @grant none // @downloadURL none // ==/UserScript== (function () { 'use strict'; // Updated tier definitions with colors const tiers = [ { min: 95, max: 100, label: 'S+', color: '#FFD700', textColor: '#000000' }, // S+ Tier, Gold { min: 85, max: 94.9, label: 'S', color: '#ff7f00', textColor: '#FFFFFF' }, // S Tier, Orange { min: 75, max: 84.9, label: 'A', color: '#aa00ff', textColor: '#FFFFFF' }, // A Tier, Purple { min: 65, max: 74.9, label: 'B', color: '#007fff', textColor: '#FFFFFF' }, // B Tier, Blue { min: 55, max: 64.9, label: 'C', color: '#00aa00', textColor: '#FFFFFF' }, // C Tier, Green { min: 41, max: 54.9, label: 'D', color: '#aaaaaa', textColor: '#FFFFFF' }, // D Tier, Gray { min: 0, max: 40.9, label: 'F', color: '#666666', textColor: '#FFFFFF' } // F Tier, Dark Gray ]; function getTier(rating) { return tiers.find(tier => rating >= tier.min && rating <= tier.max) || null; } function createBadge(tier, isBlockView = false) { let badge = document.createElement('span'); // Use shortened labels for block view badge.textContent = tier.label; badge.style.cssText = ` background-color: ${tier.color}; color: ${tier.textColor}; font-size: ${isBlockView ? '10px' : '12px'}; font-weight: bold; padding: ${isBlockView ? '1px 4px' : '2px 6px'}; border-radius: 4px; display: inline-block; margin-left: 5px; vertical-align: middle; white-space: nowrap; `; return badge; } function processScoreElement(el, isPercentage = false, isBlockView = false) { if (el.dataset.tierModified) return; el.dataset.tierModified = "true"; // Get score from the score attribute if available, otherwise from text content let ratingText = el.getAttribute('score') || el.innerText.trim().replace('%', ''); let rating = parseFloat(ratingText); if (isNaN(rating)) return; let tier = getTier(isPercentage ? rating : rating * 10); if (tier) { // Create container for score and badge const container = document.createElement('div'); container.style.cssText = ` display: inline-flex; align-items: center; gap: 4px; ${isBlockView ? 'background-color: rgba(0, 0, 0, 0.5); padding: 2px 6px; border-radius: 4px; overflow: hidden;' : ''} `; // Create score element const scoreEl = document.createElement('span'); scoreEl.textContent = isPercentage ? `${ratingText}%` : ratingText; // Add elements to container container.appendChild(scoreEl); container.appendChild(createBadge(tier, isBlockView)); // Replace original content el.textContent = ''; el.appendChild(container); } } function addTierIndicators() { // List View (Decimal Scores) document.querySelectorAll('.score:not(.media-card .score)').forEach(el => { processScoreElement(el, false, false); }); // Block View (Media Cards) document.querySelectorAll('.entry-card .score').forEach(el => { processScoreElement(el, false, true); }); // Process both Average Score and Mean Score document.querySelectorAll('.data-set').forEach(dataSet => { const label = dataSet.querySelector('.type'); const value = dataSet.querySelector('.value'); if (label && value && !value.dataset.tierModified && (label.innerText.includes('Average Score') || label.innerText.includes('Mean Score'))) { processScoreElement(value, true, false); } }); } // Initial load function initializeScript() { addTierIndicators(); // Set up a more specific observer for the statistics section const statsObserver = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList' || mutation.type === 'subtree') { addTierIndicators(); } }); }); // Observe the entire document for dynamic content loading statsObserver.observe(document.body, { childList: true, subtree: true, characterData: true }); } // Handle initial page load if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeScript); } else { initializeScript(); } // Handle navigation events in the SPA window.addEventListener('popstate', () => { setTimeout(addTierIndicators, 100); }); // Handle push state changes (when using the site's navigation) const pushState = history.pushState; history.pushState = function () { pushState.apply(history, arguments); setTimeout(addTierIndicators, 100); }; })();