An open API service indexing awesome lists of open source software.

https://github.com/johnsyweb/tampermonkey-parkrun

Messing with parkrun pages for my own enjoyment
https://github.com/johnsyweb/tampermonkey-parkrun

parkrun parkrun-alphabet-challenge parkrun-analysis parkrun-bingo parkrun-challenges parkrun-charts parkrun-compass parkrun-data-analysis parkrun-statistics parkrun-visualisations tampermonkey userscript

Last synced: 5 days ago
JSON representation

Messing with parkrun pages for my own enjoyment

Awesome Lists containing this project

README

          

# Pete's parkrun userscripts

## A script or scripts for messing with parkrun results pages

You'll need something like [Tampermonkey](https://www.tampermonkey.net) if
you'd like to enjoy them. Or you can use the bookmarklet versions.

## Development Setup

This project uses [mise](https://mise.jdx.dev/) for development environment management.

### Prerequisites

- [mise](https://mise.jdx.dev/) - for managing development tools

### Setup

1. Install dependencies with mise (installs Ruby, Node, and pnpm):
```bash
mise install
```

2. Install npm dependencies:
```bash
pnpm install
```

The `mise.toml` file specifies the required versions:
- Ruby 3.4.7 (for Jekyll)
- Node LTS (for development tools)
- pnpm latest (for package management)

3. For microsite development, install Ruby/Jekyll dependencies:
```bash
cd docs
bundle install
```

### Previewing the Microsite Locally

The microsite documentation is built with Jekyll and served from the `docs/` directory.

1. Start the Jekyll server:
```bash
cd docs
bundle exec jekyll serve --port 4000 --host 0.0.0.0
```

2. Visit http://localhost:4000/tampermonkey-parkrun/ in your browser

### Generating Screenshots

To generate screenshots for the microsite:

```bash
pnpm screenshots
```

This will create screenshots of all userscripts in `docs/images/`.

### Building the Microsite

The microsite is automatically deployed to GitHub Pages on every push to `main`.

To build the site locally:

```bash
pnpm docs:build
```

To serve the site locally:

```bash
pnpm docs:serve
```

The site will be available at http://localhost:4000/tampermonkey-parkrun/

### Git Hooks

This project uses [husky](https://typicode.github.io/husky/) to manage git hooks. Hooks are automatically installed when you run `pnpm install`.

#### Pre-commit Hook

The pre-commit hook ensures code quality by running:
- `pnpm check-format` - Checks code formatting with Prettier
- `pnpm test` - Runs all Jest tests
- `pnpm lint` - Runs ESLint to check for linting errors

All checks must pass before a commit is allowed. This ensures all code is properly formatted, tested, and linted before being committed.

#### Pre-push Hook

The pre-push hook automatically generates screenshots when userscripts are modified:

- Checks if any `.user.js` files have been modified in the commits being pushed
- Automatically runs `pnpm screenshots` if userscripts were changed
- Prevents the push if screenshot generation fails

**Note:** Screenshots must be generated locally because parkrun websites block automated agents from accessing them in CI environments.

### GitHub Actions Workflow

The project includes a GitHub Actions workflow that:

1. Sets up Ruby (for Jekyll) and Node.js (for scripts)
2. Installs dependencies (via mise, pnpm, and bundler)
3. Updates the scripts data file from userscript metadata
4. Builds the Jekyll site
5. Deploys to GitHub Pages

This workflow runs on every push to `main` and automatically keeps the microsite up to date with the latest scripts.

## Why?

Why not?

## How to Install Bookmarklets

To use these scripts as bookmarklets, follow these steps:

1. Copy the JavaScript code for the desired bookmarklet from the sections below.
2. Open your browser's bookmarks manager.
3. Create a new bookmark.
4. Paste the JavaScript code into the URL field of the bookmark.
5. Save the bookmark.

Now, you can click the bookmark while on a parkrun results page to run the script.

## Updating Versions

To update the `@version` field in all userscripts, run the following Node.js script:

```bash
node scripts/update-version.js
```

This will automatically update the `@version` field in all `.user.js` files based on the current date and time.

## Bookmarklets

You can also use these scripts as bookmarklets by creating bookmarks with the following URLs:

### parkrun Alphabet Challenge

> Tracks progress on the unofficial parkrun alphabet challenge (A-Z, excluding X) with a 5x5 grid visualization and download feature.

[alphabet-challenge.user.js](https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/alphabet-challenge.user.js)

```javascript
javascript:(async()=>{var o,e,t,p;function c(e,t){var n,o,r,l,a="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(a)return r=!(o=!0),{s:function(){a=a.call(e)},n:function(){var e=a.next();return o=e.done,e},e:function(e){r=!0,n=e},f:function(){try{o||null==a.return||a.return()}finally{if(r)throw n}}};if(Array.isArray(e)||(a=((e,t)=>{var n;if(e)return"string"==typeof e?i(e,t):"Map"===(n="Object"===(n={}.toString.call(e).slice(8,-1))&&e.constructor?e.constructor.name:n)||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?i(e,t):void 0})(e))||t&&e&&"number"==typeof e.length)return a&&(e=a),l=0,{s:t=function(){},n:function(){return l>=e.length?{done:!0}:{done:!1,value:e[l++]}},e:function(e){throw e},f:t};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=Array(t);n')+""+l.completedCount+" of 25 letters completed"+'

After ')+l.totalEvents+" parkruns
"+(l.dateOfCompletion?'
🎉 Challenge completed on: ').concat(l.dateOfCompletion,"
"):""),o.appendChild(a),document.createElement("div"));return i.style.display="grid",i.style.gridTemplateColumns="repeat(5, 1fr)",i.style.gap=n.grid.gapSize,i.style.marginTop=n.grid.marginTop,p.forEach(function(r){var e=document.createElement("div"),t=(e.style.aspectRatio="1",e.style.position="relative",e.style.border="1px solid #666",e.style.borderRadius="4px",e.style.backgroundColor=l.completedLetters[r]?"#FFA300":"#008080",e.style.color="#fff",e.style.display="flex",e.style.flexDirection="column",e.style.alignItems="flex-start",e.style.justifyContent="flex-start",e.style.padding=n.grid.cellPadding,e.style.fontWeight="bold",e.style.fontSize="1em",e.style.cursor=l.completedLetters[r]?"pointer":"default",document.createElement("div"));t.textContent=r,t.style.fontSize=n.grid.letterFontSize,t.style.marginBottom=n.grid.letterMarginBottom,e.appendChild(t),l.completedLetters[r]&&((t=document.createElement("div")).innerHTML='
')+l.completedLetters[r].eventName+"
"+'(')+l.completedLetters[r].date+")
",n.isMobile&&(t.style.display="none"),e.appendChild(t),e.addEventListener("click",function(){var e=d(),t=document.createElement("div"),n=(t.style.position="fixed",t.style.top="0",t.style.left="0",t.style.width="100%",t.style.height="100%",t.style.backgroundColor="rgba(0, 0, 0, 0.5)",t.style.zIndex="999",t.addEventListener("click",function(){document.body.removeChild(t)}),document.createElement("div")),o=(n.style.position="fixed",n.style.top="50%",n.style.left="50%",n.style.transform="translate(-50%, -50%)",n.style.backgroundColor="#2b223d",n.style.color="#fff",e.isMobile?"15px":"20px"),o=(n.style.padding=o,n.style.borderRadius="8px",n.style.boxShadow="0 2px 4px rgba(0,0,0,0.5)",n.style.zIndex="1000",n.style.maxWidth=e.isMobile?"95%":"90%",n.style.maxHeight=e.isMobile?"85%":"80%",n.style.overflowY="auto",n.style.fontSize=e.isMobile?"0.9em":"1em",document.createElement("h4")),e=(o.textContent="Letter ".concat(r),o.style.marginBottom="10px",o.style.color="#FFA300",n.appendChild(o),document.createElement("div"));e.style.marginBottom="10px",e.innerHTML=""+l.completedLetters[r].eventName+'
'+l.completedLetters[r].date+"",n.appendChild(e),t.appendChild(n),document.body.appendChild(t)})),i.appendChild(e)}),o.appendChild(i),e=o,a=d(),(t=document.createElement("div")).style.marginTop=a.button.marginTop,t.id="alphabet-download-btn-container",(r=document.createElement("button")).textContent="💾 Save as Image",r.style.padding=a.button.padding,r.style.backgroundColor="#FFA300",r.style.color="#2b223d",r.style.border="none",r.style.borderRadius="4px",r.style.cursor="pointer",r.style.fontWeight="bold",r.style.fontSize=a.button.fontSize,r.addEventListener("mouseover",function(){this.style.backgroundColor="#e59200"}),r.addEventListener("mouseout",function(){this.style.backgroundColor="#FFA300"}),r.addEventListener("click",function(){r.style.display="none",html2canvas(e,{backgroundColor:"#2b223d",scale:2,logging:!1,allowTaint:!0,useCORS:!0}).then(function(e){r.style.display="block";var t=document.createElement("a"),n=(new Date).toISOString().split("T")[0],o=window.location.pathname.split("/")[2]||"parkrunner";t.download="alphabet-challenge-".concat(o,"-").concat(n,".png"),t.href=e.toDataURL("image/png"),t.click()})}),t.appendChild(r),e.appendChild(t),o}o="https://html2canvas.hertzen.com/dist/html2canvas.min.js",await new Promise((e,t)=>{var n=document.createElement("script");n.src=o,n.onload=e,n.onerror=t,document.head.appendChild(n)}),p="ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("").filter(function(e){return"X"!==e}),(t=(t=document.querySelectorAll("#results"))[t.length-1])?(t=t=n((e=>{var t,n={},o=0,r=null,l=c(Array.from(e.querySelectorAll("tr")).reverse());try{for(l.s();!(t=l.n()).done;){var a=t.value.querySelectorAll("td");if(!(a.length<1)){var i=a[0].textContent.trim(),d=a[1].textContent.trim(),s=i.charAt(0).toUpperCase();if(o++,p.includes(s)&&!n[s]&&(n[s]={eventName:i,date:d},25===Object.keys(n).length)){r=d;break}}}}catch(e){l.e(e)}finally{l.f()}return e=Object.keys(n).length,{completedLetters:n,remainingLetters:p.filter(function(e){return!n[e]}),completedCount:e,dateOfCompletion:r,totalEvents:o}})(t)),(e=document.querySelector("h2"))&&e.parentNode&&(e.nextSibling?e.parentNode.insertBefore(t,e.nextSibling):e.parentNode.appendChild(t))):console.log("Results table not found")})();
```

### parkrun Compass Challenge

> Visualizes your progress on the compass challenge (North, South, East, West parkruns)

[compass-challenge.user.js](https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/compass-challenge.user.js)

```javascript
javascript:(async()=>{var o,t,e,b,w;function y(t,e){return(t=>{if(Array.isArray(t))return t})(t)||((t,e)=>{var n=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=n){var o,a,r,i,c=[],s=!0,l=!1;try{if(r=(n=n.call(t)).next,0===e){if(Object(n)!==n)return;s=!1}else for(;!(s=(o=r.call(n)).done)&&(c.push(o.value),c.length!==e);s=!0);}catch(t){l=!0,a=t}finally{try{if(!s&&null!=n.return&&(i=n.return(),Object(i)!==i))return}finally{if(l)throw a}}return c}})(t,e)||((t,e)=>{var n;if(t)return"string"==typeof t?a(t,e):"Map"===(n="Object"===(n={}.toString.call(t).slice(8,-1))&&t.constructor?t.constructor.name:n)||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?a(t,e):void 0})(t,e)||(()=>{throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")})()}function a(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,o=Array(e);n').concat(t.completedCount," of 4 compass directions completed")),r=(a+='

After ').concat(t.totalEvents," parkruns
"),t.dateOfCompletion&&(a+='
🧭 Challenge completed on ').concat(t.dateOfCompletion,"
")),o.innerHTML=a,n.appendChild(o),e.isMobile?Math.min(e.compass.baseSize,window.innerWidth-40):e.compass.baseSize),a=document.createElement("div"),p=(a.style.position="relative",a.style.width="".concat(r,"px"),a.style.height="".concat(r,"px"),a.style.margin="0 auto",a.style.boxSizing="content-box",document.createElementNS("http://www.w3.org/2000/svg","svg")),m=(p.setAttribute("width","100%"),p.setAttribute("height","100%"),p.setAttribute("viewBox","0 0 ".concat(700," ").concat(700)),p.style.position="absolute",p.style.top="0",p.style.left="0",350),h=350,o=document.createElementNS("http://www.w3.org/2000/svg","circle");o.setAttribute("cx",m),o.setAttribute("cy",h),o.setAttribute("r",230),o.setAttribute("fill","#334"),o.setAttribute("stroke",b.accentColor),o.setAttribute("stroke-width","5"),p.appendChild(o);[{name:"north",angle:270,label:"N",data:t.directions.north},{name:"east",angle:0,label:"E",data:t.directions.east},{name:"south",angle:90,label:"S",data:t.directions.south},{name:"west",angle:180,label:"W",data:t.directions.west}].forEach(function(t){var e=t.angle*(Math.PI/180),n=m+230*Math.cos(e),o=h+230*Math.sin(e),a=230*.12,r=m+115*Math.cos(e),i=h+115*Math.sin(e),c=e+Math.PI/2,s=r+a*Math.cos(c),l=i+a*Math.sin(c),r=r-a*Math.cos(c),i=i-a*Math.sin(c),a=document.createElementNS("http://www.w3.org/2000/svg","path"),c="M ".concat(m," ").concat(h," L ").concat(s," ").concat(l," L ").concat(n," ").concat(o," L ").concat(r," ").concat(i," Z"),s=(a.setAttribute("d",c),a.setAttribute("fill",t.data?b.completedColor:b.pendingColor),a.setAttribute("stroke","#000"),a.setAttribute("stroke-width","1"),a.setAttribute("stroke-opacity","0.3"),p.appendChild(a),m+115*Math.cos(e)),l=h+115*Math.sin(e),n=document.createElementNS("http://www.w3.org/2000/svg","text");if(n.setAttribute("x",s),n.setAttribute("y",l),n.setAttribute("text-anchor","middle"),n.setAttribute("dominant-baseline","middle"),n.setAttribute("fill","#fff"),n.setAttribute("font-size","28px"),n.setAttribute("font-weight","bold"),n.setAttribute("stroke","#000"),n.setAttribute("stroke-width","1"),n.setAttribute("paint-order","stroke"),n.textContent=t.label,p.appendChild(n),t.data){var o=t.data.eventName.match(/(.+?)(?:\s+parkrun)?$/i),r=o?o[1]:t.data.eventName,i="".concat(r," - ").concat(t.data.date),c="text-path-".concat(t.name),a=document.createElementNS("http://www.w3.org/2000/svg","path"),d="",u=245;switch(t.name){case"north":d="M ".concat(180," ").concat(105," A ").concat(u," ").concat(u," 0 0 1 ").concat(520," ").concat(105);break;case"east":d="M ".concat(595," ").concat(180," A ").concat(u," ").concat(u," 0 0 1 ").concat(595," ").concat(520);break;case"south":d="M ".concat(180," ").concat(595," A ").concat(u," ").concat(u," 0 0 0 ").concat(520," ").concat(595);break;case"west":d="M ".concat(105," ").concat(520," A ").concat(u," ").concat(u," 0 0 1 ").concat(105," ").concat(180);break;default:console.warn("Unknown direction: ".concat(t.name))}a.setAttribute("id",c),a.setAttribute("d",d),a.setAttribute("fill","none"),p.appendChild(a);e=document.createElementNS("http://www.w3.org/2000/svg","text"),s=(e.setAttribute("fill","#FFFFFF"),e.setAttribute("font-size","14px"),e.setAttribute("font-weight","bold"),document.createElementNS("http://www.w3.org/2000/svg","textPath"));s.setAttribute("href","#".concat(c)),s.setAttribute("startOffset","50%"),s.setAttribute("text-anchor","middle"),"south"===t.name&&s.setAttribute("side","left"),s.textContent=i,e.appendChild(s),p.appendChild(e)}});var i,c,e=document.createElementNS("http://www.w3.org/2000/svg","defs"),r=document.createElementNS("http://www.w3.org/2000/svg","filter"),o=(r.setAttribute("id","text-shadow"),r.setAttribute("x","-20%"),r.setAttribute("y","-20%"),r.setAttribute("width","140%"),r.setAttribute("height","140%"),document.createElementNS("http://www.w3.org/2000/svg","feDropShadow")),o=(o.setAttribute("dx","0"),o.setAttribute("dy","0"),o.setAttribute("stdDeviation","2"),o.setAttribute("flood-color","#000000"),o.setAttribute("flood-opacity","0.7"),r.appendChild(o),e.appendChild(r),p.appendChild(e),document.createElementNS("http://www.w3.org/2000/svg","circle")),r=(o.setAttribute("cx",m),o.setAttribute("cy",h),o.setAttribute("r",30),o.setAttribute("fill",b.accentColor),p.appendChild(o),document.createElementNS("http://www.w3.org/2000/svg","text"));return r.setAttribute("x",m),r.setAttribute("y",h),r.setAttribute("text-anchor","middle"),r.setAttribute("dominant-baseline","middle"),r.setAttribute("fill",b.backgroundColor),r.setAttribute("font-size","24px"),r.setAttribute("font-weight","bold"),r.textContent="".concat(Math.round(t.completedCount/4*100),"%"),p.appendChild(r),a.appendChild(p),n.appendChild(a),i=n,e=s(),(o=document.createElement("div")).style.marginTop=e.button.marginTop,o.id="compass-download-btn-container",(c=document.createElement("button")).textContent="💾 Save as Image",c.style.padding=e.button.padding,c.style.backgroundColor=b.accentColor,c.style.color=b.backgroundColor,c.style.border="none",c.style.borderRadius="4px",c.style.cursor="pointer",c.style.fontWeight="bold",c.style.fontSize=e.button.fontSize,c.addEventListener("mouseover",function(){this.style.backgroundColor="#e59200"}),c.addEventListener("mouseout",function(){this.style.backgroundColor=b.accentColor}),c.addEventListener("click",function(){c.style.display="none",html2canvas(i,{backgroundColor:b.backgroundColor,scale:2,logging:!1,allowTaint:!0,useCORS:!0}).then(function(t){c.style.display="block";var e=document.createElement("a"),n=(new Date).toISOString().split("T")[0],o=window.location.pathname.split("/")[2]||"parkrunner";e.download="compass-challenge-".concat(o,"-").concat(n,".png"),e.href=t.toDataURL("image/png"),e.click()})}),o.appendChild(c),i.appendChild(o),n}o="https://html2canvas.hertzen.com/dist/html2canvas.min.js",await new Promise((t,e)=>{var n=document.createElement("script");n.src=o,n.onload=t,n.onerror=e,document.head.appendChild(n)}),b={backgroundColor:"#2b223d",accentColor:"#FFA300",completedColor:"#53BA9D",pendingColor:"#666",textColor:"#e0e0e0",subtleTextColor:"#cccccc"},w={north:/north/i,south:/south/i,east:/east/i,west:/west/i},document.querySelector("#results")?(t=t=n((t=>{for(var e={north:null,south:null,east:null,west:null},n=0,o=null,a=0,t=t.querySelectorAll("tr"),r=[],i=(t.forEach(function(t){var e,n,o,t=t.querySelectorAll("td");t.length<5||(e=t[0].textContent.trim(),n=t[1].textContent.trim(),o=t[2].textContent.trim(),(t=t[4].textContent.trim())&&o&&n&&e&&r.push({eventName:e,date:n,eventNumber:o,time:t}))}),r.reverse()),c=0;!o&&c Identifies and displays participants who attended both the launch event and the latest event

[launch-returnees.user.js](https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/launch-returnees.user.js)

```javascript
javascript:(async()=>{function m(t){return(t=>{if(Array.isArray(t))return r(t)})(t)||(t=>{if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)})(t)||((t,e)=>{var n;if(t)return"string"==typeof t?r(t,e):"Map"===(n="Object"===(n={}.toString.call(t).slice(8,-1))&&t.constructor?t.constructor.name:n)||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?r(t,e):void 0})(t)||(()=>{throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")})()}function r(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);ne||a The parkrun p-index is an unofficial statistic that measures the number of different parkrun events a person has completed a specific number of times. To achieve a p-index of 10, you must have completed at least 10 different parkrun events 10 times each. This script calculate the p-index for a parkrunner and displays it on their results page.

[p-index.user.js](https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/p-index.user.js)

```javascript
javascript:(async()=>{var r,t,e,n,o,i,a,l,d;function s(t,e){return(t=>{if(Array.isArray(t))return t})(t)||((t,e)=>{var n=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=n){var r,o,i,a,l=[],d=!0,s=!1;try{if(i=(n=n.call(t)).next,0===e){if(Object(n)!==n)return;d=!1}else for(;!(d=(r=i.call(n)).done)&&(l.push(r.value),l.length!==e);d=!0);}catch(t){s=!0,o=t}finally{try{if(!d&&null!=n.return&&(a=n.return(),Object(a)!==a))return}finally{if(s)throw o}}return l}})(t,e)||((t,e)=>{var n;if(t)return"string"==typeof t?u(t,e):"Map"===(n="Object"===(n={}.toString.call(t).slice(8,-1))&&t.constructor?t.constructor.name:n)||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?u(t,e):void 0})(t,e)||(()=>{throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")})()}function u(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);n tr").forEach(function(t){var e=t.querySelector("td:nth-child(1) > a").textContent.trim(),n=t.querySelector("td:nth-child(2)").textContent.trim(),t=t.querySelector("td:nth-child(3)").textContent.trim();r.unshift({eventName:e,date:n,eventNumber:t})}),r.reduce(function(t,e){var n=e.eventName,r=e.date,e=e.eventNumber;return t[n]||(t[n]=[]),t[n].push({date:r,eventNumber:e}),t},{}));return Object.entries(t).sort(function(t,e){return e[1].length-t[1].length})}function m(){var t=document.querySelectorAll("#results");return t[t.length-1]}function y(t){var t=t.filter(function(t,e){return s(t,2)[1].length>e}),o=t.length;function i(t){return new Date(t.split("/").reverse().join("-"))}t=t.map(function(t){var t=s(t,2),e=t[0],t=t[1],n=t[0],r=t[o-1];return{eventName:e,eventCount:t.length,firstDate:n.date,firstEventNumber:n.eventNumber,pIndexDate:r.date,pIndexEventNumber:r.eventNumber,firstDateForSorting:i(n.date),pIndexDateForSorting:i(r.date)}}).sort(function(t,e){return t.pIndexDateForSorting-e.pIndexDateForSorting}).slice(0,o).map(function(t){return t.eventName+" ("+t.eventCount+"): "+t.firstDate+" (#"+t.firstEventNumber+") - "+t.pIndexDate+" (#"+t.pIndexEventNumber+")"});return{pIndex:o,contributingEvents:t}}r="https://html2canvas.hertzen.com/dist/html2canvas.min.js",await new Promise((t,e)=>{var n=document.createElement("script");n.src=r,n.onload=t,n.onerror=e,document.head.appendChild(n)}),"undefined"!=typeof module&&module.exports?module.exports={calculatePIndex:y,extractEventDetails:p,findResultsTable:m}:(d=m())?(t=(d=y(p(d))).pIndex,d=d.contributingEvents,a=c(),(l=document.querySelector("h2"))&&((e=document.createElement("div")).textContent="p-index: "+t,e.style.fontSize=a.typography.pIndex,e.style.fontWeight="bold",e.style.marginTop=a.container.marginTop,e.style.backgroundColor="#2b223d",e.style.color="#ffa300",e.style.padding=a.container.padding,e.style.borderRadius="5px",e.style.display="flex",e.style.flexDirection="column",e.style.alignItems="center",e.style.justifyContent="center",e.style.width=a.container.width,e.style.maxWidth=a.container.maxWidth,e.style.marginLeft="auto",e.style.marginRight="auto",e.style.aspectRatio=a.container.aspectRatio,e.setAttribute("id","p-index-display"),(n=document.createElement("ul")).style.listStyleType="none",n.style.padding="0",n.style.marginTop=a.spacing.small,n.style.width="100%",d.forEach(function(t){var e=document.createElement("li");e.textContent=t,e.style.fontWeight="normal",e.style.fontSize=a.typography.listItem,e.style.marginBottom=a.listItem.marginBottom,e.style.textAlign=a.listItem.textAlign,e.style.wordBreak="break-word",n.appendChild(e)}),e.appendChild(n),o=e,t=c(),(d=document.createElement("div")).style.marginTop=t.button.marginTop,d.id="p-index-download-btn-container",(i=document.createElement("button")).textContent="💾 Save as Image",i.style.padding=t.button.padding,i.style.backgroundColor="#ffa300",i.style.color="#2b223d",i.style.border="none",i.style.borderRadius="4px",i.style.cursor="pointer",i.style.fontWeight="bold",i.style.fontSize=t.button.fontSize,i.addEventListener("mouseover",function(){this.style.backgroundColor="#e59200"}),i.addEventListener("mouseout",function(){this.style.backgroundColor="#ffa300"}),i.addEventListener("click",function(){i.style.display="none",html2canvas(o,{backgroundColor:"#2b223d",scale:2,logging:!1,allowTaint:!0,useCORS:!0}).then(function(t){i.style.display="block";var e=document.createElement("a"),n=(new Date).toISOString().split("T")[0],r=window.location.pathname.split("/")[2]||"parkrunner";e.download="p-index-".concat(r,"-").concat(n,".png"),e.href=t.toDataURL("image/png"),e.click()})}),d.appendChild(i),o.appendChild(d),l.parentNode.insertBefore(e,l.nextSibling),a.isMobile||setTimeout(function(){var t=e.getBoundingClientRect(),t=Math.max(t.width,t.height);e.style.width=t+"px",e.style.height=t+"px"},0))):console.error("Results table not found")})();
```

### parkrun Annual Summary

> Adds an annual participation summary (totals, averages, min/max) to parkrun event history pages

[parkrun-annual-summary.user.js](https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/parkrun-annual-summary.user.js)

```javascript
javascript:(async()=>{var r,w,E,S;function p(e){return(p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function A(e){return(e=>{if(Array.isArray(e))return o(e)})(e)||(e=>{if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)})(e)||n(e)||(()=>{throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")})()}function F(){var h,e="function"==typeof Symbol?Symbol:{},t=e.iterator||"@@iterator",n=e.toStringTag||"@@toStringTag";function r(e,t,n,r){var o,a,l,i,s,c,u,d,p,t=t&&t.prototype instanceof f?t:f,t=Object.create(t.prototype);return b(t,"_invoke",(o=e,a=n,u=r||[],d=!1,p={p:c=0,n:0,v:h,a:m,f:m.bind(h,4),d:function(e,t){return l=e,i=0,s=h,p.n=t,y}},function(e,t,n){if(1t||a(e=((e,t)=>{if("object"!=p(e)||!e)return e;var n=e[Symbol.toPrimitive];if(void 0===n)return("string"===t?String:Number)(e);if("object"!=p(n=n.call(e,t||"default")))return n;throw new TypeError("@@toPrimitive must return a primitive value.")})(e,"string"),"symbol"==p(e)?e:e+""))(e))in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n}):Object.getOwnPropertyDescriptors?Object.defineProperties(r,Object.getOwnPropertyDescriptors(o)):t(Object(o)).forEach(function(e){Object.defineProperty(r,e,Object.getOwnPropertyDescriptor(o,e))})}return r}function N(e,t){return(e=>{if(Array.isArray(e))return e})(e)||((e,t)=>{var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var r,o,a,l,i=[],s=!0,c=!1;try{if(a=(n=n.call(e)).next,0===t){if(Object(n)!==n)return;s=!1}else for(;!(s=(r=a.call(n)).done)&&(i.push(r.value),i.length!==t);s=!0);}catch(e){c=!0,o=e}finally{try{if(!s&&null!=n.return&&(l=n.return(),Object(l)!==l))return}finally{if(c)throw o}}return i}})(e,t)||n(e,t)||(()=>{throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")})()}function n(e,t){var n;if(e)return"string"==typeof e?o(e,t):"Map"===(n="Object"===(n={}.toString.call(e).slice(8,-1))&&e.constructor?e.constructor.name:n)||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?o(e,t):void 0}function o(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n\n

parkrun Annual Summary

\n
\n

Event: '.concat(n.eventShortName,"
\n

Generated: ").concat(n.generatedAt,"
\n
\n \n "),e.a(2,'parkrun Annual Summary - '.concat(n.eventShortName,"").concat(a,"").concat(l).concat(r.outerHTML,""))},e)}))).apply(this,arguments)}function P(){return e.apply(this,arguments)}function e(){return(e=O(F().m(function e(t,n){var r,o;return F().w(function(e){for(;;)switch(e.n){case 0:return e.n=1,function(){return l.apply(this,arguments)}(t,n);case 1:return r=e.v,o="parkrun-annual-summary-".concat(n.eventShortName,"-").concat(n.generatedAtISO,".html"),e.a(2,{blob:new Blob([r],{type:"text/html"}),filename:o})}},e)}))).apply(this,arguments)}function i(){return(i=O(F().m(function e(t,n){var r,o,a,l,i,s,c;return F().w(function(e){for(;;)switch(e.p=e.n){case 0:return e.p=0,r="".concat(n,"/").concat(t,"/results/eventhistory/"),e.n=1,fetch(r);case 1:return r=e.v,e.n=2,r.text();case 2:return o=e.v,c=new DOMParser,c=c.parseFromString(o,"text/html"),o=null!=(o=null==(o=c.querySelector("h1"))?void 0:o.textContent.trim())?o:"".concat(t," Event History"),a=[],l=[],i=[],s=[],c=c.querySelectorAll("tr.Results-table-row"),Array.from(c).reverse().forEach(function(e){var t=e.getAttribute("data-parkrun"),t=(t&&a.push(t),e.getAttribute("data-date")),t=(t&&(t=new Date(t).toLocaleDateString(void 0,{year:"numeric",month:"short",day:"numeric"}),l.push(t)),e.getAttribute("data-finishers")),t=(t&&i.push(parseInt(t,10)),e.getAttribute("data-volunteers"));t&&s.push(parseInt(t,10))}),e.a(2,{eventName:t,title:o,eventNumbers:a,dates:l,finishers:i,volunteers:s});case 3:return e.p=3,c=e.v,console.error("Failed to fetch event history for ".concat(t,":"),c),e.a(2,null)}},e,null,[[0,3]])}))).apply(this,arguments)}function R(a){var l={};return a.dates.forEach(function(e,t){var e=new Date(e).getFullYear(),n=null!=(n=a.finishers[t])?n:0,r=null!=(r=a.volunteers[t])?r:0,o=a.eventNumbers[t],e=(l[e]||(l[e]={year:e,eventCount:0,totalFinishers:0,totalVolunteers:0,minFinishers:null,maxFinishers:null,minVolunteers:null,maxVolunteers:null}),l[e]);e.eventCount++,e.totalFinishers+=n,e.totalVolunteers+=r,(null===e.minFinishers||ne.maxFinishers.value)&&(e.maxFinishers={value:n,date:a.dates[t],eventNumber:o}),(null===e.minVolunteers||re.maxVolunteers.value)&&(e.maxVolunteers={value:r,date:a.dates[t],eventNumber:o})}),Object.keys(l).map(Number).sort(function(e,t){return e-t}).map(function(e){var t=l[e];return{year:e,eventCount:t.eventCount,totalFinishers:t.totalFinishers,totalVolunteers:t.totalVolunteers,avgFinishers:Math.round(t.totalFinishers/t.eventCount),avgVolunteers:Math.round(t.totalVolunteers/t.eventCount),minFinishers:t.minFinishers,maxFinishers:t.maxFinishers,minVolunteers:t.minVolunteers,maxVolunteers:t.maxVolunteers,finishersGrowth:null,volunteersGrowth:null}}).map(function(e,t,n){return 0===t?e:(t=(n=n[t-1]).avgFinishers?(e.avgFinishers-n.avgFinishers)/n.avgFinishers*100:null,n=n.avgVolunteers?(e.avgVolunteers-n.avgVolunteers)/n.avgVolunteers*100:null,T(T({},e),{},{finishersGrowth:t,volunteersGrowth:n}))})}function m(e){return e?e.value.toLocaleString():"-"}function h(e){var t,n;return null===e||Number.isNaN(e)?"-":(t=0').concat(t).concat(e.toFixed(1),"%"))}function L(o){var e=document.createElement("div"),t=(e.style.marginBottom="15px",e.style.padding="10px",e.style.backgroundColor=w.backgroundColor,e.style.borderRadius="6px",e.style.display="flex",e.style.alignItems="center",e.style.gap="10px",e.style.flexWrap="wrap",document.createElement("span")),a=(t.textContent="Compare with:",t.style.color=w.textColor,t.style.fontWeight="bold",e.appendChild(t),document.createElement("select")),t=(a.style.padding="6px 12px",a.style.backgroundColor="#3a3250",a.style.color=w.textColor,a.style.border="1px solid ".concat(w.gridColor),a.style.borderRadius="4px",a.style.cursor="pointer",document.createElement("option")),l=(t.value="",t.textContent="-- Select parkrun --",a.appendChild(t),o.forEach(function(e){var t=document.createElement("option");t.value=e.properties.eventname,t.textContent="".concat(e.properties.EventShortName," (").concat(e.distance.toFixed(1),"km)"),a.appendChild(t)}),document.createElement("button"));return l.textContent="+ Add",l.style.padding="6px 12px",l.style.backgroundColor=w.lineColor,l.style.color="#2b223d",l.style.border="none",l.style.borderRadius="4px",l.style.cursor="pointer",l.style.fontWeight="bold",l.addEventListener("click",O(F().m(function e(){var t,n,r;return F().w(function(e){for(;;)switch(e.n){case 0:if(t=a.value){e.n=1;break}return e.a(2);case 1:if(S.comparisonEvents.some(function(e){return e.eventName===t}))return alert("This parkrun is already selected for comparison"),e.a(2);e.n=2;break;case 2:return l.disabled=!0,l.textContent="Loading...",n=j(),e.n=3,function(){return i.apply(this,arguments)}(t,n.url);case 3:(n=e.v)?(r=o.find(function(e){return e.properties.eventname===t}),S.comparisonEvents.push(T(T({},n),{},{distance:null==r?void 0:r.distance})),W()):alert("Failed to fetch event history"),l.disabled=!1,l.textContent="+ Add",a.value="";case 4:return e.a(2)}},e)}))),e.appendChild(a),e.appendChild(l),e}function M(e,t){var n=R(e);if(0===n.length)return null;var e=document.createElement("div"),r=(e.className="tab-content",e.style.display=0===t?"block":"none",e.style.backgroundColor=w.backgroundColor,e.style.padding="15px",e.style.borderRadius="6px",document.createElement("div")),o=(r.style.overflowX="auto",document.createElement("table")),a=(o.className="annualSummaryTable",o.style.width="100%",o.style.borderCollapse="collapse",o.style.fontSize="14px",o.style.color=w.textColor,o.style.backgroundColor=w.backgroundColor,{key:"year",dir:"asc"}),l=[{key:"year",label:"Year",align:"left"},{key:"eventCount",label:"Events",align:"center"},{key:"totalFinishers",label:"Finishers Total",align:"right",color:w.barColor},{key:"minFinishers",label:"Finishers Min",align:"right",color:w.barColor},{key:"maxFinishers",label:"Finishers Max",align:"right",color:w.barColor},{key:"avgFinishers",label:"Finishers Avg",align:"right",color:w.barColor},{key:"finishersGrowth",label:"Finishers YoY",align:"right"},{key:"totalVolunteers",label:"Volunteers Total",align:"right",color:w.lineColor},{key:"minVolunteers",label:"Volunteers Min",align:"right",color:w.lineColor},{key:"maxVolunteers",label:"Volunteers Max",align:"right",color:w.lineColor},{key:"avgVolunteers",label:"Volunteers Avg",align:"right",color:w.lineColor},{key:"volunteersGrowth",label:"Volunteers YoY",align:"right"}],i=document.createElement("thead"),s=document.createElement("tr"),c=(s.style.borderBottom="2px solid ".concat(w.gridColor),l.forEach(function(e){var t=document.createElement("th");t.textContent=e.label,t.style.padding="10px",t.style.textAlign=e.align,t.style.cursor="pointer",e.color&&(t.style.color=e.color),t.addEventListener("click",function(){a.key===e.key?a.dir="asc"===a.dir?"desc":"asc":(a.key=e.key,a.dir="desc"),u()}),s.appendChild(t)}),i.appendChild(s),o.appendChild(i),document.createElement("tbody"));function u(){c.innerHTML="",A(n).sort(function(e,t){function n(e){return null==(e=e[r])?-1/0:"object"===p(e)&&void 0!==e.value?e.value:e}var r=a.key,o="asc"===a.dir?1:-1,e=n(e),t=n(t);return e===t?0:t'.concat(e.year,'\n ').concat(e.eventCount,'\n ').concat(e.totalFinishers.toLocaleString(),'\n ').concat(m(e.minFinishers),'\n ').concat(m(e.maxFinishers),'\n ').concat(e.avgFinishers,'\n ').concat(h(e.finishersGrowth),'\n ').concat(e.totalVolunteers.toLocaleString(),'\n ').concat(m(e.minVolunteers),'\n ').concat(m(e.maxVolunteers),'\n ').concat(e.avgVolunteers,'\n ').concat(h(e.volunteersGrowth),"\n "),c.appendChild(t)})}o.appendChild(c),r.appendChild(o),e.appendChild(r),u();var l=document.createElement("div"),i=(l.style.display="grid",l.style.gridTemplateColumns="1fr 1fr",l.style.gap="20px",l.style.marginTop="20px",document.createElement("div")),o=(i.style.minWidth="0",document.createElement("canvas")),r=(o.className="annualTotalsChart-".concat(t),i.appendChild(o),document.createElement("div")),d=(r.style.minWidth="0",document.createElement("canvas"));return d.className="annualGrowthChart-".concat(t),r.appendChild(d),l.appendChild(i),l.appendChild(r),e.appendChild(l),"undefined"!=typeof Chart&&(t=o.getContext("2d"),i=d.getContext("2d"),r=n.map(function(e){return e.year.toString()}),new Chart(t,{type:"bar",data:{labels:r,datasets:[{label:"Total Finishers",data:n.map(function(e){return e.totalFinishers}),backgroundColor:w.barColor,borderColor:w.barColor,borderWidth:1},{label:"Total Volunteers",data:n.map(function(e){return e.totalVolunteers}),backgroundColor:w.lineColor,borderColor:w.lineColor,borderWidth:1}]},options:{animation:!1,responsive:!0,maintainAspectRatio:!0,aspectRatio:1.3,plugins:{legend:{labels:{color:w.textColor}},title:{display:!0,text:"Annual Totals",color:w.textColor}},scales:{x:{title:{display:!0,text:"Year",color:w.textColor},ticks:{color:w.subtleTextColor},grid:{color:w.gridColor}},y:{beginAtZero:!0,title:{display:!0,text:"Participants",color:w.textColor},ticks:{precision:0,color:w.subtleTextColor},grid:{color:w.gridColor}}}}}),l=n.filter(function(e){return null!==e.finishersGrowth}),new Chart(i,{type:"line",data:{labels:l.map(function(e){return e.year.toString()}),datasets:[{label:"Finishers Growth",data:l.map(function(e){return e.finishersGrowth}),borderColor:w.barColor,backgroundColor:w.barColor,borderWidth:2,pointRadius:4,pointBackgroundColor:w.barColor,fill:!1,tension:.2},{label:"Volunteers Growth",data:l.map(function(e){return e.volunteersGrowth}),borderColor:w.lineColor,backgroundColor:w.lineColor,borderWidth:2,pointRadius:4,pointBackgroundColor:w.lineColor,fill:!1,tension:.2}]},options:{animation:!1,responsive:!0,maintainAspectRatio:!0,aspectRatio:1.3,plugins:{legend:{labels:{color:w.textColor}},title:{display:!0,text:"Year-over-Year Growth (%)",color:w.textColor}},scales:{x:{title:{display:!0,text:"Year",color:w.textColor},ticks:{color:w.subtleTextColor},grid:{color:w.gridColor}},y:{title:{display:!0,text:"Growth (%)",color:w.textColor},ticks:{color:w.subtleTextColor,callback:function(e){return e+"%"}},grid:{color:w.gridColor}}}}})),e}function G(e){var t=document.createElement("div");return t.style.minWidth="0",t.appendChild(e),t}function I(e,t,o,n,a,r,l){l=6{var n=document.createElement("script");n.src=r,n.onload=e,n.onerror=t,document.head.appendChild(n)}),w={backgroundColor:"#1c1b2a",barColor:"#f59e0b",lineColor:"#22d3ee",textColor:"#f3f4f6",subtleTextColor:"#d1d5db",gridColor:"rgba(243, 244, 246, 0.18)"},E=["#f59e0b","#22d3ee","#f97316","#10b981","#a855f7","#ef4444","#3b82f6","#84cc16"],S={currentEvent:null,comparisonEvents:[],allParkruns:null},function(){c.apply(this,arguments)}()})();
```

### parkrun Cancellation Impact

> Analyzes the impact of cancelled parkrun events on nearby alternatives

[parkrun-cancellation-impact.user.js](https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/parkrun-cancellation-impact.user.js)

```javascript
javascript:(async()=>{var r,k,v,C,g,w;function a(e){return(a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function E(){var g,e="function"==typeof Symbol?Symbol:{},t=e.iterator||"@@iterator",n=e.toStringTag||"@@toStringTag";function r(e,t,n,r){var o,a,l,s,i,c,d,u,p,t=t&&t.prototype instanceof m?t:m,t=Object.create(t.prototype);return b(t,"_invoke",(o=e,a=n,d=r||[],u=!1,p={p:c=0,n:0,v:g,a:h,f:h.bind(g,4),d:function(e,t){return l=e,s=0,i=g,p.n=t,y}},function(e,t,n){if(1t||a{if(Array.isArray(e))return o(e)})(e)||(e=>{if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)})(e)||n(e)||(()=>{throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")})()}function t(t,e){var n,r=Object.keys(t);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(t),e&&(n=n.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,n)),r}function d(r){for(var e=1;e(e=((e,t)=>{if("object"!=a(e)||!e)return e;var n=e[Symbol.toPrimitive];if(void 0===n)return("string"===t?String:Number)(e);if("object"!=a(n=n.call(e,t||"default")))return n;throw new TypeError("@@toPrimitive must return a primitive value.")})(e,"string"),"symbol"==a(e)?e:e+""))(e))in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n}):Object.getOwnPropertyDescriptors?Object.defineProperties(r,Object.getOwnPropertyDescriptors(o)):t(Object(o)).forEach(function(e){Object.defineProperty(r,e,Object.getOwnPropertyDescriptor(o,e))})}return r}function u(e,t){return(e=>{if(Array.isArray(e))return e})(e)||((e,t)=>{var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var r,o,a,l,s=[],i=!0,c=!1;try{if(a=(n=n.call(e)).next,0===t){if(Object(n)!==n)return;i=!1}else for(;!(i=(r=a.call(n)).done)&&(s.push(r.value),s.length!==t);i=!0);}catch(e){c=!0,o=e}finally{try{if(!i&&null!=n.return&&(l=n.return(),Object(l)!==l))return}finally{if(c)throw o}}return s}})(e,t)||n(e,t)||(()=>{throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")})()}function n(e,t){var n;if(e)return"string"==typeof e?o(e,t):"Map"===(n="Object"===(n={}.toString.call(e).slice(8,-1))&&e.constructor?e.constructor.name:n)||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?o(e,t):void 0}function o(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n{var e=null!=(e=null==(e=document.querySelector("h1"))?void 0:e.textContent.trim())?e:"Event History",n=[],r=[],o=[],a=[],l=[],t=document.querySelectorAll("tr.Results-table-row");return Array.from(t).reverse().forEach(function(e){var t=e.getAttribute("data-parkrun"),t=(t&&n.push(t),e.getAttribute("data-date")),t=(t&&(o.push(t),t=new Date(t).toLocaleDateString(void 0,{year:"numeric",month:"short",day:"numeric"}),r.push(t)),e.getAttribute("data-finishers")),t=(t&&a.push(parseInt(t,10)),e.getAttribute("data-volunteers"));t&&l.push(parseInt(t,10))}),{title:e,eventNumbers:n,dates:r,rawDates:o,finishers:a,volunteers:l}})()).eventNumbers.length)return console.log("No event history data found"),e.a(2);e.n=1;break;case 1:if(n=(e=>{var t=e.rawDates.map(x);if(t.length<2)return null;for(var n=[],r=1;r{var t=document.createElement("div"),n=(t.style.padding="20px",t.style.backgroundColor="#2b223d",t.style.borderRadius="8px",t.style.marginBottom="20px",t.style.border="1px solid ".concat(k.gridColor),document.createElement("h3")),r=(n.textContent="Cancellation Impact Analysis",n.style.color=k.barColor,n.style.margin="0 0 15px 0",n.style.fontSize="20px",t.appendChild(n),(n=document.createElement("div")).style.fontSize="16px",n.style.color=k.textColor,n.style.marginBottom="15px",n.innerHTML='').concat(e,""),t.appendChild(n),(e=document.createElement("div")).style.fontSize="14px",e.style.lineHeight="1.8",e.style.color=k.subtleTextColor,e.style.marginBottom="18px",e.innerHTML="📍 Analyzing impact on nearby parkruns within 50km",t.appendChild(e),document.createElement("button"));return r.textContent="▶ Start Analysis",r.className="start-analysis-btn",r.style.padding="12px 24px",r.style.backgroundColor=k.lineColor,r.style.color="#1c1b2a",r.style.border="none",r.style.borderRadius="6px",r.style.cursor="pointer",r.style.fontWeight="bold",r.style.fontSize="14px",r.style.transition="all 0.2s",r.addEventListener("mouseenter",function(){r.style.backgroundColor="#0ea5e9",r.style.transform="translateY(-1px)"}),r.addEventListener("mouseleave",function(){r.style.backgroundColor=k.lineColor,r.style.transform="translateY(0)"}),t.appendChild(r),{section:t,startButton:r}})(l),s=l.section,i=l.startButton,o.appendChild(s),c("h1",o),i.addEventListener("click",function(){i.disabled=!0,i.textContent="Starting...",i.style.opacity="0.6",i.style.cursor="not-allowed";var e=(()=>{var e=document.createElement("div"),t=(e.className="parkrun-cancellation-progress",e.style.padding="15px",e.style.backgroundColor=k.backgroundColor,e.style.borderRadius="6px",e.style.marginBottom="15px",e.style.border="1px solid ".concat(k.gridColor),document.createElement("h4")),r=(t.textContent="Analyzing Nearby parkrun Impact",t.style.margin="0 0 12px 0",t.style.color=k.barColor,e.appendChild(t),(t=document.createElement("div")).style.width="100%",t.style.height="20px",t.style.backgroundColor="#3a3250",t.style.borderRadius="4px",t.style.marginBottom="10px",t.style.overflow="hidden",document.createElement("div")),o=(r.style.width="0%",r.style.height="100%",r.style.backgroundColor=k.lineColor,r.style.transition="width 0.3s ease",t.appendChild(r),e.appendChild(t),document.createElement("div")),n=(o.style.fontSize="13px",o.style.color=k.subtleTextColor,o.style.marginBottom="12px",e.appendChild(o),document.createElement("div"));return n.style.fontSize="12px",n.style.color=k.lineColor,n.style.fontWeight="bold",n.style.marginBottom="10px",e.appendChild(n),(t=document.createElement("button")).textContent="Stop Analysis",t.style.padding="6px 12px",t.style.backgroundColor=k.alertColor,t.style.color=k.textColor,t.style.border="none",t.style.borderRadius="4px",t.style.cursor="pointer",t.style.fontWeight="bold",t.style.fontSize="12px",e.appendChild(t),{progressSection:e,updateProgress:function(e,t){var n=Math.round(e/t*100);r.style.width=n+"%",o.textContent="".concat(e,"/").concat(t," parkruns analyzed")},updateStatus:function(e){n.textContent=e},stop:t,hide:function(){e.style.display="none"}}})();s.insertAdjacentElement("afterend",e.progressSection),w.fetchController=new AbortController,function(){h.apply(this,arguments)}(e,o,s)});case 5:return e.a(2)}},e)}))).apply(this,arguments)}function h(){return(h=D(E().m(function e(t,n,r){var o,a,l,c,s,i,d,u,p,h,g,y,m,f,b;return E().w(function(e){for(;;)switch(e.p=e.n){case 0:o=T(),a=w.nearbyParkruns,s=(e=>{var t=e.rawDates.map(x);if(t.length<2)return[];for(var n=[],r=1;r{var n=[],r=(6-(e=x(e.toISOString().split("T")[0])).getUTCDay())%7,o=(0===r&&(r=7),new Date(e));for(o.setUTCDate(o.getUTCDate()+r);o{for(var n=t.toISOString().split("T")[0],r=0;r⚠ No Valid Analysis Dates\n

All detected cancellation dates appear to be part of global cancellation periods (e.g., COVID-19).

\n

\n No nearby parkruns held events on these dates, indicating system-wide cancellations rather than single-event cancellations.\n

\n '),n.appendChild(m),t.stop.addEventListener("click",function(){t.hide()}),e.a(2);e.n=9;break;case 9:t.updateStatus("Analysis complete! Found ".concat(g.length," valid cancellation date(s)")),(m=document.querySelector(".start-analysis-btn"))&&(m.style.display="none"),t.stop.textContent="Close",t.stop.style.backgroundColor=k.successColor,w.resultsByDate=h,w.cancellationDates=y,w.analysisComplete=!0,-1===w.currentCancellationIndex&&0{var e=document.createElement("div"),t=(e.className="parkrun-cancellation-nav",e.style.padding="15px",e.style.backgroundColor="#2b223d",e.style.borderRadius="8px",e.style.marginBottom="20px",e.style.border="1px solid ".concat(k.gridColor),document.createElement("div"));t.style.color=k.textColor,t.style.fontSize="14px",t.style.marginBottom="12px",t.innerHTML="\n ".concat(a.length," Cancellation Date").concat(1!==a.length?"s":"",' Available\n
\n Use dropdown or buttons to navigate • Keyboard: \n
\n '),e.appendChild(t);(t=document.createElement("div")).style.display="flex",t.style.alignItems="center",t.style.gap="8px",t.style.flexWrap="wrap";var n=document.createElement("button"),s=(n.textContent="←",n.style.padding="6px 10px",l\n

parkrun Cancellation Impact

\n
\n

Event: '.concat(n.eventShortName,"
\n

Cancelled date: ").concat(n.cancellationDateStr,"
\n

Generated: ").concat(n.generatedAt,"
\n
\n \n "),e.a(2,'parkrun Cancellation Impact - '.concat(n.eventShortName," - ").concat(n.cancellationDateStr,"").concat(a,"").concat(l).concat(r.outerHTML,""))},e)}))).apply(this,arguments)}function P(){return e.apply(this,arguments)}function e(){return(e=D(E().m(function e(t,n){var r,o;return E().w(function(e){for(;;)switch(e.n){case 0:return e.n=1,function(){return y.apply(this,arguments)}(t,n);case 1:return r=e.v,o="parkrun-cancellation-impact-".concat(n.eventShortName,"-").concat(n.cancellationDateStr,".html"),e.a(2,{blob:new Blob([r],{type:"text/html"}),filename:o})}},e)}))).apply(this,arguments)}function I(e,t){var n=A(t,C),t=(n.end=new Date(t),n.end.setUTCDate(n.end.getUTCDate()-1),O(e,n.start,n.end));return{window:n,filtered:t,baseline:L(t)}}function F(e,t,n,r){function o(e){var t=p.querySelector("tbody"),c=(t&&t.remove(),document.createElement("tbody"));e.forEach(function(e){var t,n,r=document.createElement("tr"),o=(r.style.borderBottom="1px solid ".concat(k.gridColor),r.style.transition="background-color 0.15s ease",null!==e.eventOnDate),a=(o||(r.style.opacity="0.6"),r.addEventListener("mouseenter",function(){r.style.backgroundColor=o?"rgba(34, 211, 238, 0.08)":"rgba(243, 244, 246, 0.03)"}),r.addEventListener("mouseleave",function(){r.style.backgroundColor="transparent"}),document.createElement("td")),l=(a.style.padding="10px",a.style.textAlign="left",a.style.fontWeight="bold",document.createElement("a")),a=(l.href="".concat(T().url,"/").concat(e.eventName,"/results/eventhistory/"),l.textContent=e.displayName||e.eventName,l.style.color=k.lineColor,l.style.textDecoration="none",l.target="_blank",l.addEventListener("mouseenter",function(){l.style.textDecoration="underline"}),l.addEventListener("mouseleave",function(){l.style.textDecoration="none"}),a.appendChild(l),r.appendChild(a),document.createElement("td")),a=(a.style.padding="10px",a.style.textAlign="right",a.style.color=k.subtleTextColor,a.textContent="".concat(e.distance,"km"),r.appendChild(a),document.createElement("td")),a=(a.style.padding="10px",a.style.textAlign="right",e.eventOnDate&&e.eventOnDate.eventNumber?(a.textContent=e.eventOnDate.eventNumber,a.style.color=k.textColor):(a.textContent="—",a.style.color=k.subtleTextColor),r.appendChild(a),document.createElement("td")),a=(a.style.padding="10px",a.style.textAlign="right",a.innerHTML="".concat(e.baseline.avgFinishers," / ").concat(e.baseline.avgVolunteers),r.appendChild(a),document.createElement("td")),a=(a.style.padding="10px",a.style.textAlign="right",e.eventOnDate?a.innerHTML="".concat(e.eventOnDate.finishers," / ").concat(e.eventOnDate.volunteers):(a.textContent="—",a.style.color=k.subtleTextColor),r.appendChild(a),document.createElement("td")),s=(a.style.padding="10px",a.style.textAlign="right",e.change?(t=0').concat(t).concat(e.change.finishersChange,' /\n ').concat(i).concat(e.change.volunteersChange,"\n ")):(a.textContent="—",a.style.color=k.subtleTextColor),r.appendChild(a),document.createElement("td")),i=(s.style.padding="10px",s.style.textAlign="right",e.change?(t=0').concat(n).concat(e.change.finishersPct.toFixed(1),"%")):(s.textContent="—",s.style.color=k.subtleTextColor),r.appendChild(s),document.createElement("td"));i.style.padding="10px",i.style.textAlign="right",e.eventOnDate?e.change.finishersChange<-5?(i.textContent="↓ Loss",i.style.color=k.alertColor):5Average finishers: ".concat(g.baseline.avgFinishers,"\n
Min finishers: ").concat(g.baseline.minFinishers,"
\n
Max finishers: ").concat(g.baseline.maxFinishers,"
\n
Average volunteers: ").concat(g.baseline.avgVolunteers,"
\n
Min volunteers: ").concat(g.baseline.minVolunteers,"
\n
Max volunteers: ").concat(g.baseline.maxVolunteers,"
\n
Total events: ").concat(g.baseline.totalEvents,"
\n "),t.appendChild(n),"undefined"!=typeof Chart&&g.filtered.finishers&&0".concat(f.displayName||f.eventName," (+").concat(f.change.finishersChange," finishers, ").concat(f.change.finishersPct.toFixed(1),"%)"))),r[0]&&(f=r[0],t.push("Largest relative gain: ".concat(f.displayName||f.eventName," (+").concat(f.change.finishersPct.toFixed(1),"%, +").concat(f.change.finishersChange," finishers)"))),n.innerHTML=t.join("
"),m.appendChild(n),f=[y[0],r[0]].filter(Boolean),l=new Set,f.forEach(function(e){var t,n,r,o,a;e&&e.seasonalTrend&&e.seasonalTrend.filtered&&(l.has(e.eventName)||(l.add(e.eventName),(t=document.createElement("div")).style.marginTop="12px",t.style.padding="10px",t.style.backgroundColor="#3a3250",t.style.borderRadius="4px",(n=document.createElement("div")).style.color=k.textColor,n.style.fontWeight="bold",n.style.marginBottom="6px",n.textContent=e.displayName||e.eventName,t.appendChild(n),(n=document.createElement("div")).style.color=k.textColor,n.style.fontSize="12px",n.style.display="grid",n.style.gridTemplateColumns="repeat(auto-fit, minmax(160px, 1fr))",n.style.gap="6px",n.innerHTML="\n
Avg finishers: ".concat(e.seasonalTrend.baseline.avgFinishers,"
\n
Min finishers: ").concat(e.seasonalTrend.baseline.minFinishers,"
\n
Max finishers: ").concat(e.seasonalTrend.baseline.maxFinishers,"
\n
Avg volunteers: ").concat(e.seasonalTrend.baseline.avgVolunteers,"
\n
Min volunteers: ").concat(e.seasonalTrend.baseline.minVolunteers,"
\n
Max volunteers: ").concat(e.seasonalTrend.baseline.maxVolunteers,"
\n "),t.appendChild(n),"undefined"!=typeof Chart&&e.seasonalTrend.filtered.finishers&&0\n '.concat(s.length,' nearby parkruns analyzed\n \n \n

\n ').concat(f.length," held events on ").concat(i,'\n \n

\n

\n Average change in finishers: ').concat(0\n \n

\n

\n Average change in volunteers: ').concat(0\n \n

\n

\n Estimated total additional finishers: ').concat(0\n \n

\n

\n Estimated total additional volunteers: ').concat(0\n \n

\n '),t.appendChild(v),u.appendChild(t),document.createElement("div")),C=(f.style.display="flex",f.style.justifyContent="center",f.style.marginTop="20px",f.style.gap="10px",f.style.flexWrap="wrap",document.createElement("button")),x=(C.textContent="📄 Export HTML",C.style.padding="8px 16px",C.style.backgroundColor=k.barColor,C.style.color="#1c1b2a",C.style.border="none",C.style.borderRadius="4px",C.style.cursor="pointer",C.style.fontWeight="bold",C.style.fontSize="14px",C.addEventListener("click",D(E().m(function e(){var t,n,r,o,a,l,s;return E().w(function(e){for(;;)switch(e.p=e.n){case 0:return t=C.textContent,n=C.style.display,C.textContent="Exporting...",C.disabled=!0,C.style.display="none",e.p=1,o=d(),r=o.eventShortName,o=o.cancellationDateStr,e.n=2,P(u,{eventShortName:r,cancellationDateStr:o,generatedAt:(new Date).toLocaleString()});case 2:r=e.v,o=r.blob,s=r.filename,a=URL.createObjectURL(o),(l=document.createElement("a")).href=a,l.download=s,l.click(),setTimeout(function(){return URL.revokeObjectURL(a)},1e3),console.log("HTML export complete"),e.n=4;break;case 3:e.p=3,s=e.v,console.error("HTML export failed:",s),alert("Error exporting HTML: "+s.message);case 4:return e.p=4,C.disabled=!1,C.textContent=t,C.style.display=n,e.f(4);case 5:return e.a(2)}},e,null,[[1,3,4,5]])}))),document.createElement("button"));x.textContent="📤 Share Report",x.style.padding="8px 16px",x.style.backgroundColor=k.lineColor,x.style.color="#2b223d",x.style.border="none",x.style.borderRadius="4px",x.style.cursor="pointer",x.style.fontWeight="bold",x.style.fontSize="14px",x.addEventListener("click",D(E().m(function e(){var t,n,r,o,a,l,s,i,c;return E().w(function(e){for(;;)switch(e.p=e.n){case 0:return t=x.textContent,n=x.style.display,x.textContent="Sharing...",x.disabled=!0,x.style.display="none",e.p=1,o=d(),r=o.eventShortName,o=o.cancellationDateStr,e.n=2,P(u,{eventShortName:r,cancellationDateStr:o,generatedAt:(new Date).toLocaleString()});case 2:if(l=e.v,a=l.blob,l=l.filename,i=new File([a],l,{type:"text/html"}),navigator.canShare&&navigator.canShare({files:[i]}))return e.n=3,navigator.share({title:"parkrun Cancellation Impact - ".concat(r),text:"Cancellation date: ".concat(o),files:[i]});e.n=4;break;case 3:console.log("Report shared via Web Share API"),e.n=5;break;case 4:s=URL.createObjectURL(a),(i=document.createElement("a")).href=s,i.download=l,i.click(),setTimeout(function(){return URL.revokeObjectURL(s)},1e3),alert("Sharing is not supported in this browser, so the HTML report was downloaded instead.");case 5:e.n=7;break;case 6:e.p=6,c=e.v,console.error("Share failed:",c),alert("Error sharing report: "+c.message);case 7:return e.p=7,x.disabled=!1,x.textContent=t,x.style.display=n,e.f(7);case 8:return e.a(2)}},e,null,[[1,6,7,8]])}))),f.appendChild(C),f.appendChild(x),u.appendChild(f),e.appendChild(u)}function m(){return(m=D(E().m(function e(){var t,n;return E().w(function(e){for(;;)switch(e.n){case 0:if(t=document.querySelector(".Results-table"),n=window.location.href,n=n.includes("/eventhistory/"),t&&n){e.n=1;break}return e.a(2);case 1:return e.n=2,function(){return l.apply(this,arguments)}();case 2:w.allParkruns=e.v,!function(){s.apply(this,arguments)}();case 3:return e.a(2)}},e)}))).apply(this,arguments)}r="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js",await new Promise((e,t)=>{var n=document.createElement("script");n.src=r,n.onload=e,n.onerror=t,document.head.appendChild(n)}),k={backgroundColor:"#1c1b2a",barColor:"#f59e0b",alertColor:"#ef4444",lineColor:"#22d3ee",textColor:"#f3f4f6",subtleTextColor:"#d1d5db",gridColor:"rgba(243, 244, 246, 0.18)",successColor:"#10b981"},v=7,C=12,w={currentEvent:null,allParkruns:null,gapInfo:null,nearbyParkruns:[],fetchController:null,analysisComplete:!(g=864e5),impactData:null,currentCancellationIndex:-1,cancellationDates:[],sortColumn:"distance",sortDirection:"asc"},function(){m.apply(this,arguments)}()})();
```

### parkrun Charts

> Displays charts on parkrun pages: finishers per minute on results pages and event history on event history pages

[parkrun-charts.user.js](https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/parkrun-charts.user.js)

```javascript
javascript:(async()=>{var r,f,d;function n(e,t){return(e=>{if(Array.isArray(e))return e})(e)||((e,t)=>{var o=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=o){var r,n,a,l,i=[],s=!0,c=!1;try{if(a=(o=o.call(e)).next,0===t){if(Object(o)!==o)return;s=!1}else for(;!(s=(r=a.call(o)).done)&&(i.push(r.value),i.length!==t);s=!0);}catch(e){c=!0,n=e}finally{try{if(!s&&null!=o.return&&(l=o.return(),Object(l)!==l))return}finally{if(c)throw n}}return i}})(e,t)||((e,t)=>{var o;if(e)return"string"==typeof e?a(e,t):"Map"===(o="Object"===(o={}.toString.call(e).slice(8,-1))&&e.constructor?e.constructor.name:o)||"Set"===o?Array.from(e):"Arguments"===o||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(o)?a(e,t):void 0})(e,t)||(()=>{throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")})()}function a(e,t){(null==t||t>e.length)&&(t=e.length);for(var o=0,r=Array(t);o{for(var t=e.timeData,o=e.maxMinute,r=[],n=[],a=e.minMinute;a<=o;a++)r.push(a),n.push(t[a]||0);return{labels:r.map(function(e){var t=Math.floor(e/60),e=e%60;return"".concat(t,":").concat(e.toString().padStart(2,"0"))}),data:n}})((()=>{var r={},e=document.querySelectorAll("tr.Results-table-row"),n=1/0,a=0;e.forEach(function(e){e=e.querySelector("td.Results-table-td--time");if(e){var e=e.textContent.trim(),t=e.match(/(\d+):(\d+):(\d+)/);if(t)var o=60*parseInt(t[1])+parseInt(t[2]);else{t=e.match(/(\d+):(\d+)/);if(!t)return;o=parseInt(t[1])}n=Math.min(n,o),a=Math.max(a,o),r[o]?r[o]++:r[o]=1}});for(var t=n;t<=a;t++)r[t]||(r[t]=0);return{timeData:r,minMinute:n,maxMinute:a}})());0===r.labels.length?console.log("No finish time data found"):document.getElementById("finishersChart")?console.log("Finishers chart already exists, skipping render"):(e=(o=y(o,"finishersChart")).container,t=o.canvas,g("h3",e),v(e),o=t.getContext("2d"),new Chart(o,{type:"bar",data:{labels:r.labels,datasets:[{label:"Number of Finishers",data:r.data,backgroundColor:f.barColor,borderColor:f.barColor,borderWidth:1}]},options:{animation:!1,responsive:!0,plugins:{legend:{labels:{color:f.textColor}},title:{display:!1,color:f.textColor},tooltip:{callbacks:{title:function(e){var t,o,e=e[0].label;return e.includes(":")?(o=(t=n(e.split(":"),2))[0],t=t[1],"".concat(o," hour").concat("1"===o?"":"s"," ").concat(t," minute").concat("01"===t?"":"s")):(o=e.replace("'",""),"".concat(o," minute").concat("1"===o?"":"s"))},label:function(e){return"".concat(e.raw," finisher").concat(1===e.raw?"":"s")}}}},scales:{x:{title:{display:!0,text:"Finish Time",color:f.textColor},ticks:{color:f.subtleTextColor},grid:{color:f.gridColor}},y:{beginAtZero:!0,title:{display:!0,text:"Number of Finishers",color:f.textColor},ticks:{precision:0,color:f.subtleTextColor},grid:{color:f.gridColor}}},onHover:function(){setTimeout(function(){return x(t)},0)},onResize:function(){setTimeout(function(){return x(t)},0)},customPlugin:{id:"watermarkPlugin",afterDraw:function(){setTimeout(function(){return x(t)},0)}}}}),setTimeout(function(){return x(t)},0))}function C(e,t){for(var o=[],r=0;rFinishers: Min: ').concat(m.min.value," (").concat(m.min.date,", Event #").concat(m.min.eventNumber,") |\n Max: ").concat(m.max.value," (").concat(m.max.date,", Event #").concat(m.max.eventNumber,')
\n Volunteers: Min: ').concat(h.min.value," (").concat(h.min.date,", Event #").concat(h.min.eventNumber,") |\n Max: ").concat(h.max.value," (").concat(h.max.date,", Event #").concat(h.max.eventNumber,")\n "),u.appendChild(b),m={title:{display:!0,text:"Date",color:f.textColor},ticks:{color:f.subtleTextColor,maxRotation:45,minRotation:45,callback:function(e,t){var o=n.dates.length,r=1;return 100{var o=document.createElement("script");o.src=r,o.onload=e,o.onerror=t,document.head.appendChild(o)}),d=!(f={backgroundColor:"#2b223d",barColor:"#FFA300",lineColor:"#53BA9D",textColor:"#e0e0e0",subtleTextColor:"#cccccc",gridColor:"rgba(200, 200, 200, 0.2)"}),"undefined"!=typeof module&&void 0!==module.exports?module.exports.sameOrderOfMagnitude=A:"undefined"==typeof Chart?console.error("Chart.js not loaded"):document.querySelector(".Results-table")?(window.location.href.includes("/eventhistory/")?t:e)():console.log("Results table not found")})();
```

### parkrun Walker Analysis

> Highlight and summarize walkers (>=10:00/km) and compare with faster participants on parkrun results pages.

[parkrun-walker-analysis.user.js](https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/parkrun-walker-analysis.user.js)

```javascript
javascript:(async()=>{var r;function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function N(){var f,e="function"==typeof Symbol?Symbol:{},t=e.iterator||"@@iterator",n=e.toStringTag||"@@toStringTag";function r(e,t,n,r){var o,i,a,l,u,c,s,d,p,t=t&&t.prototype instanceof b?t:b,t=Object.create(t.prototype);return g(t,"_invoke",(o=e,i=n,s=r||[],d=!1,p={p:c=0,n:0,v:f,a:m,f:m.bind(f,4),d:function(e,t){return a=e,l=0,u=f,p.n=t,y}},function(e,t,n){if(1t||i(e=((e,t)=>{if("object"!=i(e)||!e)return e;var n=e[Symbol.toPrimitive];if(void 0===n)return("string"===t?String:Number)(e);if("object"!=i(n=n.call(e,t||"default")))return n;throw new TypeError("@@toPrimitive must return a primitive value.")})(e,"string"),"symbol"==i(e)?e:e+""))(e))in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n}):Object.getOwnPropertyDescriptors?Object.defineProperties(r,Object.getOwnPropertyDescriptors(o)):t(Object(o)).forEach(function(e){Object.defineProperty(r,e,Object.getOwnPropertyDescriptor(o,e))})}return r}r="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js",await new Promise((e,t)=>{var n=document.createElement("script");n.src=r,n.onload=e,n.onerror=t,document.head.appendChild(n)});var T,V,j,B,M,U,D,e,I="undefined"!=typeof window&&window.Chart?window.Chart:void 0;function n(o){return o.map(function(e,t){var n,r;return e.timeStr&&0').concat(e.label,"")}).join(""),D.querySelectorAll("button").forEach(function(t){t.onclick=function(){var e;e=t.getAttribute("data-key"),B=e,o()}})}function o(){G();for(var o,r,n,i,a,t,l,u,c,s,d,p,m,e=document.getElementById("walkerRunnerSummaryTable"),f=(e||((e=document.createElement("div")).id="walkerRunnerSummaryTable"),e.innerHTML=(o=B,t=L("undefined"!=typeof document&&document.location?document.location.href:""),l=T.filter(function(e){return e.timeSec>=t}),u=T.filter(function(e){return 0\n Walkers: '.concat(c," (").concat(f,"%)   |   Runners: ").concat(s," (").concat(y,"%)   |   Total finishers: ").concat(p,"\n "),m+='\n '.concat(j.find(function(e){return e.key===o}).label,"Walkers (n)Walkers (%)Runners (n)Runners (%)Total (n)Total (%)"),b.forEach(function(t){var e=l.filter(function(e){return(e[o]||"Unknown")===t}).length,n=u.filter(function(e){return(e[o]||"Unknown")===t}).length,r=e+n;m+="".concat(t,'').concat(e,'').concat(c?(e/c*100).toFixed(1):"0.0",'%').concat(n,'').concat(s?(n/s*100).toFixed(1):"0.0",'%').concat(r,'').concat(p?(r/p*100).toFixed(1):"0.0","%")}),m=m+'Total'.concat(c,'100.0%').concat(s,'100.0%').concat(p,'100.0%')+""),document.getElementById("walkerAnalysisContainer")),y=(f||((f=document.createElement("div")).id="walkerAnalysisContainer",f.style.width="100%",f.style.maxWidth="900px",f.style.margin="20px auto"),f.innerHTML="",document.getElementById(M)),b=(y||((y=document.createElement("div")).id=M),f.appendChild(y),f.appendChild(D),f.appendChild(e),document.querySelector("h3")),h=(b&&b.parentNode?f.parentNode===b.parentNode&&f.previousSibling===b||(b.nextSibling?b.parentNode.insertBefore(f,b.nextSibling):b.parentNode.appendChild(f)):document.body.appendChild(f),B),y=j.find(function(e){return e.key===B}).label,e=M,g=(b=(n=>{var r={},o=1/0,i=0;return T.forEach(function(e){var t;0!==e.timeSec&&(t=Math.floor(e.timeSec/60),o=Math.min(o,t),i=Math.max(i,t),r[t]||(r[t]={}),e=e[n]||"Unknown",r[t][e]=(r[t][e]||0)+1)}),{bins:r,minMinute:o,maxMinute:i}})(h)).bins,v=b.maxMinute,k=[],x=b.minMinute;x<=v;x++)k.push(x);var C=new Set,w=(Object.values(g).forEach(function(e){return Object.keys(e).forEach(function(e){return C.add(e)})}),Array.from(C)),E=(w=((e,t)=>{var n,r,o,i,a;return"ageGroup"===t?((n=e.filter(function(e){return e&&"Unknown"!==e&&"Not specified"!==e})).sort(function(e,t){e=parseInt((e||"").split("-")[0],10),t=parseInt((t||"").split("-")[0],10);return isNaN(e)?1:isNaN(t)?-1:e-t}),e.includes("Not specified")&&n.push("Not specified"),e.includes("Unknown")&&n.push("Unknown"),n):"parkrunExperience"===t?(r=["First Timer (anywhere)","First Timer (to this event)","Multiple parkruns","parkrun 10 Club","parkrun 25 Club","parkrun 50 Club","parkrun 100 Club","parkrun 250 Club","parkrun 500 Club","parkrun 1000 Club"],o=function(e){var t,n=r.indexOf(e);return-1!==n?n:(n=e.match(/parkrun (\d+) Club/))?(n=parseInt(n[1],10),-1!==(t=[10,25,50,100,250,500,1e3].indexOf(n))?3+t:200+n):"Unknown"===e?9999:999},e.slice().sort(function(e,t){return o(e)-o(t)})):"volunteerStatus"===t?(i=["Yet to Volunteer","Volunteered once","Volunteered multiple times","Volunteer 10 Club","Volunteer 25 Club","Volunteer 50 Club","Volunteer 100 Club","Volunteer 250 Club","Volunteer 500 Club","Volunteer 1000 Club"],a=function(e){var t=i.indexOf(e);return-1!==t?t:(t=e.match(/(\d+)/))?200+parseInt(t[1],10):"Has Volunteered"===e?150:"Unknown"===e?9999:999},e.slice().sort(function(e,t){return a(e)-a(t)})):e.slice().sort()})(w,h)).map(function(t){return{label:t,data:k.map(function(e){return g[e]&&g[e][t]?g[e][t]:0}),backgroundColor:(e=t,"volunteerStatus"===h?(r=e.match(/(\d+)/))?(r=parseInt(r[1],10),V[r]||"#cccccc"):V[e]||"#cccccc":"parkrunExperience"===h?(r=e.match(/(\d+)/))?(r=parseInt(r[1],10),V[r]||"#cccccc"):{"First Timer (anywhere)":"#FFE049","First Timer (to this event)":"#FFA300","Multiple parkruns":"#00CEAE",Unknown:"#A1B6B7"}[e]||"#cccccc":"gender"===h?{Male:"#00CEAE",Female:"#E21145","Not specified":"#FFE049",Unknown:"#A1B6B7"}[e]||"#FFA300":"ageGroup"===h?"Not specified"===e?"#F2F2F2":"Unknown"===e?"#A1B6B7":(r=e.match(/^(\d+)-/))&&(r=parseInt(r[1],10),n=["#DA70D6","#9370DB","#6495ED","#4169E1","#1E90FF","#00BFFF","#00CED1","#20B2AA","#3CB371","#32CD32","#9ACD32","#FFD700","#FFA500","#FF8C00","#FF6347","#DC143C","#DB7093"],r=Math.floor((r-10)/5),n[Math.min(r,n.length-1)])||"#cccccc":["#FFA300","#00CEAE","#E21145","#EBE9F0","#FFE049","#2C504A","#6D5698","#C81D31","#A1B6B7"][w.indexOf(e)%9]),stack:"stack1"};var e,n,r}),F=k.map(function(e){var t=Math.floor(e/60),e=e%60;return"".concat(t,":").concat(e.toString().padStart(2,"0"))}),S=document.getElementById(e);S?S.innerHTML="":(S=document.createElement("div")).id=e,S.style.background="#2b223d",S.style.borderRadius="8px",S.style.margin="20px auto",S.style.padding="15px",S.style.maxWidth="900px",S.style.boxShadow="0 2px 4px rgba(0,0,0,0.1)",(b=document.createElement("h3")).textContent="Finishers per Minute by ".concat(y),b.style.textAlign="center",b.style.marginBottom="15px",b.style.color="#FFA300",S.appendChild(b);var A=document.createElement("canvas");S.appendChild(A),(e=document.createElement("button")).textContent="💾 Save as Image",e.style.padding="6px 12px",e.style.backgroundColor="#FFA300",e.style.color="#2b223d",e.style.border="none",e.style.borderRadius="4px",e.style.cursor="pointer",e.style.fontWeight="bold",e.style.display="inline-block",e.style.margin="10px auto 0 auto",e.title="Download chart as PNG image",e.addEventListener("mouseover",function(){this.style.backgroundColor="#e59200"}),e.addEventListener("mouseout",function(){this.style.backgroundColor="#FFA300"}),e.addEventListener("click",O(N().m(function e(){var t,n,r,o,i,a,l,u,c,s,d,p;return N().w(function(e){for(;;)switch(e.n){case 0:try{t=P(),n=S.querySelector("h3"),r=n?n.textContent.replace(/[^a-z0-9]+/gi,"-").replace(/^-+|-+$/g,"").toLowerCase():"chart",o=R(t,r),a=(i=A).width,l=a,u=i.height+100,(c=document.createElement("canvas")).width=l,c.height=u,(s=c.getContext("2d")).fillStyle="#2b223d",s.fillRect(0,0,l,u),s.fillStyle="#FFA300",s.textAlign="center",s.textBaseline="middle",s.font="bold 56px Arial",d=n?n.textContent:"Finishers per Minute",s.fillText(d,l/2,50),s.drawImage(i,0,100),(p=document.createElement("a")).download=o,p.href=c.toDataURL("image/png"),p.click()}catch(e){alert("Failed to export image: "+e)}case 1:return e.a(2)}},e)}))),(y=S.querySelector(".walker-controls-footer"))||((y=document.createElement("div")).className="walker-controls-footer",y.style.display="flex",y.style.justifyContent="center",y.style.marginTop="12px",S.appendChild(y)),y.innerHTML="",y.appendChild(e),U&&(U.destroy(),U=null),setTimeout(function(){I&&(U=new I(A.getContext("2d"),{type:"bar",data:{labels:F,datasets:E},options:{responsive:!0,layout:{padding:{left:60,right:60,top:40,bottom:40}},plugins:{legend:{labels:{color:"#e0e0e0"}},title:{display:!1}},scales:{x:{stacked:!0,title:{display:!0,text:"Finish Time",color:"#e0e0e0"},ticks:{color:"#cccccc"},grid:{color:"rgba(200,200,200,0.2)"}},y:{stacked:!0,beginAtZero:!0,title:{display:!0,text:"Number of Finishers",color:"#e0e0e0"},ticks:{color:"#cccccc",precision:0},grid:{color:"rgba(200,200,200,0.2)"}}}}}))},0),(f=document.getElementById(M))&&f.parentNode?f.nextSibling?f.parentNode.insertBefore(D,f.nextSibling):f.parentNode.appendChild(D):document.body.appendChild(D)}(e=Array.from(document.querySelectorAll("tr.Results-table-row"))).length&&(T=n(e.map(function(e,t){var n=e.querySelector(".Results-table-td--time .compact"),n=n?n.textContent.trim():"",r=(r=n)?2===(r=r.split(":").map(Number)).length?60*r[0]+r[1]:3===r.length?3600*r[0]+60*r[1]+r[2]:0:0,o=(e.getAttribute("data-gender")||"").trim(),i=parseInt(e.getAttribute("data-runs"),10),a=parseInt(e.getAttribute("data-vols"),10),l=(e.getAttribute("data-achievement")||"").trim(),u="Unknown",l=(!isNaN(i)&&0 Tracks progress on the unofficial parkrun position bingo challenge (last two digits of position) with a 10x10 grid visualization and detailed event info.

[position-bingo.user.js](https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/position-bingo.user.js)

```javascript
javascript:(async()=>{var o,e,t,c;function p(e,t){var n,o,i,r,a="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(a)return i=!(o=!0),{s:function(){a=a.call(e)},n:function(){var e=a.next();return o=e.done,e},e:function(e){i=!0,n=e},f:function(){try{o||null==a.return||a.return()}finally{if(i)throw n}}};if(Array.isArray(e)||(a=((e,t)=>{var n;if(e)return"string"==typeof e?l(e,t):"Map"===(n="Object"===(n={}.toString.call(e).slice(8,-1))&&e.constructor?e.constructor.name:n)||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?l(e,t):void 0})(e))||t&&e&&"number"==typeof e.length)return a&&(e=a),r=0,{s:t=function(){},n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:t};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function l(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=Array(t);n')+""+r.completedCount+" of "+c+" positions completed"+'

After ')+r.totalEvents+" parkruns
",t.appendChild(o),Math.ceil(c/10)),a=n.grid.boxSize,l=n.grid.gapSize,s=document.createElement("div"),d=(s.style.display="grid",s.style.gridTemplateColumns="repeat(".concat(10,", ").concat(a,"px)"),s.style.gridTemplateRows="repeat(".concat(o,", ").concat(a,"px)"),s.style.gap="".concat(l,"px"),s.style.margin="0 auto",s.style.justifyContent="center",0);d{var e=document.createElement("div"),t=(e.style.boxSizing="border-box",e.style.border="1px solid #666",e.style.borderRadius="4px",e.style.backgroundColor=r.completedPositions[o]?"#FFA300":"#008080",e.style.color="#fff",e.style.textAlign="left",e.style.padding=n.grid.cellPadding,e.style.fontWeight="bold",e.style.fontSize=n.grid.cellFontSize,e.style.cursor=r.completedPositions[o]?"pointer":"default",e.style.aspectRatio="1",document.createElement("div"));t.textContent=o.toString().padStart(2,"0"),t.style.fontSize=n.grid.positionFontSize,t.style.marginBottom=n.isMobile?"2px":"5px",e.appendChild(t),r.completedPositions[o]&&((t=document.createElement("div")).innerHTML='
')+r.completedPositions[o][0].eventName+"
"+'')+r.completedPositions[o][0].date+" ("+r.completedPositions[o][0].position+")
",n.isMobile&&(t.style.display="none"),e.appendChild(t),e.addEventListener("click",function(){var e=m(),t=document.createElement("div"),i=(t.style.position="fixed",t.style.top="0",t.style.left="0",t.style.width="100%",t.style.height="100%",t.style.backgroundColor="rgba(0, 0, 0, 0.5)",t.style.zIndex="999",t.addEventListener("click",function(){document.body.removeChild(t)}),document.createElement("div")),n=(i.style.position="fixed",i.style.top="50%",i.style.left="50%",i.style.transform="translate(-50%, -50%)",i.style.backgroundColor="#2b223d",i.style.color="#fff",e.isMobile?"15px":"20px"),n=(i.style.padding=n,i.style.borderRadius="8px",i.style.boxShadow="0 2px 4px rgba(0,0,0,0.5)",i.style.zIndex="1000",i.style.maxWidth=e.isMobile?"95%":"90%",i.style.maxHeight=e.isMobile?"85%":"80%",i.style.overflowY="auto",i.style.fontSize=e.isMobile?"0.9em":"1em",document.createElement("h4"));n.textContent="Position ".concat(o.toString().padStart(2,"0")),n.style.marginBottom="10px",n.style.color="#FFA300",i.appendChild(n),r.completedPositions[o].forEach(function(e){var t=e.eventName,n=e.date,e=e.position,o=document.createElement("div");o.style.marginBottom="10px",o.innerHTML=""+t+'
'+n+" ("+e+")",i.appendChild(o)}),t.appendChild(i),document.body.appendChild(t)})),s.appendChild(e)})(d);return t.appendChild(s),e=t,o=m(),(a=document.createElement("div")).style.marginTop=o.button.marginTop,a.id="position-bingo-download-btn-container",(i=document.createElement("button")).textContent="💾 Save as Image",i.style.padding=o.button.padding,i.style.backgroundColor="#FFA300",i.style.color="#2b223d",i.style.border="none",i.style.borderRadius="4px",i.style.cursor="pointer",i.style.fontWeight="bold",i.style.fontSize=o.button.fontSize,i.addEventListener("mouseover",function(){this.style.backgroundColor="#e59200"}),i.addEventListener("mouseout",function(){this.style.backgroundColor="#FFA300"}),i.addEventListener("click",function(){i.style.display="none",html2canvas(e,{backgroundColor:"#2b223d",scale:2,logging:!1,allowTaint:!0,useCORS:!0}).then(function(e){i.style.display="block";var t=document.createElement("a"),n=(new Date).toISOString().split("T")[0],o=window.location.pathname.split("/")[2]||"parkrunner";t.download="position-bingo-".concat(o,"-").concat(n,".png"),t.href=e.toDataURL("image/png"),t.click()})}),a.appendChild(i),e.appendChild(a),t}o="https://html2canvas.hertzen.com/dist/html2canvas.min.js",await new Promise((e,t)=>{var n=document.createElement("script");n.src=o,n.onload=e,n.onerror=t,document.head.appendChild(n)}),c=100,(t=(t=document.querySelectorAll("#results"))[t.length-1])?(t=t=n((e=>{var t,n={},o=0,i=p(Array.from(e.querySelectorAll("tr")).reverse());try{for(i.s();!(t=i.n()).done;){var r=t.value.querySelectorAll("td");if(!(r.length<5)){var a=r[3].textContent.trim(),l=parseInt(a.slice(-2),10),s=r[0].textContent.trim(),d=r[1].textContent.trim();if(!isNaN(l)&&0<=l&&l Visualizes your progress on the stopwatch bingo challenge (collecting seconds 00-59)

[stopwatch-bingo.user.js](https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/stopwatch-bingo.user.js)

```javascript
javascript:(async()=>{var n,k;function o(){var t=document.querySelectorAll("#results");return t[t.length-1]}function l(t){for(var e={},o={},n=0,l=0,r=null,a=null,t=t.querySelectorAll("tr"),i=[],c=(t.forEach(function(t){t=t.querySelectorAll("td");if(!(t.length<5)){var e=t[0].textContent.trim(),o=t[1].textContent.trim(),n=t[2].textContent.trim(),t=t[4].textContent.trim();if(t&&n&&o&&e){var l,e="".concat(e," # ").concat(n),n=t.match(/(\d+):(\d+):(\d+)/);if(n)l=parseInt(n[3],10);else{n=t.match(/(\d+):(\d+)/);if(!n)return;l=parseInt(n[2],10)}i.push({secondValue:l,date:o,event:e,time:t})}}}),i.reverse()),s=0;s').concat(g.collectedCount," of 60 seconds collected"),x=(l+='

After ').concat(g.totalParkruns," parkruns
"),g.dateOfCompletion&&(l+='
🏆 Bingo completed on ').concat(g.dateOfCompletion," (").concat(g.firstCompleteEvent,")
")),o.innerHTML=l,t.appendChild(o),e?Math.min(320,window.innerWidth-40):500),b=x/500,C=document.createElement("div"),v=(C.style.position="relative",C.style.width="".concat(x,"px"),C.style.height="".concat(x,"px"),C.style.margin="0 auto",C.style.borderRadius="50%",C.style.border="".concat(Math.round(10*b),"px solid ").concat(k.clockBorderColor),C.style.backgroundColor=k.clockFaceColor,C.style.boxSizing="content-box",1),r=0;r<60;r++){var a=g.timeData[r]?g.timeData[r].length:0;v'.concat(t.content,"")}).join(""));o.innerHTML=e,r.appendChild(o)}),t.appendChild(r),n.appendChild(t),document.body.appendChild(n),(a=document.createElement("div")).style.position="fixed",a.style.top="0",a.style.left="0",a.style.width="100%",a.style.height="100%",a.style.backgroundColor="rgba(0, 0, 0, 0.5)",a.style.zIndex="999",a.addEventListener("click",function(){n.remove(),a.remove()}),document.body.appendChild(a)}),s.appendChild(d),n.appendChild(s),C.appendChild(n)},c=0;c<60;c++)i(c);var s=document.createElementNS("http://www.w3.org/2000/svg","svg");s.setAttribute("width","100%"),s.setAttribute("height","100%"),s.style.position="absolute",s.style.top="0",s.style.left="0",s.style.pointerEvents="none",s.style.zIndex="2";for(var d=0;d<60;d+=5){var p=(d/60*360-90)*(Math.PI/180),m=245*b,u=(d%15==0?210:225)*b,y=x/2,h=x/2,f=document.createElementNS("http://www.w3.org/2000/svg","line"),m=(f.setAttribute("x1",y+u*Math.cos(p)),f.setAttribute("y1",h+u*Math.sin(p)),f.setAttribute("x2",y+m*Math.cos(p)),f.setAttribute("y2",h+m*Math.sin(p)),f.setAttribute("stroke",k.textColor),f.setAttribute("stroke-width",(d%15==0?3:2)*b),s.appendChild(f),u-(d%15==0?25:20)*b),f=y+m*Math.cos(p),u=h+m*Math.sin(p),y=document.createElementNS("http://www.w3.org/2000/svg","text");y.setAttribute("x",f),y.setAttribute("y",u),y.setAttribute("fill",k.textColor),y.setAttribute("font-size","".concat((d%15==0?16:14)*b,"px")),y.setAttribute("font-weight","bold"),y.setAttribute("text-anchor","middle"),y.setAttribute("dominant-baseline","middle"),y.textContent=d.toString(),s.appendChild(y)}C.appendChild(s);var w,E,n=document.createElement("div"),n=(n.style.position="absolute",n.style.top="50%",n.style.left="50%",n.style.transform="translate(-50%, -50%)",n.style.width="".concat(100*b,"px"),n.style.height="".concat(100*b,"px"),n.style.borderRadius="50%",n.style.backgroundColor=k.clockBorderColor,n.style.display="flex",n.style.justifyContent="center",n.style.alignItems="center",n.style.color=k.backgroundColor,n.style.fontWeight="bold",n.style.fontSize="".concat(20*b,"px"),n.style.zIndex="3",n.textContent="".concat(Math.round(g.collectedCount/60*100),"%"),C.appendChild(n),t.appendChild(C),w=t,l=window.innerWidth<768,(o=document.createElement("div")).style.marginTop=l?"10px":"15px",o.id="bingo-download-btn-container",(E=document.createElement("button")).textContent="💾 Save as Image",E.style.padding=l?"6px 12px":"8px 15px",E.style.backgroundColor=k.clockBorderColor,E.style.color=k.backgroundColor,E.style.border="none",E.style.borderRadius="4px",E.style.cursor="pointer",E.style.fontWeight="bold",E.style.fontSize=l?"0.9em":"1em",E.addEventListener("mouseover",function(){this.style.backgroundColor="#e59200"}),E.addEventListener("mouseout",function(){this.style.backgroundColor=k.clockBorderColor}),E.addEventListener("click",function(){E.style.display="none",html2canvas(w,{backgroundColor:k.backgroundColor,scale:2,logging:!1,allowTaint:!0,useCORS:!0}).then(function(t){E.style.display="block";var e=document.createElement("a"),o=(new Date).toISOString().split("T")[0],n=window.location.pathname.split("/")[2]||"parkrunner";e.download="stopwatch-bingo-".concat(n,"-").concat(o,".png"),e.href=t.toDataURL("image/png"),e.click()})}),o.appendChild(E),w.appendChild(o),document.createElement("div"));return n.style.marginTop=e?"10px":"20px",n.style.color=k.subtleTextColor,n.style.fontSize=e?"0.8em":"0.9em",n.style.padding=e?"0 5px":"0",n.innerHTML="Stopwatch Bingo: collect finish times with every second from 00-59.
Orange segments show seconds you've collected. Segment length indicates frequency.
Click on any segment to see details.",t.appendChild(n),t}function t(){var t,e;document.querySelector("#results")?document.querySelector("h2")&&(t=r(l(o())),t=t,e=document.querySelector("h2"))&&e.parentNode&&(e.nextSibling?e.parentNode.insertBefore(t,e.nextSibling):e.parentNode.appendChild(t)):console.log("Results table not found")}n="https://html2canvas.hertzen.com/dist/html2canvas.min.js",await new Promise((t,e)=>{var o=document.createElement("script");o.src=n,o.onload=t,o.onerror=e,document.head.appendChild(o)}),k={backgroundColor:"#2b223d",clockBorderColor:"#FFA300",clockFaceColor:"#333",completedColor:"#FFA300",textColor:"#e0e0e0",subtleTextColor:"#cccccc"},"undefined"!=typeof module&&module.exports?module.exports={findResultsTable:o,extractFinishTimes:l}:t()})();
```

### parkrun Countries Visited

> Shows country flag emojis next to parkrunner name for all countries they have completed parkruns in

[visited-countries.user.js](https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/visited-countries.user.js)

```javascript
javascript:(async()=>{function t(e,n){return(e=>{if(Array.isArray(e))return e})(e)||((e,n)=>{var a=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=a){var t,r,l,o,i=[],u=!0,c=!1;try{if(l=(a=a.call(e)).next,0===n){if(Object(a)!==a)return;u=!1}else for(;!(u=(t=l.call(a)).done)&&(i.push(t.value),i.length!==n);u=!0);}catch(e){c=!0,r=e}finally{try{if(!u&&null!=a.return&&(o=a.return(),Object(o)!==o))return}finally{if(c)throw r}}return i}})(e,n)||((e,n)=>{var a;if(e)return"string"==typeof e?r(e,n):"Map"===(a="Object"===(a={}.toString.call(e).slice(8,-1))&&e.constructor?e.constructor.name:a)||"Set"===a?Array.from(e):"Arguments"===a||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(a)?r(e,n):void 0})(e,n)||(()=>{throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")})()}function r(e,n){(null==n||n>e.length)&&(n=e.length);for(var a=0,t=Array(n);a Displays the number of volunteer days for parkrun finishers on results pages, for celebration purposes (and let's not make assumptions about ratios)

[volunteer-days-display.user.js](https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/volunteer-days-display.user.js)

```javascript
javascript:(async()=>{!function t(){var e,a,n,o;function s(o){var t;e&&((t=Array.from(e.querySelectorAll("tr")).map(function(t,e){return{row:t,index:e}})).sort(function(t,e){var a=parseInt(t.row.getAttribute("data-vols")||"0",10),n=parseInt(e.row.getAttribute("data-vols")||"0",10);return a===n?t.index-e.index:"asc"===o?a-n:n-a}),t.forEach(function(t){return t=t.row,e.appendChild(t)}))}document.querySelector("tr[data-vols]")?(document.querySelectorAll("tr[data-vols] > td.Results-table-td.Results-table-td--name > div.detailed").forEach(function(t){var e,a,n=t.closest("tr").getAttribute("data-vols");n&&0 The "Wilson index" in parkrun is the highest consecutive event number completed, starting from #1. This script calculates and displays a parkrunner's Wilson index on their results page.

[w-index.user.js](https://raw.githubusercontent.com/johnsyweb/tampermonkey-parkrun/refs/heads/main/w-index.user.js)

```javascript
javascript:(async()=>{var r;function s(){var h,t="function"==typeof Symbol?Symbol:{},e=t.iterator||"@@iterator",n=t.toStringTag||"@@toStringTag";function r(t,e,n,r){var o,a,i,l,c,s,u,d,f,e=e&&e.prototype instanceof m?e:m,e=Object.create(e.prototype);return g(e,"_invoke",(o=t,a=n,u=r||[],d=!1,f={p:s=0,n:0,v:h,a:p,f:p.bind(h,4),d:function(t,e){return i=t,l=0,c=h,f.n=e,y}},function(t,e,n){if(1e||a{if(Array.isArray(t))return o(t)})(t)||(t=>{if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)})(t)||f(t)||(()=>{throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")})()}function l(t,e){var n,r,o,a,i="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(i)return o=!(r=!0),{s:function(){i=i.call(t)},n:function(){var t=i.next();return r=t.done,t},e:function(t){o=!0,n=t},f:function(){try{r||null==i.return||i.return()}finally{if(o)throw n}}};if(Array.isArray(t)||(i=f(t))||e&&t&&"number"==typeof t.length)return i&&(t=i),a=0,{s:e=function(){},n:function(){return a>=t.length?{done:!0}:{done:!1,value:t[a++]}},e:function(t){throw t},f:e};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function f(t,e){var n;if(t)return"string"==typeof t?o(t,e):"Map"===(n="Object"===(n={}.toString.call(t).slice(8,-1))&&t.constructor?t.constructor.name:n)||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?o(t,e):void 0}function o(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);n tr")).reverse().map(function(t){var e=t.querySelector("td:nth-child(1)").textContent.trim(),n=t.querySelector("td:nth-child(2)").textContent.trim(),t=t.querySelector("td:nth-child(3)").textContent.trim();return{eventName:e,eventDate:n,eventNumber:parseInt(t,10)}})}function y(t){var e,n=0,r=l(t.map(function(t){return t.eventNumber}).sort(function(t,e){return t-e}));try{for(r.s();!(e=r.n()).done;){var o=e.value;if(n+2<=o)break;o===n+1&&n++}}catch(t){r.e(t)}finally{r.f()}return n}function m(t){for(var e=[],n=0;n{var e=u(s().m(function t(e){var n,r,o;return s().w(function(t){for(;;)switch(t.p=t.n){case 0:if(e.preventDefault(),n=i.value.trim().replace(/^[aA]/,"")){t.n=1;break}return t.a(2);case 1:return l.disabled=!0,l.textContent="Loading...",t.p=2,t.n=3,function(){return v.apply(this,arguments)}(n);case 3:r=t.v,o=r.friendIndices,a(o,r.friendInfo),t.n=5;break;case 4:t.p=4,o=t.v,console.error("Failed to fetch friend's results:",o),alert("Failed to fetch friend's results. Please check the ID and try again.");case 5:return t.p=5,l.disabled=!1,l.textContent="Compare",t.f(5);case 6:return t.a(2)}},t,null,[[2,4,5,6]])}));return function(t){return e.apply(this,arguments)}})()),t.insertBefore(n,t.firstChild)}function k(t){return t.textContent.trim()}function S(t){var e=["#FFA300","#90EE90","#FF69B4","#4169E1","#FFD700","#9370DB","#20B2AA","#FF6347","#DDA0DD","#00CED1"];return e[t%e.length]}function t(){var t,e,l,n,r,o,a,i,c=p(document);c?(t=document.querySelector("h2"))?(a=k(t))?(n=y(c=h(c)),c=m(c),t&&(r=x(),(e=document.createElement("div")).id="w-index-display",e.style.width="100%",e.style.maxWidth="800px",e.style.margin="".concat(r.container.marginTop," auto"),e.style.backgroundColor="#2b223d",e.style.padding=r.container.padding,e.style.borderRadius="5px",(o=document.createElement("div")).textContent="Wilson index: ".concat(n),o.style.fontSize=r.typography.wilsonIndex,o.style.color="#ffa300",o.style.fontWeight="bold",o.style.marginBottom=r.spacing.small,o.style.textAlign="center",e.appendChild(o),n=c,r=e,o=a,c=x(),(a=document.createElement("div")).style.width="100%",a.style.maxWidth="100%",a.style.height=c.chart.height,a.style.position="relative",a.style.boxSizing="border-box",a.style.overflow="hidden",i=document.createElement("canvas"),a.appendChild(i),r.appendChild(a),r=i.getContext("2d"),l=new Chart(r,{type:"line",data:{labels:n.map(function(t){return t.parkruns}),datasets:[{label:o,data:n.map(function(t){return{x:t.parkruns,y:t.wilsonIndex,event:t.event}}),borderColor:S(0),backgroundColor:"#2b223d"}]},options:{responsive:!0,maintainAspectRatio:!1,scales:{y:{beginAtZero:!0,title:{display:!0,text:"Wilson Index",font:{size:c.chart.fonts.axisTitle}},ticks:{font:{size:c.chart.fonts.axisTicks}},suggestedMax:Math.ceil(1.1*Math.max.apply(Math,d(n.map(function(t){return t.wilsonIndex}))))},x:{title:{display:!0,text:"parkruns",font:{size:c.chart.fonts.axisTitle}},ticks:{font:{size:c.chart.fonts.axisTicks}},min:0,suggestedMax:Math.ceil(1.1*n.length)}},plugins:{title:{display:!0,text:"Wilson Index Progress",font:{size:c.chart.fonts.title}},legend:{labels:{font:{size:c.chart.fonts.legend}}},tooltip:{callbacks:{label:function(t){t=t.raw;return["Wilson Index: ".concat(t.y),"Event: ".concat(t.event)]}},titleFont:{size:c.chart.fonts.tooltipTitle},bodyFont:{size:c.chart.fonts.tooltipBody}}}}}),w(e,(()=>{var n=u(s().m(function t(a,i){return s().w(function(t){for(;;)switch(t.n){case 0:n=a,r=i,o=void 0,o=(e=l).data.datasets.length,r={label:i,data:n.map(function(t){return{x:t.parkruns,y:t.wilsonIndex,event:t.event}}),borderColor:S(o),backgroundColor:"#2b223d"},e.data.datasets.push(r),e.update(),n=Math.max.apply(Math,d(e.data.datasets.flatMap(function(t){return t.data.map(function(t){return t.x})}))),o=Math.max.apply(Math,d(e.data.datasets.flatMap(function(t){return t.data.map(function(t){return t.y})}))),e.options.scales.x.suggestedMax=Math.ceil(1.1*n),e.options.scales.y.suggestedMax=Math.ceil(1.1*o),e.update();case 1:return t.a(2)}var e,n,r,o},t)}));return function(t,e){return n.apply(this,arguments)}})()),t.parentNode.insertBefore(e,t.nextSibling))):console.error("Could not extract athlete info"):console.error("H2 element not found"):console.error("Results table not found")}r="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js",await new Promise((t,e)=>{var n=document.createElement("script");n.src=r,n.onload=t,n.onerror=e,document.head.appendChild(n)}),"undefined"!=typeof module&&module.exports?module.exports={calculateWilsonIndex:y,calculateWilsonIndexOverTime:m,extractEventDetails:h,findResultsTable:p}:t()})();
```

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

## Contributing

Contributions are welcome! Please see the [CONTRIBUTING.md](CONTRIBUTING.md) file for guidelines.