您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
OnlineMathContest problemsページの問題セルに、得点、分野、いいねしているかどうか表示します。
// ==UserScript== // @name OMC Problems Editor // @namespace none // @version 1.0.0 // @description OnlineMathContest problemsページの問題セルに、得点、分野、いいねしているかどうか表示します。 // @author yuyu // @match https://onlinemathcontest.com/problems // @grant none // @license MIT // ==/UserScript== (() => { 'use strict'; // ログインチェック let is_login = false; let username = ''; const userNameEl = document.getElementById('nav-username'); if (userNameEl && userNameEl.dataset.uid) { is_login = true; username = userNameEl.dataset.uid; } let allProblems = {}; let favoritesSet = new Set(); // API 取得 const get_api = (t) => { const url = `https://onlinemathcontest.com/api/problems/list?type=${t}`; return fetch(url) .then(res => res.json()) .then(data => { const result = {}; if (data && data.contests) { data.contests.forEach(contest => { contest.tasks.forEach(task => { result[task.title] = { point: task.point, field: task.field }; }); }); } return result; }); }; // favorites取得 const fetchFavorites = (user) => { const url = `https://onlinemathcontest.com/users/${user}/favorites`; return fetch(url) .then(res => { if (!res.ok) throw new Error('Favorites fetch failed'); return res.text(); }) .then(html => { const set = new Set(); const re = />([A-Z0-9]+\(\w\))</g; let match; while ((match = re.exec(html)) !== null) { set.add(match[1].trim()); } return set; }); }; const problemPromises = ['B', 'R', 'E', 'S', 'O', 'V'].map(t => get_api(t)); const promises = [Promise.all(problemPromises).then(results => { // 各API呼び出しの結果を統合 results.forEach(data => { allProblems = Object.assign(allProblems, data); }); })]; if (is_login) { promises.push(fetchFavorites(username).then(set => { favoritesSet = set; })); } // オーバーレイ適用、DOM更新を開始 Promise.all(promises).then(() => { Overlay(); setupMutation(); }); function Overlay() { document.querySelectorAll('td').forEach(td => { td.querySelectorAll('.omc-overlay').forEach(el => el.remove()); const cellText = td.textContent.trim(); const prob = allProblems[cellText]; if (!prob) return; if (window.getComputedStyle(td).position === 'static') { td.style.position = 'relative'; } if (prob.point !== undefined) { const change = document.createElement('span'); change.classList.add('omc-overlay'); change.textContent = prob.point; change.style.position = 'absolute'; change.style.right = '2px'; change.style.bottom = '2px'; change.style.fontSize = '10px'; change.style.color = 'gray'; change.style.pointerEvents = 'none'; change.style.zIndex = '0'; td.appendChild(change); } if (prob.field !== undefined) { const change = document.createElement('span'); change.classList.add('omc-overlay'); change.textContent = prob.field; change.style.position = 'absolute'; change.style.left = '2px'; change.style.bottom = '2px'; change.style.fontSize = '10px'; change.style.color = 'gray'; change.style.pointerEvents = 'none'; change.style.zIndex = '0'; td.appendChild(change); } if (favoritesSet.has(cellText)) { const change = document.createElement('span'); change.classList.add('omc-overlay'); change.textContent = '❤️'; // かわいいハート。らぶ。 change.style.position = 'absolute'; change.style.right = '2px'; change.style.top = '2px'; change.style.fontSize = '10px'; change.style.pointerEvents = 'none'; change.style.zIndex = '0'; td.appendChild(change); } }); } function applyOverlays() { Overlay(); } function setupMutation() { let Timer = null; const delay = 500; const CallBack = () => { if (Timer) clearTimeout(Timer); Timer = setTimeout(() => { applyOverlays(); Timer = null; }, delay); }; const observer = new MutationObserver(CallBack); observer.observe(document.body, { childList: true, subtree: true }); window.addEventListener('scroll', applyOverlays); window.addEventListener('resize', applyOverlays); } })();