FH og Stjarnan mættust í fyrstu umferð Olís. deildar karla í Kaplakrika í kvöld. FH byrjaði leikinn vel og komst fljótt í þriggja marka forystu og FH leiddi inn í hálfleik með fimm marka forskot, 21-17.
FH kom vel inn í síðari hálfleikinn og endaði á því að vinna sjö marka sigur 38-31 og voru Hafnfirðingar ekki í miklum vandræðum með gestina úr Garðabæ og FH er strax komið á blað í Olís deildinni.
Atkvæðamestur liði FH var Símon Michael Guðjónsson en hann skoraði átta mörk í kvöld. Antoine Óskar Pantano og Ómar Darri Sigurgeirsson komu á eftir Símoni með fimm mörk og aðrir skoruðu minna. Jón Þórarinn Þorsteinsson fór á kostum í marki FH og varði 21.skot.
Atkvæðamestur í liði Stjörnunnar var Ísak Logi Einarsson en hann skoraði átta mörk í kvöld. Dagur Sverrir Kirstjánsson skoraði fimm. Gauti Gunarsson og Starri Friðriksson skoruðu báðir fjögur og aðrir minna. Sigurður Dan Óskarsson var í marki Stjörnunnar í kvöld og varði sjö bolta.
/* ===== 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: 100%; 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:100%; margin: 6px 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: 100%;
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;
}
.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: 100%;
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 h4 {
display: flex;
align-items: center;
min-height: 32px;
margin: 0 0 12px;
padding: 0 2px 8px;
color: var(--text);
border-bottom: 2px solid #e5e8f0;
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .06em;
}
.hb-standings h4::before {
content: '';
width: 4px;
height: 18px;
margin-right: 8px;
border-radius: 2px;
background: var(--accent);
flex: 0 0 4px;
}
.hb-standings-scroll {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
border: 1px solid #e3e7ef;
border-radius: 10px;
}
.hb-standings-table {
width: 100%;
min-width: 380px;
border-collapse: collapse;
background: #ffffff;
}
.hb-standings-table th,
.hb-standings-table td {
padding: 4px 8px;
text-align: right;
white-space: nowrap;
vertical-align: middle !important;
line-height: 1;
color: #3b3f48 !important;
font-weight: 400 !important;
font-size: 14px !important;
}
.hb-standings-table tbody tr:not(:last-child) td {
border-bottom: 1px solid #e6e9ef;
}
.hb-standings-table th {
color: var(--muted) !important;
font-size: 11px !important;
font-weight: 800 !important;
text-transform: uppercase;
background: #f7f8fb;
border-bottom: 1px solid #e3e7ef;
position: sticky;
top: 0;
}
.hb-standings-table tbody tr:hover td { background: #f7f8ff; }
.hb-standings-table tr.is-match-team td { background: rgba(52,76,255,.07); }
.hb-standings-table tr.is-match-team:hover td { background: rgba(52,76,255,.12); }
.hb-standings-table tr.is-match-team .hb-standings-pos { box-shadow: inset 3px 0 0 var(--accent); }
.hb-standings-table th:first-child,
.hb-standings-table td:first-child { width: 28px; padding-left: 12px; padding-right: 4px; text-align: left; vertical-align: middle !important; }
.hb-standings-table td:first-child { color: var(--muted) !important; font-weight: 700 !important; }
.hb-standings-table th:nth-child(2),
.hb-standings-table td:nth-child(2) { width: auto; padding-left: 10px; padding-top: 6px; padding-bottom: 6px; text-align: left; vertical-align: middle !important; }
.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: 30px; }
.hb-standings-table td:nth-child(3),
.hb-standings-table td:nth-child(4),
.hb-standings-table td:nth-child(5),
.hb-standings-table td:nth-child(6) { color: #3b3f48 !important; font-weight: 400 !important; font-size: 14px !important; }
.hb-standings-table th:nth-child(7),
.hb-standings-table td:nth-child(7) { width: 42px; }
.hb-standings-table td:nth-child(7) { color: #3b3f48 !important; font-weight: 400 !important; font-size: 14px !important; }
.hb-standings-table th:nth-child(8),
.hb-standings-table td:nth-child(8) { width: 38px; padding-right: 12px; }
.hb-standings-table td.hb-standings-points { color: #3b3f48 !important; font-size: 14px !important; font-weight: 400 !important; }
.hb-standings-team { white-space: nowrap; margin-top: 5px; }
.hb-standings-logo { display: inline-block !important; vertical-align: middle !important; width: 25px !important; height: 25px !important; margin-right: 8px; overflow: hidden; }
.hb-standings-logo img { width: 100%!important; height: 100%!important; max-width: 100%!important; max-height: 100%!important; object-fit: contain; vertical-align: top; display: block !important; }
.hb-standings-team span { display: inline-block !important; vertical-align: middle !important; overflow: hidden; max-width: calc(100% - 33px); white-space: nowrap; text-overflow: ellipsis; font-weight: 650; }
/* 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:100%; margin:16px auto 0; }
#hb-pointbypoint-root{ max-width:100%; margin:16px auto; }
.hb-debug-controls{ max-width:100%; 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-x:auto; }
.hb-tab{
flex:1 1 0;
min-width:0;
min-height:48px;
padding:10px 3px 7px;
font-size:11px;
letter-spacing:0;
line-height:1.15;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}
.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{ min-width: 0; }
.hb-standings-table th,
.hb-standings-table td{ padding-top:3px; padding-bottom:3px; padding-left:3px; padding-right:3px; }
.hb-standings-table td{ font-size:11px !important; }
.hb-standings-table th:first-child,
.hb-standings-table td:first-child{ width:18px; padding-left:6px; padding-right:2px; }
.hb-standings-table th:nth-child(2),
.hb-standings-table td:nth-child(2){ padding-left:6px; }
.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:20px; }
.hb-standings-table th:nth-child(7),
.hb-standings-table td:nth-child(7){ width:30px; }
.hb-standings-table th:nth-child(8),
.hb-standings-table td:nth-child(8){ width:26px; padding-right:6px; }
.hb-standings-logo{ width:20px !important; height:20px !important; margin-right:6px; }
.hb-standings-team span{ max-width: calc(100% - 26px); }
}
/* 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, homeId, awayId)}
${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, homeId, awayId) {
if (!Array.isArray(standings) || standings.length === 0) {
return '';
}
const matchTeamIds = new Set([homeId, awayId].filter(id => id != null).map(String));
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
? ` `
: '';
const isMatchTeam = matchTeamIds.has(teamId);
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);
}
});