<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title> Infobeamer </title> <link rel="stylesheet" href="/style.css"> <script> main().then(() => console.log("loaded")); function parseDuration(duration) { if (!/^\d+:\d+$/.test(duration)) { return 0; } const [hours, minutes] = duration.split(":"); return (hours * 60 * 60 + minutes * 60) * 1000; } function sanitize(unsafe) { const element = document.createElement("div"); element.innerHTML = unsafe; return element.innerText; } function render(talk) { const now = Date.now(); const max = talk.end - talk.start; let value = 0; if (talk.start < now && talk.end > now) { value = talk.end - now; value = max - value } else if (talk.end < now) { value = max; } return ` <li class="box"> <h2 class="box-header">${sanitize(talk.title)}</h2> <div class="box-content clamp-height"> <p class="date"> Speakers: ${sanitize(talk.persons.join(", ") || "---")}<br> Stage: ${sanitize(talk.room)}<br> Time: ${sanitize(talk.start_string)}<br> Duration: ${sanitize(talk.duration)}<br> </p> <p>${sanitize(talk.abstract)}</p> </div> <div class="box-footer"> <div class="progress"><div class="fill" style="width: ${value / max * 100}%"></div></div> </div> </li>`; } async function main() { const resp = await fetch("https://static.rc3.world/schedule/everything.json"); const schedule = await resp.json(); const now = Date.now(); const upcoming = []; for (const day of schedule.schedule.conference.days) { for (const [channel, talks] of Object.entries(day.rooms)) { for (const talk of talks) { const talk_end = Date.parse(talk.date) + parseDuration(talk.duration); if (talk_end > now) { const parsed = { start: Date.parse(talk.date), date_string: talk.date, end: talk_end, start_string: talk.start, duration: talk.duration, room: talk.room, title: talk.title, abstract: talk.abstract, persons: talk.persons.map(person => person.public_name), }; upcoming.push(parsed); break; } } } } let content = ""; upcoming.sort((a, b) => a.end - b.end); for (const talk of upcoming.slice(0, 6)) { content += render(talk); } const time = new Date(); document.getElementById("list").innerHTML = content; const hours = `${time.getHours()}`.padStart(2, '0'); const minutes = `${time.getMinutes()}`.padStart(2, '0'); document.getElementById("time").innerText = `${hours}:${minutes}`; setTimeout(main, (60-time.getSeconds())*1000+500); } </script> </head> <body class="infobeamer"> <header> <nav class="nav nav-main"> <a class="nav-logo" href="/"><img src="/franconianNet.svg" alt="Logo of franconian.net"> <span class="logotype"> <span>franc</span> <span>onian</span> <span>.net</span> </span> </a> <a class="nav-link" id="time"></a> </nav> </header> <main> <h1>running / upcoming</h1> <ul class="box-grid" id="list"> </ul> </main> </body> </html>