const MAX_COUNTDOWN = 60; var countdown = MAX_COUNTDOWN; var now = Date.now(); var eventsOnNow = []; var eventsOnNext = []; grist.ready({ requiredAccess: 'read table' }); grist.onRecords(function (records) { eventsOnNow = []; eventsOnNext = []; now = Date.now(); var i = 0; for (const r of records) { let privacy = r["video_privacy"]; let videoNeeded = r["Video_team_attending"]; if (privacy == "none" || videoNeeded == true) { let e = new Event( i++, r["title"], r["venue"], videoNeeded, privacy, r["Comments"], Date.parse(r["start_date"]), Date.parse(r["end_date"]) ); if (e.hasStarted() && !e.hasEnded()) { eventsOnNow.push(e); } else if (!e.hasStarted() && !e.hasEnded()) { eventsOnNext.push(e); } } } eventsOnNow.sort((e1, e2) => { return (e1.timeUntilEnd() > e2.timeUntilEnd()) ? 1 : ((e2.timeUntilEnd() > e1.timeUntilEnd()) ? -1 : 0) }); eventsOnNext.sort((e1, e2) => { return (e1.timeUntilStart() > e2.timeUntilStart()) ? 1 : ((e2.timeUntilStart() > e1.timeUntilStart()) ? -1 : 0) }); populateList("happeningNow", eventsOnNow, false); populateList("happeningNext", eventsOnNext, true); window.setInterval(() => handleCountdown(), 1000); }); grist.onOptions(function(options, interaction) { console.info("Options reloaded"); }); class Event { constructor(id, name, venue, videoNeeded, privacy, comments, startTime, endTime) { this.id = id; this.name = name; this.venue = venue; this.videoNeeded = videoNeeded; this.privacy = privacy; this.comments = comments; this.startTime = startTime; this.endTime = endTime; } hasStarted() { return this.startTime < now; } hasEnded() { return this.endTime < now; } timeUntilStart() { return new Date(this.startTime - now); } timeUntilEnd() { return new Date(this.endTime - now); } } function makeTextBold(text) { return "" + text + ""; } function convertTimeToString(time) { // Using getHours() is bullshit for this usage, it won't include any days in the hours let hours = Math.floor(time / (1000 * 60 * 60)); let minutes = time.getMinutes(); return hours + " hours, " + minutes + " minutes"; } function populateList(elementName, items, getStartsIn) { for (const i of items) { const col = document.createElement("div"); col.setAttribute("class", "col-sm-12 col-md-3") const card = document.createElement("div"); card.setAttribute("class", "card m-3") const cardHeader = document.createElement("div"); cardHeader.setAttribute("class", "card-header") cardHeader.innerHTML = i.name; const list = document.createElement("ul"); list.setAttribute("class", "list-group list-group-flush") list.setAttribute("id", i.id); var venue = document.createElement("ul"); var videoNeeded = document.createElement("ul"); var privacy = document.createElement("ul"); var comments = document.createElement("ul"); var timer = document.createElement("ul"); venue.setAttribute("class", "list-group-item"); videoNeeded.setAttribute("class", "list-group-item"); privacy.setAttribute("class", "list-group-item"); comments.setAttribute("class", "list-group-item"); venue.innerHTML = "Venue: " + i.venue; videoNeeded.innerHTML = "Video Team Present: " + i.videoNeeded; privacy.innerHTML = "Privacy: " + i.privacy; comments.innerHTML = "Comment: " + i.comments; timer.setAttribute("class", "list-group-item"); timer.setAttribute("id", i.id + "_time"); if (getStartsIn) { timer.innerHTML = "Starts In: " + convertTimeToString(i.timeUntilStart()) + " (" + new Date(i.startTime).toLocaleString("en-GB", { timeZone: "UTC" }) + ")"; // Red border if show starts in 15 minutes if (i.timeUntilStart() <= 15 * 60 * 1000){ card.setAttribute("class", "card m-3 border border-5 border-danger"); timer.setAttribute("class", "list-group-item text-danger"); } } else { timer.innerHTML = "Ends In: " + convertTimeToString(i.timeUntilEnd()) + " (" + new Date(i.endTime).toLocaleString("en-GB", { timeZone: "UTC" }) + ")"; } list.appendChild(venue); list.appendChild(videoNeeded); list.appendChild(privacy); if (i.comments) { list.appendChild(comments); } list.appendChild(timer); // Colour in any rows if (i.privacy != "public") { if (i.privacy == "none") { privacy.setAttribute("class", "list-group-item bg-danger") privacy.innerHTML = makeTextBold("Privacy: Do not record!"); } else if (i.privacy == "review") { privacy.setAttribute("class", "list-group-item bg-warning") privacy.innerHTML = makeTextBold(privacy.innerHTML); } } if (i.comments) { comments.setAttribute("class", "list-group-item bg-info"); comments.innerHTML = makeTextBold(comments.innerHTML); } if (i.videoNeeded == true) { videoNeeded.setAttribute("class", "list-group-item bg-warning"); videoNeeded.innerHTML = makeTextBold(videoNeeded.innerHTML); } card.appendChild(cardHeader); card.appendChild(list); col.append(card); document.getElementById(elementName).appendChild(col); } } function handleCountdown() { countdown--; if (countdown == 0) { countdown = MAX_COUNTDOWN; window.location.reload(); } let counterElement = document.getElementById("counter"); counterElement.innerHTML = "Next refresh in " + countdown; }