您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork镜像 is available in English.
AtCoderのお気に入り管理のユーザーに色がつきます+レート順ソート(テーブル2つ対応)
// ==UserScript== // @name AtCoder-Favorite-Person-Colors // @namespace https://ruku.tellpro.net // @version 2025-06-19-ver2 // @description AtCoderのお気に入り管理のユーザーに色がつきます+レート順ソート(テーブル2つ対応) // @author ruku // @match https://atcoder.jp/settings/fav // @icon https://www.google.com/s2/favicons?sz=64&domain=atcoder.jp // @grant none // @license MIT // ==/UserScript== const diffToColor = (diff) => { if(diff === 0) return "black"; if(diff < 399) return "#808080"; if(diff < 799) return "#804000"; if(diff < 1199) return "#008000"; if(diff < 1599) return "#00C0C0"; if(diff < 1999) return "#0000FF"; if(diff < 2399) return "#C0C000"; if(diff < 2799) return "#FF8000"; return "#FF0000"; }; (function() { 'use strict'; const users = document.querySelectorAll("td a[href^='/users/']"); const userElements = Array.from(users); const processed = new Set(); const userRatingMap = {}; // --- テーブルごとにトグルボタン追加 --- const tables = Array.from(document.querySelectorAll("table")); tables.forEach((table, tableIdx) => { const tbody = table.querySelector("tbody"); if (!tbody) return; const rows = Array.from(tbody.querySelectorAll("tr")); // 元の順序保存 rows.forEach((tr, i) => tr.setAttribute("data-original-index", i)); // ボタン作成 const btn = document.createElement("button"); btn.textContent = "Sort by Rating: OFF"; btn.style.margin = "8px"; btn.style.background = "#eee"; let sorted = false; btn.onclick = function() { if (!sorted) { // レート順(降順)でソート rows.sort((a, b) => { const userA = a.querySelector("a[href^='/users/']")?.textContent.trim(); const userB = b.querySelector("a[href^='/users/']")?.textContent.trim(); const rateA = userRatingMap[userA] ?? 0; const rateB = userRatingMap[userB] ?? 0; return rateB - rateA; }); btn.textContent = "Sort by Rating: ON"; btn.style.background = "#cceeff"; sorted = true; } else { // 元の順序に戻す rows.sort((a, b) => { return Number(a.getAttribute("data-original-index")) - Number(b.getAttribute("data-original-index")); }); btn.textContent = "Sort by Rating: OFF"; btn.style.background = "#eee"; sorted = false; } // 並び替え rows.forEach(tr => tbody.appendChild(tr)); }; // テーブルの前にボタンを追加 table.parentNode.insertBefore(btn, table); }); // --- 色付け・レート取得は非同期で --- (async function() { for(const userElem of userElements){ const user = userElem.textContent.trim(); if(!user) continue; if(processed.has(user)) continue; processed.add(user); try { const response = await fetch(`https://kenkoooo.com/atcoder/proxy/users/${user}/history/json`); if (!response.ok) continue; const data = await response.json(); if (data.length === 0) continue; const lastColor = data[data.length - 1].NewRating; const color = diffToColor(lastColor); userRatingMap[user] = lastColor; userElements .filter(el => el.textContent.trim() === user) .forEach(el => { el.style.color = color; // (Rate)を追加 if (!el.nextSibling || !el.nextSibling.classList || !el.nextSibling.classList.contains('atcoder-rate-label')) { const rateSpan = document.createElement('span'); rateSpan.textContent = ` (${lastColor})`; rateSpan.className = 'atcoder-rate-label'; rateSpan.style.color = color; el.parentNode.insertBefore(rateSpan, el.nextSibling); } }); } catch (e) { // ignore } } })(); })();