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.

What is a userscript?

A userscript is a small piece of JavaScript that runs in your browser and enhances specific websites. These scripts work with parkrun event pages, parkrunner profile pages, and results pages, and can be used with any userscript manager including Userscripts, Tampermonkey, Violentmonkey, or any compatible browser extension.

Installation

  1. Install a userscript manager for your browser:
  2. Click the install link below for this script.
  3. Click “Install” when prompted by your userscript manager.

parkrun Wilson index display Screenshot

Install parkrun Wilson index display

Bookmarklet version

You can also use this script as a bookmarklet (a bookmark whose URL is JavaScript), which can be useful on browsers that support bookmarks but not userscript managers.

Desktop bookmarklet

Drag this link to your bookmarks bar:

parkrun Wilson index display bookmarklet

Mobile bookmarklet

On mobile browsers that let you edit bookmark URLs:

  1. Copy the JavaScript code below to your clipboard.
  2. Create a new bookmark for any page.
  3. Edit the bookmark and replace its URL with the code you copied.
  4. Navigate to the relevant parkrun page.
  5. Select the bookmark to run the script.
javascript:function _regenerator(){var y,t="function"==typeof Symbol?Symbol:{},e=t.iterator||"@@iterator",r=t.toStringTag||"@@toStringTag";function n(t,e,r,n){var o,a,i,l,s,c,u,d,f,e=e&&e.prototype instanceof m?e:m,e=Object.create(e.prototype);return _regeneratorDefine2(e,"_invoke",(o=t,a=r,u=n||[],d=!1,f={p:c=0,n:0,v:y,a:p,f:p.bind(y,4),d:function(t,e){return i=t,l=0,s=y,f.n=e,h}},function(t,e,r){if(1<c)throw TypeError("Generator is already running");for(d&&1===e&&p(e,r),l=e,s=r;(g=l<2?y:s)||!d;){i||(l?l<3?(1<l&&(f.n=-1),p(l,s)):f.n=s:f.v=s);try{if(c=2,i){if(g=i[t=l?t:"next"]){if(!(g=g.call(i,s)))throw TypeError("iterator result is not an object");if(!g.done)return g;s=g.value,l<2&&(l=0)}else 1===l&&(g=i.return)&&g.call(i),l<2&&(s=TypeError("The iterator does not provide a '"+t+"' method"),l=1);i=y}else if((g=(d=f.n<0)?s:o.call(a,f))!==h)break}catch(t){i=y,l=1,s=t}finally{c=1}}return{value:g,done:d}}),!0),e;function p(t,e){for(l=t,s=e,g=0;!d&&c&&!r&&g<u.length;g++){var r,n=u[g],o=f.p,a=n[2];3<t?(r=a===e)&&(s=n[(l=n[4])?5:l=3],n[4]=n[5]=y):n[0]<=o&&((r=t<2&&o<n[1])?(l=0,f.v=e,f.n=n[1]):o<a&&(r=t<3||n[0]>e||a<e)&&(n[4]=t,n[5]=e,f.n=a,l=0))}if(r||1<t)return h;throw d=!0,e}}var h={};function m(){}function o(){}function a(){}var g=Object.getPrototypeOf,t=[][e]?g(g([][e]())):(_regeneratorDefine2(g={},e,function(){return this}),g),i=a.prototype=m.prototype=Object.create(t);function l(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,a):(t.__proto__=a,_regeneratorDefine2(t,r,"GeneratorFunction")),t.prototype=Object.create(i),t}return _regeneratorDefine2(i,"constructor",o.prototype=a),_regeneratorDefine2(a,"constructor",o),_regeneratorDefine2(a,r,o.displayName="GeneratorFunction"),_regeneratorDefine2(i),_regeneratorDefine2(i,r,"Generator"),_regeneratorDefine2(i,e,function(){return this}),_regeneratorDefine2(i,"toString",function(){return"[object Generator]"}),(_regenerator=function(){return{w:n,m:l}})()}function _regeneratorDefine2(t,e,r,n){var a=Object.defineProperty;try{a({},"",{})}catch(t){a=0}(_regeneratorDefine2=function(t,e,r,n){function o(e,r){_regeneratorDefine2(t,e,function(t){return this._invoke(e,r,t)})}e?a?a(t,e,{value:r,enumerable:!n,configurable:!n,writable:!n}):t[e]=r:(o("next",0),o("throw",1),o("return",2))})(t,e,r,n)}function asyncGeneratorStep(t,e,r,n,o,a,i){try{var l=t[a](i),s=l.value}catch(t){return void r(t)}l.done?e(s):Promise.resolve(s).then(n,o)}function _asyncToGenerator(l){return function(){var t=this,i=arguments;return new Promise(function(e,r){var n=l.apply(t,i);function o(t){asyncGeneratorStep(n,e,r,o,a,"next",t)}function a(t){asyncGeneratorStep(n,e,r,o,a,"throw",t)}o(void 0)})}}function _toConsumableArray(t){return _arrayWithoutHoles(t)||_iterableToArray(t)||_unsupportedIterableToArray(t)||_nonIterableSpread()}function _nonIterableSpread(){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 _iterableToArray(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}function _arrayWithoutHoles(t){if(Array.isArray(t))return _arrayLikeToArray(t)}function _createForOfIteratorHelper(t,e){var r,n,o,a,i="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(i)return o=!(n=!0),{s:function(){i=i.call(t)},n:function(){var t=i.next();return n=t.done,t},e:function(t){o=!0,r=t},f:function(){try{n||null==i.return||i.return()}finally{if(o)throw r}}};if(Array.isArray(t)||(i=_unsupportedIterableToArray(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 _unsupportedIterableToArray(t,e){var r;if(t)return"string"==typeof t?_arrayLikeToArray(t,e):"Map"===(r="Object"===(r={}.toString.call(t).slice(8,-1))&&t.constructor?t.constructor.name:r)||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?_arrayLikeToArray(t,e):void 0}function _arrayLikeToArray(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=Array(e);r<e;r++)n[r]=t[r];return n}(()=>{function c(t){var e,r=1<arguments.length&&void 0!==arguments[1]?arguments[1]:7,n=null,o=_createForOfIteratorHelper(t.querySelectorAll('[id="results"]'));try{for(o.s();!(e=o.n()).done;){var a=e.value,i=a.querySelector("tr");if(i)if(i.querySelectorAll("th, td").length===r){n=a;break}}}catch(t){o.e(t)}finally{o.f()}return n}function u(t){return Array.from(t.querySelectorAll("tbody > tr")).reverse().map(function(t){var e=t.querySelector("td:nth-child(1)").textContent.trim(),r=t.querySelector("td:nth-child(2)").textContent.trim(),t=t.querySelector("td:nth-child(3)").textContent.trim();return{eventName:e,eventDate:r,eventNumber:parseInt(t,10)}})}function d(t){var e,r=0,n=_createForOfIteratorHelper(t.map(function(t){return t.eventNumber}).sort(function(t,e){return t-e}));try{for(n.s();!(e=n.n()).done;){var o=e.value;if(r+2<=o)break;o===r+1&&r++}}catch(t){n.e(t)}finally{n.f()}return r}function f(t){for(var e=[],r=0;r<t.length;r++){var n=t.slice(0,r+1),o=r+1,a="".concat(t[r].eventName," # ").concat(t[r].eventNumber," on ").concat(t[r].eventDate),n=d(n);e.push({parkruns:o,event:a,wilsonIndex:n})}return e}function p(){return window.innerWidth<768?{isMobile:!0,spacing:{small:"10px",medium:"15px"},container:{padding:"10px",marginTop:"10px"},typography:{wilsonIndex:"1.2em",input:"16px",button:"16px"},chart:{height:"250px",fonts:{title:14,axisTitle:12,axisTicks:10,legend:11,tooltipTitle:12,tooltipBody:11}},form:{marginBottom:"10px",input:{width:"calc(100% - 20px)",maxWidth:"300px",padding:"8px",marginRight:"0"},button:{padding:"8px 15px",width:"calc(100% - 20px)",maxWidth:"300px"},layout:{display:"flex",flexDirection:"column",gap:"10px",alignItems:"center"}}}:{isMobile:!1,spacing:{small:"20px",medium:"20px"},container:{padding:"20px",marginTop:"20px"},typography:{wilsonIndex:"1.5em",input:"inherit",button:"inherit"},chart:{height:"300px",fonts:{title:16,axisTitle:14,axisTicks:12,legend:12,tooltipTitle:14,tooltipBody:12}},form:{marginBottom:"20px",input:{width:"300px",maxWidth:"300px",padding:"5px",marginRight:"10px"},button:{padding:"5px 10px",width:"auto",maxWidth:"none"},layout:{display:"block",flexDirection:"row",gap:"0",alignItems:"flex-start"}}}}function y(){return(y=_asyncToGenerator(_regenerator().m(function t(e,r){var n,o,a,i,l=arguments;return _regenerator().w(function(t){for(;;)switch(t.n){case 0:if(n=2<l.length&&void 0!==l[2]?l[2]:36e5,o=sessionStorage.getItem(r))if(i=JSON.parse(o),a=i.data,i=i.timestamp,Date.now()-i<n)return t.a(2,a);t.n=1;break;case 1:return t.a(2,fetch(e).then(function(t){if(t.ok)return t.text();throw new Error("Failed to fetch: ".concat(t.status," ").concat(t.statusText))}).then(function(t){return sessionStorage.setItem(r,JSON.stringify({data:t,timestamp:Date.now()})),t}).catch(function(t){if(console.error("Error fetching ".concat(e,":"),t),o)return console.warn("Using stale cached data after fetch failure"),JSON.parse(o).data;throw t}))}},t)}))).apply(this,arguments)}function s(){return(s=_asyncToGenerator(_regenerator().m(function t(e){var r,n,o,a,i,l,s;return _regenerator().w(function(t){for(;;)switch(t.n){case 0:return n="parkrunner_".concat(e,"_all"),r="".concat(window.location.origin,"/parkrunner/").concat(e,"/all/"),t.n=1,function(){return y.apply(this,arguments)}(r,n);case 1:if(r=t.v,n=new DOMParser,o=n.parseFromString(r,"text/html"),a=c(o)){t.n=2;break}return console.error("Friend results table not found"),t.a(2,null);case 2:if(i=o.querySelector("h2")){t.n=3;break}return console.error("Friend H2 element not found"),t.a(2,null);case 3:if(l=m(i)){t.n=4;break}return console.error("Could not extract friend athlete info"),t.a(2,null);case 4:return s=u(a),s=f(s),t.a(2,{friendIndices:s,friendInfo:l})}},t)}))).apply(this,arguments)}function h(t,a){var e=p(),r=document.createElement("form"),i=(r.style.marginBottom=e.form.marginBottom,r.style.textAlign="center",r.style.display=e.form.layout.display,r.style.flexDirection=e.form.layout.flexDirection,r.style.gap=e.form.layout.gap,r.style.alignItems=e.form.layout.alignItems,document.createElement("input")),l=(i.style.width=e.form.input.width,i.style.maxWidth=e.form.input.maxWidth,i.type="text",i.placeholder="Enter friend's athlete ID (e.g. A507)",i.style.padding=e.form.input.padding,i.style.marginRight=e.form.input.marginRight,i.style.borderRadius="3px",i.style.border="1px solid #ffa300",i.style.backgroundColor="#2b223d",i.style.color="#ffa300",i.style.fontSize=e.typography.input,document.createElement("button"));l.textContent="Compare",l.style.padding=e.form.button.padding,l.style.width=e.form.button.width,l.style.maxWidth=e.form.button.maxWidth,l.style.backgroundColor="#ffa300",l.style.color="#2b223d",l.style.border="none",l.style.borderRadius="3px",l.style.cursor="pointer",l.style.fontSize=e.typography.button,l.style.fontWeight="bold",r.appendChild(i),r.appendChild(l),r.addEventListener("submit",(()=>{var e=_asyncToGenerator(_regenerator().m(function t(e){var r,n,o;return _regenerator().w(function(t){for(;;)switch(t.p=t.n){case 0:if(e.preventDefault(),r=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 s.apply(this,arguments)}(r);case 3:n=t.v,o=n.friendIndices,a(o,n.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(r,t.firstChild)}function m(t){return t.textContent.trim()}function g(t){var e=["#FFA300","#90EE90","#FF69B4","#4169E1","#FFD700","#9370DB","#20B2AA","#FF6347","#DDA0DD","#00CED1"];return e[t%e.length]}function t(){var t,e,l,r,n,o,a,i,s=c(document);s?(t=document.querySelector("h2"))?(a=m(t))?(r=d(s=u(s)),s=f(s),t&&(n=p(),(e=document.createElement("div")).id="w-index-display",e.style.width="100%",e.style.maxWidth="800px",e.style.margin="".concat(n.container.marginTop," auto"),e.style.backgroundColor="#2b223d",e.style.padding=n.container.padding,e.style.borderRadius="5px",(o=document.createElement("div")).textContent="Wilson index: ".concat(r),o.style.fontSize=n.typography.wilsonIndex,o.style.color="#ffa300",o.style.fontWeight="bold",o.style.marginBottom=n.spacing.small,o.style.textAlign="center",e.appendChild(o),r=s,n=e,o=a,s=p(),(a=document.createElement("div")).style.width="100%",a.style.maxWidth="100%",a.style.height=s.chart.height,a.style.position="relative",a.style.boxSizing="border-box",a.style.overflow="hidden",i=document.createElement("canvas"),a.appendChild(i),n.appendChild(a),n=i.getContext("2d"),l=new Chart(n,{type:"line",data:{labels:r.map(function(t){return t.parkruns}),datasets:[{label:o,data:r.map(function(t){return{x:t.parkruns,y:t.wilsonIndex,event:t.event}}),borderColor:g(0),backgroundColor:"#2b223d"}]},options:{responsive:!0,maintainAspectRatio:!1,scales:{y:{beginAtZero:!0,title:{display:!0,text:"Wilson Index",font:{size:s.chart.fonts.axisTitle}},ticks:{font:{size:s.chart.fonts.axisTicks}},suggestedMax:Math.ceil(1.1*Math.max.apply(Math,_toConsumableArray(r.map(function(t){return t.wilsonIndex}))))},x:{title:{display:!0,text:"parkruns",font:{size:s.chart.fonts.axisTitle}},ticks:{font:{size:s.chart.fonts.axisTicks}},min:0,suggestedMax:Math.ceil(1.1*r.length)}},plugins:{title:{display:!0,text:"Wilson Index Progress",font:{size:s.chart.fonts.title}},legend:{labels:{font:{size:s.chart.fonts.legend}}},tooltip:{callbacks:{label:function(t){t=t.raw;return["Wilson Index: ".concat(t.y),"Event: ".concat(t.event)]}},titleFont:{size:s.chart.fonts.tooltipTitle},bodyFont:{size:s.chart.fonts.tooltipBody}}}}}),h(e,(()=>{var r=_asyncToGenerator(_regenerator().m(function t(a,i){return _regenerator().w(function(t){for(;;)switch(t.n){case 0:r=a,n=i,o=void 0,o=(e=l).data.datasets.length,n={label:i,data:r.map(function(t){return{x:t.parkruns,y:t.wilsonIndex,event:t.event}}),borderColor:g(o),backgroundColor:"#2b223d"},e.data.datasets.push(n),e.update(),r=Math.max.apply(Math,_toConsumableArray(e.data.datasets.flatMap(function(t){return t.data.map(function(t){return t.x})}))),o=Math.max.apply(Math,_toConsumableArray(e.data.datasets.flatMap(function(t){return t.data.map(function(t){return t.y})}))),e.options.scales.x.suggestedMax=Math.ceil(1.1*r),e.options.scales.y.suggestedMax=Math.ceil(1.1*o),e.update();case 1:return t.a(2)}var e,r,n,o},t)}));return function(t,e){return r.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")}"undefined"!=typeof module&&module.exports?module.exports={calculateWilsonIndex:d,calculateWilsonIndexOverTime:f,extractEventDetails:u,findResultsTable:c}:t()})();
Last updated
Version
1.1.8
Author
Pete Johns (@johnsyweb)
Homepage
https://www.johnsy.com/tampermonkey-parkrun/
Support
Issues / support
License
MIT