Better stats-table
// ==UserScript==
// @name Autodarts - Better X01-Stats
// @namespace http://tampermonkey.net/
// @version 0.79
// @description Better stats-table
// @author benebelter
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @require https://update.greasyfork.org/scripts/570871/AD%20-%20Bearer-update%20v3.js
// @match https://play.autodarts.io/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=autodarts.io
// @license MIT
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
const NAME_COL_WIDTH = "200px";
const COL_WIDTH_1 = "50px";
let currentGameId = null;
let LANG = "en";
function detectLanguage() {
let uiLang =
localStorage.getItem("i18nextLng") ||
localStorage.getItem("language") ||
"";
let browserLang = navigator.language || "";
let lang = (uiLang || browserLang).toLowerCase();
if (lang.startsWith("de")) {
LANG = "de";
} else if (lang.startsWith("nl")) {
LANG = "nl";
} else {
LANG = "en";
}
}
const TEXT = {
en: {
matchStats: "📊 Match Stats",
avg: "Avg",
first9: "First 9",
to170: "— 170",
trebles: "Trebless",
triples: "T17 — T20",
corrections: "Corrections",
checkout: "Checkout",
highest: "Highest",
finishes: "90+ Finishes",
dartsRest: "<span style='color: #00e676;'>Darts</span> / <span style='color: grey'>Rest</span>",
noticeTitle: "⚠️ Notice:",
noticeText: "A winning dart was corrected by the system. Hover over the value for details.",
corrected: "has been corrected for the win"
},
de: {
matchStats: "📊 Match Statistiken",
avg: "Avg",
first9: "First 9",
to170: "— 170",
trebles: "Ohne Triple",
triples: "T17 — T20",
corrections: "Korrekturen",
checkout: "Checkout",
highest: "Höchstes",
finishes: "90+ Finishes",
dartsRest: "<span style='color: #00e676;'>Darts</span> / <span style='color: grey'>Rest</span>",
noticeTitle: "⚠️ Hinweis:",
noticeText: "Ein Leg-Finish wurde vom System korrigiert. Fahre mit der Maus über den Wert für Details.",
corrected: "wurde für den Gewinn korrigiert"
},
nl: {
matchStats: "📊 Wedstrijd Statistieken",
avg: "Gem",
first9: "Eerste 9",
to170: "— 170",
trebles: "Zonder Triple",
triples: "T17 — T20",
corrections: "Correcties",
checkout: "Checkout",
highest: "Hoogste",
finishes: "90+ Finishes",
dartsRest: "<span style='color: #00e676;'>Darts</span> / <span style='color: grey'>Rest</span>",
noticeTitle: "⚠️ Opmerking:",
noticeText: "Een winnende dart werd door het systeem gecorrigeerd. Beweeg met de muis over de waarde voor details.",
corrected: "is gecorrigeerd voor de winst"
}
};
function t(key) {
try {
return TEXT?.[LANG]?.[key] ?? key;
} catch {
return key;
}
}
async function getMatchStats(gameid) {
const res = await fetch(
'https://api.autodarts.io/as/v0/matches/' + gameid + '/stats',
{
credentials: 'include',
headers: { "Authorization": localStorage.getItem("bearer") },
}
);
return await res.json();
}
function buildStats(match) {
const customScoreMap = countCustomScores(match);
const highFinishListMap = getHighFinishList(match);
const nonDetectedMap = countNonDetectedThrows(match);
const tripleMap = countHighTriples(match);
const noTripleMap = countNoTripleOver100(match);
const result = {};
const playerMap = {};
// 👉 Player-ID → Name
match.players.forEach(p => {
playerMap[p.id] = p.name;
});
match.matchStats.forEach(s => {
const name = playerMap[s.playerId];
if (!name) return;
const upper = name.toUpperCase();
const attempts = s.checkouts ?? 0;
const hits = s.checkoutsHit ?? 0;
result[name] = {
counts: {
// ✅ FIX: Uppercase Key verwenden
highFinishList: highFinishListMap[upper] ?? [],
nonDetected: nonDetectedMap[upper] ?? 0,
noTriple100: noTripleMap[name] ?? 0,
triplesHigh: tripleMap[upper] ?? 0,
"60+": s.plus60 ?? 0,
"90+": customScoreMap[upper]?.["90+"] ?? 0,
"130+": customScoreMap[upper]?.["130+"] ?? 0,
"171+": customScoreMap[upper]?.["171+"] ?? 0,
"170+": s.plus170 ?? 0,
"180": s.total180 ?? 0
},
averages: {
total: s.average ?? 0,
first9: s.first9Average ?? 0,
to170: s.averageUntil170 ?? 0
},
checkout: {
attempts,
hits,
percentage: attempts > 0 ? (hits / attempts) * 100 : 0,
high: s.checkoutPoints ?? 0
}
};
});
return result;
}
function buildLegStats(match) {
const isOnlineGame = !!match.host;
const playerMap = {};
match.players.forEach(p => {
playerMap[p.id] = p.name;
});
const START_SCORE =
match?.settings?.baseScore ??
match?.games?.[0]?.settings?.baseScore ??
501;
const legs = [];
match.games?.forEach((game, index) => {
const leg = {
leg: index + 1,
set: game.set ?? 0,
players: {}
};
const scorePerPlayer = {};
const dartsPerPlayer = {};
const correctedMap = {};
const correctedDetailMap = {};
match.players.forEach(p => {
scorePerPlayer[p.id] = START_SCORE;
dartsPerPlayer[p.id] = 0;
correctedMap[p.id] = false;
correctedDetailMap[p.id] = ""; // 👈 NEU
});
game.turns?.forEach(turn => {
const pid = turn.playerId;
let scored =
turn.points ??
turn.score ??
turn.roundScore ??
0;
let darts =
turn.dartsThrown ??
turn.darts ??
3;
if (scorePerPlayer[pid] > 0) {
// 🎯 Checkout Turn
if (scorePerPlayer[pid] - scored <= 0) {
let remaining = scorePerPlayer[pid];
let usedDarts = 0;
const throws = turn.throws ?? [];
let corrected = false;
let correctedDetail = "";
const GOOD_ENTRIES = [
"detected",
"corrected_bouncer",
"referee_ai_corrected",
"corrected_after_referee_ai",
"referee_ai_confirmed"
];
if (throws.length > 0) {
for (let th of throws) {
const val =
th?.points ??
th?.value ??
(th?.segment?.multiplier ?? 0) * (th?.segment?.number ?? 0);
remaining -= val;
usedDarts++;
if (remaining <= 0) {
const VALID_ENTRIES = [
"detected",
"corrected_bouncer",
"referee_ai_corrected",
"corrected_after_referee_ai",
"referee_ai_confirmed"
];
if (!VALID_ENTRIES.includes(th.entry)) {
corrected = true;
const seg = th?.segment?.name ?? "";
correctedDetail = `⚠️ ${seg} ${t("corrected")}`;
}
break;
}
}
darts = usedDarts;
}
scorePerPlayer[pid] = 0;
dartsPerPlayer[pid] += darts;
correctedMap[pid] = corrected;
correctedDetailMap[pid] = correctedDetail;
} else {
scorePerPlayer[pid] -= scored;
dartsPerPlayer[pid] += darts;
}
}
});
// 👉 FINAL setzen
match.players.forEach(p => {
const id = p.id;
const name = playerMap[id];
const remaining = scorePerPlayer[id];
const winnerId = game.winnerPlayerId ?? game.winner;
if (id === winnerId) {
leg.players[name] = {
type: "darts",
value: dartsPerPlayer[id],
correctedFinish: correctedMap[id],
correctedDetail: correctedDetailMap[id]
};
} else {
leg.players[name] = {
type: "remaining",
value: remaining
};
}
});
legs.push(leg);
});
return legs;
}
function appendDate(match) {
// ❌ Schon vorhanden → nichts tun
if (document.querySelector("#ad-dateplayed")) return;
const target = document.querySelector(".css-a6m3v9");
if (!target) return;
// 👉 mögliche Felder aus der API
const rawDate =
match.finishedAt ??
match.endedAt ??
match.startedAt ??
null;
const span = document.createElement("span");
span.id = "ad-dateplayed";
span.className = "css-1xbroe7";
// ❌ kein Datum vorhanden
if (!rawDate) {
span.textContent = "läuft aktuell...";
target.appendChild(span);
return;
}
const date = new Date(rawDate);
// ❌ ungültiges Datum
if (isNaN(date.getTime())) {
span.textContent = "läuft aktuell...";
target.appendChild(span);
return;
}
// ✅ korrekt formatieren
const LOCALE_MAP = {
en: "en-US",
de: "de-DE",
nl: "nl-NL"
};
const locale = LOCALE_MAP[LANG] || "en-US";
const formatted = date.toLocaleString(locale, {
day: "numeric",
month: "short",
year: "numeric",
hour: "2-digit",
minute: "2-digit"
});
// 👉 wenn Match noch läuft
if (!match.finishedAt) {
span.textContent = "läuft aktuell...";
} else {
span.textContent = LANG === "de" ? formatted + " Uhr" : formatted;
}
target.appendChild(span);
}
function render(stats, match) {
detectLanguage();
if (!match?.variant || !match.variant.toLowerCase().includes("x01")) {
return;
}
appendDate(match);
const players = Object.values(stats);
const bestAvg = Math.max(...players.map(p => p.averages.total || 0));
const bestFirst9 = Math.max(...players.map(p => p.averages.first9 || 0));
const bestTo170 = Math.max(...players.map(p => p.averages.to170 || 0));
const bestCheckout = Math.max(...players.map(p => p.checkout.percentage || 0));
const bestCheckoutRounded = Math.round(bestCheckout * 100) / 100;
document.querySelector('#ad-stats')?.remove();
const container = document.createElement('div');
container.id = "ad-stats";
container.style.width = "96%";
container.style.maxWidth = "96%";
container.style.margin = "5px 2% 20px";
container.style.padding = "12px";
container.style.background = "#111";
container.style.color = "#fff";
container.style.border = "1px solid #333";
container.style.borderRadius = "8px";
container.style.fontFamily = "Arial";
container.style.fontSize = "16px";
container.style.boxShadow = "0 6px 20px rgba(0,0,0,0.5)";
container.style.fontVariantNumeric = "tabular-nums";
let html = `<div style="font-weight:bold; margin-bottom:10px; font-size: 3.0em; text-align: center;">${t("matchStats")}</div>`;
// 🔥 Highscores
html += `
<table style="
width:auto;
border-collapse: collapse;
margin-bottom:12px;
table-layout:fixed;
">
<colgroup>
<col style="width:${NAME_COL_WIDTH};">
<col style="width:70px;">
<col style="width:70px;">
<col style="width:70px;">
<col style="width:70px;">
<col style="width:70px;">
</colgroup>
<tr style="border-bottom:1px solid #444;">
<th style="text-align:left"></th>
<th style="text-align:center">60+</th>
<th style="text-align:center">90+</th>
<th style="text-align:center">130+</th>
<th style="text-align:center">171+</th>
<th style="text-align:center">180</th>
</tr>
`;
Object.entries(stats).forEach(([name, s]) => {
html += `
<tr>
<td style="text-align:left; padding-right:10px; text-transform:uppercase;">
${name}
</td>
<td style="text-align:center;">
${s.counts["60+"] ?? 0}
</td>
<td style="text-align:center; color:#ffd700;">
${s.counts["90+"] ?? 0}
</td>
<td style="text-align:center; color:#ff8c00;">
${s.counts["130+"] ?? 0}
</td>
<td style="text-align:center; color:#ff8c00;">
${s.counts["171+"] ?? 0}
</td>
<td style="text-align:center; color:#ff4d4d;">
${s.counts["180"] ?? 0}
</td>
</tr>
`;
});
html += `</table>`;
// 📈 Averages
const bestClean = Math.min(...players.map(p => p.counts.noTriple100 || 0));
const bestTriplesHigh = Math.max(...players.map(p => p.counts.triplesHigh || 0));
const bestNonDetected = Math.min(...players.map(p => p.counts.nonDetected ?? Infinity));
html += `
<table style="
width:auto;
border-collapse: collapse;
margin-bottom:12px;
table-layout:fixed;
">
<colgroup>
<col style="width:${NAME_COL_WIDTH};">
<col style="width:80px;">
<col style="width:80px;">
<col style="width:80px;">
<col style="width:110px;">
<col style="width:110px;">
<col style="width:110px;">
</colgroup>
<tr style="border-bottom:1px solid #444;">
<th style="text-align:center"></th>
<th style="text-align:center">${t("avg")}</th>
<th style="text-align:center">${t("first9")}</th>
<th style="text-align:center">${t("to170")}</th>
<th style="text-align:center">${t("trebles")}</th>
<th style="text-align:center">${t("triples")}</th>
<th style="text-align:center">${t("corrections")}</th>
</tr>
`;
Object.entries(stats).forEach(([name, s]) => {
html += `
<tr>
<td style="
text-align:left;
padding-right:10px;
text-transform:uppercase;
">
${name}
</td>
<td style="
text-align:center;
color: ${s.averages.total === bestAvg ? '#00e676' : 'inherit'};
font-weight: ${s.averages.total === bestAvg ? 'bold' : 'normal'};
">
${(s.averages.total ?? 0).toFixed(2)}
</td>
<td style="
text-align:center;
color: ${s.averages.first9 === bestFirst9 ? '#00e676' : 'inherit'};
font-weight: ${s.averages.first9 === bestFirst9 ? 'bold' : 'normal'};
">
${(s.averages.first9 ?? 0).toFixed(2)}
</td>
<td style="
text-align:center;
color: ${s.averages.to170 === bestTo170 ? '#00e676' : 'inherit'};
font-weight: ${s.averages.to170 === bestTo170 ? 'bold' : 'normal'};
">
${(s.averages.to170 ?? 0).toFixed(2)}
</td>
<!-- 🟢 Clean (weniger ist besser!) -->
<td style="
text-align:center;
color: ${s.counts.noTriple100 === bestClean ? '#00e676' : 'inherit'};
font-weight: ${s.counts.noTriple100 === bestClean ? 'bold' : 'normal'};
">
${s.counts.noTriple100}
</td>
<!-- 🎯 Triple -->
<td style="
text-align:center;
color: ${s.counts.triplesHigh === bestTriplesHigh ? '#00e676' : 'inherit'};
font-weight: ${s.counts.triplesHigh === bestTriplesHigh ? 'bold' : 'normal'};
">
${s.counts.triplesHigh ?? 0}
</td>
<td style="
text-align:center;
color: ${s.counts.nonDetected === bestNonDetected ? '#00e676' : '#ff5252'};
font-weight: ${s.counts.nonDetected === bestNonDetected ? 'bold' : 'normal'};
">
${s.counts.nonDetected}
</td>
</tr>
`;
});
html += `</table>`;
// 🎯 Checkout
const bestHigh = Math.max(...players.map(p => p.checkout.high || 0));
html += `
<table style="
width:auto;
border-collapse: collapse;
margin-bottom:12px;
table-layout:fixed;
">
<colgroup>
<col style="width:${NAME_COL_WIDTH};">
<col style="width:140px;">
<col style="width:140px;">
<col style="width:300px;"> <!-- 👈 NEU -->
</colgroup>
<tr style="border-bottom:1px solid #444;">
<th style="text-align:center"> </th>
<th style="text-align:center">${t("checkout")}</th>
<th style="text-align:center">${t("highest")}</th>
<th style="text-align:left">${t("finishes")}</th>
</tr>
`;
Object.entries(stats).forEach(([name, s]) => {
html += `
<tr>
<td style="
text-align:left;
padding-right:10px;
text-transform:uppercase;
">
${name}
</td>
<td style="text-align:center;">
<span style="
font-weight:bold;
color: ${
(Math.round((s.checkout.percentage ?? 0) * 100) / 100) === bestCheckoutRounded
? '#00e676'
: 'inherit'
};
">
${s.checkout.percentage.toFixed(2)} %
</span>
<span style="opacity:0.6; margin-left:4px;">
(${s.checkout.hits}/${s.checkout.attempts})
</span>
</td>
<td style="
text-align:center;
font-weight:bold;
color: ${s.checkout.high === bestHigh ? '#00e676' : 'inherit'};
">
${s.checkout.high > 0 ? s.checkout.high : ""}
</td>
<!-- 👇 WICHTIG: eigenes TD -->
<td style="
text-align:left;
padding-left:10px;
font-size:0.9em;
opacity:0.85;
">
${s.counts.highFinishList.length > 0
? s.counts.highFinishList.join(", ")
: "--"}
</td>
</tr>
`;
});
html += `</table>`;
// 🟢 Leg Übersicht
const legs = buildLegStats(match);
const setGroups = [];
let currentSet = null;
legs.forEach((l, i) => {
if (l.set !== currentSet) {
setGroups.push({
set: l.set,
start: i,
count: 1
});
currentSet = l.set;
} else {
setGroups[setGroups.length - 1].count++;
}
});
let hasCorrections = false;
legs.forEach(l => {
Object.values(l.players).forEach(p => {
if (p.correctedFinish) {
hasCorrections = true;
}
});
});
if (legs.length > 0) {
html += `
<table style="
width:auto;
border-collapse: collapse;
margin-top:8px;
table-layout:fixed;
">
<colgroup>
<col style="width:${NAME_COL_WIDTH};">
${legs.map(() => `<col style="width:70px;">`).join("")}
</colgroup>
<tr style="border-bottom:1px solid #444;">
<th style="text-align:left">${t("dartsRest")}</th>
${legs.map((l, i) => {
const prevSet = legs[i - 1]?.set;
const isNewSet = i > 0 && l.set !== prevSet;
return `
<th style="
text-align:center;
font-size:0.85em;
${isNewSet ? 'border-left:3px solid #888; padding-left:6px;' : ''}
">
L${l.leg}
</th>
`;
}).join("")}
</tr>
`;
if (setGroups.length > 1) {
html += `
<tr style="border-bottom:1px solid #444;">
<th></th>
${setGroups.map((s, i) => `
<th colspan="${s.count}" style="
text-align:center;
font-size:0.75em;
color:#aaa;
border-left:${i > 0 ? '3px solid #888' : 'none'};
">
SET ${s.set + 1}
</th>
`).join("")}
</tr>
`;
}
const playerNames = Object.keys(legs[0].players);
playerNames.forEach(name => {
html += `<tr style="border-top:1px solid #333;">`;
// Name
html += `
<td style="
text-align:left;
padding-right:10px;
text-transform:uppercase;
">
${name}
</td>
`;
// Legs
const setGroups = [];
let currentSet = null;
legs.forEach((l, i) => {
const data = l.players[name];
const prevSet = legs[i - 1]?.set;
const isNewSet = i > 0 && l.set !== prevSet;
if (data.type === "darts") {
legs.forEach((l, i) => {
if (l.set !== currentSet) {
setGroups.push({
set: l.set,
start: i,
count: 1
});
currentSet = l.set;
} else {
setGroups[setGroups.length - 1].count++;
}
});
html += `
<td style="
text-align:center;
font-weight:bold;
${isNewSet ? 'border-left:3px solid #888; padding-left:6px;' : ''}
"
title="${data.correctedFinish ? data.correctedDetail : ''}"
>
<span style="
display:inline-block;
min-width:28px;
padding:2px 6px;
border-radius:50%;
border:${data.correctedFinish ? '2px solid orange' : 'none'};
color:${data.correctedFinish ? '#00e676' : '#00e676'};
">
${data.value}
</span>
</td>
`;
}
else {
html += `
<td style="
text-align:center;
color:#c0c0c0;
opacity:0.8;
${isNewSet ? 'border-left:3px solid #888; padding-left:6px;' : ''}
">
${data.value}
</td>
`;
}
});
html += `</tr>`;
});
html += `</table>`;
if (hasCorrections) {
html += `
<div style="
margin-top:10px;
padding:8px 10px;
font-size:0.85em;
opacity:0.85;
border-top:1px solid #333;
">
<span style="color:orange; font-weight:bold;">${t("noticeTitle")}</span>
${t("noticeText")}
</div>
`;
}
}
container.innerHTML = html;
const target = document.querySelector('.chakra-card__body');
if (target) {
target.parentNode.insertBefore(container, target);
} else {
// Fallback falls UI noch nicht geladen
document.body.prepend(container);
}
}
function getGameId() {
return window.location.href.match(/matches\/([a-z0-9-]+)/)?.[1];
}
function countCustomScores(match) {
const result = {};
// init
match.players.forEach(p => {
result[p.name.toUpperCase()] = {
"90+": 0,
"130+": 0,
"171+": 0
};
});
match.games?.forEach(game => {
game.turns?.forEach(turn => {
const player = match.players
.find(p => p.id === turn.playerId)
?.name.toUpperCase();
const score =
turn.points ??
turn.score ??
turn.roundScore ??
0;
if (!player) return;
if (score === 180) {
// optional: falls du 180 separat zählen willst
// result[player]["180"]++;
}
else if (score >= 171 && score < 180) {
result[player]["171+"]++;
}
else if (score >= 130 && score < 171) {
result[player]["130+"]++;
}
else if (score >= 90 && score < 130) {
result[player]["90+"]++;
}
});
});
return result;
}
function countNoTripleOver100(match) {
const result = {};
match.players.forEach(p => result[p.name] = 0);
match.games?.forEach(game => {
const scorePerPlayer = {};
match.players.forEach(p => {
scorePerPlayer[p.id] = 501;
});
game.turns?.forEach(turn => {
const pid = turn.playerId;
let scored =
turn.points ??
turn.score ??
turn.roundScore ??
0;
const player = match.players.find(p => p.id === pid)?.name;
const currentScore = scorePerPlayer[pid];
// 🔥 echte Würfe holen
const throws = Array.isArray(turn.throws) ? turn.throws : [];
// 🔥 NUR diese Triples zählen
const allowedTriples = ["T20", "T19", "T18", "T17"];
const hasTriple = throws.some(th =>
allowedTriples.includes(th?.segment?.name)
);
// ✅ Deine finale Bedingung
if (
currentScore > 100 &&
!hasTriple
) {
result[player]++;
}
// Score runterzählen (immer am Ende!)
const score =
turn.points ??
turn.score ??
turn.roundScore ??
0;
scorePerPlayer[pid] -= score;
if (scorePerPlayer[pid] < 0) scorePerPlayer[pid] = 0;
});
});
return result;
}
function countHighTriples(match) {
const result = {};
match.players.forEach(p => result[p.name.toUpperCase()] = 0);
match.games?.forEach(game => {
game.turns?.forEach(turn => {
const player = match.players.find(p => p.id === turn.playerId)?.name.toUpperCase();
const throws = turn.throws ?? [];
throws.forEach(th => {
const name = th?.segment?.name;
if (["T20", "T19", "T18", "T17"].includes(name)) {
result[player]++;
}
});
});
});
return result;
}
function countNonDetectedThrows(match) {
const result = {};
// init
match.players.forEach(p => {
result[p.name.toUpperCase()] = 0;
});
match.games?.forEach(game => {
game.turns?.forEach(turn => {
const player = match.players.find(p => p.id === turn.playerId)?.name.toUpperCase();
const throws = turn.throws ?? [];
throws.forEach(th => {
if (th.entry !== "detected"
&& th.entry !== "corrected_bouncer"
&& th.entry !== "fill_miss"
&& th.entry !== "referee_ai_corrected"
&& th.entry !== "corrected_after_referee_ai"
&& th.entry !== "referee_ai_confirmed") {
result[player]++;
}
});
});
});
return result;
}
function getHighFinishList(match) {
const result = {};
// init
match.players.forEach(p => {
result[p.name.toUpperCase()] = [];
});
match.games?.forEach(game => {
const winnerId = game.winnerPlayerId;
if (!winnerId) return;
const playerName = match.players
.find(p => p.id === winnerId)
?.name.toUpperCase();
const turns = game.turns ?? [];
// 👉 letzten Turn des Gewinners finden
const lastTurn = [...turns]
.reverse()
.find(turn => turn.playerId === winnerId);
if (!lastTurn) return;
const points =
lastTurn.points ??
lastTurn.score ??
lastTurn.roundScore ??
0;
// 👉 jetzt klappt’s garantiert
if (points >= 90) {
result[playerName].push(points);
}
});
// sortieren
Object.keys(result).forEach(name => {
result[name].sort((a, b) => b - a);
});
return result;
}
async function init() {
const gameId = getGameId();
if (!gameId) return;
try {
const match = await getMatchStats(gameId);
// 🔥 Warten bis Daten wirklich da sind
if (!match || !match.players || !match.matchStats) {
return;
}
const stats = buildStats(match);
// 👉 nur bei neuem Match loggen
if (gameId !== currentGameId) {
console.log("NEW MATCH:", gameId);
currentGameId = gameId;
}
// ❌ während Match → nichts anzeigen
if (!match.finishedAt) {
document.querySelector('#ad-stats')?.remove();
return;
}
// 🔥 WICHTIG: warten bis Games da sind
if (!match.games || match.games.length === 0) {
console.log("Warte auf Legs...");
return;
}
// ✅ jetzt erst rendern
render(stats, match);
} catch (e) {
console.error("Stats Error:", e);
}
}
(function () {
const pushState = history.pushState;
const replaceState = history.replaceState;
function onChange() {
console.log("URL CHANGE");
currentGameId = null;
// kleine Verzögerung → API ready
setTimeout(init, 1000);
}
history.pushState = function () {
pushState.apply(this, arguments);
onChange();
};
history.replaceState = function () {
replaceState.apply(this, arguments);
onChange();
};
window.addEventListener("popstate", onChange);
})();
setInterval(init, 2000);
})();