Plympton Flyers at a Sunday meet
Plympton Flyers Little Athletics · 2026/27

Season Calendar

Every Sunday meet, championship and special event for the Summer and Winter seasons.
Legend Home meet at Hamilton State event Special (Awards, Mini Olympics) Cancelled
Season
'+ '
Season complete
'+ '
See you next season
'+ '
'+ICON_PLUS+'The 2027/28 Summer Season starts October 2027
'+ ''+ '
'; return; } var next = upcoming[0]; var isToday = sameDay(next._d, TODAY); var isStateOrSpecial = next.type==='state' || next.type==='special'; var meta=[]; if (next.start) meta.push(ICON_CLOCK+next.start); if (next.loc) meta.push(ICON_PIN+next.loc); if (next.program) meta.push(ICON_PROG+next.program); mount.innerHTML = '
'+ '
'+fmtMonthShort(next._d)+'
'+fmtDayNum(next._d)+'
'+fmtDow(next._d,false)+'
'+ '
'+(isToday?'Happening today':'Next meet')+' · '+fmtDow(next._d,true)+' '+fmtDayNum(next._d)+' '+MONTHS[next._d.getMonth()]+'
'+ '
'+meetTitle(next)+'
'+ '
'+meta.map(function(x){return '
'+x+'
';}).join('')+'
'+ ''+ '
'; var b = mount.querySelector('[data-add-cal]'); if (b) b.addEventListener('click', function(){ downloadIcs([next]); }); } function renderMeets(){ var mount = ROOT.querySelector('#meets-mount'); var filtered = MEETS.filter(function(m){return m.season===currentSeason;}) .filter(function(m){return currentFilter==='all'||m.type===currentFilter;}) .map(function(m){var o=Object.assign({},m);o._d=parseLocal(m.date);return o;}) .sort(function(a,b){return a._d-b._d;}); var inSeason = MEETS.filter(function(m){return m.season===currentSeason;}); function setCount(k,v){ var el=ROOT.querySelector('[data-count="'+k+'"]'); if(el) el.textContent=v; } setCount('all',inSeason.length); setCount('home',inSeason.filter(function(m){return m.type==='home';}).length); setCount('state',inSeason.filter(function(m){return m.type==='state';}).length); setCount('special',inSeason.filter(function(m){return m.type==='special';}).length); setCount('cancelled',inSeason.filter(function(m){return m.type==='cancelled';}).length); if (inSeason.length===0){ mount.innerHTML='
The Winter 2027 cross country calendar is being finalised — dates will be announced here soon.
'; return; } if (filtered.length===0){ mount.innerHTML='
No meets match this filter for the '+(currentSeason==='summer'?'Summer':'Winter')+' season.
'; return; } var html='', lastMonthKey=null, lastMeet=null; filtered.forEach(function(m){ if (lastMeet){ var gap=(m._d-lastMeet._d)/(1000*60*60*24); if (gap>21 && currentFilter==='all'){ html+='
'+ICON_STAR+'
'+ '
Holiday Break
'+ '
No meets between '+fmtDow(lastMeet._d,true)+' '+fmtDayNum(lastMeet._d)+' '+MONTHS[lastMeet._d.getMonth()]+' and '+fmtDow(m._d,true)+' '+fmtDayNum(m._d)+' '+MONTHS[m._d.getMonth()]+'
'; } } var monthKey=m._d.getFullYear()+'-'+m._d.getMonth(); if (monthKey!==lastMonthKey){ if (lastMonthKey!==null) html+='
'; var mm=filtered.filter(function(x){return x._d.getFullYear()===m._d.getFullYear()&&x._d.getMonth()===m._d.getMonth();}); html+='
'+MONTHS[m._d.getMonth()]+'
'+ '
'+m._d.getFullYear()+'
'+mm.length+' '+(mm.length===1?'event':'events')+'
'; lastMonthKey=monthKey; } var isToday=sameDay(m._d,TODAY); var classes=['meet-card','is-'+m.type]; if(isToday) classes.push('is-today'); var dateStr=m.twoDay ? fmtDayNum(m._d)+'–'+(parseLocal(m.endDate)).getDate() : ''+fmtDayNum(m._d); var meta=''; if (m.type!=='cancelled'){ meta='
'+ (m.start?'
'+ICON_CLOCK+m.start+'
':'')+ (m.loc?'
'+ICON_PIN+m.loc+'
':'')+ (m.program&&m.type!=='special'?'
'+ICON_PROG+m.program+'
':'')+ '
'; } else { meta='
'+(m.note||'')+'
'; } html+='
'+dateStr+'
'+ '
'+(m.twoDay?'Sat / Sun':fmtDow(m._d,false))+'
'+ (isToday?'
Today
':'')+'
'+ '
'+meetTagLabel(m)+''+ '
'+meetTitle(m)+'
'+meta+'
'+ (m.type!=='cancelled'?'
':'')+ '
'; lastMeet=m; }); if (lastMonthKey!==null) html+='
'; mount.innerHTML=html; mount.querySelectorAll('[data-add-cal]').forEach(function(btn){ btn.addEventListener('click', function(){ var d=btn.getAttribute('data-add-cal'); var m=MEETS.filter(function(x){return x.date===d;}); if (m.length) downloadIcs(m); }); }); } function pad(n){ return String(n).padStart(2,'0'); } function icsDate(d){ return d.getFullYear()+pad(d.getMonth()+1)+pad(d.getDate()); } function icsDateTime(d){ return icsDate(d)+'T'+pad(d.getHours())+pad(d.getMinutes())+'00'; } function buildIcs(meets){ var lines=['BEGIN:VCALENDAR','VERSION:2.0','PRODID:-//Plympton Flyers//Season Calendar//EN','CALSCALE:GREGORIAN']; meets.forEach(function(m,i){ if (m.type==='cancelled') return; var start=parseLocal(m.date); var t=(m.start||'09:00').split(':').map(Number); start.setHours(t[0],t[1],0); var end=new Date(start); end.setHours(start.getHours()+3); lines.push('BEGIN:VEVENT'); lines.push('UID:ply-'+m.date+'-'+i+'@mirevixa.website'); lines.push('DTSTAMP:'+icsDateTime(new Date())+'Z'); lines.push('DTSTART:'+icsDateTime(start)); lines.push('DTEND:'+icsDateTime(end)); lines.push('SUMMARY:PLY · '+meetTitle(m)); if (m.loc) lines.push('LOCATION:'+m.loc); lines.push('END:VEVENT'); }); lines.push('END:VCALENDAR'); return lines.join('\r\n'); } function downloadIcs(meets){ var blob=new Blob([buildIcs(meets)],{type:'text/calendar'}); var url=URL.createObjectURL(blob); var a=document.createElement('a'); a.href=url; a.download=meets.length===1?'ply-'+meets[0].date+'.ics':'ply-season-'+currentSeason+'.ics'; document.body.appendChild(a); a.click(); setTimeout(function(){ URL.revokeObjectURL(url); a.remove(); },100); } ROOT.querySelectorAll('.season-tab').forEach(function(t){ t.addEventListener('click', function(){ ROOT.querySelectorAll('.season-tab').forEach(function(x){x.classList.remove('active');}); t.classList.add('active'); currentSeason=t.getAttribute('data-season'); renderMeets(); }); }); ROOT.querySelectorAll('.chip').forEach(function(c){ c.addEventListener('click', function(){ ROOT.querySelectorAll('.chip').forEach(function(x){x.classList.remove('active');}); c.classList.add('active'); currentFilter=c.getAttribute('data-filter'); renderMeets(); }); }); ROOT.querySelector('[data-export-season]').addEventListener('click', function(){ downloadIcs(MEETS.filter(function(m){return m.season===currentSeason && m.type!=='cancelled';})); }); renderNextCard(); renderMeets(); })();