Skip to content
Team-level game performances — each row is one team in one match. Sort by any stat column to find the best single-game team performances (e.g., most goals, most tackles). High = above average for that stat. Low = below average.
Show code
statsEsc = window . statsEsc
statsTable = window . statsTable
base = window . DATA_BASE_URL
leagueCodes = window . footballMaps . domesticLeagues
leagueNames = window . footballMaps . leagueNames
statDefs = window . footballStatDefs || {}
// Categories: exclude ratings and custom
catKeys = Object . keys (statDefs). filter (k => ! statDefs[k]. page && k !== "custom" )
Show code
// ── Category toggle ──────────────────────────────────────────
viewof category = {
const _key = "_teamGameLogCat_" + window . location . pathname . replace (/ [^a-z0-9] /gi , "_" )
const _saved = window [_key] || "scoring"
const _default = catKeys. includes (_saved) ? _saved : "scoring"
const container = html `<div class="stats-category-toggle football"></div>`
for (const key of catKeys) {
const btn = document . createElement ("button" )
btn. className = "stats-cat-btn" + (key === _default ? " active" : "" )
btn. textContent = statDefs[key]. label
btn. dataset . cat = key
btn. addEventListener ("click" , () => {
container. querySelectorAll (".stats-cat-btn" ). forEach (b => b. classList . remove ("active" ))
btn. classList . add ("active" )
container. value = key
window [_key] = key
container. dispatchEvent (new Event ("input" , { bubbles : true }))
})
container. appendChild (btn)
}
container. value = _default
return container
}
Show code
viewof filters = {
function makeSelect (options, defaultVal, label) {
const wrap = document . createElement ("div" )
wrap. className = "filter-select-wrap"
const lbl = document . createElement ("span" )
lbl. className = "filter-label"
lbl. textContent = label
const sel = document . createElement ("select" )
sel. className = "filter-select"
for (const opt of options) {
const o = document . createElement ("option" )
o. value = opt; o. textContent = opt
if (opt === defaultVal) o. selected = true
sel. appendChild (o)
}
wrap. appendChild (lbl); wrap. appendChild (sel)
return { wrap, sel }
}
const container = document . createElement ("div" )
container. className = "player-filter-bar"
const row = document . createElement ("div" )
row. className = "filter-row"
const leagueOpts = ["All Leagues" , ... leagueCodes. map (c => ` ${ leagueNames[c] || c} ( ${ c} )` )]
const league = makeSelect (leagueOpts, leagueOpts. find (l => l. includes ("ENG" )) || leagueOpts[0 ], "League" )
const season = makeSelect (["All Seasons" ], "All Seasons" , "Season" )
row. appendChild (league. wrap )
row. appendChild (season. wrap )
container. appendChild (row)
container. value = { league : "ENG" , season : "All Seasons" }
function emit () { container. dispatchEvent (new Event ("input" , { bubbles : true })) }
league. sel . addEventListener ("change" , () => {
const raw = league. sel . value
const code = raw === "All Leagues" ? "All Leagues" : raw. match (/ \((\w+)\)$ / )?. [1 ] || raw
container. value = { ... container. value , league : code, season : "All Seasons" }
emit ()
})
season. sel . addEventListener ("change" , () => {
container. value = { ... container. value , season : season. sel . value }
emit ()
})
container. _updateSeasons = function (opts, defaultVal) {
const oldVal = container. value . season
while (season. sel . firstChild ) season. sel . removeChild (season. sel . firstChild )
for (const opt of opts) {
const o = document . createElement ("option" )
o. value = opt; o. textContent = opt
if (opt === defaultVal) o. selected = true
season. sel . appendChild (o)
}
if (oldVal !== defaultVal) {
container. value = { ... container. value , season : defaultVal }
emit ()
}
}
return container
}
leagueFilter = filters. league
seasonFilter = filters. season
Show code
matchStats = {
const codes = leagueFilter === "All Leagues" ? leagueCodes : [leagueFilter]
const results = []
for (const code of codes) {
try {
const data = await window . fetchParquet (base + `football/match-stats- ${ code} .parquet` )
if (data) results. push (... data)
} catch (e) {
console . warn (`[team-game-logs] match-stats- ${ code} load failed:` , e)
}
}
return results. length > 0 ? results : null
}
// Load game-logs for Value tab
_gameLogs = {
try { return await window . fetchParquet (base + "football/game-logs.parquet" ) } catch (e) { return null }
}
seasonOptions = {
const src = matchStats || _gameLogs
if (! src) return ["All Seasons" ]
const seasons = [... new Set (src. map (d => String (d. season )))]. sort (). reverse ()
return ["All Seasons" , ... seasons]
}
// Update season dropdown
{
const el = document . querySelector (".player-filter-bar" )
if (el && el. _updateSeasons ) {
const defaultSeason = seasonOptions[1 ] || "All Seasons"
el. _updateSeasons (seasonOptions, defaultSeason)
}
}
Show code
// ── Build team game log table data ──────────────────────────
tableData = {
const catDef = statDefs[category]
if (! catDef) return null
const source = catDef. source
let rawData
if (source === "gameLogs" ) {
rawData = _gameLogs
} else {
rawData = matchStats
}
if (! rawData) return null
const effectiveSeason = seasonFilter === "All Seasons" ? null : seasonFilter
let games = rawData
// League filter (for game-logs which may span leagues)
if (leagueFilter !== "All Leagues" && source === "gameLogs" ) {
games = games. filter (d => d. league === leagueFilter)
}
// Season filter
if (effectiveSeason) games = games. filter (d => String (d. season ) === effectiveSeason)
// Aggregate player rows into team-game rows
const statCols = catDef. columns . filter (c => c !== "pass_pct" )
const teamGameMap = new Map ()
for (const row of games) {
const team = row. team_name || ""
if (! team) continue
const date = row. match_date ? String (row. match_date ). replace ("Z" , "" ). slice (0 , 10 ) : ""
const opp = row. opponent || ""
const key = ` ${ team} ||| ${ date} ||| ${ opp} `
if (! teamGameMap. has (key)) {
teamGameMap. set (key, { team, date, opponent : opp, season : row. season , vals : {} })
for (const c of statCols) teamGameMap. get (key). vals [c] = 0
}
const entry = teamGameMap. get (key)
for (const c of statCols) {
entry. vals [c] = (entry. vals [c] || 0 ) + (Number (row[c]) || 0 )
}
}
// Build output rows
return [... teamGameMap. values ()]. map (e => {
const row = {
team : e. team ,
date : e. date ,
opponent : e. opponent
}
for (const col of statCols) {
const v = e. vals [col]
row[col] = isNaN (v) ? null : + (v. toFixed (1 ))
}
// Recompute pass_pct from summed totals
if (catDef. compute && row. passes > 0 ) {
row. pass_pct = Math . round (100 * row. passes_accurate / row. passes )
} else if (catDef. compute ) {
row. pass_pct = null
}
return row
})
}
Show code
viewof search = tableData == null
? html ``
: Inputs. search (tableData, { placeholder : "Search teams..." })
Show code
// ── Render table ─────────────────────────────────────────────
{
if (tableData == null || tableData. length === 0 )
return html `<p class="text-muted">No data available for this category and filter combination.</p>`
const catDef = statDefs[category]
const statCols = catDef. columns
const columns = ["team" , "date" , "opponent" , ... statCols]
const header = {
team : "Team" , date : "Date" , opponent : "Vs" ,
... catDef. header
}
const format = {}
for (const col of statCols) {
format[col] = v => v != null ? String (Math . round (Number (v))) : ""
}
const heatmap = {}
for (const col of statCols) {
if (catDef. heatmap ?. [col]) heatmap[col] = catDef. heatmap [col]
}
const render = {}
const renderTeamCell = window . footballMaps ?. renderTeamCell
if (renderTeamCell) {
render. team = (val) => renderTeamCell (val)
render. opponent = (val) => renderTeamCell (val)
}
const groups = [
{ label : "" , span : 3 },
{ label : catDef. label , span : statCols. length }
]
const sortCol = catDef. sortCol || statCols[0 ]
return statsTable (search, {
columns, header, groups, format, heatmap, render,
sort : sortCol, reverse : true ,
pageSize : 25
})
}