moved the javascrpit to its own file and moved the json to its own file
This commit is contained in:
1 parent
ac08a5b7e5
commit
38f38c89fe
3 files changed
+2424
-301
No files matched your search
+2114
File diff suppressed because it is too large.
Load diff
+309
@@ -0,0 +1,309 @@
|
||||
// ─── DATA ───────────────────────────────────────────────────
|
||||
const RAW_DATA = [{"call":"WE2M","name":"ROBERT A HOGUE","date":"2021-01-24","time":"0553","band":"40M","mode":"FT8","rst_sent":"-20","rst_rcvd":"-10","country":"United States","state":"WA","qth":"E WENATCHEE","lotw":"Y","dist":"2782","grid":"CN97UJ"},{"call":"WD8AXA","name":"CHARLES D AGOSTI","date":"2021-01-24","time":"0555","band":"40M","mode":"FT8","rst_sent":"-07","rst_rcvd":"-11","country":"United States","state":"MI","qth":"PORTAGE","lotw":"N","dist":"1291","grid":"EN72ef"},{"call":"KG4IXS","name":"RICHARD A VAUGHAN","date":"2021-01-24","time":"0558","band":"40M","mode":"FT8","rst_sent":"+00","rst_rcvd":"-13","country":"United States","state":"VA","qth":"CHATHAM","lotw":"Y","dist":"1390","grid":"FM06HU"},{"call":"KE0DC","name":"","date":"2021-01-24","time":"0600","band":"40M","mode":"FT8","rst_sent":"-08","rst_rcvd":"-09","country":"United States","state":"CO","qth":"AURORA","lotw":"Y","dist":"1274","grid":"DM79OQ"},{"call":"KC0HHN","name":"","date":"2021-01-24","time":"0604","band":"40M","mode":"FT8","rst_sent":"-12","rst_rcvd":"-08","country":"United States","state":"IA","qth":"","lotw":"N","dist":"","grid":"EN31"},{"call":"WB0OEW","name":"JAMES D VOGT","date":"2021-01-24","time":"0606","band":"40M","mode":"FT8","rst_sent":"-10","rst_rcvd":"-15","country":"United States","state":"MO","qth":"CAMDENTON","lotw":"Y","dist":"747","grid":"EM37TH"},{"call":"N4JKO","name":"GARY E HORTON","date":"2021-01-24","time":"0608","band":"40M","mode":"FT8","rst_sent":"-12","rst_rcvd":"-15","country":"United States","state":"TN","qth":"","lotw":"N","dist":"","grid":"EM65RT"},{"call":"WW4LL","name":"LESTER P LEEPER","date":"2021-01-24","time":"0610","band":"40M","mode":"FT8","rst_sent":"-14","rst_rcvd":"-13","country":"United States","state":"GA","qth":"BOWDON","lotw":"Y","dist":"575","grid":"EM73DS"},{"call":"K0MBT","name":"MICHAEL B TEDESCO","date":"2021-01-24","time":"0612","band":"40M","mode":"FT8","rst_sent":"-09","rst_rcvd":"-18","country":"United States","state":"MN","qth":"SHAKOPEE","lotw":"Y","dist":"1247","grid":"EN34HE"},{"call":"WB4LHD","name":"ROY A MCDANIEL","date":"2021-01-24","time":"0618","band":"40M","mode":"FT8","rst_sent":"-14","rst_rcvd":"-12","country":"United States","state":"NC","qth":"SANFORD","lotw":"Y","dist":"1118","grid":"FM03HF"},{"call":"N0KJA","name":"FRANK O KORANDA","date":"2021-01-24","time":"0620","band":"40M","mode":"FT8","rst_sent":"-10","rst_rcvd":"-14","country":"United States","state":"KS","qth":"WICHITA","lotw":"Y","dist":"641","grid":"EM17PT"},{"call":"W9JML","name":"JAMES M LAURENDEAU","date":"2021-01-24","time":"0623","band":"40M","mode":"FT8","rst_sent":"-05","rst_rcvd":"-16","country":"United States","state":"IL","qth":"COLLINSVILLE","lotw":"Y","dist":"668","grid":"EM58GI"},{"call":"K9ADV","name":"DWIGHT A VANDERLINDEN","date":"2021-01-24","time":"0625","band":"40M","mode":"FT8","rst_sent":"-11","rst_rcvd":"-13","country":"United States","state":"IN","qth":"INDIANAPOLIS","lotw":"Y","dist":"929","grid":"EM79MF"},{"call":"WB8YYY","name":"ERNEST E MUENZER","date":"2021-01-24","time":"0628","band":"40M","mode":"FT8","rst_sent":"-10","rst_rcvd":"-05","country":"United States","state":"MI","qth":"","lotw":"Y","dist":"","grid":"EN72"},{"call":"KD2UKS","name":"RAYMOND JOSEPH MCDERMOTT","date":"2021-01-24","time":"0641","band":"40M","mode":"FT8","rst_sent":"-12","rst_rcvd":"-15","country":"United States","state":"NY","qth":"MASTIC","lotw":"N","dist":"1561","grid":"FN30HT"},{"call":"KD8IYI","name":"JEFFREY C WHITLEY","date":"2021-01-24","time":"0644","band":"40M","mode":"FT8","rst_sent":"-13","rst_rcvd":"-13","country":"United States","state":"OH","qth":"","lotw":"Y","dist":"","grid":"EM89LG"},{"call":"K9DW","name":"DAN WELBORN","date":"2021-01-24","time":"0647","band":"40M","mode":"FT8","rst_sent":"-09","rst_rcvd":"-06","country":"United States","state":"AL","qth":"DOTHAN","lotw":"Y","dist":"435","grid":"EM64SA"},{"call":"K8DO","name":"DEAN L OSTERMAN","date":"2021-01-24","time":"0649","band":"40M","mode":"FT8","rst_sent":"-11","rst_rcvd":"-10","country":"United States","state":"MI","qth":"ALLENDALE","lotw":"Y","dist":"1298","grid":"EN62OX"},{"call":"K6VMV","name":"VICTOR M VALADEZ","date":"2021-01-24","time":"0651","band":"40M","mode":"FT8","rst_sent":"-08","rst_rcvd":"-12","country":"United States","state":"CA","qth":"MANTECA","lotw":"N","dist":"1954","grid":"CM97KC"},{"call":"W0ULF","name":"MIKE WOLFE","date":"2021-01-24","time":"0653","band":"40M","mode":"FT8","rst_sent":"-11","rst_rcvd":"-13","country":"United States","state":"NE","qth":"OMAHA","lotw":"Y","dist":"1012","grid":"EN21WC"}];
|
||||
|
||||
let ALL_DATA = [];
|
||||
let filteredData = [];
|
||||
let sortKey = 'date';
|
||||
let sortDir = -1; // -1 = desc
|
||||
let currentPage = 1;
|
||||
const PAGE_SIZE = 50;
|
||||
|
||||
// ─── FULL LOG ────────────────────────────────────────────────
|
||||
const FULL_LOG_URL = 'data/k5bss-log.json';
|
||||
|
||||
async function loadFullLog() {
|
||||
try {
|
||||
const res = await fetch(FULL_LOG_URL);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return await res.json();
|
||||
} catch (err) {
|
||||
console.warn('Could not load full log, using sample data:', err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function init() {
|
||||
const fullLog = await loadFullLog();
|
||||
ALL_DATA = fullLog.length ? fullLog : RAW_DATA;
|
||||
filteredData = [...ALL_DATA];
|
||||
|
||||
// Update stats
|
||||
const countries = new Set(ALL_DATA.map(r => r.country).filter(Boolean));
|
||||
const states = new Set(ALL_DATA.filter(r => r.country === 'United States').map(r => r.state).filter(Boolean));
|
||||
document.getElementById('stat-dxcc').textContent = countries.size;
|
||||
document.getElementById('stat-states').textContent = states.size;
|
||||
|
||||
buildDashboard();
|
||||
populateFilters();
|
||||
renderTable();
|
||||
}
|
||||
|
||||
// ─── DASHBOARD ───────────────────────────────────────────────
|
||||
function buildDashboard() {
|
||||
// Modes
|
||||
const modes = countBy(ALL_DATA, 'mode');
|
||||
const topModes = Object.entries(modes).sort((a,b)=>b[1]-a[1]).slice(0,7);
|
||||
const maxMode = topModes[0][1];
|
||||
const modesEl = document.getElementById('modes-chart');
|
||||
const modeColors = { FT8:'#ffaa00', FT4:'#ffcc44', DIG:'#ff8800', USB:'#4499ff', LSB:'#66aaff', SSB:'#88bbff', RTTY:'#ff4455' };
|
||||
modesEl.innerHTML = topModes.map(([m, c]) => `
|
||||
<div class="bar-row">
|
||||
<div class="bar-label">${m}</div>
|
||||
<div class="bar-track"><div class="bar-fill" style="width:0%;background:${modeColors[m]||'#888'}" data-target="${(c/maxMode*100).toFixed(1)}"></div></div>
|
||||
<div class="bar-count">${c}</div>
|
||||
</div>`).join('');
|
||||
|
||||
// Bands
|
||||
const bands = countBy(ALL_DATA, 'band');
|
||||
const topBands = Object.entries(bands).sort((a,b)=>b[1]-a[1]).slice(0,8);
|
||||
const maxBand = topBands[0][1];
|
||||
const bandEl = document.getElementById('bands-chart');
|
||||
const bandColors = { '20M':'#4499ff','40M':'#00ff88','80M':'#ff4455','10M':'#00ddff','30M':'#88aaff','17M':'#aaddff','15M':'#ffaaff','12M':'#ffdd88' };
|
||||
bandEl.innerHTML = topBands.map(([b, c]) => `
|
||||
<div class="bar-row">
|
||||
<div class="bar-label">${b}</div>
|
||||
<div class="bar-track"><div class="bar-fill" style="width:0%;background:${bandColors[b]||'#888'}" data-target="${(c/maxBand*100).toFixed(1)}"></div></div>
|
||||
<div class="bar-count">${c}</div>
|
||||
</div>`).join('');
|
||||
|
||||
// Countries
|
||||
const countries = countBy(ALL_DATA, 'country');
|
||||
const topCountries = Object.entries(countries).sort((a,b)=>b[1]-a[1]).slice(0,20);
|
||||
const ctEl = document.getElementById('countries-list');
|
||||
ctEl.innerHTML = topCountries.map(([c,n]) => `
|
||||
<div class="country-row">
|
||||
<span class="country-name">${c||'Unknown'}</span>
|
||||
<span class="country-count">${n}</span>
|
||||
</div>`).join('');
|
||||
|
||||
// Animate bars after a tick
|
||||
requestAnimationFrame(() => {
|
||||
setTimeout(() => {
|
||||
document.querySelectorAll('.bar-fill').forEach(el => {
|
||||
el.style.width = el.dataset.target + '%';
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// Activity chart
|
||||
drawActivityChart();
|
||||
}
|
||||
|
||||
function drawActivityChart() {
|
||||
const canvas = document.getElementById('activityCanvas');
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const W = canvas.parentElement.offsetWidth - 48;
|
||||
const H = 120;
|
||||
canvas.width = W * dpr;
|
||||
canvas.height = H * dpr;
|
||||
canvas.style.width = W + 'px';
|
||||
canvas.style.height = H + 'px';
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.scale(dpr, dpr);
|
||||
|
||||
// Group by year-month
|
||||
const monthly = {};
|
||||
ALL_DATA.forEach(r => {
|
||||
if (!r.date) return;
|
||||
const key = r.date.slice(0,7);
|
||||
monthly[key] = (monthly[key]||0) + 1;
|
||||
});
|
||||
const keys = Object.keys(monthly).sort();
|
||||
const vals = keys.map(k => monthly[k]);
|
||||
const max = Math.max(...vals);
|
||||
|
||||
const pad = { l: 10, r: 10, t: 15, b: 30 };
|
||||
const chartW = W - pad.l - pad.r;
|
||||
const chartH = H - pad.t - pad.b;
|
||||
const barW = Math.max(2, (chartW / keys.length) - 1);
|
||||
|
||||
ctx.clearRect(0,0,W,H);
|
||||
|
||||
// Grid line
|
||||
ctx.strokeStyle = 'rgba(30,37,48,0.8)';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(pad.l, pad.t + chartH);
|
||||
ctx.lineTo(W - pad.r, pad.t + chartH);
|
||||
ctx.stroke();
|
||||
|
||||
// Bars
|
||||
keys.forEach((k, i) => {
|
||||
const x = pad.l + i * (chartW / keys.length);
|
||||
const h = (vals[i] / max) * chartH;
|
||||
const y = pad.t + chartH - h;
|
||||
const grad = ctx.createLinearGradient(0, y, 0, y + h);
|
||||
grad.addColorStop(0, 'rgba(0,255,136,0.9)');
|
||||
grad.addColorStop(1, 'rgba(0,255,136,0.15)');
|
||||
ctx.fillStyle = grad;
|
||||
ctx.fillRect(x, y, barW, h);
|
||||
});
|
||||
|
||||
// X labels (every 6 months)
|
||||
ctx.fillStyle = 'rgba(90,112,128,0.9)';
|
||||
ctx.font = `${9 * dpr / dpr}px 'Share Tech Mono', monospace`;
|
||||
ctx.textAlign = 'center';
|
||||
keys.forEach((k, i) => {
|
||||
if (i % 6 === 0) {
|
||||
const x = pad.l + i * (chartW / keys.length) + barW/2;
|
||||
ctx.fillText(k, x, H - 8);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ─── TABLE ───────────────────────────────────────────────────
|
||||
function populateFilters() {
|
||||
const bands = [...new Set(ALL_DATA.map(r=>r.band).filter(Boolean))].sort();
|
||||
const modes = [...new Set(ALL_DATA.map(r=>r.mode).filter(Boolean))].sort();
|
||||
const bandSel = document.getElementById('filter-band');
|
||||
const modeSel = document.getElementById('filter-mode');
|
||||
bands.forEach(b => { const o = document.createElement('option'); o.value=b; o.text=b; bandSel.appendChild(o); });
|
||||
modes.forEach(m => { const o = document.createElement('option'); o.value=m; o.text=m; modeSel.appendChild(o); });
|
||||
}
|
||||
|
||||
function applyFilters() {
|
||||
const q = document.getElementById('search-input').value.trim().toLowerCase();
|
||||
const band = document.getElementById('filter-band').value;
|
||||
const mode = document.getElementById('filter-mode').value;
|
||||
const lotw = document.getElementById('filter-lotw').value;
|
||||
|
||||
filteredData = ALL_DATA.filter(r => {
|
||||
if (band && r.band !== band) return false;
|
||||
if (mode && r.mode !== mode) return false;
|
||||
if (lotw && r.lotw !== lotw) return false;
|
||||
if (q) {
|
||||
const hay = [r.call, r.name, r.country, r.qth, r.state, r.grid].join(' ').toLowerCase();
|
||||
if (!hay.includes(q)) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
currentPage = 1;
|
||||
renderTable();
|
||||
}
|
||||
|
||||
function sortTable(key) {
|
||||
if (sortKey === key) sortDir *= -1;
|
||||
else { sortKey = key; sortDir = key === 'date' || key === 'time' ? -1 : 1; }
|
||||
|
||||
filteredData.sort((a,b) => {
|
||||
let av = a[key]||'', bv = b[key]||'';
|
||||
if (key === 'dist') { av = parseInt(av)||0; bv = parseInt(bv)||0; }
|
||||
if (av < bv) return -sortDir;
|
||||
if (av > bv) return sortDir;
|
||||
return 0;
|
||||
});
|
||||
|
||||
// Update header indicators
|
||||
document.querySelectorAll('thead th').forEach(th => {
|
||||
th.classList.remove('sort-asc','sort-desc');
|
||||
});
|
||||
const headers = ['call','name','date','time','band','mode','rst_sent','country','dist','lotw'];
|
||||
const idx = headers.indexOf(key);
|
||||
if (idx >= 0) {
|
||||
const ths = document.querySelectorAll('thead th');
|
||||
ths[idx].classList.add(sortDir === 1 ? 'sort-asc' : 'sort-desc');
|
||||
}
|
||||
|
||||
renderTable();
|
||||
}
|
||||
|
||||
function renderTable() {
|
||||
const tbody = document.getElementById('log-tbody');
|
||||
const total = filteredData.length;
|
||||
const pages = Math.ceil(total / PAGE_SIZE);
|
||||
const start = (currentPage - 1) * PAGE_SIZE;
|
||||
const slice = filteredData.slice(start, start + PAGE_SIZE);
|
||||
|
||||
document.getElementById('log-meta').textContent =
|
||||
`${total.toLocaleString()} QSOs · Page ${currentPage} of ${Math.max(1,pages)}`;
|
||||
|
||||
if (slice.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="10" style="text-align:center;padding:2rem;color:var(--muted);font-family:var(--font-mono);font-size:0.8rem;">NO CONTACTS FOUND</td></tr>';
|
||||
} else {
|
||||
tbody.innerHTML = slice.map(r => {
|
||||
const timeStr = r.time ? r.time.slice(0,2)+':'+r.time.slice(2,4) : '';
|
||||
const bandClass = `band-${r.band}`;
|
||||
const lotwClass = r.lotw === 'Y' ? 'lotw-y' : 'lotw-n';
|
||||
const lotwSym = r.lotw === 'Y' ? '✓' : '·';
|
||||
const rst = r.rst_sent && r.rst_rcvd ? `${r.rst_sent} / ${r.rst_rcvd}` : r.rst_sent || '';
|
||||
return `<tr>
|
||||
<td class="call-cell">${esc(r.call)}</td>
|
||||
<td>${esc(r.name ? titleCase(r.name) : '')}</td>
|
||||
<td>${esc(r.date)}</td>
|
||||
<td>${timeStr}Z</td>
|
||||
<td><span class="band-badge ${bandClass}">${esc(r.band)}</span></td>
|
||||
<td class="mode-cell">${esc(r.mode)}</td>
|
||||
<td class="rst-cell">${esc(rst)}</td>
|
||||
<td>${esc(r.country||'')}${r.state ? ' <span style="color:var(--muted)">'+esc(r.state)+'</span>' : ''}</td>
|
||||
<td class="dist-cell">${r.dist ? Number(r.dist).toLocaleString() : ''}</td>
|
||||
<td class="${lotwClass}" style="text-align:center">${lotwSym}</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
renderPagination(pages);
|
||||
}
|
||||
|
||||
function renderPagination(pages) {
|
||||
const el = document.getElementById('pagination');
|
||||
if (pages <= 1) { el.innerHTML = ''; return; }
|
||||
|
||||
let html = `<button class="page-btn" onclick="goPage(${currentPage-1})" ${currentPage===1?'disabled':''}>‹ Prev</button>`;
|
||||
|
||||
const range = [];
|
||||
if (pages <= 7) {
|
||||
for (let i=1;i<=pages;i++) range.push(i);
|
||||
} else {
|
||||
range.push(1);
|
||||
if (currentPage > 3) range.push('...');
|
||||
for (let i=Math.max(2,currentPage-1); i<=Math.min(pages-1,currentPage+1); i++) range.push(i);
|
||||
if (currentPage < pages-2) range.push('...');
|
||||
range.push(pages);
|
||||
}
|
||||
|
||||
range.forEach(p => {
|
||||
if (p === '...') html += `<span class="page-info">…</span>`;
|
||||
else html += `<button class="page-btn ${p===currentPage?'active':''}" onclick="goPage(${p})">${p}</button>`;
|
||||
});
|
||||
|
||||
html += `<button class="page-btn" onclick="goPage(${currentPage+1})" ${currentPage===pages?'disabled':''}>Next ›</button>`;
|
||||
el.innerHTML = html;
|
||||
}
|
||||
|
||||
function goPage(p) {
|
||||
currentPage = p;
|
||||
renderTable();
|
||||
document.querySelector('.table-wrap').scrollIntoView({behavior:'smooth',block:'nearest'});
|
||||
}
|
||||
|
||||
// ─── HELPERS ─────────────────────────────────────────────────
|
||||
function countBy(arr, key) {
|
||||
return arr.reduce((acc, r) => {
|
||||
const v = r[key];
|
||||
if (v) acc[v] = (acc[v]||0)+1;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function esc(s) {
|
||||
return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||
}
|
||||
|
||||
function titleCase(s) {
|
||||
return s.toLowerCase().replace(/\b\w/g, c => c.toUpperCase());
|
||||
}
|
||||
|
||||
function showSection(id, btn) {
|
||||
document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
|
||||
document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active'));
|
||||
document.getElementById('section-' + id).classList.add('active');
|
||||
btn.classList.add('active');
|
||||
}
|
||||
|
||||
window.addEventListener('load', init);
|
||||
window.addEventListener('resize', () => {
|
||||
if (document.getElementById('section-dashboard').classList.contains('active')) {
|
||||
drawActivityChart();
|
||||
}
|
||||
});
|
||||
+1
-301
@@ -213,306 +213,6 @@
|
||||
LOGBOOK GENERATED FROM ADIF EXPORT
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// ─── DATA ───────────────────────────────────────────────────
|
||||
const RAW_DATA = [{"call":"WE2M","name":"ROBERT A HOGUE","date":"2021-01-24","time":"0553","band":"40M","mode":"FT8","rst_sent":"-20","rst_rcvd":"-10","country":"United States","state":"WA","qth":"E WENATCHEE","lotw":"Y","dist":"2782","grid":"CN97UJ"},{"call":"WD8AXA","name":"CHARLES D AGOSTI","date":"2021-01-24","time":"0555","band":"40M","mode":"FT8","rst_sent":"-07","rst_rcvd":"-11","country":"United States","state":"MI","qth":"PORTAGE","lotw":"N","dist":"1291","grid":"EN72ef"},{"call":"KG4IXS","name":"RICHARD A VAUGHAN","date":"2021-01-24","time":"0558","band":"40M","mode":"FT8","rst_sent":"+00","rst_rcvd":"-13","country":"United States","state":"VA","qth":"CHATHAM","lotw":"Y","dist":"1390","grid":"FM06HU"},{"call":"KE0DC","name":"","date":"2021-01-24","time":"0600","band":"40M","mode":"FT8","rst_sent":"-08","rst_rcvd":"-09","country":"United States","state":"CO","qth":"AURORA","lotw":"Y","dist":"1274","grid":"DM79OQ"},{"call":"KC0HHN","name":"","date":"2021-01-24","time":"0604","band":"40M","mode":"FT8","rst_sent":"-12","rst_rcvd":"-08","country":"United States","state":"IA","qth":"","lotw":"N","dist":"","grid":"EN31"},{"call":"WB0OEW","name":"JAMES D VOGT","date":"2021-01-24","time":"0606","band":"40M","mode":"FT8","rst_sent":"-10","rst_rcvd":"-15","country":"United States","state":"MO","qth":"CAMDENTON","lotw":"Y","dist":"747","grid":"EM37TH"},{"call":"N4JKO","name":"GARY E HORTON","date":"2021-01-24","time":"0608","band":"40M","mode":"FT8","rst_sent":"-12","rst_rcvd":"-15","country":"United States","state":"TN","qth":"","lotw":"N","dist":"","grid":"EM65RT"},{"call":"WW4LL","name":"LESTER P LEEPER","date":"2021-01-24","time":"0610","band":"40M","mode":"FT8","rst_sent":"-14","rst_rcvd":"-13","country":"United States","state":"GA","qth":"BOWDON","lotw":"Y","dist":"575","grid":"EM73DS"},{"call":"K0MBT","name":"MICHAEL B TEDESCO","date":"2021-01-24","time":"0612","band":"40M","mode":"FT8","rst_sent":"-09","rst_rcvd":"-18","country":"United States","state":"MN","qth":"SHAKOPEE","lotw":"Y","dist":"1247","grid":"EN34HE"},{"call":"WB4LHD","name":"ROY A MCDANIEL","date":"2021-01-24","time":"0618","band":"40M","mode":"FT8","rst_sent":"-14","rst_rcvd":"-12","country":"United States","state":"NC","qth":"SANFORD","lotw":"Y","dist":"1118","grid":"FM03HF"},{"call":"N0KJA","name":"FRANK O KORANDA","date":"2021-01-24","time":"0620","band":"40M","mode":"FT8","rst_sent":"-10","rst_rcvd":"-14","country":"United States","state":"KS","qth":"WICHITA","lotw":"Y","dist":"641","grid":"EM17PT"},{"call":"W9JML","name":"JAMES M LAURENDEAU","date":"2021-01-24","time":"0623","band":"40M","mode":"FT8","rst_sent":"-05","rst_rcvd":"-16","country":"United States","state":"IL","qth":"COLLINSVILLE","lotw":"Y","dist":"668","grid":"EM58GI"},{"call":"K9ADV","name":"DWIGHT A VANDERLINDEN","date":"2021-01-24","time":"0625","band":"40M","mode":"FT8","rst_sent":"-11","rst_rcvd":"-13","country":"United States","state":"IN","qth":"INDIANAPOLIS","lotw":"Y","dist":"929","grid":"EM79MF"},{"call":"WB8YYY","name":"ERNEST E MUENZER","date":"2021-01-24","time":"0628","band":"40M","mode":"FT8","rst_sent":"-10","rst_rcvd":"-05","country":"United States","state":"MI","qth":"","lotw":"Y","dist":"","grid":"EN72"},{"call":"KD2UKS","name":"RAYMOND JOSEPH MCDERMOTT","date":"2021-01-24","time":"0641","band":"40M","mode":"FT8","rst_sent":"-12","rst_rcvd":"-15","country":"United States","state":"NY","qth":"MASTIC","lotw":"N","dist":"1561","grid":"FN30HT"},{"call":"KD8IYI","name":"JEFFREY C WHITLEY","date":"2021-01-24","time":"0644","band":"40M","mode":"FT8","rst_sent":"-13","rst_rcvd":"-13","country":"United States","state":"OH","qth":"","lotw":"Y","dist":"","grid":"EM89LG"},{"call":"K9DW","name":"DAN WELBORN","date":"2021-01-24","time":"0647","band":"40M","mode":"FT8","rst_sent":"-09","rst_rcvd":"-06","country":"United States","state":"AL","qth":"DOTHAN","lotw":"Y","dist":"435","grid":"EM64SA"},{"call":"K8DO","name":"DEAN L OSTERMAN","date":"2021-01-24","time":"0649","band":"40M","mode":"FT8","rst_sent":"-11","rst_rcvd":"-10","country":"United States","state":"MI","qth":"ALLENDALE","lotw":"Y","dist":"1298","grid":"EN62OX"},{"call":"K6VMV","name":"VICTOR M VALADEZ","date":"2021-01-24","time":"0651","band":"40M","mode":"FT8","rst_sent":"-08","rst_rcvd":"-12","country":"United States","state":"CA","qth":"MANTECA","lotw":"N","dist":"1954","grid":"CM97KC"},{"call":"W0ULF","name":"MIKE WOLFE","date":"2021-01-24","time":"0653","band":"40M","mode":"FT8","rst_sent":"-11","rst_rcvd":"-13","country":"United States","state":"NE","qth":"OMAHA","lotw":"Y","dist":"1012","grid":"EN21WC"}];
|
||||
|
||||
// We'll load more data dynamically
|
||||
let ALL_DATA = [];
|
||||
let filteredData = [];
|
||||
let sortKey = 'date';
|
||||
let sortDir = -1; // -1 = desc
|
||||
let currentPage = 1;
|
||||
const PAGE_SIZE = 50;
|
||||
|
||||
// ─── FULL DATA EMBEDDED ──────────────────────────────────────
|
||||
// Data will be injected here
|
||||
const FULL_LOG = [{"call":"WE2M","name":"ROBERT A HOGUE","date":"2021-01-24","time":"0553","band":"40M","mode":"FT8","rst_sent":"-20","rst_rcvd":"-10","country":"United States","state":"WA","qth":"E WENATCHEE","lotw":"Y","dist":"2782","grid":"CN97UJ"},{"call":"WD8AXA","name":"CHARLES D AGOSTI","date":"2021-01-24","time":"0555","band":"40M","mode":"FT8","rst_sent":"-07","rst_rcvd":"-11","country":"United States","state":"MI","qth":"PORTAGE","lotw":"N","dist":"1291","grid":"EN72ef"},{"call":"KG4IXS","name":"RICHARD A VAUGHAN","date":"2021-01-24","time":"0558","band":"40M","mode":"FT8","rst_sent":"+00","rst_rcvd":"-13","country":"United States","state":"VA","qth":"CHATHAM","lotw":"Y","dist":"1390","grid":"FM06HU"},{"call":"KE0DC","name":"DOUGLAS J COMER","date":"2021-01-24","time":"0600","band":"40M","mode":"FT8","rst_sent":"-08","rst_rcvd":"-09","country":"United States","state":"CO","qth":"AURORA","lotw":"Y","dist":"1274","grid":"DM79OQ"},{"call":"KC2DUX","name":"John W Hamann, Jr","date":"2021-01-24","time":"0608","band":"40M","mode":"FT8","rst_sent":"-11","rst_rcvd":"-11","country":"United States","state":"NJ","qth":"Dennisville","lotw":"N","dist":"1850","grid":"FM29oe"},{"call":"N3XLS","name":"Joseph C Raymer","date":"2021-01-24","time":"0613","band":"40M","mode":"FT8","rst_sent":"-07","rst_rcvd":"-15","country":"United States","state":"PA","qth":"North Abington Twp","lotw":"Y","dist":"1880","grid":"FN21DN"},{"call":"HK1O","name":"Juan Guillermo Ochoa P","date":"2021-01-24","time":"0617","band":"40M","mode":"FT8","rst_sent":"-01","rst_rcvd":"-15","country":"Colombia","state":"","qth":"Puerto Colombia","lotw":"Y","dist":"3071","grid":"FK21NA"},{"call":"WJ9E","name":"Michael D Elder","date":"2021-01-24","time":"0645","band":"40M","mode":"FT8","rst_sent":"+05","rst_rcvd":"-14","country":"United States","state":"IN","qth":"Rushville","lotw":"Y","dist":"1081","grid":"EM79GO"},{"call":"K8TE","name":"WILLIAM A MADER, JR","date":"2021-01-24","time":"0647","band":"40M","mode":"FT8","rst_sent":"+06","rst_rcvd":"-05","country":"United States","state":"NM","qth":"RIO RANCHO","lotw":"Y","dist":"1230","grid":"DM65QG"},{"call":"N1UL","name":"ULRICH L ROHDE, PhD","date":"2021-01-24","time":"0703","band":"40M","mode":"FT8","rst_sent":"-08","rst_rcvd":"-11","country":"United States","state":"FL","qth":"MARCO ISLAND","lotw":"Y","dist":"1373","grid":"EL95DW"},{"call":"K9IHM","name":"JERROLD L JOHNSON","date":"2021-01-24","time":"0708","band":"40M","mode":"FT8","rst_sent":"+11","rst_rcvd":"+07","country":"United States","state":"TN","qth":"NEW MARKET","lotw":"Y","dist":"1010","grid":"EM86FC"},{"call":"WB7WUQ","name":"Malcolm A Clark","date":"2021-01-24","time":"0710","band":"40M","mode":"FT8","rst_sent":"-11","rst_rcvd":"-10","country":"United States","state":"WA","qth":"Inchelium","lotw":"Y","dist":"2714","grid":"DN08RG"},{"call":"N6AKO","name":"Luis Dimagiba","date":"2021-01-24","time":"0717","band":"40M","mode":"FT8","rst_sent":"-13","rst_rcvd":"+05","country":"United States","state":"CA","qth":"Arleta","lotw":"Y","dist":"2299","grid":"DM04SG"},{"call":"KC2DUX","name":"John W Hamann, Jr","date":"2021-01-24","time":"0719","band":"40M","mode":"FT8","rst_sent":"-01","rst_rcvd":"-04","country":"United States","state":"NJ","qth":"Dennisville","lotw":"Y","dist":"1850","grid":"FM29OE"},{"call":"KA9FOX","name":"SCOTT E NEADER","date":"2021-01-24","time":"0721","band":"40M","mode":"FT8","rst_sent":"+04","rst_rcvd":"+03","country":"United States","state":"WI","qth":"LA CROSSE","lotw":"Y","dist":"1271","grid":"EN43KS"},{"call":"KI3N","name":"Kenneth A Yates","date":"2021-01-24","time":"0723","band":"40M","mode":"FT8","rst_sent":"+00","rst_rcvd":"-03","country":"United States","state":"AL","qth":"Oxford","lotw":"Y","dist":"742","grid":"EM73CN"},{"call":"KA4NIV","name":"MAURICE E RICE, JR","date":"2021-01-24","time":"0725","band":"40M","mode":"FT8","rst_sent":"-07","rst_rcvd":"+07","country":"United States","state":"SC","qth":"SIMPSONVILLE","lotw":"Y","dist":"1087","grid":"EM84UT"},{"call":"KB1EFS","name":"Steven J Henchy","date":"2021-01-24","time":"0727","band":"40M","mode":"FT8","rst_sent":"-07","rst_rcvd":"-06","country":"United States","state":"MA","qth":"Franklin","lotw":"Y","dist":"2230","grid":"FN42HC"},{"call":"NR5G","name":"LEE E SWANSON","date":"2021-01-24","time":"0728","band":"40M","mode":"FT8","rst_sent":"+01","rst_rcvd":"-06","country":"United States","state":"GA","qth":"WARNER ROBINS","lotw":"N","dist":"943","grid":"EM82ep"},{"call":"KO4ARA","name":"David W Hunt, Jr","date":"2021-01-24","time":"0733","band":"40M","mode":"FT8","rst_sent":"-09","rst_rcvd":"-05","country":"United States","state":"VA","qth":"Annandale","lotw":"Y","dist":"1642","grid":"FM18JT"},{"call":"N7FN","name":"FRANKLIN B NORAGON","date":"2021-01-26","time":"1334","band":"20M","mode":"FT8","rst_sent":"+03","rst_rcvd":"-09","country":"United States","state":"AZ","qth":"Buckeye","lotw":"N","dist":"1765","grid":"DM33qq"},{"call":"N0DOW","name":"Richard WLine truncated
|
||||
|
||||
function init() {
|
||||
ALL_DATA = FULL_LOG.length ? FULL_LOG : RAW_DATA;
|
||||
filteredData = [...ALL_DATA];
|
||||
|
||||
// Update stats
|
||||
const countries = new Set(ALL_DATA.map(r => r.country).filter(Boolean));
|
||||
const states = new Set(ALL_DATA.filter(r => r.country === 'United States').map(r => r.state).filter(Boolean));
|
||||
document.getElementById('stat-dxcc').textContent = countries.size;
|
||||
document.getElementById('stat-states').textContent = states.size;
|
||||
|
||||
buildDashboard();
|
||||
populateFilters();
|
||||
renderTable();
|
||||
}
|
||||
|
||||
// ─── DASHBOARD ───────────────────────────────────────────────
|
||||
function buildDashboard() {
|
||||
// Modes
|
||||
const modes = countBy(ALL_DATA, 'mode');
|
||||
const topModes = Object.entries(modes).sort((a,b)=>b[1]-a[1]).slice(0,7);
|
||||
const maxMode = topModes[0][1];
|
||||
const modesEl = document.getElementById('modes-chart');
|
||||
const modeColors = { FT8:'#ffaa00', FT4:'#ffcc44', DIG:'#ff8800', USB:'#4499ff', LSB:'#66aaff', SSB:'#88bbff', RTTY:'#ff4455' };
|
||||
modesEl.innerHTML = topModes.map(([m, c]) => `
|
||||
<div class="bar-row">
|
||||
<div class="bar-label">${m}</div>
|
||||
<div class="bar-track"><div class="bar-fill" style="width:0%;background:${modeColors[m]||'#888'}" data-target="${(c/maxMode*100).toFixed(1)}"></div></div>
|
||||
<div class="bar-count">${c}</div>
|
||||
</div>`).join('');
|
||||
|
||||
// Bands
|
||||
const bands = countBy(ALL_DATA, 'band');
|
||||
const topBands = Object.entries(bands).sort((a,b)=>b[1]-a[1]).slice(0,8);
|
||||
const maxBand = topBands[0][1];
|
||||
const bandEl = document.getElementById('bands-chart');
|
||||
const bandColors = { '20M':'#4499ff','40M':'#00ff88','80M':'#ff4455','10M':'#00ddff','30M':'#88aaff','17M':'#aaddff','15M':'#ffaaff','12M':'#ffdd88' };
|
||||
bandEl.innerHTML = topBands.map(([b, c]) => `
|
||||
<div class="bar-row">
|
||||
<div class="bar-label">${b}</div>
|
||||
<div class="bar-track"><div class="bar-fill" style="width:0%;background:${bandColors[b]||'#888'}" data-target="${(c/maxBand*100).toFixed(1)}"></div></div>
|
||||
<div class="bar-count">${c}</div>
|
||||
</div>`).join('');
|
||||
|
||||
// Countries
|
||||
const countries = countBy(ALL_DATA, 'country');
|
||||
const topCountries = Object.entries(countries).sort((a,b)=>b[1]-a[1]).slice(0,20);
|
||||
const ctEl = document.getElementById('countries-list');
|
||||
ctEl.innerHTML = topCountries.map(([c,n]) => `
|
||||
<div class="country-row">
|
||||
<span class="country-name">${c||'Unknown'}</span>
|
||||
<span class="country-count">${n}</span>
|
||||
</div>`).join('');
|
||||
|
||||
// Animate bars after a tick
|
||||
requestAnimationFrame(() => {
|
||||
setTimeout(() => {
|
||||
document.querySelectorAll('.bar-fill').forEach(el => {
|
||||
el.style.width = el.dataset.target + '%';
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// Activity chart
|
||||
drawActivityChart();
|
||||
}
|
||||
|
||||
function drawActivityChart() {
|
||||
const canvas = document.getElementById('activityCanvas');
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const W = canvas.parentElement.offsetWidth - 48;
|
||||
const H = 120;
|
||||
canvas.width = W * dpr;
|
||||
canvas.height = H * dpr;
|
||||
canvas.style.width = W + 'px';
|
||||
canvas.style.height = H + 'px';
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.scale(dpr, dpr);
|
||||
|
||||
// Group by year-month
|
||||
const monthly = {};
|
||||
ALL_DATA.forEach(r => {
|
||||
if (!r.date) return;
|
||||
const key = r.date.slice(0,7);
|
||||
monthly[key] = (monthly[key]||0) + 1;
|
||||
});
|
||||
const keys = Object.keys(monthly).sort();
|
||||
const vals = keys.map(k => monthly[k]);
|
||||
const max = Math.max(...vals);
|
||||
|
||||
const pad = { l: 10, r: 10, t: 15, b: 30 };
|
||||
const chartW = W - pad.l - pad.r;
|
||||
const chartH = H - pad.t - pad.b;
|
||||
const barW = Math.max(2, (chartW / keys.length) - 1);
|
||||
|
||||
ctx.clearRect(0,0,W,H);
|
||||
|
||||
// Grid line
|
||||
ctx.strokeStyle = 'rgba(30,37,48,0.8)';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(pad.l, pad.t + chartH);
|
||||
ctx.lineTo(W - pad.r, pad.t + chartH);
|
||||
ctx.stroke();
|
||||
|
||||
// Bars
|
||||
keys.forEach((k, i) => {
|
||||
const x = pad.l + i * (chartW / keys.length);
|
||||
const h = (vals[i] / max) * chartH;
|
||||
const y = pad.t + chartH - h;
|
||||
const grad = ctx.createLinearGradient(0, y, 0, y + h);
|
||||
grad.addColorStop(0, 'rgba(0,255,136,0.9)');
|
||||
grad.addColorStop(1, 'rgba(0,255,136,0.15)');
|
||||
ctx.fillStyle = grad;
|
||||
ctx.fillRect(x, y, barW, h);
|
||||
});
|
||||
|
||||
// X labels (every 6 months)
|
||||
ctx.fillStyle = 'rgba(90,112,128,0.9)';
|
||||
ctx.font = `${9 * dpr / dpr}px 'Share Tech Mono', monospace`;
|
||||
ctx.textAlign = 'center';
|
||||
keys.forEach((k, i) => {
|
||||
if (i % 6 === 0) {
|
||||
const x = pad.l + i * (chartW / keys.length) + barW/2;
|
||||
ctx.fillText(k, x, H - 8);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ─── TABLE ───────────────────────────────────────────────────
|
||||
function populateFilters() {
|
||||
const bands = [...new Set(ALL_DATA.map(r=>r.band).filter(Boolean))].sort();
|
||||
const modes = [...new Set(ALL_DATA.map(r=>r.mode).filter(Boolean))].sort();
|
||||
const bandSel = document.getElementById('filter-band');
|
||||
const modeSel = document.getElementById('filter-mode');
|
||||
bands.forEach(b => { const o = document.createElement('option'); o.value=b; o.text=b; bandSel.appendChild(o); });
|
||||
modes.forEach(m => { const o = document.createElement('option'); o.value=m; o.text=m; modeSel.appendChild(o); });
|
||||
}
|
||||
|
||||
function applyFilters() {
|
||||
const q = document.getElementById('search-input').value.trim().toLowerCase();
|
||||
const band = document.getElementById('filter-band').value;
|
||||
const mode = document.getElementById('filter-mode').value;
|
||||
const lotw = document.getElementById('filter-lotw').value;
|
||||
|
||||
filteredData = ALL_DATA.filter(r => {
|
||||
if (band && r.band !== band) return false;
|
||||
if (mode && r.mode !== mode) return false;
|
||||
if (lotw && r.lotw !== lotw) return false;
|
||||
if (q) {
|
||||
const hay = [r.call, r.name, r.country, r.qth, r.state, r.grid].join(' ').toLowerCase();
|
||||
if (!hay.includes(q)) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
currentPage = 1;
|
||||
renderTable();
|
||||
}
|
||||
|
||||
function sortTable(key) {
|
||||
if (sortKey === key) sortDir *= -1;
|
||||
else { sortKey = key; sortDir = key === 'date' || key === 'time' ? -1 : 1; }
|
||||
|
||||
filteredData.sort((a,b) => {
|
||||
let av = a[key]||'', bv = b[key]||'';
|
||||
if (key === 'dist') { av = parseInt(av)||0; bv = parseInt(bv)||0; }
|
||||
if (av < bv) return -sortDir;
|
||||
if (av > bv) return sortDir;
|
||||
return 0;
|
||||
});
|
||||
|
||||
// Update header indicators
|
||||
document.querySelectorAll('thead th').forEach(th => {
|
||||
th.classList.remove('sort-asc','sort-desc');
|
||||
});
|
||||
const headers = ['call','name','date','time','band','mode','rst_sent','country','dist','lotw'];
|
||||
const idx = headers.indexOf(key);
|
||||
if (idx >= 0) {
|
||||
const ths = document.querySelectorAll('thead th');
|
||||
ths[idx].classList.add(sortDir === 1 ? 'sort-asc' : 'sort-desc');
|
||||
}
|
||||
|
||||
renderTable();
|
||||
}
|
||||
|
||||
function renderTable() {
|
||||
const tbody = document.getElementById('log-tbody');
|
||||
const total = filteredData.length;
|
||||
const pages = Math.ceil(total / PAGE_SIZE);
|
||||
const start = (currentPage - 1) * PAGE_SIZE;
|
||||
const slice = filteredData.slice(start, start + PAGE_SIZE);
|
||||
|
||||
document.getElementById('log-meta').textContent =
|
||||
`${total.toLocaleString()} QSOs · Page ${currentPage} of ${Math.max(1,pages)}`;
|
||||
|
||||
if (slice.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="10" style="text-align:center;padding:2rem;color:var(--muted);font-family:var(--font-mono);font-size:0.8rem;">NO CONTACTS FOUND</td></tr>';
|
||||
} else {
|
||||
tbody.innerHTML = slice.map(r => {
|
||||
const timeStr = r.time ? r.time.slice(0,2)+':'+r.time.slice(2,4) : '';
|
||||
const bandClass = `band-${r.band}`;
|
||||
const lotwClass = r.lotw === 'Y' ? 'lotw-y' : 'lotw-n';
|
||||
const lotwSym = r.lotw === 'Y' ? '✓' : '·';
|
||||
const rst = r.rst_sent && r.rst_rcvd ? `${r.rst_sent} / ${r.rst_rcvd}` : r.rst_sent || '';
|
||||
return `<tr>
|
||||
<td class="call-cell">${esc(r.call)}</td>
|
||||
<td>${esc(r.name ? titleCase(r.name) : '')}</td>
|
||||
<td>${esc(r.date)}</td>
|
||||
<td>${timeStr}Z</td>
|
||||
<td><span class="band-badge ${bandClass}">${esc(r.band)}</span></td>
|
||||
<td class="mode-cell">${esc(r.mode)}</td>
|
||||
<td class="rst-cell">${esc(rst)}</td>
|
||||
<td>${esc(r.country||'')}${r.state ? ' <span style="color:var(--muted)">'+esc(r.state)+'</span>' : ''}</td>
|
||||
<td class="dist-cell">${r.dist ? Number(r.dist).toLocaleString() : ''}</td>
|
||||
<td class="${lotwClass}" style="text-align:center">${lotwSym}</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
renderPagination(pages);
|
||||
}
|
||||
|
||||
function renderPagination(pages) {
|
||||
const el = document.getElementById('pagination');
|
||||
if (pages <= 1) { el.innerHTML = ''; return; }
|
||||
|
||||
let html = `<button class="page-btn" onclick="goPage(${currentPage-1})" ${currentPage===1?'disabled':''}>‹ Prev</button>`;
|
||||
|
||||
const range = [];
|
||||
if (pages <= 7) {
|
||||
for (let i=1;i<=pages;i++) range.push(i);
|
||||
} else {
|
||||
range.push(1);
|
||||
if (currentPage > 3) range.push('...');
|
||||
for (let i=Math.max(2,currentPage-1); i<=Math.min(pages-1,currentPage+1); i++) range.push(i);
|
||||
if (currentPage < pages-2) range.push('...');
|
||||
range.push(pages);
|
||||
}
|
||||
|
||||
range.forEach(p => {
|
||||
if (p === '...') html += `<span class="page-info">…</span>`;
|
||||
else html += `<button class="page-btn ${p===currentPage?'active':''}" onclick="goPage(${p})">${p}</button>`;
|
||||
});
|
||||
|
||||
html += `<button class="page-btn" onclick="goPage(${currentPage+1})" ${currentPage===pages?'disabled':''}>Next ›</button>`;
|
||||
el.innerHTML = html;
|
||||
}
|
||||
|
||||
function goPage(p) {
|
||||
currentPage = p;
|
||||
renderTable();
|
||||
document.querySelector('.table-wrap').scrollIntoView({behavior:'smooth',block:'nearest'});
|
||||
}
|
||||
|
||||
// ─── HELPERS ─────────────────────────────────────────────────
|
||||
function countBy(arr, key) {
|
||||
return arr.reduce((acc, r) => {
|
||||
const v = r[key];
|
||||
if (v) acc[v] = (acc[v]||0)+1;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function esc(s) {
|
||||
return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||
}
|
||||
|
||||
function titleCase(s) {
|
||||
return s.toLowerCase().replace(/\b\w/g, c => c.toUpperCase());
|
||||
}
|
||||
|
||||
function showSection(id, btn) {
|
||||
document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
|
||||
document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active'));
|
||||
document.getElementById('section-' + id).classList.add('active');
|
||||
btn.classList.add('active');
|
||||
}
|
||||
|
||||
window.addEventListener('load', init);
|
||||
window.addEventListener('resize', () => {
|
||||
if (document.getElementById('section-dashboard').classList.contains('active')) {
|
||||
drawActivityChart();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script src="js/k5bss.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user