Connect with us

India vs Sri Lanka Live Score - Live Cricket Scorecard IND vs SL, 2nd Semi-Final, Asian Games Men’s T20I, 2026, Korogi Sports Park, Nisshin

India vs Sri Lanka Live Score - Live Cricket Scorecard IND vs SL, 2nd Semi-Final, Asian Games Men’s T20I, 2026, Korogi Sports Park, Nisshin
Loading…

${esc(inn.batting_team)}

${inn.total}/${inn.wickets} (${inn.overs})

`;
t.onclick = () => switchInnings(i);
c.appendChild(t);
});
}

function switchInnings(idx) {
selInnings = idx; selRole = 'batsman'; selPlayer = 0;
inningsData.forEach((_,i) =>
document.getElementById(`inn-tab-${i}`)?.classList.toggle('active', i===idx));
document.getElementById('btn-bat').classList.add('active');
document.getElementById('btn-bowl').classList.remove('active');
updateMatchHeader(inningsData[idx]);
buildChips();
renderCurrentPlayer();
}

function updateMatchHeader(inn) {
const _b = document.getElementById('mh-badge'); if(_b) _b.textContent = inn.innings_label;
document.getElementById('mh-sub').textContent = inn.innings_label;
document.getElementById('mh-score').textContent = inn.total;
document.getElementById('mh-wkts').textContent = inn.wickets;
document.getElementById('mh-overs').textContent = inn.overs;
// document.getElementById('mh-innings-tag').textContent = inn.innings_label;
document.getElementById('mh-rr-tag').textContent = `RR ${parseFloat(inn.run_rate||0).toFixed(2)}`;
}

function switchRole(role) {
selRole = role; selPlayer = 0;
document.getElementById('btn-bat').classList.toggle('active', role==='batsman');
document.getElementById('btn-bowl').classList.toggle('active', role==='bowler');
buildChips();
renderCurrentPlayer();
}

function buildChips() {
const players = getPlayers();

// Clamp selected index in case the list shrank
if (selPlayer >= players.length) selPlayer = 0;

sigChips = chipsSig(players);
const row = document.getElementById('chip-row');
row.innerHTML = '';
players.forEach((p, i) => {
const c = document.createElement('div');
c.className = 'chip' + (i === selPlayer ? ' active' : '');
c.textContent = p.short_name || p.name.split(' ').pop();
c.onclick = () => { selPlayer = i; buildChips(); clearTimeout(chipDebounce); chipDebounce = setTimeout(renderCurrentPlayer, 100); };
row.appendChild(c);
});
}

function getPlayers() {
const inn = inningsData[selInnings];
if (!inn) return [];
const list = (selRole==='batsman' ? inn.top_batsmen : inn.top_bowlers) || [];
if (selRole !== 'bowler') return list;
// Bowlers with no name or no deliveries bowled are never shown as chips —
// filter in one place so chip index i always maps to the same player here.
return list.filter(p =>
(p.short_name || p.name || '').trim().length > 0 && oversToBalls(p.overs || 0) > 0);
}

function renderCurrentPlayer() {
const players = getPlayers();
if (!players?.length) return;
const p = players[selPlayer] || players[0];
sigPlayer = playerSig(p);
selRole==='batsman' ? renderBatsman(p) : renderBowler(p);
}

/* ── BATSMAN VIEW ─────────────────────────────────────────────── */
function renderBatsman(p) {
clearTimers(); resetGlow();

document.getElementById('hero-role-lbl').textContent = 'Batsman Stats';
document.getElementById('h-name').textContent = p.name;
document.getElementById('h-sub').textContent = p.how_out || 'Batting';
document.getElementById('h-big-unit').textContent = '';
document.getElementById('h-balls').textContent = `${p.balls}\u00A0balls faced`;
countUp(document.getElementById('h-big'), p.runs, 750);

const srColor = p.strike_rate >= 140 ? 'var(--green)'
: p.strike_rate >= 100 ? 'var(--amber)' : 'var(--red-neg)';
document.getElementById('h-pills').innerHTML = `

4s: ${p.fours}

6s: ${p.sixes}

SR ${p.strike_rate.toFixed(1)}

`;

const s = p.scoring;
/* Runs-based breakdown: every slice is the RUNS contributed, so slice %
= share of total runs. Dot balls contribute 0 runs, so they aren't in the
ring — the "Dot ball %" tile and stat bars carry them instead. */
renderDonut(
[s.fours_runs != null ? s.fours_runs : p.fours*4,
s.sixes_runs != null ? s.sixes_runs : p.sixes*6,
s.singles, s.twos*2, s.threes*3],
['4s','6s','Singles','Twos','Threes'],
['#3b9eff','#a78bfa','#34d399','#0d9488','#f59e0b'],
p.runs, 'Runs', 'Scoring breakdown',
`Strike rate:  ${p.strike_rate.toFixed(0)}`
);

showBowlExtras(false);
document.getElementById('stat-bars-wrap').style.display = '';
setText('meter-card-lbl','Strike rate meter');
setText('meter-unit','/ 600');
document.getElementById('meter-ticks').innerHTML =
'

';
setMeter(p.strike_rate, 600,
p.strike_rate>=140 ? 'linear-gradient(90deg,#16a34a,#22c55e)'
: p.strike_rate>=100 ? 'linear-gradient(90deg,#b45309,#f59e0b)'
: 'linear-gradient(90deg,#8a1a16,#d80d11)');
document.getElementById('meter-num').textContent = p.strike_rate.toFixed(0);

const dots = p.dots || s.dots || 0;
const dotPct = p.balls>0 ? Math.round(dots/p.balls*100) : 0;
const bdyRuns= p.fours*4 + p.sixes*6;
const bdyPct = p.runs>0 ? Math.round(bdyRuns/p.runs*100) : 0;
const _mz = document.getElementById('momentum-zone');
_mz.style.opacity = '1'; _mz.style.pointerEvents = '';
setMzTile('mz1','mz1-val','mz1-lbl', p.fours+p.sixes, 'Boundaries', false,
(p.fours+p.sixes) >= 5 ? 'highlight' : '');
setMzTile('mz2','mz2-val','mz2-lbl', dotPct+'%', 'Dot ball %', false,
dotPct >= 40 ? 'highlight' : dotPct = 60 ? 'positive' : '');

const seq = p.seq || [];

setText('stat-bars-lbl','Ball-by-ball breakdown');
setBarLabels('Boundaries','Singles / 2s','Dot balls');
setBarColors('var(--four)','var(--two)','var(--dot-ball)');
const tot = Math.max(p.balls,1);
setSbar('sb1','sv1', p.fours+p.sixes, tot);
setSbar('sb2','sv2', s.singles+s.twos, tot);
setSbar('sb3','sv3', dots, tot);

const last = seq.slice(-12).filter(v=>v!=='W').reduce((a,v)=>a+(typeof v==='number'?v:0),0);
setInfoTiles([
{val:last, lbl:'Last 12 balls'},
{val:dotPct+'%', lbl:'Dot ball %'},
{val:bdyPct+'%', lbl:'Boundary %'},
]);

// This block runs a tick later, after the caller has cleared SILENT —
// carry the flag across so a live refresh still renders without animating.
const _silent = SILENT;
const _tb = setTimeout(() => {
const _prev = SILENT; SILENT = _silent;
setText('wagon-lbl','Wagon wheel');
document.getElementById('wagon-legend').style.display='';
renderWagon(p.wagon||[], 'batsman');
setText('timeline-lbl','Last 12 balls');
renderTimeline(seq.slice(-12), false);
SILENT = _prev;
}, 0);
animTimers.push(_tb);
}

/* ── BOWLER VIEW ──────────────────────────────────────────────── */
function renderBowler(p) {
console.log('Rendering bowler:', p);
clearTimers(); resetGlow();

/* ── ECONOMY: recalculate from raw runs + overs using correct
ball-count conversion instead of trusting p.economy which
may have been computed as runs / overs_decimal ── */
const economy = calcEconomy(p.runs_conceded, p.overs);

document.getElementById('hero-role-lbl').textContent = 'Bowler Stats';
document.getElementById('h-name').textContent = p.name;
document.getElementById('h-sub').textContent = `${p.overs}\u00A0overs · ${p.runs_conceded}\u00A0runs`;
/* 1 wicket is singular everywhere it's labelled - short form in the hero
unit + pill, full word in the stat grid and info tiles. */
const wktUnit = Number(p.wickets) === 1 ? 'wkt' : 'wkts';
const wktWord = Number(p.wickets) === 1 ? 'Wicket' : 'Wickets';
document.getElementById('h-big-unit').textContent = ' ' + wktUnit;
document.getElementById('h-balls').textContent =
`${oversToBalls(p.overs)}\u00A0Deliveries`;
countUp(document.getElementById('h-big'), p.wickets, 600);

const ecoColor = economy${p.wickets} ${wktUnit}

Eco: ${economy.toFixed(2)}

Mdn: ${p.maidens}

`;

const bs = p.bowling_stats;

/* Legal-delivery breakdown (ball basis): the slices partition the legal
balls bowled, so every slice % is share-of-legal-balls — including the
Dot balls slice, and matching the "Dot ball %" tile / stat bars, which use
the same denominator. "Others" is the remainder (1s/2s/3s etc.), derived
here so the ring can't drift from the dots count (different server source).
Extras (wides/no-balls) aren't legal balls, so they're not in the ring. */
const dLegal = Math.max(oversToBalls(p.overs), 1);
const dDots = bs.dots || 0;
const dFours = bs.fours || 0;
const dSixes = bs.sixes || 0;
const dOther = Math.max(0, dLegal - dDots - dFours - dSixes);
renderDonut(
[dDots, dFours, dSixes, dOther],
['Dot balls','4s conceded','6s conceded','Others'],
['#94A3B8','#3b9eff','#a78bfa','#34d399'],
p.runs_conceded, 'Runs', 'Bowling breakdown',
`Economy:  ${economy.toFixed(2)}`
);

showBowlExtras(true);
const _mz = document.getElementById('momentum-zone');
_mz.style.opacity = '0'; _mz.style.pointerEvents = 'none';

document.getElementById('stat-bars-wrap').style.display = '';
setText('meter-card-lbl', 'Economy meter');
setText('meter-unit', '/ 24');
document.getElementById('meter-ticks').innerHTML =
'

';
setMeter(economy, 24,
economy 0 ? Math.round((bs.dots / legal) * 100) : 0;
const bdyConcPct = p.runs_conceded > 0
? Math.round((((bs.fours||0)*4 + (bs.sixes||0)*6) / p.runs_conceded) * 100) : 0;
setInfoTiles([
{ val: p.wickets, lbl: wktWord },
{ val: dotPct + '%', lbl: 'Dot ball %' },
{ val: bdyConcPct+'%', lbl: 'Boundary %' },
]);

// See renderBatsman: SILENT is already cleared by the time this fires.
const _silent = SILENT;
const _tw = setTimeout(() => {
const _prev = SILENT; SILENT = _silent;
setText('wagon-lbl', 'Runs Conceded Map');
document.getElementById('wagon-legend').style.display = '';
renderWagon(p.wagon || [], 'bowler');
setText('timeline-lbl', 'Last 12 deliveries');
renderTimeline((p.seq || []).slice(-12), true, (p.over_seq||[]).slice(-12));
SILENT = _prev;
}, 0);
animTimers.push(_tw);
}

/* ── DONUT ──────────────────────────────────────────────────────── */
function renderDonut(data, labels, colors, cVal, cLbl, title, topLine) {
setText('donut-label', title);
setText('donut-val', cVal);
setText('donut-lbl', cLbl);
const srEl = document.getElementById('donut-sr');
srEl.style.display = topLine ? '' : 'none';
srEl.innerHTML = topLine || '';
const total = data.reduce((s,v)=>s+(v||0), 0);
const pct = data.map(v => total > 0 ? Math.round((v||0)/total*100) : 0);

if (donutInst) {
donutInst.data.labels = labels;
donutInst.data.datasets[0].data = data;
donutInst.data.datasets[0].backgroundColor = colors;
donutInst.options.plugins.tooltip.callbacks.label = c => ` ${c.label}: ${pct[c.dataIndex]}%`;
donutInst.update('active');
} else {
donutInst = new Chart(document.getElementById('donut-chart'), {
type:'doughnut',
data:{ labels, datasets:[{ data, backgroundColor:colors, borderWidth:0, hoverOffset:5, borderRadius:2 }] },
options:{
responsive:true, maintainAspectRatio:true, cutout:'68%',
layout:{ padding:10 },
animation:{ animateRotate:true, duration:800 },
plugins:{
legend:{display:false},
tooltip:{
callbacks:{ label:c=>` ${c.label}: ${pct[c.dataIndex]}%` },
backgroundColor:'#12445D', titleColor:'#F8F9FB',
bodyColor:'#8794A7', borderColor:'#084971', borderWidth:1,
}
}
}
});
}
document.getElementById('donut-legend').innerHTML = labels.map((l,i)=>
`

`
).join('');
}

/* ── WAGON WHEEL ────────────────────────────────────────────────── */
function renderWagon(wagon, role) {
const linesG = document.getElementById('wagon-lines');
const dotsG = document.getElementById('wagon-dots');
linesG.innerHTML = dotsG.innerHTML = '';

if (!wagon || wagon.length===0) {
const txt = document.createElementNS('http://www.w3.org/2000/svg','text');
txt.setAttribute('x','100'); txt.setAttribute('y','108');
txt.setAttribute('text-anchor','middle');
txt.setAttribute('font-size','9');
txt.setAttribute('fill','#8794A7');
txt.setAttribute('font-family','Inter,sans-serif');
txt.setAttribute('letter-spacing','0.08em');
txt.textContent = 'NO SHOT DATA';
dotsG.appendChild(txt);
return;
}

function wagonColor(shot) {
if (shot.runs === 6 || shot.type === 'six') return '#8B5CF6';
if (shot.runs === 4 || shot.type === 'four') return '#2563EB';
if (shot.runs >= 3) return '#166534';
if (shot.runs === 2) return '#15803D';
if (shot.runs === 1) return '#16A34A';
return '#94A3B8';
}

wagon.forEach((shot,i) => {
const col = shot.color || wagonColor(shot);
const r = shot.radius || 3;
const x2 = shot.x ?? 100;
const y2 = shot.y ?? 100;
if (isNaN(x2) || isNaN(y2)) return;

const line = svgEl('line',{
x1:'100', y1:'100', x2:'100', y2:'100',
stroke:col, 'stroke-width':'1.4', 'stroke-opacity':'.65'
});
linesG.appendChild(line);

const dot = svgEl('circle',{
cx:'100', cy:'100', r:'0',
fill:col, 'fill-opacity':'.95',
stroke:'#fff', 'stroke-width':'0.8'
});
dotsG.appendChild(dot);

// Live refresh: draw the shot where it lands, don't re-fly every dot
if (SILENT) {
line.setAttribute('x2',x2); line.setAttribute('y2',y2);
dot.setAttribute('cx',x2); dot.setAttribute('cy',y2); dot.setAttribute('r',r);
return;
}

const delay = i*70 + 60;
const t = setTimeout(()=>{
let frame=0, frames=16, dur=240;
const iv = setInterval(()=>{
frame++;
const ease = 1-Math.pow(1-frame/frames,3);
const cx = (100+(x2-100)*ease).toFixed(2);
const cy = (100+(y2-100)*ease).toFixed(2);
line.setAttribute('x2',cx); line.setAttribute('y2',cy);
dot.setAttribute('cx',cx); dot.setAttribute('cy',cy);
dot.setAttribute('r',(r*ease).toFixed(2));
if(frame>=frames) clearInterval(iv);
}, dur/frames);
animTimers.push(iv);
}, delay);
animTimers.push(t);
});
}

/* ── BALL TIMELINE ──────────────────────────────────────────────── */
function renderTimeline(seq, isBowler, overSeq) {
const track = document.getElementById('ball-track');
track.innerHTML = '';

if(!seq||seq.length===0){
track.innerHTML='

No ball data

';
return;
}

const hasOvers = overSeq && overSeq.length === seq.length && overSeq.some(o => o >= 0);

seq.forEach((v,i)=>{
if(i > 0) {
const needSep = hasOvers
? overSeq[i] !== overSeq[i-1] && overSeq[i] >= 0 && overSeq[i-1] >= 0
: false;
if(needSep){
const sep=document.createElement('div');
sep.className='ball-over-sep';
track.appendChild(sep);
if(SILENT) sep.classList.add('show');
else animTimers.push(setTimeout(()=>sep.classList.add('show'),i*46+280));
}
}
const ball=document.createElement('div');
ball.className='ball'+(isBowler?' bowler-ball':'');
ball.style.background=isBowler ? bowlerBallColor(v) : batsmanBallColor(v);
if(String(v).length>2) ball.style.fontSize='9px';
ball.textContent=v;
track.appendChild(ball);
// Live refresh: the strip is already on screen, pop it in without the stagger
if(SILENT) ball.classList.add('show');
else animTimers.push(setTimeout(()=>ball.classList.add('show'),i*46+120));
});
}

function batsmanBallColor(v) {
if(v==='W') return '#D80D11';
if(v===6) return '#7c3aed';
if(v===4) return '#1d6fb8';
if(v===2) return '#0d7a69';
if(v===1) return '#166534';
return '#2E3340';
}

function bowlerBallColor(v) {
if(v==='W') return '#8A1A16';
if(typeof v==='string'){
if(v.indexOf('wd')>-1) return '#64748B';
if(v.indexOf('nb')>-1) return '#BE185D';
}
if(typeof v==='number'){
if(v>=6) return '#7c3aed';
if(v>=4) return '#b91c1c';
if(v>=2) return '#b45309';
if(v===1) return '#166534';
return '#2E3340';
}
return '#2E3340';
}

/* ── MOMENTUM TILE HELPER ────────────────────────────────────────── */
function setMzTile(tileId, valId, lblId, val, lbl, _, cls) {
const tile = document.getElementById(tileId);
tile.className = 'mz-tile' + (cls ? ' '+cls : '');
document.getElementById(valId).textContent = val;
document.getElementById(lblId).textContent = lbl;
}

/* ── UI HELPERS ──────────────────────────────────────────────────── */
function setMeter(val, max, grad) {
const pct=Math.min(val/max*100,100).toFixed(1);
const f=document.getElementById('meter-fill');
f.style.width='0'; f.style.background=grad;
defer(()=>{ f.style.width=pct+'%'; });
}

function setSbar(fId,vId,val,tot) {
const pct=tot>0?Math.min(val/tot*100,100).toFixed(1):0;
document.getElementById(fId).style.width='0';
document.getElementById(vId).textContent=val;
defer(()=>{ document.getElementById(fId).style.width=pct+'%'; });
}

function setBarLabels(l1,l2,l3){
setText('lb1',l1); setText('lb2',l2); setText('lb3',l3);
}
function setBarColors(c1,c2,c3){
document.getElementById('sb1').style.background=c1;
document.getElementById('sb2').style.background=c2;
document.getElementById('sb3').style.background=c3;
}
function showBowlExtras(show){
const el = document.getElementById('bowl-extra');
el.style.opacity = show ? '1' : '0';
el.style.pointerEvents = show ? '' : 'none';
}
function setInfoTiles(tiles){
document.getElementById('info-row').innerHTML=tiles.map(t=>
`

`
).join('');
}
function setText(id,v){ const el=document.getElementById(id); if(el) el.textContent=v; }

function countUp(el,target,dur){
// On a live refresh tick up from what's already on screen instead of
// replaying the whole 0→target animation.
const start = SILENT ? (parseFloat(el.textContent) || 0) : 0;
if(start===target){ el.textContent=target; return; }
el.textContent=start;
const step=16,inc=(target-start)/(dur/step);
let val=start;
const t=setInterval(()=>{
val = inc>=0 ? Math.min(val+inc,target) : Math.max(val+inc,target);
el.textContent=Math.round(val);
if(val===target) clearInterval(t);
},step);
animTimers.push(t);
}

function resetGlow(){
const hc=document.getElementById('hero-card');
hc.style.animation='none'; hc.offsetHeight; hc.style.animation='';
}
function defer(fn){ const t=setTimeout(fn,55); animTimers.push(t); }
function clearTimers(){
animTimers.forEach(t=>(typeof t==='number'?clearTimeout:clearInterval)(t));
animTimers=[];
}
function svgEl(tag,attrs){
const el=document.createElementNS('http://www.w3.org/2000/svg',tag);
Object.entries(attrs).forEach(([k,v])=>el.setAttribute(k,v));
return el;
}
function esc(s){
return String(s||'')
.replace(/&/g,'&').replace(//g,'>');
}
function showWidget(){
document.getElementById('widget-skeleton').style.display='none';
document.getElementById('widget').style.display='flex';
}
function showError(msg){
document.getElementById('app-loader').style.display='none';
setText('err-msg-txt',msg);
document.getElementById('error-state').style.display='flex';
}

init();

More in Cricket