// ==UserScript== // @name parkrun Alphabet Challenge // @description Tracks progress on the unofficial parkrun alphabet challenge (A-Z, excluding X) with a 5x5 grid visualization and download feature. // @author Pete Johns (@johnsyweb) // @downloadURL https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/alphabet-challenge.user.js // @grant none // @homepage https://github.com/johnsyweb/tampermonkey-parkrun // @icon https://www.google.com/s2/favicons?sz=64&domain=parkrun.com.au // @license MIT // @match *://www.parkrun.ca/parkrunner/*/all* // @match *://www.parkrun.co.at/parkrunner/*/all* // @match *://www.parkrun.co.nl/parkrunner/*/all* // @match *://www.parkrun.co.nz/parkrunner/*/all* // @match *://www.parkrun.co.za/parkrunner/*/all* // @match *://www.parkrun.com.au/parkrunner/*/all* // @match *://www.parkrun.com.de/parkrunner/*/all* // @match *://www.parkrun.dk/parkrunner/*/all* // @match *://www.parkrun.fi/parkrunner/*/all* // @match *://www.parkrun.fr/parkrunner/*/all* // @match *://www.parkrun.ie/parkrunner/*/all* // @match *://www.parkrun.it/parkrunner/*/all* // @match *://www.parkrun.jp/parkrunner/*/all* // @match *://www.parkrun.lt/parkrunner/*/all* // @match *://www.parkrun.my/parkrunner/*/all* // @match *://www.parkrun.no/parkrunner/*/all* // @match *://www.parkrun.org.uk/parkrunner/*/all* // @match *://www.parkrun.pl/parkrunner/*/all* // @match *://www.parkrun.se/parkrunner/*/all* // @match *://www.parkrun.sg/parkrunner/*/all* // @match *://www.parkrun.us/parkrunner/*/all* // @namespace http://tampermonkey.net/ // @require https://html2canvas.hertzen.com/dist/html2canvas.min.js // @run-at document-end // @supportURL https://github.com/johnsyweb/tampermonkey-parkrun/issues/ // @tag parkrun // @updateURL https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/alphabet-challenge.user.js // @version 1.0.24 // ==/UserScript== (function () { 'use strict'; const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').filter((letter) => letter !== 'X'); function findResultsTable() { const tables = document.querySelectorAll('#results'); return tables[tables.length - 1]; } function extractAlphabetData(table) { const completedLetters = {}; const rows = Array.from(table.querySelectorAll('tr')).reverse(); // Reverse rows for chronological order let totalEvents = 0; let dateOfCompletion = null; for (const row of rows) { const cells = row.querySelectorAll('td'); if (cells.length < 1) continue; const eventName = cells[0].textContent.trim(); const date = cells[1].textContent.trim(); const firstLetter = eventName.charAt(0).toUpperCase(); totalEvents++; if (ALPHABET.includes(firstLetter) && !completedLetters[firstLetter]) { completedLetters[firstLetter] = { eventName, date }; // Stop processing if all 25 letters are attained if (Object.keys(completedLetters).length === 25) { dateOfCompletion = date; break; } } } const completedCount = Object.keys(completedLetters).length; return { completedLetters, remainingLetters: ALPHABET.filter((letter) => !completedLetters[letter]), completedCount, dateOfCompletion, totalEvents, }; } function createAlphabetContainer(data) { const container = document.createElement('div'); container.className = 'parkrun-alphabet-container'; container.style.width = '100%'; container.style.maxWidth = '800px'; container.style.margin = '20px auto'; container.style.padding = '20px'; container.style.backgroundColor = '#2b223d'; container.style.borderRadius = '8px'; container.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)'; container.style.color = '#e0e0e0'; container.style.textAlign = 'center'; const heading = document.createElement('h3'); heading.textContent = 'Alphabet Challenge'; heading.style.marginBottom = '15px'; heading.style.color = '#FFA300'; container.appendChild(heading); const stats = document.createElement('div'); stats.innerHTML = '