// ==UserScript==
// @name AniList Tier Labels
// @namespace http://tampermonkey.net/
// @version 2.1
// @description Adds a tier badge next to ratings on Anilist, including Mean Score. Supports different scoring systems.
// @author hiddenhokage, Claude, ChatGPT
// @match *://anilist.co/*
// @grant 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');
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 getScoreSystem() {
const container = document.querySelector('.content.container');
if (container) {
if (container.querySelector('.medialist.table.POINT_100')) return 'POINT_100';
if (container.querySelector('.medialist.table.POINT_10_DECIMAL')) return 'POINT_10';
if (container.querySelector('.medialist.table.POINT_5')) return 'POINT_5';
}
return 'UNKNOWN';
}
function normalizeScore(score, scoreSystem, isPercentage = false) {
if (isPercentage) return score; // Percentage scores are already in the right format
const numericScore = parseFloat(score);
if (isNaN(numericScore)) return null;
switch (scoreSystem) {
case 'POINT_100':
return numericScore; // Already in 100-point scale
case 'POINT_10':
return numericScore * 10; // Convert 10-point to 100-point
case 'POINT_5':
return numericScore * 20; // Convert 5-point to 100-point (5 → 100, 4 → 80, 3 → 60, 2 → 40, 1 → 20)
default:
return numericScore * 10; // Default to 10-point conversion
}
}
function processScoreElement(el, isPercentage = false, isBlockView = false) {
if (el.dataset.tierModified) return;
el.dataset.tierModified = "true";
const scoreSystem = getScoreSystem();
let ratingText = el.getAttribute('score') || el.innerText.trim().replace('%', '');
let normalizedRating = normalizeScore(ratingText, scoreSystem, isPercentage);
if (normalizedRating === null) return;
let tier = getTier(normalizedRating);
if (tier) {
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;' : ''}
`;
const scoreEl = document.createElement('span');
scoreEl.textContent = isPercentage ? `${ratingText}%` : ratingText;
container.appendChild(scoreEl);
container.appendChild(createBadge(tier, isBlockView));
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);
}
});
}
function initializeScript() {
addTierIndicators();
const statsObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList' || mutation.type === 'subtree') {
addTierIndicators();
}
});
});
statsObserver.observe(document.body, {
childList: true,
subtree: true,
characterData: true
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeScript);
} else {
initializeScript();
}
window.addEventListener('popstate', () => {
setTimeout(addTierIndicators, 100);
});
const pushState = history.pushState;
history.pushState = function () {
pushState.apply(history, arguments);
setTimeout(addTierIndicators, 100);
};
})();