您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Enhances the strategus.appspot.com site by adding colours to your life
// ==UserScript== // @name Prettier Strategus // @namespace https://github.com/ckfaraday // @version 1.2.0 // @description Enhances the strategus.appspot.com site by adding colours to your life // @author ckfaraday // @match https://strategus.appspot.com/* // @icon https://strategus.appspot.com/icons/blackboard.png // @grant none // @run-at document-idle // @license MIT // ==/UserScript== function debounce(fn, delay) { let timer = null; return (...args) => { if (timer) clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); }; } function watchHiddenContainers(selectors) { selectors.forEach(selector => { const container = document.querySelector(selector); if (!container) return; const observeContent = () => { const contentObserver = new MutationObserver(debounce(() => { applyAllColors(); }, 5)); contentObserver.observe(container, { childList: true, subtree: true }); applyAllColors(); }; if (!container.hasAttribute('hidden')) { observeContent(); } else { const attrObserver = new MutationObserver(() => { if (!container.hasAttribute('hidden')) { attrObserver.disconnect(); observeContent(); } }); attrObserver.observe(container, { attributes: true, attributeFilter: ['hidden'] }); } }); } function styleNumbers() { const regex = /[+-]\d+(\.\d+)?/g; document.querySelectorAll('td').forEach(td => { const text = td.textContent; if (!regex.test(text)) return; td.textContent = ''; let lastIndex = 0; regex.lastIndex = 0; let match; while ((match = regex.exec(text)) !== null) { if (match.index > lastIndex) { td.appendChild(document.createTextNode(text.slice(lastIndex, match.index))); } const span = document.createElement('span'); span.textContent = match[0]; span.style.color = span.textContent.startsWith('-') ? 'red' : 'green'; td.appendChild(span); lastIndex = match.index + match[0].length; } if (lastIndex < text.length) { td.appendChild(document.createTextNode(text.slice(lastIndex))); } }); } function styleRankingNames() { const listing = document.querySelector('#listing'); if (!listing) return; const rows = Array.from(listing.querySelectorAll('tr')).slice(0, 3); const colors = ['gold', 'silver', '#cd7f32']; rows.forEach((row, i) => { const nameCell = row.children[1]; if (nameCell) { nameCell.style.color = colors[i]; nameCell.style.fontWeight = 'bold'; } }); } function styleMatchResults() { const stylePlayer = (text) => { const span = document.createElement('span'); span.textContent = text; if (/\[W\]/.test(text)) span.style.color = 'green'; else if (/\[L\]/.test(text)) span.style.color = 'red'; else if (/\[D\]/.test(text)) span.style.color = 'goldenrod'; return span; }; const matchTables = ['#results', '#recentGameList', '#activeGameList', '#setupGameList', '#groupGames', '#playoffGames', '#gameList', '#podiumResults']; const updateTable = (table, selector) => { table.querySelectorAll('tr').forEach(row => { const tds = Array.from(row.querySelectorAll('td')); if (tds.length === 0) return; tds.forEach(td => { const txt = td.textContent.trim().toLowerCase(); if (txt === 'won' || txt === 'lost' || txt === 'draw') { td.textContent = ''; const span = document.createElement('span'); span.textContent = txt; if (txt === 'won') span.style.color = 'green'; else if (txt === 'lost') span.style.color = 'red'; else if (txt === 'draw') span.style.color = 'goldenrod'; td.appendChild(span); } }); if (selector === '#groupGames' || selector === '#playoffGames' || selector === '#gameList') { [2, 4].forEach(i => { const cell = row.cells[i]; if (!cell) return; const text = cell.textContent.trim(); if (!/\[W\]|\[L\]|\[D\]/.test(text)) return; while (cell.firstChild) cell.removeChild(cell.firstChild); cell.appendChild(stylePlayer(text)); }); } else { let td = selector === '#recentGameList' ? tds.find(cell => /\s+vs\.?\s+/i.test(cell.textContent)) : tds[0]; if (!td) return; const text = td.textContent.trim(); const parts = text.split(/\s+vs\.?\s+/i); if (parts.length !== 2) return; const [left, right] = parts.map(s => s.trim()); while (td.firstChild) td.removeChild(td.firstChild); const leftSpan = stylePlayer(left); const rightSpan = stylePlayer(right); if (!/\[W\]|\[L\]|\[D\]/.test(text)) { leftSpan.style.color = '#e60000'; rightSpan.style.color = '#0066ff'; } td.appendChild(leftSpan); td.appendChild(document.createTextNode(' vs. ')); td.appendChild(rightSpan); } }); }; matchTables.forEach(selector => { const table = document.querySelector(selector); if (table) updateTable(table, selector); }); ['#groupGames', '#playoffGames', '#gameList'].forEach(selector => { const table = document.querySelector(selector); if (!table || table._observerAdded) return; table._observerAdded = true; const observer = new MutationObserver(() => { observer.disconnect(); updateTable(table, selector); observer.observe(table, { childList: true, subtree: true, characterData: true }); }); observer.observe(table, { childList: true, subtree: true, characterData: true }); }); function observeMatchTable(id) { const table = document.getElementById(id); if (table && !table._observerAdded) { table._observerAdded = true; const observer = new MutationObserver(() => { observer.disconnect(); updateTable(table, `#${id}`); observer.observe(table, { childList: true, subtree: true }); }); observer.observe(table, { childList: true, subtree: true }); } } ['rankedResults', 'podiumResults'].forEach(observeMatchTable); function updateElement(el) { if (!el) return; const text = el.textContent; if (!text) return; while (el.firstChild) el.removeChild(el.firstChild); const pattern = /(\w+(?: \w+)*\s\[[WLD]\])/g; let lastIndex = 0, match; while ((match = pattern.exec(text)) !== null) { if (match.index > lastIndex) { el.appendChild(document.createTextNode(text.substring(lastIndex, match.index))); } const span = document.createElement('span'); span.textContent = match[0]; if (/\[W\]/.test(match[0])) span.style.color = 'green'; else if (/\[L\]/.test(match[0])) span.style.color = 'red'; else if (/\[D\]/.test(match[0])) span.style.color = 'goldenrod'; el.appendChild(span); lastIndex = pattern.lastIndex; } if (lastIndex < text.length) { el.appendChild(document.createTextNode(text.substring(lastIndex))); } } ['playerStatusA', 'playerStatusB', 'statusText'].forEach(id => { const el = document.getElementById(id); if (!el) return; updateElement(el); if (el._observerAdded) return; el._observerAdded = true; const observer = new MutationObserver(() => { observer.disconnect(); updateElement(el); observer.observe(el, { childList: true, characterData: true, subtree: true }); }); observer.observe(el, { childList: true, characterData: true, subtree: true }); }); const sel = document.getElementById('selGroupRound'); if (sel && !sel._colorMatchResultsListenerAdded) { sel._colorMatchResultsListenerAdded = true; sel.addEventListener('change', () => setTimeout(styleMatchResults, 5)); } const btnPrev = document.querySelector('#divGroupRoundSel input[value="<"]'); if (btnPrev && !btnPrev._colorMatchResultsListenerAdded) { btnPrev._colorMatchResultsListenerAdded = true; btnPrev.addEventListener('click', () => setTimeout(styleMatchResults, 5)); } const btnNext = document.querySelector('#divGroupRoundSel input[value=">"]'); if (btnNext && !btnNext._colorMatchResultsListenerAdded) { btnNext._colorMatchResultsListenerAdded = true; btnNext.addEventListener('click', () => setTimeout(styleMatchResults, 5)); } } function styleAwards() { const colors = ['gold', 'silver', '#cd7f32']; document.querySelectorAll('td').forEach(td => { const imgs = td.querySelectorAll('img[src*="podium"]'); imgs.forEach((img, i) => { const span = img.parentElement; if (!span) return; Array.from(span.childNodes).forEach(node => { if (node.nodeType === 3 && node.textContent.trim()) { const nameSpan = document.createElement('span'); nameSpan.textContent = node.textContent.trim(); nameSpan.style.color = colors[i] || 'black'; nameSpan.style.fontWeight = 'bold'; span.replaceChild(nameSpan, node); } if (node.nodeType === 1 && node.tagName === 'SPAN') { node.style.color = colors[i]; node.style.fontWeight = 'bold'; } }); let node = span.nextSibling; while (node) { if (node.nodeType === 3 && node.textContent.trim()) { const parent = node.parentNode; const parts = node.textContent.split(','); const fragments = parts.flatMap((part, index) => { const text = part.trim(); const nameSpan = document.createElement('span'); nameSpan.textContent = text; nameSpan.style.color = colors[i]; nameSpan.style.fontWeight = 'bold'; return index > 0 ? [document.createTextNode(', '), nameSpan] : [nameSpan]; }); fragments.forEach(f => parent.insertBefore(f, node)); parent.removeChild(node); break; } node = node.nextSibling; } }); }); const awards = document.querySelector('#divAwards'); if (awards) { const imgs = awards.querySelectorAll('img[src*="podium"]'); imgs.forEach((img, i) => { let node = img.nextSibling; while (node && (node.nodeType !== 3 || !node.textContent.trim())) { node = node.nextSibling; } if (node && node.nodeType === 3) { const nameSpan = document.createElement('span'); nameSpan.textContent = node.textContent.trim(); nameSpan.style.color = colors[i]; nameSpan.style.fontWeight = 'bold'; awards.insertBefore(nameSpan, node); awards.removeChild(node); } }); } } function highlightWinner() { const bracket = document.querySelector('#divBrackets'); if (!bracket || !bracket.classList.contains('visible')) return; bracket.querySelectorAll('.winner-highlight').forEach(el => { el.classList.remove('winner-highlight'); el.style.border = ''; el.style.color = ''; el.style.fontWeight = ''; el.style.borderRadius = ''; el.style.padding = ''; }); const final = bracket.querySelector('article.round[data-round-id="2"]'); if (!final) return; const match = final.querySelector('.match[data-match-status="4"]'); if (!match) return; let winner = null; let best = -Infinity; match.querySelectorAll('.participant').forEach(p => { const score = parseFloat(p.querySelector('.result')?.textContent || ''); if (!isNaN(score) && score > best) { best = score; winner = p.getAttribute('title'); } }); if (!winner) return; bracket.querySelectorAll('.participant').forEach(p => { if (p.getAttribute('title') === winner) { p.classList.add('winner-highlight'); p.style.color = 'gold'; p.style.fontWeight = 'bold'; p.style.border = '2px solid gold'; p.style.borderRadius = '4px'; p.style.padding = '2px 4px'; } }); } function styleTop3Standings() { const colors = ['#FFD700', '#C0C0C0', '#CD7F32']; const applyToTable = (table, index = 2) => { if (!table?.rows.length) return; Array.from(table.rows).forEach(row => { const cell = row.cells[index]; if (cell) { cell.style.color = ''; cell.style.fontWeight = ''; } }); for (let i = 0; i < Math.min(3, table.rows.length); i++) { const cell = table.rows[i].cells[index]; if (cell) { cell.style.color = colors[i]; cell.style.fontWeight = 'bold'; } } }; applyToTable(document.getElementById('groupStandings')); applyToTable(document.getElementById('standings')); applyToTable(document.querySelector('#tableRankingTeams tbody'), 1); } function applyAllColors() { styleNumbers(); styleRankingNames(); styleMatchResults(); styleAwards(); highlightWinner(); styleTop3Standings(); [ '#gameList', '#listing', '#results', '#recentGameList', '#divAwards', '#divBrackets', '#groupStandings' ].forEach(sel => { const el = document.querySelector(sel); if (el) el.style.visibility = 'visible'; }); } function setupObservers() { const config = { childList: true, subtree: true }; const targets = [ '#recentGameList', '#listing', '#results', '#divAwards', '#divBrackets', '#rankedResults', '#divGameStatus', '#statusText' ]; targets.forEach(selector => { const target = document.querySelector(selector); if (!target) return; const observer = new MutationObserver(debounce(() => applyAllColors(), 5)); observer.observe(target, config); }); const groupParent = document.querySelector('#groupStandings')?.parentElement; if (groupParent) { const observer = new MutationObserver(debounce(() => applyAllColors(), 5)); observer.observe(groupParent, config); } const gameListParent = document.querySelector('#gameList')?.parentElement; if (gameListParent) { const observer = new MutationObserver(debounce(() => applyAllColors(), 5)); observer.observe(gameListParent, config); } } function watchBracketButton() { const btn = document.querySelector('#btnBrackets2'); if (btn) { btn.addEventListener('click', () => { setTimeout(highlightWinner, 5); }); } } function init() { applyAllColors(); setupObservers(); watchBracketButton(); watchHiddenContainers([ '#divResults', '#divPerOpponent', '#divRankingTeams' ]); } window.addEventListener('load', () => setTimeout(init, 5));