Íslandsmeistarar Vals hófu titilvörn sína í Olís-deild karla með fjögurra marka sigri á Selfossi í Set-höllinni í kvöld. Lokatölur urðu 30-34 eftir að Selfyssingar höfðu veitt meisturunum hörkukeppni lengst af.
Selfoss byrjaði leikinn af miklum krafti og komst í 5-1. Valsmenn voru þó fljótir að vinna sig aftur inn í leikinn og jafnt var á nánast öllum tölum fram eftir fyrri hálfleik. Staðan var 10-8 fyrir Selfoss áður en gestirnir tóku við sér. Valur sneri stöðunni úr 11-10 í 11-14 og var tveimur mörkum yfir í hálfleik, 15-17.
Selfyssingar gáfust ekki upp og jöfnuðu metin í 19-19 snemma í síðari hálfleik. Þar virtist hins vegar hafa kviknað á Valsmönnum sem svöruðu með fjórum mörkum í röð og breyttu stöðunni í 19-23.
Eftir það voru heimamenn allan tímann að elta. Selfoss náði nokkrum sinnum að minnka muninn í tvö mörk, meðal annars í 24-26 og 25-27, en komst ekki nær. Á þeim kafla voru Selfyssingar sjálfum sér verstir og töpuðu boltanum í gríð og erg sem kostaði þá mark í bakið. Sveinn Ísak Hauksson tapaði meðal annars boltanum tvívegis í röð og skoruðu Valsarar hraðaupphlaups mark í bæði skiptin.
Sama var upp á teningnum í stöðunni 29-32 en þá voru Selfyssingar manni fleiri en í stað þess að minnka muninn og setja alvöru pressu á Íslandsmeistarana þá töpuðu Selfyssingar kaflanum, manni fleiri og Valur hélt Selfyssingum í hæfilegri fjarlægð á lokakaflanum og sigldi að lokum heim 34-30 sigri.
Arnór Snær Óskarsson fór mikinn í liði Vals og skoraði tíu mörk. Gunnar Róbertsson fylgdi fast á hæla hans með níu mörk en Allan Norðberg og Bjarni í Selvindi skoruðu fjögur mörk hvor.
Hjá Selfossi var Hákon Garri Gestsson markahæstur með sjö mörk og Tryggvi Sigurberg Traustason skoraði sex. Ragnar Hilmarsson bætti við fjórum.
Í markinu varði Björgvin Páll Gústavsson sjö skot fyrir Val en Alexander Hrafnkelsson og Ísak Kristinn Jónsson vörðu fimm skot hvor fyrir Selfoss. Ísak Kristinn kom með góða innkomu í mark Selfyssinga í seinni hálfleik. Jens Sigurðarson kom inná undir lok leiksins í marki Vals og varði meðal annars vítakast.
Valur tekur því fyrstu tvö stig tímabilsins en liðið hefur tekið töluverðum breytingum frá síðasta tímabili eftir að meðal annars þrír reynslumiklir leikmenn lögðu skóna á hilluna í sumar.
Margir hafa spáð Selfyssingum erfiðu gengi á tímabilinu en þeir sýndu það í kvöld að það verður erfitt að mæta þeim í vetur.
/* ===== Mobile-first styles ===== */
:root{
--bg: #f2f3f7;
--card: #ffffff;
--muted: #8e929a;
--text: #25272d;
--accent: #344cff;
--accent-2: #10b981;
--danger: #ef4444;
--radius: 16px;
--shadow: 0 8px 22px rgba(22,31,55,.08);
}
/* Chrome's mobile font-boosting inflates font-size inconsistently per block; lock it off */
#hb-match-root{ font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial; color: var(--text); background: var(--bg); padding: 14px; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
#hb-match-root *{ -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
.hb-card{ max-width: 720px; margin: 0 auto; background: var(--card); border: 1px solid #e1e4eb; border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; }
.hb-top{ display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); gap: 8px; align-items: center; padding: 14px; border-bottom: 1px solid #edf0f5; }
/* Team rows stack on mobile */
.hb-team{ display: flex; flex-direction: column; gap: 8px; align-items: center; justify-content:center; text-align:center; }
.hb-team img{ width: 44px; height: 44px; border-radius: 12px; object-fit: contain; background: #f0f2f6; border: 1px solid #e1e4eb; }
.hb-team .name{ overflow-wrap:anywhere; font-weight: 800; font-size: 16px; line-height: 1.1 }
.hb-team .meta{ color: var(--muted); font-size: 12px }
/* Score block centered and touch-friendly */
.hb-vs{ display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px; padding: 6px 0; }
.hb-score{ display:flex; align-items: baseline; gap: 8px; font-weight: 900; letter-spacing: .5px; }
.hb-score .home, .hb-score .away{ font-size: 28px }
.hb-score .dash{ color: var(--muted); font-size: 22px }
.hb-badge{ font-size: 11px; text-transform: uppercase; letter-spacing: .08em; padding: 6px 10px; border-radius: 999px; background: rgba(58,160,255,.15); color: var(--accent); border: 1px solid rgba(58,160,255,.35); }
.hb-badge.live{ background: rgba(239,68,68,.12); color: #ffb3b3; border-color: rgba(239,68,68,.35) }
.hb-badge.final{ background: rgba(16,185,129,.14); color: #a6f3d7; border-color: rgba(16,185,129,.35) }
.hb-meta{ display:grid; gap: 10px; padding: 12px 14px; }
.hb-rows{ display:grid; grid-template-columns: 1fr; gap: 10px; }
.hb-pill{ background: #f6f7fa; border: 1px solid #e6e9ef; padding: 12px; border-radius: 12px; }
.hb-pill .label{ color: var(--muted); font-size: 12px; letter-spacing:.02em }
.hb-pill .value{ font-size: clamp(14px, 4.2vw, 14px); margin-top: 4px }
.hb-footer{ display:flex; flex-wrap: wrap; gap:8px; align-items:center; justify-content: space-between; padding: 12px 14px; background: #fafbfc; border-top: 1px solid #edf0f5; }
.hb-tags{ display:flex; gap:8px; flex-wrap:wrap }
.hb-tag{ font-size: 12px; color: var(--muted); background: #f1f3f7; border: 1px solid #e3e6ed; padding: 6px 10px; border-radius: 999px; }
.hb-time{ color: var(--muted); font-size: 13px }
.hb-tabs{ max-width:720px; margin: 0 auto; display:flex; overflow-x:auto; gap: 2px; border-bottom: 1px solid #dfe3ea; background: #ffffff; scrollbar-width: none; }
.hb-tabs::-webkit-scrollbar{ display:none; }
.hb-tab{ flex: 1 0 auto; min-width: max-content; appearance:none; border:0; border-bottom: 3px solid transparent; background: transparent; color: var(--muted); padding: 14px 16px 11px; font: inherit; font-size: 14px; font-weight: 800; cursor: pointer; }
.hb-tab:hover,
.hb-tab:focus,
.hb-tab.is-active,
.hb-tab.is-active:hover,
.hb-tab.is-active:focus{ background:transparent!important; }
.hb-tab:hover,
.hb-tab:focus{ color:var(--muted)!important; }
.hb-tab.is-active{ color: var(--accent); border-bottom-color: var(--accent); }
.hb-tab.is-active:hover,
.hb-tab.is-active:focus{ color:var(--accent)!important; }
.hb-tab:focus-visible{ outline: 2px solid var(--accent); outline-offset: -4px; }
.hb-panel{ display:none; }
.hb-panel.is-active{ display:block; }
.hb-panel[hidden]{ display:none!important; }
.hb-empty{ padding: 16px 14px; color: var(--muted); font-size: 14px; }
/* match statistics container */
/* same background as your card */
.hb-stats {
max-width: 720px;
margin: 8px auto;
padding: 14px;
background: #ffffff;
border: 1px solid #e1e4eb;
border-radius: var(--radius);
box-shadow: var(--shadow);
}
/* column headings */
.hb-col h4 {
font-size: 13px;
font-weight: 700;
margin: 0 0 6px;
text-transform: uppercase;
letter-spacing: .08em;
}
/* tables styled like scoreboard */
.hb-stats table{
width: 100%;
border-collapse: collapse;
background: #f7f8fb;
border: 1px solid #e6e9ef;
border-radius: 10px;
overflow: hidden;
table-layout: fixed; /* important */
}
.hb-stats td {
border: none;
}
.hb-stats tr:not(:last-child) td {
border-bottom: 1px solid #e6e9ef;
}
.hb-stats td + td {
border-left: none; /* make sure no vertical line between name & score */
}
.hb-stats tr:last-child td{ border-bottom: none; }
/* .hb-stats td:first-child{ text-align:left; } */
/* name column: take remaining space, never wrap; ellipsize instead */
.hb-stats td:first-child{
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
/* .hb-stats td:last-child{
text-align:right; font-weight:800; font-variant-numeric: tabular-nums;
} */
/* score column: right aligned, no wrap, fixed width that fits "10 / 2" */
.hb-stats td:last-child{
width: 8ch; /* fits "10 / 2" or "12" comfortably */
white-space: nowrap;
text-align: right;
font-weight: 800;
font-variant-numeric: tabular-nums;
}
.hb-panel-box-score { padding-top: 8px; }
.hb-panel-box-score .hb-stats {
padding: 16px;
background: #f8f9fc;
}
.hb-panel-box-score .hb-row { gap: 18px; }
.hb-panel-box-score section { min-width: 0; }
.hb-panel-box-score section h4 {
display: flex;
align-items: center;
min-height: 32px;
margin: 0 0 8px;
padding: 0 2px 8px;
color: var(--text);
border-bottom: 2px solid #e5e8f0;
font-size: 12px;
letter-spacing: .06em;
}
.hb-panel-box-score section h4::before {
content: '';
width: 4px;
height: 18px;
margin-right: 8px;
border-radius: 2px;
background: var(--accent);
}
.hb-panel-box-score table {
background: #ffffff;
border: 1px solid #e3e7ef;
border-radius: 10px;
}
.hb-panel-box-score td {
padding: 11px 12px;
font-size: 14px;
}
.hb-panel-box-score td:first-child {
color: #3b3f48;
font-weight: 650;
}
.hb-panel-box-score td:last-child {
color: var(--accent);
font-size: 15px;
}
.hb-panel-box-score tr:hover td { background: #f7f8ff; }
/* roster table: shirt number first (narrow, blue), name second (black, fills space) */
.hb-panel-box-score .hb-roster-table td:first-child {
width: 6ch;
white-space: nowrap;
color: var(--accent);
text-align: left;
}
.hb-panel-box-score .hb-roster-table td:last-child {
width: auto;
color: #3b3f48;
text-align: left;
font-weight: 650;
font-size: 14px;
}
/* staff rows show role name (e.g. "Þjálfari") instead of a shirt number, needs more width */
.hb-panel-box-score .hb-roster-table-staff td:first-child {
width: 34%;
cursor: help;
}
.hb-standings {
max-width: 720px;
margin: 8px auto;
padding: 14px;
background: #ffffff;
border: 1px solid #e1e4eb;
border-radius: var(--radius);
box-shadow: var(--shadow);
}
.hb-panel-box-score .hb-stats,
.hb-standings.hb-standings-empty {
padding: 16px;
background: #f8f9fc;
}
.hb-standings.hb-standings-empty { margin-top: 16px; }
.hb-standings-title {
margin: 0 0 14px;
font-size: 18px;
font-weight: 800;
}
.hb-standings-table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
.hb-standings-table th,
.hb-standings-table td {
padding: 10px 4px;
text-align: right;
white-space: nowrap;
border-bottom: 1px solid #edf0f5;
font-variant-numeric: tabular-nums;
}
.hb-standings-table th {
color: var(--muted);
font-size: 12px;
font-weight: 800;
text-transform: uppercase;
}
.hb-standings-table tr:last-child td { border-bottom: 1px solid #edf0f5; }
.hb-standings-table th:first-child,
.hb-standings-table td:first-child { width: 22px; padding-right: 2px; text-align: left; }
.hb-standings-table th:nth-child(2),
.hb-standings-table td:nth-child(2) { width: auto; padding-left: 8px; text-align: left; }
.hb-standings-table th:nth-child(3),
.hb-standings-table td:nth-child(3),
.hb-standings-table th:nth-child(4),
.hb-standings-table td:nth-child(4),
.hb-standings-table th:nth-child(5),
.hb-standings-table td:nth-child(5),
.hb-standings-table th:nth-child(6),
.hb-standings-table td:nth-child(6) { width: 26px; }
.hb-standings-table th:nth-child(7),
.hb-standings-table td:nth-child(7) { width: 38px; }
.hb-standings-table th:nth-child(8),
.hb-standings-table td:nth-child(8) { width: 34px; }
.hb-standings-team { display: flex; flex-wrap: nowrap; align-items: center; gap: 10px; min-width: 0; }
.hb-standings-team img { width: 20px!important; height: 20px!important; max-width: 20px!important; object-fit: contain; flex: 0 0 20px; display: block; }
.hb-standings-team span { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.hb-standings-points { font-weight: 800; }
/* rows container */
.hb-stats-rows {
display: grid;
gap: 16px;
}
.hb-row {
display: grid;
grid-template-columns: 1fr; /* one column on mobile */
gap: 16px;
align-items: start;
}
@media (min-width: 680px) {
.hb-row { grid-template-columns: 1fr 1fr; }
}
/* keep your existing .hb-stats table styles */
.hb-stats section h4 {
font-size: 13px;
font-weight: 700;
margin: 0 0 6px;
text-transform: uppercase;
letter-spacing: .08em;
/*color: rgba(255,255,255,.85);*/
}
#hb-events-root{ max-width:720px; margin:16px auto 0; }
#hb-pointbypoint-root{ max-width:720px; margin:8px auto; }
.hb-debug-controls{ max-width:720px; margin:16px auto 0; padding: 10px 14px; display:flex; flex-wrap:wrap; gap:8px; align-items:center; justify-content:center; background: #ffffff; border:1px solid #e1e4eb; border-radius:12px; box-shadow: var(--shadow); }
.hb-debug-controls button{ appearance:none; border:1px solid #dfe3ea; border-radius:10px; background: #f3f5f9; color: var(--text); min-width:44px; min-height:38px; padding:8px 12px; font:inherit; font-weight:800; cursor:pointer; }
.hb-debug-controls button:hover{ background: #e9edf5; }
.hb-debug-controls .hb-debug-minute{ min-width:112px; text-align:center; font-weight:800; }
.hb-events{ display:grid; gap:10px; }
.hb-pointbypoint{ display:grid; gap:0; }
.hb-pointbypoint-row{ display:grid; grid-template-columns:1fr auto 1fr; align-items:center; min-height:44px; border-bottom:1px solid #edf0f5; background:#ffffff; }
.hb-pointbypoint-row:last-child{ border-bottom:none; }
.hb-pointbypoint-divider{
display:flex; align-items:center; justify-content:center;
gap:10px; padding:10px 8px; margin: 2px 0;
}
.hb-pointbypoint-divider::before,
.hb-pointbypoint-divider::after{ content:''; flex:1; height:1px; background:#e1e4eb; }
.hb-pointbypoint-divider span{
font-size:12px; font-weight:800; text-transform:uppercase; letter-spacing:.06em;
color: var(--muted); white-space:nowrap;
}
.hb-pointbypoint-side{ display:flex; align-items:center; min-width:0; padding:5px 8px; }
.hb-pointbypoint-side.is-home{ justify-content:flex-end; }
.hb-pointbypoint-side.is-away{ justify-content:flex-start; }
.hb-pointbypoint .score{ font-weight:800; font-size:17px; line-height:1; white-space:nowrap; padding:6px 10px; }
.hb-pointbypoint .score .scoring-team{ color:#e11d48; }
.hb-pointbypoint .score .opposing-team{ color:#111827; }
.hb-pointbypoint .score-difference{ font-weight:800; font-size:11px; line-height:1; color:#1da463; white-space:nowrap; background:#f1fbf5; border:1px solid #d9f1e2; border-radius:5px; padding:4px 6px; }
.hb-ev{
display:flex; align-items:center;
background: #ffffff; border:1px solid #e1e4eb;
border-radius:12px; padding:10px 12px;
}
.hb-ev.is-home{ justify-content:flex-start; }
.hb-ev.is-away{ justify-content:flex-end; text-align:right; }
.hb-ev-content{ display:grid; grid-template-columns:44px 20px 42px 74px minmax(0, 1fr); gap:10px; align-items:center; max-width:100%; }
.hb-ev.is-away .hb-ev-content{ grid-template-columns:minmax(0, 1fr) 74px 42px 20px 44px; }
.hb-ev .clock{ font-weight:800; font-size:14px; white-space:nowrap; }
.hb-ev .team-logo-slot{ width:20px; height:20px; display:flex; align-items:center; justify-content:center; }
.hb-ev .team-logo{ width:20px!important; height:20px!important; max-width:20px!important; max-height:20px!important; object-fit:contain; display:block; }
.hb-ev .kind{ font-size:12px; padding:6px 10px; border-radius:999px;
border:1px solid #e1e4eb; background: #f3f5f9; }
.hb-ev .kind .hb-event-icon{ width:20px!important; height:20px!important; max-width:20px!important; max-height:20px!important; object-fit:contain; display:block; }
.hb-ev .main{ line-height:1.2 }
.hb-ev .title{ font-weight:700; font-size:14px }
.hb-ev .score{ font-weight:800; font-size:18px; line-height:1; white-space:nowrap; background:#eef0f4; border-radius:999px; padding:6px 10px; }
.hb-ev .score-difference{ font-weight:800; font-size:14px; line-height:1; color:#1da463; white-space:nowrap; background:#f1fbf5; border:1px solid #d9f1e2; border-radius:6px; padding:6px 8px; }
/* ===== Responsive upsizes ===== */
@media (min-width: 480px){
#hb-match-root{ padding: 18px }
.hb-top{ padding: 16px }
.hb-team img{ width: 56px; height: 56px }
.hb-team .name{ font-size: 18px }
.hb-score .home, .hb-score .away{ font-size: 34px }
.hb-score .dash{ font-size: 24px }
}
@media (min-width: 680px){
.hb-top{ grid-template-columns: 1fr auto 1fr; text-align: left }
.hb-rows{ grid-template-columns: repeat(3, minmax(0,1fr)); }
}
@media (max-width: 479px){
.hb-tabs{ overflow:visible; }
.hb-tab{
flex:1 1 0;
min-width:0;
min-height:56px;
padding:10px 4px 7px;
font-size:12px;
line-height:1.15;
white-space:normal;
}
.hb-ev-content{ grid-template-columns:44px 20px 42px 74px; }
.hb-ev.is-away .hb-ev-content{ grid-template-columns:74px 42px 20px 44px; }
.hb-ev .main{ grid-column:1 / -1; }
.hb-ev.is-away .main{ text-align:left; }
.hb-standings-table th,
.hb-standings-table td{ padding-top:6px; padding-bottom:6px; }
}
/* Accessibility: reduce motion */
@media (prefers-reduced-motion: reduce){ *{ animation: none!important; transition: none!important } }
const MATCH_STATS_SRC = '/wp-json/azure/v1/matchstats?hsi_match_number=';
const DEBUG_USE_LOCAL_MATCH_STATS = window.location.protocol === 'file:' || ['localhost', '127.0.0.1'].includes(window.location.hostname);
const DEBUG_DEMO_BASE_SRC = new URL('Demo/', window.location.href).href;
const DEBUG_UPLOADS_BASE_SRC = new URL('wp-content/uploads/', window.location.href).href;
const DEBUG_MATCH_STATS_SRC = new URL('GameStatsDemo.json', DEBUG_DEMO_BASE_SRC).href;
// Json mapped files
const TOURNAMENT_MAP_SRC = '/wp-content/uploads/tournaments.json?v=1'; // Tournaments
const TEAM_LOGO_MAP_SRC = '/wp-content/uploads/teams-logo.json?v=1'; // Team logos
const TEAM_MAP_SRC = '/wp-content/uploads/teams.json?v=1'; // Team names
const TOURNAMENT_MAP_REQUEST_SRC = DEBUG_USE_LOCAL_MATCH_STATS ? new URL('tournaments.json', DEBUG_UPLOADS_BASE_SRC).href : TOURNAMENT_MAP_SRC;
const TEAM_LOGO_MAP_REQUEST_SRC = DEBUG_USE_LOCAL_MATCH_STATS ? new URL('teams-logo.json', DEBUG_UPLOADS_BASE_SRC).href : TEAM_LOGO_MAP_SRC;
const TEAM_MAP_REQUEST_SRC = DEBUG_USE_LOCAL_MATCH_STATS ? new URL('teams.json', DEBUG_UPLOADS_BASE_SRC).href : TEAM_MAP_SRC;
let HOME_LOGO_PATH = '';
let AWAY_LOGO_PATH = '';
let HOME_TEAM_NAME = '';
let AWAY_TEAM_NAME = '';
let MATCH_EVENTS = [];
let DEBUG_EVENT_MINUTE = 60;
// Load Tournament Map
async function loadJsonMap(url){
try {
const res = await fetch(url, { cache: "reload" }); // or "force-cache"
if (!res.ok) throw new Error('HTTP ' + res.status);
return await res.json();
} catch (e) {
console.warn('Json map load failed:', e);
return {};
}
}
function getTournamentName(id, map){
const key = String(id || '').trim();
return (map && map[key]) || `Mót #${key || '—'} fannst ekki`;
}
// Helpers
const toInt = (v, d=0) => { const n = parseInt(v, 10); return Number.isFinite(n) ? n : d; };
const eventMs = ev => {
const t = (ev['Current Time'] || '').toString().replace(' ', 'T');
const ms = Date.parse(t);
return Number.isFinite(ms) ? ms : toInt(ev['HBStatz Event ID'], 0);
};
function renderMatch(rootId, matchData, tournamentMap, teamLogoMap){
const root = document.getElementById(rootId);
if (!root){ return; }
if (!matchData){
root.innerHTML = '';
return;
}
// Local variables
const match = matchData.MatchStats;
const topScorers = matchData.TopScorers;
const goalKeeperSaves = matchData.GoalkeeperSaves;
const warnings = matchData.Warnings;
const standings = matchData.Tournament?.Standings;
const homeId = match.HomeTeamId;
const homeName = match.HomeTeamName;
const homeScore = match.HomeTeamScore;
const homeHT = match.HomeTeamHalftimeScore;
const awayId = match.AwayTeamId;
const awayName = match.AwayTeamName;
const awayScore = match.AwayTeamScore;
const awayHT = match.AwayTeamHalftimeScore;
const homeLogo = teamLogoMap && teamLogoMap[homeId] ? teamLogoMap[homeId] : match.HomeTeamLogo;
const awayLogo = teamLogoMap && teamLogoMap[awayId] ? teamLogoMap[awayId] : match.AwayTeamLogo;
HOME_LOGO_PATH = homeLogo;
AWAY_LOGO_PATH = awayLogo;
HOME_TEAM_NAME = homeName;
AWAY_TEAM_NAME = awayName;
const tourId = match.TournamentId;
const tourName = getTournamentName(tourId, tournamentMap);
const gameTime = document.getElementById('hb-hsi-data')?.dataset.gametime ?? null;
const dateFmt = gameTime !== null ? match.MatchDateFmt.replace('00:00', gameTime) : match.MatchDateFmt;
const clock = match.Clock;
const statusCode = match.StatusCode;
const statusText = match.StatusText;
const gender = match.Gender;
const isLive = statusCode === 2;
const isFinal = statusCode === 4;
// Update the HTML Body (Posts)
document.querySelectorAll("p.testid").forEach(el => {
el.innerHTML = el.innerHTML.replace("{HomeTeamName}", homeName);
el.innerHTML = el.innerHTML.replace("{AwayTeamName}", awayName);
el.innerHTML = el.innerHTML.replace("{TourName}", tourName);
});
// IDs / tags
const tags = [
gender,
tourName
].filter(Boolean);
// Template
root.innerHTML = `
${homeScore}
-
${awayScore}
${statusText}
Atburðir
Markaskorar
<!-- Leikmannahópur -->
Stöðutafla
Þróun leiks
${homeName} – Markaskorarar
${renderTopScorers(topScorers, "home")}
${awayName} – Markaskorarar
${renderTopScorers(topScorers, "away")}
${homeName} – Varin skot
${renderGoalKeeperSaves(goalKeeperSaves, "home")}
${awayName} – Varin skot
${renderGoalKeeperSaves(goalKeeperSaves, "away")}
${homeName} - Brottvísanir
${renderWarnings(warnings, "home")}
${awayName} - Brottvísanir
${renderWarnings(warnings, "away")}
${renderRoster(matchData.MatchReport, homeId, homeName, awayId, awayName)}
${renderStandings(standings, matchData.Tournament?.TournamentName || tourName, teamLogoMap)}
${DEBUG_USE_LOCAL_MATCH_STATS ? renderDebugEventControls() : ''}
`;
initMatchTabs(root);
initDebugEventControls();
}
function renderDebugEventControls() {
return `
0
-
${DEBUG_EVENT_MINUTE} min
+
60
`;
}
function initDebugEventControls() {
if (!DEBUG_USE_LOCAL_MATCH_STATS) return;
const label = document.getElementById('hb-debug-minute-label');
const updateMinute = minute => {
DEBUG_EVENT_MINUTE = Math.max(0, Math.min(60, minute));
if (label) label.textContent = `${DEBUG_EVENT_MINUTE} min`;
renderPointByPoint('hb-pointbypoint-root', MATCH_EVENTS);
};
document.querySelectorAll('[data-hb-debug-step]').forEach(button => {
button.addEventListener('click', () => {
updateMinute(DEBUG_EVENT_MINUTE + toInt(button.dataset.hbDebugStep, 0));
});
});
document.querySelectorAll('[data-hb-debug-minute]').forEach(button => {
button.addEventListener('click', () => {
updateMinute(toInt(button.dataset.hbDebugMinute, DEBUG_EVENT_MINUTE));
});
});
}
function initMatchTabs(root) {
const tabs = [...root.querySelectorAll('[data-hb-tab]')];
const panels = [...root.querySelectorAll('[data-hb-panel]')];
tabs.forEach(tab => {
tab.addEventListener('click', () => {
const activeTab = tab.dataset.hbTab;
tabs.forEach(item => {
const isActive = item === tab;
item.classList.toggle('is-active', isActive);
item.setAttribute('aria-selected', String(isActive));
});
panels.forEach(panel => {
const isActive = panel.dataset.hbPanel === activeTab;
panel.classList.toggle('is-active', isActive);
panel.hidden = !isActive;
});
tab.blur();
});
});
}
function renderMatchNotFound(rootId, hsi) {
const root = document.getElementById(rootId);
if (!root) return;
root.innerHTML = `
`;
}
function renderTopScorers(topScorers, homeOrAway) {
if (topScorers === null) return ` `;
return topScorers
.filter(s => (s.HomeOrAway || '').toLowerCase() === homeOrAway)
.sort((a, b) =>
b.Goals - a.Goals ||
b.Penalties - a.Penalties ||
a.PlayerName.localeCompare(b.PlayerName)
)
.map(s => {
const score = s.Penalties > 0 ? `${s.Goals} / ${s.Penalties}` : s.Goals;
return `${s.PlayerName} ${score} `;
})
.join('');
}
function renderGoalKeeperSaves(goalKeepers, homeOrAway) {
if (goalKeepers === null) return ` `;
return goalKeepers
.filter(s => (s.HomeOrAway || '').toLowerCase() === homeOrAway)
.sort((a, b) =>
b.Saves - a.Saves ||
a.PlayerName.localeCompare(b.PlayerName)
)
.map(s => {
const save = s.Saves;
return `${s.PlayerName} ${save} `;
})
.join('');
}
function renderWarnings(warnings, homeOrAway) {
if (warnings === null) return ` `;
return warnings
.filter(s => (s.HomeOrAway || '').toLowerCase() === homeOrAway)
.map(s => {
const warning = s.TotalMin;
return `${s.TotalMin} mín. `;
})
.join('');
}
function renderRoster(matchReport, homeId, homeName, awayId, awayName) {
const players = matchReport?.Players || [];
const staff = matchReport?.Staff || [];
const renderTeamRoster = (teamId, teamName) => {
const teamIdStr = String(teamId);
const teamPlayers = players
.filter(p => String(p.TeamId) === teamIdStr)
.sort((a, b) => toInt(a.ShirtNumber, 0) - toInt(b.ShirtNumber, 0));
const teamStaff = staff.filter(p => String(p.TeamId) === teamIdStr);
const playerRows = teamPlayers.length
? teamPlayers.map(p => `${p.ShirtNumber ?? ''} ${p.PlayerName} `).join('')
: ` `;
const staffRows = teamStaff.length
? teamStaff.map(p => `${p.RoleName} ${p.PlayerName} `).join('')
: ` `;
return `
`;
};
if (!players.length && !staff.length) {
return 'Enginn leikmannahópur fannst
';
}
return `
${renderTeamRoster(homeId, homeName)}
${renderTeamRoster(awayId, awayName)}
`;
}
function renderStandings(standings, tournamentName, teamLogoMap) {
if (!Array.isArray(standings) || standings.length === 0) {
return '';
}
const rows = [...standings]
.sort((a, b) => toInt(a.Position) - toInt(b.Position))
.map(team => {
const teamId = String(team.TeamId ?? '');
const logo = teamLogoMap?.[teamId] || '';
const logoHtml = logo
? ` `
: '';
return `
${team.Position ?? ''}
${logoHtml}${team.TeamName ?? ''}
${team.GamesPlayed ?? 0}
${team.Wins ?? 0}
${team.Draws ?? 0}
${team.Losses ?? 0}
${team.GoalDifference ?? 0}
${team.Points ?? 0}
`;
})
.join('');
return `
${tournamentName || 'Stöðutafla'}
`;
}
async function loadAndRenderMatch(url, TOURNAMENT_MAP, TEAM_LOGO_MAP) {
try {
const matchNumber = document.getElementById('hb-hsi-data')?.dataset.hsi ?? null;
const requestUrl = DEBUG_USE_LOCAL_MATCH_STATS
? `${DEBUG_MATCH_STATS_SRC}?t=${Date.now()}`
: `${url}${encodeURIComponent(matchNumber)}&t=${Date.now()}`;
const res = await fetch(requestUrl, { cache: 'reload' });
if (!res.ok) throw new Error('HTTP ' + res.status);
const data = await res.json();
if (!data || data === null || data.MatchStats === null) {
renderMatchNotFound('hb-match-root', matchNumber);
const evRoot = document.getElementById('hb-events-root');
if (evRoot) evRoot.innerHTML = ''; // don’t show events if no match
} else {
renderMatch('hb-match-root', data, TOURNAMENT_MAP, TEAM_LOGO_MAP);
MATCH_EVENTS = data.MatchEvents || [];
if (data.MatchEvents !== null) {
renderEvents('hb-events-root', MATCH_EVENTS);
renderPointByPoint('hb-pointbypoint-root', MATCH_EVENTS);
}
}
} catch (e) {
console.warn('Match load failed:', e);
renderMatch('hb-match-root', null, {}, {}); // graceful fallback
}
}
function renderEvents(rootId, events) {
const root = document.getElementById(rootId);
if (!root){ return; }
if (!events){
root.innerHTML = 'Engir atburðir fundust
';
return;
}
const visibleEvents = events
.filter(ev => String(ev.Event ?? '') !== '7')
.filter(ev => !DEBUG_USE_LOCAL_MATCH_STATS || getEventClockSeconds(ev) <= getDebugMinuteLimitSeconds());
root.innerHTML = `${
visibleEvents.map(ev => {
const isHalftime = String(ev.Event ?? '') === '18' && (ev.Label || '').toLowerCase() === 'hálfleikur';
if (isHalftime) {
return `
${ev.Label}
`;
}
const clock = ev.Clock;
const label = ev.Label;
const homeScore = toInt(ev.HomeScore, 0);
const awayScore = toInt(ev.AwayScore, 0);
const scoreHtml = `${homeScore} - ${awayScore}`;
const rawEventText = ev.IsPlayerOrTeamInfo ? ev.PlayerOrTeamInfo : label;
const eventText = rawEventText.replace(/^#\d+\s*/, '');
const isPlayerRelated = Boolean(ev.IsPlayerOrTeamInfo);
const isHomeEvent = !isPlayerRelated || (ev.HomeOrAway || '').toLowerCase() === 'home';
const sideClass = isHomeEvent ? 'is-home' : 'is-away';
const iconPath = getEventIconPath(ev.Event);
const iconHTML = iconPath ? `
` : label;
const teamLogo = isHomeEvent ? HOME_LOGO_PATH : AWAY_LOGO_PATH;
const teamName = isHomeEvent ? HOME_TEAM_NAME : AWAY_TEAM_NAME;
const teamLogoHTML = isPlayerRelated && teamLogo
? `
`
: '';
const homeEventHtml = `
${clock}
${teamLogoHTML}
${iconHTML}
${scoreHtml}
`;
const awayEventHtml = `
${scoreHtml}
${iconHTML}
${teamLogoHTML}
${clock}
`;
return `
${isHomeEvent ? homeEventHtml : awayEventHtml}
`;
}).join('')
}
`;
}
function renderPointByPoint(rootId, events) {
const root = document.getElementById(rootId);
if (!root){ return; }
if (!events){
root.innerHTML = 'Engin stig fundust
';
return;
}
const scoringEvents = events
.filter(ev => String(ev.Event ?? '') === '1')
.filter(ev => !DEBUG_USE_LOCAL_MATCH_STATS || getEventClockSeconds(ev) getEventClockSeconds(a) - getEventClockSeconds(b));
const halftimeEvents = events
.filter(ev => String(ev.Event ?? '') === '18' && (ev.Label || '').toLowerCase() === 'hálfleikur')
.filter(ev => !DEBUG_USE_LOCAL_MATCH_STATS || getEventClockSeconds(ev) ({ type: 'score', ev, clockSeconds: getEventClockSeconds(ev) })),
...halftimeEvents.map(ev => ({ type: 'halftime', ev, clockSeconds: getEventClockSeconds(ev) })),
].sort((a, b) => a.clockSeconds - b.clockSeconds || (a.type === 'halftime' ? -1 : 1));
root.innerHTML = `
${rows.map(row => {
if (row.type === 'halftime') {
return `
${row.ev.Label}
`;
}
const ev = row.ev;
const homeScore = toInt(ev.HomeScore, 0);
const awayScore = toInt(ev.AwayScore, 0);
const difference = Math.abs(homeScore - awayScore);
const scoringSide = (ev.HomeOrAway || '').toLowerCase();
const differenceHtml = difference > 0 ? `
+${difference}
` : '';
const homeScoreClass = scoringSide === 'home' ? 'scoring-team' : 'opposing-team';
const awayScoreClass = scoringSide === 'away' ? 'scoring-team' : 'opposing-team';
return `
${homeScore > awayScore ? differenceHtml : ''}
${homeScore} - ${awayScore}
${awayScore > homeScore ? differenceHtml : ''}
`;
}).join('')}
`;
}
function getDebugMinuteLimitSeconds() {
return DEBUG_EVENT_MINUTE >= 60 ? Number.POSITIVE_INFINITY : DEBUG_EVENT_MINUTE * 60 + 59;
}
function getEventClockSeconds(ev) {
const clock = String(ev.Clock || '').trim();
const parts = clock.split(':').map(part => toInt(part, 0));
if (parts.length >= 2) {
return parts[0] * 60 + parts[1];
}
return toInt(clock, 0) * 60;
}
function getEventIconPath(eventCode,subCode){
const e = parseInt(eventCode||0,10);
const s = (subCode===null||subCode===undefined||subCode==='') ? null : parseInt(subCode,10);
switch(e){
case 1: return '/wp-content/uploads/2025/08/icon_goal.png';
case 2: return '/wp-content/uploads/2025/08/icon_yellow.png';
case 3: return '/wp-content/uploads/2025/08/icon_2min.png';
case 4: return '/wp-content/uploads/2025/08/icon_redcard.png';
case 5: return '/wp-content/uploads/2025/08/icon_redcard.png';
case 6: return '/wp-content/uploads/2025/08/icon_save.png';
case 7: return s===1 ? '/wp-content/uploads/2025/08/icon_clockstart.png' : s===0 ? '/wp-content/uploads/2025/08/icon_clockstop.png' : '/wp-content/uploads/2025/08/icon_clockstart.png';
case 17: return '/wp-content/uploads/2025/08/icon_timeout.png'; // Time out
case 18:
if(s===1) return '/wp-content/uploads/2025/08/icon_halftime.png';
if(s===2) return '/wp-content/uploads/2025/08/icon_gameended.png';
if(s===3) return '/wp-content/uploads/2025/08/icon_clockstart.png';
if(s===4) return '/wp-content/uploads/2025/08/icon_clockstart.png';
if(s===5) return '/wp-content/uploads/2025/08/icon_clockstart.png';
return '/wp-content/uploads/2025/08/icon_clockstart.png';
default: return '/wp-content/uploads/2025/08/icon_clockstart.png';
}
}
// Boot: wait for JSON, then render
document.addEventListener('DOMContentLoaded', async () => {
try {
const TOURNAMENT_MAP = await loadJsonMap(TOURNAMENT_MAP_REQUEST_SRC);
//const TEAM_MAP = await loadJsonMap(TEAM_MAP_REQUEST_SRC);
const TEAM_LOGO_MAP = await loadJsonMap(TEAM_LOGO_MAP_REQUEST_SRC);
loadAndRenderMatch(MATCH_STATS_SRC, TOURNAMENT_MAP, TEAM_LOGO_MAP);
} catch (e) {
console.warn('Tournament map failed, rendering with fallback names', e);
loadAndRenderMatch(MATCH_STATS_SRC);
}
});