import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm" import * as Plot from "https://cdn.jsdelivr.net/npm/@observablehq/plot@0.6/+esm"; function clean_poll_data(data, rel_start_time) { const {start_date, end_date, question, answers, votes} = data; const num_votes = votes.length; const totals = answers.map(a => 0); totals.push(0); const start_time = new Date(start_date); const end_time = new Date(end_date); answers.forEach((a,i) => { votes.splice(0,0,{time: start_time, answer: i}); }) votes.forEach((v,i) => { v.time = new Date(v.time); v.total = totals[v.answer]; v.answer_name = answers[v.answer].answer; totals[v.answer] += 1; }); const last_vote_time = votes[votes.length-1].time; const duration = last_vote_time - start_time; answers.forEach((a,i) => { votes.push({time: last_vote_time, answer: i, total: totals[i]-1, answer_name: answers[i].answer}); }) votes.forEach(v => {v.time = v.time - start_time + rel_start_time}); return {start_date, end_date, duration, question, answers, votes, last_vote_time}; } async function plot_poll(ids, container) { const reqs = await Promise.all(ids.map(id => fetch(`https://aperiodical.com/wp-json/wp-polls/v3/results/${id}`))); const datas = await Promise.all(reqs.map(r => r.json())); console.log(datas); let all_votes = []; const rel_start_time = 60*60*8 * 1000; let max_duration = 0; let title = ''; let num_answers = 0; for(let data of datas) { const {votes, answers, question, duration, last_vote_time} = clean_poll_data(data, rel_start_time); votes.forEach(v => v.answer += num_answers); title = question; all_votes = all_votes.concat(votes); max_duration = Math.max(duration, max_duration); num_answers += answers.length; } console.table(all_votes); container.innerHTML = ''; const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = 'poll.css'; container.append(link); const header = document.createElement('h2'); header.textContent = title; container.append(header); const plot = Plot.plot({ y: { label: 'Votes', grid: true, }, x: { label: 'Time', tickFormat: t => { t /= 1000; const seconds = t % 60; t = (t-seconds)/60; const minutes = t % 60; t = (t-minutes)/60; const hours = t % 24; return `${hours.toString().padStart(2,'0')}:${minutes.toString().padStart(2,'0')}`; } }, marks: [ Plot.line([{time: rel_start_time, total: 0}, {time: max_duration + rel_start_time, total: 0}],{x:'time', y:'total'}), Plot.lineY(all_votes, {x: "time", y: "total", z: "answer", stroke: 'answer_name'}), Plot.text(all_votes, Plot.selectLast({x:'time', y: 'total', z: 'answer', text: 'answer_name', textAnchor: 'end', dx: -6})) ] }) container.append(plot); } class PollPlotElement extends HTMLElement { constructor() { super(); this.attachShadow({mode:'open'}); } static observedAttributes = ['pollids']; attributeChangedCallback() { this.make_plot(); } make_plot() { const poll_ids = this.getAttribute('pollids').split(',').map(n=>parseInt(n)); if(poll_ids.some(id=>isNaN(id))) { return; } plot_poll(poll_ids, this.shadowRoot); } } customElements.define('poll-plot', PollPlotElement); (async () => { const polls = await (await fetch('https://aperiodical.com/wp-json/wp-polls/v3/polls')).json(); window.polls = polls; polls.sort((a,b) => (new Date(b.opens)) - (new Date(a.opens))); const params = new URLSearchParams(location.search); let pollids, year; if(params.has('year')) { year = params.get('year'); console.log(year); pollids = polls.filter(p => (new Date(p.opens)).getFullYear()==year).map(p=>p.id); } else { if(params.has('pollid')) { pollids = params.get('pollid'); } else { pollids = polls[0].id; } } document.querySelector('poll-plot').setAttribute('pollids',pollids); const polls_list = document.getElementById('other-matches'); for(let p of polls) { const li = document.createElement('li'); const a = document.createElement('a'); a.href = `?pollid=${p.id}`; if(pollids.includes(p.id)) { a.setAttribute('aria-current', 'page'); } const opens = new Date(p.opens); a.textContent = `${opens.getFullYear()} - ${p.question}`; li.append(a); polls_list.append(li); } })();