change to html instead of userscript
This commit is contained in:
parent
9ea145fdfc
commit
27fc89eac8
|
@ -1,5 +1,3 @@
|
||||||
# 2024-election-results
|
# 2024-election-results
|
||||||
|
|
||||||
Userscript for the following website:
|
Simple page for election results for 2024
|
||||||
|
|
||||||
https://www.reuters.com/graphics/USA-ELECTION/RESULTS/zjpqnemxwvx/
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Document</title>
|
||||||
|
<link
|
||||||
|
href="https://cdn.jsdelivr.net/npm/daisyui@4.12.14/dist/full.min.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
/>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="navbar bg-neutral text-neutral-content">
|
||||||
|
<button class="btn btn-ghost text-xl">2024 Election Results</button>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<table class="table table-xs">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Party</th>
|
||||||
|
<th>Votes</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="table-body"></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="navbar bg-neutral text-neutral-content">
|
||||||
|
<button class="btn btn-ghost text-xl">Breakdown Per State</button>
|
||||||
|
</div>
|
||||||
|
<div id="state-container" class="grid grid-cols-3 gap-4"></div>
|
||||||
|
<script>
|
||||||
|
(async () => {
|
||||||
|
const metaResp = await fetch(
|
||||||
|
"https://graphics.thomsonreuters.com/data/2024/us-elections/production/events/20241105/metadata.json"
|
||||||
|
);
|
||||||
|
const metadata = await metaResp.json();
|
||||||
|
const states = new Map(metadata.geo.map((c) => [c.uid, c.name]));
|
||||||
|
const parties = new Map(metadata.parties.map((c) => [c.code, c.name]));
|
||||||
|
const candidates = new Map(
|
||||||
|
metadata.candidates.map((c) => [
|
||||||
|
c.uid,
|
||||||
|
[c.fullName, parties.get(c.candidateRaces[0].majorParty)],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
const presResp = await fetch(
|
||||||
|
"https://graphics.thomsonreuters.com/data/2024/us-elections/production/events/20241105/summary-votes/president.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
const president = await presResp.json();
|
||||||
|
let data = president[0].state_electionTypes
|
||||||
|
.map((t) =>
|
||||||
|
t.state.officeRaces[0].candidateVotes.map((o) => {
|
||||||
|
return {
|
||||||
|
name: candidates
|
||||||
|
? candidates.get(o.candidate)
|
||||||
|
: [o.candidate, "?"],
|
||||||
|
votes: o.totalVote,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.reduce((acc, current) => {
|
||||||
|
current.forEach((candidate) => {
|
||||||
|
const existingCandidate = acc.find(
|
||||||
|
(c) => c.name === candidate.name
|
||||||
|
);
|
||||||
|
if (existingCandidate) {
|
||||||
|
existingCandidate.votes += candidate.votes;
|
||||||
|
} else {
|
||||||
|
acc.push(candidate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
let stateVotes = president[0].state_electionTypes.map((t) => {
|
||||||
|
return {
|
||||||
|
state: states ? states.get(t.state.uid) : t.uid,
|
||||||
|
current: t.state.officeRaces[0].officeVotes[0].totalVote,
|
||||||
|
expected: t.state.officeRaces[0].officeVotes[0].totalExpectedVote,
|
||||||
|
candidates: t.state.officeRaces[0].candidateVotes.map((o) => {
|
||||||
|
return {
|
||||||
|
name: candidates
|
||||||
|
? candidates.get(o.candidate)
|
||||||
|
: [o.candidate, "?"],
|
||||||
|
votes: o.totalVote,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.length > 0) {
|
||||||
|
let tableBody = document.getElementById("table-body");
|
||||||
|
tableBody.innerHTML = "";
|
||||||
|
data
|
||||||
|
.sort((a, b) => b.votes - a.votes)
|
||||||
|
.forEach((row, index) => {
|
||||||
|
let rowElement = tableBody.insertRow();
|
||||||
|
let nameCell = rowElement.insertCell();
|
||||||
|
let partyCell = rowElement.insertCell();
|
||||||
|
let votesCell = rowElement.insertCell();
|
||||||
|
nameCell.textContent = row.name[0];
|
||||||
|
partyCell.textContent = row.name[1];
|
||||||
|
votesCell.textContent = row.votes;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateVotes.length > 0) {
|
||||||
|
let stateContainer = document.getElementById("state-container");
|
||||||
|
stateContainer.innerHTML = "";
|
||||||
|
stateVotes.forEach((row) => {
|
||||||
|
const table = document.createElement("table");
|
||||||
|
table.setAttribute("class", `table table-xs`);
|
||||||
|
const caption = document.createElement("caption");
|
||||||
|
caption.innerHTML = `<h2 class="text-xl">${row.state}</h2>`;
|
||||||
|
table.appendChild(caption);
|
||||||
|
|
||||||
|
const thead = document.createElement("thead");
|
||||||
|
const headerRow = document.createElement("tr");
|
||||||
|
const headers = ["Name", "Party", "Votes"];
|
||||||
|
headers.forEach((header) => {
|
||||||
|
const th = document.createElement("th");
|
||||||
|
th.textContent = header;
|
||||||
|
headerRow.appendChild(th);
|
||||||
|
});
|
||||||
|
thead.appendChild(headerRow);
|
||||||
|
table.appendChild(thead);
|
||||||
|
|
||||||
|
const tbody = document.createElement("tbody");
|
||||||
|
row.candidates
|
||||||
|
.sort((a, b) => b.votes - a.votes)
|
||||||
|
.forEach((candidate) => {
|
||||||
|
const rowElement = document.createElement("tr");
|
||||||
|
|
||||||
|
const nameCell = document.createElement("td");
|
||||||
|
nameCell.textContent = candidate.name[0];
|
||||||
|
rowElement.appendChild(nameCell);
|
||||||
|
|
||||||
|
const partyCell = document.createElement("td");
|
||||||
|
partyCell.textContent = candidate.name[1];
|
||||||
|
rowElement.appendChild(partyCell);
|
||||||
|
|
||||||
|
const votesCell = document.createElement("td");
|
||||||
|
votesCell.textContent = candidate.votes;
|
||||||
|
rowElement.appendChild(votesCell);
|
||||||
|
|
||||||
|
tbody.appendChild(rowElement);
|
||||||
|
});
|
||||||
|
table.appendChild(tbody);
|
||||||
|
stateContainer.appendChild(table);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})().catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
122
index.js
122
index.js
|
@ -1,122 +0,0 @@
|
||||||
(() => {
|
|
||||||
let candidates;
|
|
||||||
let parties;
|
|
||||||
let states;
|
|
||||||
let headerContainer = document.querySelector('div.article-block:nth-child(7)');
|
|
||||||
headerContainer.innerHTML = '';
|
|
||||||
headerContainer.insertAdjacentHTML('afterend', `<table class="heading h3 competitive en president svelte-1gkg98x"><thead><tr><th>Name</th><th>Party</th><th>Votes</th></tr></thead><tbody id="table-body"></tbody></table><div id="state-container" style="display: grid; grid-template-columns:repeat(4, 1fr); gap: 10px;"></div>`);
|
|
||||||
const {
|
|
||||||
fetch: originalFetch
|
|
||||||
} = window;
|
|
||||||
window.fetch = async (...args) => {
|
|
||||||
let [resource, config] = args;
|
|
||||||
let response = await originalFetch(resource, config);
|
|
||||||
if (!response.ok) {
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
const f = response.url.split('/').pop();
|
|
||||||
if (f === "metadata.json") {
|
|
||||||
const metadata = await response.json();
|
|
||||||
states = new Map(metadata.geo.map((c) => [c.uid, c.name]));
|
|
||||||
parties = new Map(metadata.parties.map((c) => [c.code, c.name]));
|
|
||||||
candidates = new Map(metadata.candidates.map((c) => [c.uid, [c.fullName, parties.get(c.candidateRaces[0].majorParty)]]));
|
|
||||||
} else if (f === "president.json") {
|
|
||||||
|
|
||||||
const president = await response.json();
|
|
||||||
let data = president[0].state_electionTypes.map((t) => t.state.officeRaces[0].candidateVotes.map((o) => {
|
|
||||||
return {
|
|
||||||
"name": (candidates) ? candidates.get(o.candidate) : [o.candidate, "?"],
|
|
||||||
"votes": o.totalVote
|
|
||||||
};
|
|
||||||
})).reduce((acc, current) => {
|
|
||||||
current.forEach((candidate) => {
|
|
||||||
const existingCandidate = acc.find((c) => c.name === candidate.name);
|
|
||||||
if (existingCandidate) {
|
|
||||||
existingCandidate.votes += candidate.votes;
|
|
||||||
} else {
|
|
||||||
acc.push(candidate);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
let stateVotes = president[0].state_electionTypes.map((t) => {
|
|
||||||
return {
|
|
||||||
"state": (states) ? states.get(t.state.uid) : t.uid,
|
|
||||||
"current": t.state.officeRaces[0].officeVotes[0].totalVote,
|
|
||||||
"expected": t.state.officeRaces[0].officeVotes[0].totalExpectedVote,
|
|
||||||
"candidates": t.state.officeRaces[0].candidateVotes.map((o) => {
|
|
||||||
return {
|
|
||||||
"name": (candidates) ? candidates.get(o.candidate) : [o.candidate, "?"],
|
|
||||||
"votes": o.totalVote
|
|
||||||
};
|
|
||||||
})
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
if (data.length > 0) {
|
|
||||||
let tableBody = document.getElementById('table-body');
|
|
||||||
tableBody.innerHTML = '';
|
|
||||||
data.sort((a, b) => b.votes - a.votes).forEach((row, index) => {
|
|
||||||
let rowElement = tableBody.insertRow();
|
|
||||||
rowElement.setAttribute('class', `with-state-links row-${index} svelte-woitu6 first-poll-close-row`);
|
|
||||||
let nameCell = rowElement.insertCell();
|
|
||||||
nameCell.setAttribute('class', `state en svelte-woitu6`);
|
|
||||||
let partyCell = rowElement.insertCell();
|
|
||||||
partyCell.setAttribute('class', `state en svelte-woitu6`);
|
|
||||||
let votesCell = rowElement.insertCell();
|
|
||||||
votesCell.setAttribute('class', `state en svelte-woitu6`);
|
|
||||||
nameCell.textContent = row.name[0];
|
|
||||||
partyCell.textContent = row.name[1];
|
|
||||||
votesCell.textContent = row.votes;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stateVotes.length > 0) {
|
|
||||||
let stateContainer = document.getElementById('state-container');
|
|
||||||
stateContainer.innerHTML = '';
|
|
||||||
stateVotes.forEach((row) => {
|
|
||||||
const table = document.createElement('table');
|
|
||||||
const caption = document.createElement('caption');
|
|
||||||
caption.innerHTML = `
|
|
||||||
<h2>${row.state} (Votes Left: ${row.expected - row.current})</h2>
|
|
||||||
`;
|
|
||||||
table.appendChild(caption);
|
|
||||||
|
|
||||||
const thead = document.createElement('thead');
|
|
||||||
const headerRow = document.createElement('tr');
|
|
||||||
const headers = ['Name', 'Party', 'Votes'];
|
|
||||||
headers.forEach(header => {
|
|
||||||
const th = document.createElement('th');
|
|
||||||
th.textContent = header;
|
|
||||||
headerRow.appendChild(th);
|
|
||||||
});
|
|
||||||
thead.appendChild(headerRow);
|
|
||||||
table.appendChild(thead);
|
|
||||||
|
|
||||||
const tbody = document.createElement('tbody');
|
|
||||||
row.candidates.sort((a, b) => b.votes - a.votes).forEach((candidate) => {
|
|
||||||
const rowElement = document.createElement('tr');
|
|
||||||
|
|
||||||
const nameCell = document.createElement('td');
|
|
||||||
nameCell.textContent = candidate.name[0];
|
|
||||||
rowElement.appendChild(nameCell);
|
|
||||||
|
|
||||||
const partyCell = document.createElement('td');
|
|
||||||
partyCell.textContent = candidate.name[1];
|
|
||||||
rowElement.appendChild(partyCell);
|
|
||||||
|
|
||||||
const votesCell = document.createElement('td');
|
|
||||||
votesCell.textContent = candidate.votes;
|
|
||||||
rowElement.appendChild(votesCell);
|
|
||||||
|
|
||||||
tbody.appendChild(rowElement);
|
|
||||||
});
|
|
||||||
table.appendChild(tbody);
|
|
||||||
stateContainer.appendChild(table);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
})();
|
|
Loading…
Reference in New Issue