About

parkrun Future Roster Printable View acts on parkrun Future Roster pages (e.g. Logan River Future Roster). It exists as a workaround to help volunteer run directors print custom task labels until parkrun adds functionality to print those labels directly from EMS.

Privacy and data

Privacy is paramount. This userscript runs entirely in your browser (@grant none). It does not connect to parkrun EMS, cannot pull custom task labels from EMS, and does not transmit roster data, your edits, or any other information to the author, parkrun, or any third party.

It only uses information already shown on the public Future Roster page. Custom role labels and the note below the table are stored and retrieved only on your device (browser local storage). You can clear that stored data at any time with one click: Reset saved labels above the roster.

Getting started

The page loads as usual. When you want a simplified view for editing and printing, click 🖨️ Prepare for printing above the roster.

Printable view

After you prepare, everything except the roster table is removed for a clean print.

Editing and persistence

Role label cells become editable so you can apply your team’s custom labels to tasks. Those role label edits persist across sessions.

There is an editable note below the table, and that note also persists.

Other table cells are editable for temporary notes, such as volunteer headlining, but those temporary cell edits are not persisted.

Click Reset saved labels above the roster to clear all saved role labels and the note from your device in one step.

Returning to the normal page

Reload the page to return to the standard parkrun layout.

Version 0.1.10 Updated

Screenshot

parkrun Future Roster Printable View Screenshot

Install

Recommended: userscript. Bookmarklet works where a userscript manager isn’t available.

Userscript (recommended)

Install parkrun Future Roster Printable View

First time? Userscript basics and installation steps

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 steps

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

Bookmarklet

A bookmarklet is a bookmark whose URL is JavaScript. Use it on browsers that support bookmarks but not userscript managers.

Drag this link to your bookmarks bar:

parkrun Future Roster Printable View bookmarklet

Mobile bookmarklet setup and full code

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(){var STYLE_ID="parkrun-future-roster-printable",PREPARE_CONTROL_ID="parkrun-future-roster-prepare-control",PREPARE_BUTTON_ID="parkrun-future-roster-prepare-button",PREPARE_CONTROL_STYLE_ID="parkrun-future-roster-prepare-control-styles",PREPARE_BUTTON_LABEL="🖨️ Prepare for printing",PREPARE_HELPER_TEXT="Opens a simplified view for editing and printing.",RESET_BUTTON_ID="parkrun-future-roster-reset-button",PERSISTENCE_ERROR_ID="parkrun-future-roster-persistence-error",PERSISTENCE_CONTROLS_ID="parkrun-future-roster-persistence-controls",STORAGE_KEY_PREFIX="parkrun-future-roster-printable",CORE_ROLES_EXPLANATION_ID="parkrun-core-roles-explanation",CORE_ROLE_FOOTNOTE_MARKER="*",DEFAULT_CORE_ROLES_EXPLANATION="Rows marked ".concat(CORE_ROLE_FOOTNOTE_MARKER," are core roles. Every core role must be covered for the event to go ahead.");function getPrintableTitle(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:"",e=null==e?void 0:e.querySelector("h1");return(null==e||null==(e=e.textContent)?void 0:e.trim())||t}function findRosterTable(e){var t;return null!=(t=null!=(t=null==e?void 0:e.querySelector("#viewroster table"))?t:null==e?void 0:e.querySelector("table"))?t:null}function findRosterTableStyles(e){e=null==e?void 0:e.querySelector("#viewroster");return e?Array.from(e.children).filter(function(e){return"STYLE"===e.tagName}):[]}function findPrepareInsertionPoint(e){var t;return null!=(t=null==e?void 0:e.querySelector("#viewroster"))?t:findRosterTable(e)}function preserveRosterTableStyles(t,e){findRosterTableStyles(e).forEach(function(e){return t.head.appendChild(e)})}function buildControlStyles(){return"\n#".concat(PREPARE_CONTROL_ID," {\n  margin: 1rem 0;\n}\n\n#").concat(PREPARE_BUTTON_ID," {\n  display: inline-block;\n  padding: 0.75em 1.5em;\n  background: #4c1a57;\n  color: #fff;\n  border: none;\n  border-radius: 999px;\n  font: inherit;\n  font-weight: 700;\n  font-size: 0.95rem;\n  letter-spacing: 0.03em;\n  cursor: pointer;\n  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n\n#").concat(PREPARE_BUTTON_ID,":hover {\n  background: #3d2f4f;\n}\n\n#").concat(PREPARE_BUTTON_ID,":focus-visible {\n  outline: 3px solid #f7a541;\n  outline-offset: 2px;\n}\n\n#").concat(PREPARE_CONTROL_ID," p {\n  margin: 0.65rem 0 0;\n  font-size: 0.9rem;\n  color: #666;\n}\n\n#").concat(RESET_BUTTON_ID," {\n  margin: 0 0 0.5rem;\n  padding: 0.5em 1em;\n  border: 1px solid #4c1a57;\n  background: #fff;\n  color: #4c1a57;\n  border-radius: 4px;\n  font: inherit;\n  cursor: pointer;\n}\n\n#").concat(RESET_BUTTON_ID,":focus-visible {\n  outline: 3px solid #f7a541;\n  outline-offset: 2px;\n}\n\n#").concat(PERSISTENCE_ERROR_ID," {\n  margin: 0 0 0.5rem;\n  color: #b00020;\n  font-size: 0.9rem;\n}\n").trim()}function injectControlStyles(){var e,t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document;t.getElementById(PREPARE_CONTROL_STYLE_ID)||((e=t.createElement("style")).id=PREPARE_CONTROL_STYLE_ID,e.textContent=buildControlStyles(),t.head.appendChild(e))}function buildSupplementalStyles(){return"\nhtml,\nbody {\n  background: #fff !important;\n  margin: 0;\n  padding: 0;\n}\n\nbody {\n  background-image: none !important;\n}\n\ntable {\n  width: 100%;\n}\n\n#rosterTable {\n  border-collapse: collapse;\n}\n\n#rosterTable,\n#rosterTable th,\n#rosterTable td {\n  border: 1px solid black;\n  padding: 4px;\n}\n\n#rosterTable td,\n#rosterTable th {\n  cursor: text;\n}\n\n#rosterTable td:focus,\n#rosterTable th:focus {\n  outline: 2px solid #0072b1;\n  outline-offset: -2px;\n}\n\n#rosterTable tr.core-role th.corerole {\n  background: #f2f2f2 !important;\n}\n\n#rosterTable tr.core-role th.corerole::after {\n  content: ' ".concat(CORE_ROLE_FOOTNOTE_MARKER,"';\n  font-weight: normal;\n}\n\n.core-roles-explanation {\n  font-size: 0.9rem;\n  margin: 8px 0 0;\n}\n\n.core-roles-explanation:focus {\n  outline: 2px solid #0072b1;\n  outline-offset: 2px;\n}\n\n@page {\n  size: A4 landscape;\n  margin: 10mm;\n}\n\n@media print {\n  html,\n  body {\n    background: #fff !important;\n  }\n\n  a,\n  a:visited {\n    color: inherit !important;\n    text-decoration: none !important;\n  }\n\n  a[href]::after {\n    content: none !important;\n  }\n\n  tr {\n    break-inside: avoid;\n    page-break-inside: avoid;\n  }\n\n  #rosterTable td:focus,\n  #rosterTable th:focus {\n    outline: none;\n  }\n\n  .core-roles-explanation:focus {\n    outline: none;\n  }\n\n  #").concat(PERSISTENCE_CONTROLS_ID," {\n    display: none !important;\n  }\n}\n").trim()}function injectSupplementalStyles(){var e,t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document;t.getElementById(STYLE_ID)||((e=t.createElement("style")).id=STYLE_ID,e.textContent=buildSupplementalStyles(),t.head.appendChild(e))}function enableCellEditing(e){e.querySelectorAll("td, th").forEach(function(e){e.setAttribute("contenteditable","true"),e.setAttribute("tabindex","0")})}function markCoreRoleRows(e){e.querySelectorAll("tr").forEach(function(e){e.querySelector("th.corerole")&&e.classList.add("core-role")})}function createCoreRolesExplanation(){var e=(0<arguments.length&&void 0!==arguments[0]?arguments[0]:document).createElement("p");return e.id=CORE_ROLES_EXPLANATION_ID,e.className="core-roles-explanation",e.setAttribute("contenteditable","true"),e.setAttribute("tabindex","0"),e.textContent=DEFAULT_CORE_ROLES_EXPLANATION,e}function getRoleHeaderCells(e){return Array.from(e.querySelectorAll("tbody tr th"))}function getHeaderText(e){return(null!=(e=e.textContent)?e:"").trim()}function setHeaderText(e,t){var n=e.querySelector("a");n?n.textContent=t:e.textContent=t}function buildStorageKey(e,t){return"".concat(STORAGE_KEY_PREFIX,":").concat(e,"|").concat(t)}function getEventContext(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document,t=null!=(t=null==(t=e.location)?void 0:t.hostname)?t:"";return{slug:null!=(e=(null!=(e=null==(e=e.location)?void 0:e.pathname)?e:"").split("/").filter(Boolean)[0])?e:"unknown",tld:t.replace(/^www\.parkrun\./,"")||"unknown"}}function getStorageKeyForDocument(){var e=getEventContext(0<arguments.length&&void 0!==arguments[0]?arguments[0]:document);return buildStorageKey(e.slug,e.tld)}function getPersistenceErrorElement(){return(0<arguments.length&&void 0!==arguments[0]?arguments[0]:document).getElementById(PERSISTENCE_ERROR_ID)}function setPersistenceError(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document,t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:"",e=getPersistenceErrorElement(e);e&&(e.textContent=t)}function rememberDefaults(e,t){e.dataset.defaultHeaders=JSON.stringify(getRoleHeaderCells(e).map(getHeaderText)),t.dataset.defaultText=t.textContent}function getDefaultHeaders(e){try{var t;return JSON.parse(null!=(t=e.dataset.defaultHeaders)?t:"[]")}catch(e){return[]}}function createPersistenceControls(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document,t=e.createElement("div"),n=(t.id=PERSISTENCE_CONTROLS_ID,e.createElement("button")),e=(n.id=RESET_BUTTON_ID,n.type="button",n.textContent="Reset saved labels",e.createElement("p"));return e.id=PERSISTENCE_ERROR_ID,e.setAttribute("role","status"),e.setAttribute("aria-live","polite"),t.append(n,e),t}function savePersistedEdits(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document,e=t.getElementById("rosterTable"),n=t.getElementById(CORE_ROLES_EXPLANATION_ID);if(!e||!n)return!1;var r=getDefaultHeaders(e),n={headers:getRoleHeaderCells(e).map(function(e,t){return{rowIndex:t,originalText:null!=(t=r[t])?t:"",value:getHeaderText(e)}}).filter(function(e){return e.value!==e.originalText}),explanation:null!=(e=n.textContent)?e:""};try{return window.localStorage.setItem(getStorageKeyForDocument(t),JSON.stringify(n)),setPersistenceError(t,""),!0}catch(e){return setPersistenceError(t,"Could not save edits locally in this browser."),!1}}function restorePersistedEdits(){var r,o,t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document,e=t.getElementById("rosterTable"),n=t.getElementById(CORE_ROLES_EXPLANATION_ID);if(!e||!n)return!1;try{var l,i=JSON.parse(null!=(l=window.localStorage.getItem(getStorageKeyForDocument(t)))?l:"null")}catch(e){return setPersistenceError(t,"Could not read saved edits from local storage."),!1}return!!i&&(r=getRoleHeaderCells(e),o=getDefaultHeaders(e),(null!=(l=i.headers)?l:[]).forEach(function(e){var t,n=null==e?void 0:e.rowIndex;!Number.isInteger(n)||n<0||n>=r.length||(null!=(t=o[n])?t:"")===(null!=(t=e.originalText)?t:"")&&setHeaderText(r[n],null!=(t=e.value)?t:"")}),"string"==typeof i.explanation&&(n.textContent=i.explanation),!0)}function resetPersistedEdits(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document,e=t.getElementById("rosterTable"),n=t.getElementById(CORE_ROLES_EXPLANATION_ID);if(!e||!n)return!1;try{window.localStorage.removeItem(getStorageKeyForDocument(t))}catch(e){return setPersistenceError(t,"Could not clear saved edits in this browser."),!1}var r=getRoleHeaderCells(e),o=getDefaultHeaders(e);return r.forEach(function(e,t){setHeaderText(e,null!=(t=o[t])?t:getHeaderText(e))}),n.textContent=null!=(e=n.dataset.defaultText)?e:DEFAULT_CORE_ROLES_EXPLANATION,setPersistenceError(t,""),!0}function attachPersistenceHandlers(){var e,t,n=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document,r=n.getElementById("rosterTable"),o=n.getElementById(CORE_ROLES_EXPLANATION_ID),l=n.getElementById(RESET_BUTTON_ID);r&&o&&l&&(t=function(){e&&window.clearTimeout(e),e=window.setTimeout(function(){savePersistedEdits(n)},250)},getRoleHeaderCells(r).forEach(function(e){e.addEventListener("input",t),e.addEventListener("blur",t)}),o.addEventListener("input",t),o.addEventListener("blur",t),l.addEventListener("click",function(){resetPersistedEdits(n)}))}function createPrepareControl(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document,t=e.createElement("div"),n=(t.id=PREPARE_CONTROL_ID,e.createElement("button")),r=(n.id=PREPARE_BUTTON_ID,n.type="button",n.textContent=PREPARE_BUTTON_LABEL,n.addEventListener("click",function(){prepareForPrinting(e)}),e.createElement("p"));return r.textContent=PREPARE_HELPER_TEXT,t.append(n,r),t}function injectPrepareControl(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document;if(!e.getElementById(PREPARE_CONTROL_ID)){var t=e.getElementById("main");if(!t)return!1;if(!findRosterTable(t))return!1;t=findPrepareInsertionPoint(t);if(null==t||!t.parentNode)return!1;injectControlStyles(e),t.parentNode.insertBefore(createPrepareControl(e),t)}return!0}function injectPersistenceControls(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document,t=e.getElementById("rosterTable");return!(!t||e.getElementById(PERSISTENCE_CONTROLS_ID)||(t.parentNode.insertBefore(createPersistenceControls(e),t),0))}function isolateMainForPrint(){var e,t,n=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document,r=n.getElementById("main");return!!r&&!!(e=findRosterTable(r))&&(t=getPrintableTitle(r,n.title),preserveRosterTableStyles(n,r),n.body.replaceChildren(e,createCoreRolesExplanation(n)),markCoreRoleRows(e),injectSupplementalStyles(n),enableCellEditing(e),n.title=t,!0)}function prepareForPrinting(){var e,t,n=0<arguments.length&&void 0!==arguments[0]?arguments[0]:document;return!!isolateMainForPrint(n)&&(e=n.getElementById("rosterTable"),t=n.getElementById(CORE_ROLES_EXPLANATION_ID),e&&t&&rememberDefaults(e,t),injectPersistenceControls(n),restorePersistedEdits(n),attachPersistenceHandlers(n),null!=(e=n.defaultView)&&e.scrollTo(0,0),!0)}function initFutureRosterPrintable(){injectPrepareControl(0<arguments.length&&void 0!==arguments[0]?arguments[0]:document)}initFutureRosterPrintable(document),"undefined"!=typeof module&&void 0!==module.exports&&(module.exports={STYLE_ID:STYLE_ID,PREPARE_CONTROL_ID:PREPARE_CONTROL_ID,PREPARE_BUTTON_ID:PREPARE_BUTTON_ID,PREPARE_CONTROL_STYLE_ID:PREPARE_CONTROL_STYLE_ID,PREPARE_BUTTON_LABEL:PREPARE_BUTTON_LABEL,PREPARE_HELPER_TEXT:PREPARE_HELPER_TEXT,RESET_BUTTON_ID:RESET_BUTTON_ID,PERSISTENCE_ERROR_ID:PERSISTENCE_ERROR_ID,PERSISTENCE_CONTROLS_ID:PERSISTENCE_CONTROLS_ID,CORE_ROLES_EXPLANATION_ID:CORE_ROLES_EXPLANATION_ID,CORE_ROLE_FOOTNOTE_MARKER:CORE_ROLE_FOOTNOTE_MARKER,DEFAULT_CORE_ROLES_EXPLANATION:DEFAULT_CORE_ROLES_EXPLANATION,buildStorageKey:buildStorageKey,buildControlStyles:buildControlStyles,buildSupplementalStyles:buildSupplementalStyles,createCoreRolesExplanation:createCoreRolesExplanation,createPrepareControl:createPrepareControl,createPersistenceControls:createPersistenceControls,enableCellEditing:enableCellEditing,findRosterTable:findRosterTable,findRosterTableStyles:findRosterTableStyles,findPrepareInsertionPoint:findPrepareInsertionPoint,getPrintableTitle:getPrintableTitle,injectControlStyles:injectControlStyles,injectPrepareControl:injectPrepareControl,injectPersistenceControls:injectPersistenceControls,injectSupplementalStyles:injectSupplementalStyles,initFutureRosterPrintable:initFutureRosterPrintable,isolateMainForPrint:isolateMainForPrint,markCoreRoleRows:markCoreRoleRows,prepareForPrinting:prepareForPrinting,preserveRosterTableStyles:preserveRosterTableStyles,restorePersistedEdits:restorePersistedEdits,savePersistedEdits:savePersistedEdits,resetPersistedEdits:resetPersistedEdits});})()

Support

Issues and support on GitHub