Changed around line 1
+ const ELO_K = 32;
+ let items = [];
+ let currentPair = [];
+ let history = new Set();
+
+ function calculateElo(winner, loser) {
+ const expectedScore = (loser) =>
+ 1 / (1 + 10 ** ((winner.rating - loser.rating) / 400));
+ const winnerExpected = expectedScore(loser);
+ const loserExpected = expectedScore(winner);
+
+ winner.rating += ELO_K * (1 - winnerExpected);
+ loser.rating += ELO_K * (0 - loserExpected);
+ }
+
+ function getRandomPair() {
+ const pool = items.filter((item) => !currentPair.includes(item));
+ if (pool.length < 2) {
+ const allPairs = items
+ .flatMap((a) => items.filter((b) => a !== b).map((b) => [a, b]))
+ .filter((pair) => !history.has(pair.join()));
+ return allPairs[Math.floor(Math.random() * allPairs.length)] || [];
+ }
+ return [currentPair[0], pool[Math.floor(Math.random() * pool.length)]];
+ }
+
+ function updateDisplay() {
+ const [a, b] = currentPair;
+ document.getElementById("frameA").src = a.url;
+ document.getElementById("frameB").src = b.url;
+ document.getElementById("scoreA").textContent = Math.round(a.rating);
+ document.getElementById("scoreB").textContent = Math.round(b.rating);
+ }
+
+ function handleVote(side) {
+ const winner = side === "a" ? currentPair[0] : currentPair[1];
+ const loser = side === "a" ? currentPair[1] : currentPair[0];
+ calculateElo(winner, loser);
+ history.add(currentPair.map((i) => i.url).join());
+ showNextPair();
+ }
+
+ function showNextPair() {
+ currentPair = getRandomPair();
+ if (!currentPair.length) return showResults();
+ updateDisplay();
+ }
+
+ function showResults() {
+ document.getElementById("versusContainer").classList.add("hidden");
+ document.getElementById("resultsContainer").classList.remove("hidden");
+ const sorted = [...items].sort((a, b) => b.rating - a.rating);
+ const table = document.getElementById("rankingsTable");
+ table.innerHTML =
+ `RankURLScore` +
+ sorted
+ .map(
+ (item, i) => `
+
+ ${i + 1}
+ ${item.url}
+ ${Math.round(item.rating)}
+
+ `,
+ )
+ .join("");
+ }
+
+ document.getElementById("startBtn").addEventListener("click", () => {
+ const urls = document
+ .getElementById("urlInput")
+ .value.split(
+ "\
+ ",
+ )
+ .filter((url) => url.trim());
+ if (urls.length < 2) return alert("Need at least 2 URLs!");
+
+ items = urls.map((url) => ({ url, rating: 1000 }));
+ document.getElementById("versusContainer").classList.remove("hidden");
+ document.querySelector(".hero").classList.add("fade-down");
+ showNextPair();
+ });
+
+ document.querySelectorAll(".vote-button").forEach((btn) => {
+ btn.addEventListener("click", (e) => handleVote(e.target.dataset.side));
+ });
+
+ document.getElementById("exportBtn").addEventListener("click", () => {
+ const csv =
+ "URL,Score\
+ " +
+ items
+ .map((i) => `${i.url},${i.rating}`)
+ .join(
+ "\
+ ",
+ );
+ const blob = new Blob([csv], { type: "text/csv" });
+ const link = document.createElement("a");
+ link.href = URL.createObjectURL(blob);
+ link.download = "elo_rankings.csv";
+ link.click();
+ });
+
+ // Preserve state on reload
+ window.addEventListener("beforeunload", () => {
+ localStorage.setItem(
+ "eloState",
+ JSON.stringify({ items, history: [...history] }),
+ );
+ });
+
+ window.addEventListener("load", () => {
+ const saved = localStorage.getItem("eloState");
+ if (saved) {
+ const state = JSON.parse(saved);
+ items = state.items;
+ history = new Set(state.history);
+ document.getElementById("versusContainer").classList.remove("hidden");
+ showNextPair();
+ }
+ });