From afe32d113c3e402342fe34f4b4e485bca2fb6d10 Mon Sep 17 00:00:00 2001 From: aricooperdavis Date: Mon, 26 Feb 2024 17:07:52 +0000 Subject: [PATCH] Automated minify of 847e616927000b0a951a8776915ec05ce0234819 --- css/style.css | 91 +-------------------- index.html | 66 +-------------- script.js | 216 +------------------------------------------------- sw.js | 48 +---------- 4 files changed, 4 insertions(+), 417 deletions(-) diff --git a/css/style.css b/css/style.css index 43f2c5d..40d1e36 100644 --- a/css/style.css +++ b/css/style.css @@ -1,90 +1 @@ -body { - font-family: Verdana, Geneva, Tahoma, sans-serif -} - -.rope input, button, input { - min-height: 1.5rem; - min-width: 1.5rem; - margin: 1px; -} - -.rope, label { - display: block; -} - -label * { - vertical-align: middle; -} - -input#join { - margin-right: 0.25rem; - margin-bottom: 0.25rem; -} - -#lengths, #results { - margin: 1rem 0rem; -} - -table, td { - border: 1px solid #333; -} - -/* Leaflet map */ - -div#map { - height: 20rem; - width: 40rem; - max-height: 95%; - max-width: 100%; -} - -.leaflet-marker-icon.black { - filter: saturate(0); -} - -.leaflet-marker-icon:not(.black) { - filter: hue-rotate(190deg) brightness(200%); -} - -#lost_message { - display: none; -} - -/* Install */ -p.install { - display: none; -} - -/* Colours */ -html { - background-color: #eee; -} - -thead, tfoot { - background-color: #333; - color: #fff; -} - -input:checked + .join, tr.join { - background: #ff9; -} - -@media (prefers-color-scheme: dark) { - - body { - filter: invert(100%); - } - - div#map { - filter: invert(100%); - } - - html { - background-color: #111; - } - - .leaflet-marker-icon:not(.black) { - filter: invert(0%); - } - -} +body{font-family:Verdana,Geneva,Tahoma,sans-serif}.rope input,button,input{min-height:1.5rem;min-width:1.5rem;margin:1px}.rope,label{display:block}label *{vertical-align:middle}input#join{margin-right:.25rem;margin-bottom:.25rem}#lengths,#results{margin:1rem 0}table,td{border:1px solid #333}div#map{height:20rem;width:40rem;max-height:95%;max-width:100%}.leaflet-marker-icon.black{filter:saturate(0)}.leaflet-marker-icon:not(.black){filter:hue-rotate(190deg) brightness(200%)}#lost_message,p.install{display:none}html{background-color:#eee}tfoot,thead{background-color:#333;color:#fff}input:checked+.join,tr.join{background:#ff9}@media (prefers-color-scheme:dark){body,div#map{filter:invert(100%)}html{background-color:#111}.leaflet-marker-icon:not(.black){filter:invert(0%)}} \ No newline at end of file diff --git a/index.html b/index.html index 4d14494..6ecd535 100644 --- a/index.html +++ b/index.html @@ -1,65 +1 @@ - - - - What can I rig? - - - - - - - - - - - - - - - -

What can I rig?

-

Given the ropes you've got, what caving trips can you rig in - - ?
This data is not necessarily accurate, nor have I asked for permission to use it.

-

Psst! This site is a PWA, so you can it on your phone to use offline!

-

- Inspiration: caving_fox.
- Data: Braemoor, CNCC, Northern Caves, Not for the Faint Hearted, Simon Wilson, CCPC Rigging Guide.
- Code: Ari Cooper-Davis. -

-

Rope lengths:

-
- - -
- - -

Results:

-
- - - - - - - - - -
TripRope lengths required
-
-

Map view:

-

Note: results include 0 trip(s) with unknown locations that can't be mapped.

-
-
- - +What can I rig?

What can I rig?

Given the ropes you've got, what caving trips can you rig in ?
This data is not necessarily accurate, nor have I asked for permission to use it.

Psst! This site is a PWA, so you can it on your phone to use offline!

Inspiration: caving_fox.
Data: Braemoor, CNCC, Northern Caves, Not for the Faint Hearted, Simon Wilson, CCPC Rigging Guide.
Code: Ari Cooper-Davis.

Rope lengths:

Results:

TripRope lengths required

Map view:

Note: results include 0 trip(s) with unknown locations that can't be mapped.

\ No newline at end of file diff --git a/script.js b/script.js index e1dc4d1..68f7f2b 100644 --- a/script.js +++ b/script.js @@ -1,215 +1 @@ -// Vars -let views = { - 'derbyshire': {'coords': [53.2,-1.78], 'zoom': 9}, - 'scotland': {'coords': [57.9,-5.17], 'zoom': 7}, - 'yorkshire': {'coords': [54.17,-2.19], 'zoom': 10}, -}; - -// Register service worker for PWA -if ("serviceWorker" in navigator) { - navigator.serviceWorker.register('/what-can-I-rig/sw.js', {scope: '/what-can-I-rig/'}); -}; - -// Make installable -let deferredPrompt; -let installBtn = document.querySelector("#install"); -let installP = document.querySelector(".install"); - -window.addEventListener("beforeinstallprompt", (e) => { - e.preventDefault(); - deferredPrompt = e; - installP.style.display = "block"; - - installBtn.addEventListener("click", (e) => { - deferredPrompt.prompt(); - deferredPrompt.userChoice.then((result) => { - deferredPrompt = null; - }); - }); -}); - -// Remove rope input -function removeRope(event) { - event.target.parentNode.remove(); -} - -// Add rope input -function addRope(el) { - let newInput = ropeInput.cloneNode(true); - newInput.lastChild.disabled = false; - newInput.firstChild.value = ''; - newInput.firstChild.onkeydown = handleTabs; - addButton.parentNode.insertBefore(newInput, el.target ?? el); - for (let el of document.getElementsByClassName('rope')) { - el.lastChild.onclick = removeRope; - }; - return newInput; -}; -let addButton = document.getElementById('add'); -addButton.onclick = addRope; - -function handleTabs (event) { - if (event.code == 'Tab') { - let newInput = addRope(event.target.parentNode.nextElementSibling); - event.preventDefault(); - newInput.firstChild.focus(); - - } -} -let ropeInput = document.getElementsByClassName('rope')[0]; -ropeInput.onkeydown = handleTabs; - -// Init leaflet map -function showMap() { - let map = L.map('map').setView([54.17, -2.19], 10); - L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { - maxZoom: 19, - attribution: '© OpenStreetMap' - }).addTo(map); - return map; -} -let caveMap = showMap(); - -// Handle region selection -let regionSel = document.getElementById('region'); -let region = new URLSearchParams(window.location.search).get('region') ?? ''; -function updateRegion(reg) { - region = ['yorkshire','derbyshire','scotland'].filter(r => r == reg.toLowerCase())[0] ?? 'yorkshire'; - regionSel.value = region; - caveMap.flyTo(views[region]['coords'], views[region]['zoom']); -} -updateRegion(region); -regionSel.addEventListener('change', event => updateRegion(event.target.value)); - -// Handle reset button -let tableData = document.getElementById('data'); -let resetButton = document.getElementById('reset'); -resetButton.onclick = function reset() { - // remove all rope inputs - for (let el of document.querySelectorAll('.rope')) { - if (!el.lastChild.disabled) { - el.remove(); - } else { - el.firstChild.value = ''; - }; - }; - // remove results data - tableData.innerHTML = ''; - clearMap(); - // Uncheck join - document.getElementById('join').checked = false; -}; - -// Handle go button -let goButton = document.getElementById('go'); -goButton.onclick = function calculate() { - // Get values of inputs - let ropes = []; - for (let el of document.getElementsByClassName('rope')) { - ropes.push(el.firstChild.value); - }; - ropes = ropes.map(el => parseInt(el)).filter(el => el).sort((a, b) => a < b); - let join_ropes = document.getElementById('join').checked; - - // Reset output - tableData.innerHTML = ''; - clearMap(); - - // Get trip lengths - fetch(`./pitchlengths/${region}.txt`).then(response => response.text()).then(function (trips) { - // Parse and sort pitch lengths - trips = trips.split('\n').filter( - line => !(line.startsWith('#') | line.length < 3) - ); - for (let [_, trip] of Object.entries(trips.sort())) { - trip = trip.split(','); - // Trips can't have more pitches than we have ropes - if (trip.filter(el => !el.includes('[')).length - 1 > ropes.length) { - continue; - }; - // Pitches must be possible - pitches = trip.slice(1).filter(el => !el.includes('[')).map(x => parseInt(x.trim())).sort((a, b) => a < b); - if (pitches.every((pitch, i) => { return pitch <= ropes[i] })) { - let td_el = (trip[1].includes('[') ? `${trip[0]}` : trip[0]); - let td_rp = trip.slice(1).filter(el => !el.includes('[')).join(', ') || 'Ropes in-situ'; - let row = `${td_el}${td_rp}`; - tableData.insertAdjacentHTML('beforeend', row); - displayOnMap(trip, false); - continue; - }; - if (join_ropes) { - for (let [_, _join] of Object.entries(join_combs(ropes))) { - if (pitches.every((pitch, i) => { return pitch <= _join[i] })) { - let td_el = (trip[1].includes('[') ? `${trip[0]}` : trip[0]); - let td_rp = trip.slice(1).filter(el => !el.includes('[')).join(', ') || 'Ropes in-situ'; - let row = `${td_el}${td_rp}`; - tableData.insertAdjacentHTML('beforeend', row); - displayOnMap(trip, true); - break; - }; - }; - }; - }; - // Fit map to markers - fitBounds(); - }); -}; - -// Get all unique rope join combinations (reverse sorted) -function join_combs(ropes) { - function join(ropes) { - let joins = []; - for (var i = 0; i < ropes.length - 1; i++) { - for (var j = i + 1; j < ropes.length; j++) { - joins.push(ropes.slice(0, i).concat(ropes[i] + ropes[j], ropes.slice(i + 1, j), ropes.slice(j + 1))); - } - } - return joins; - } - - let joins = join(ropes); - joins.forEach(i => { - if (i.length > 1) { - join(i).forEach(j => { - joins.push(j.sort().reverse()); - }); - } - }); - joins = Object.values(joins.reduce((p, c) => (p[JSON.stringify(c)] = c, p), {})); - return (joins); -} - -// Display on map -let markers = []; -let lost_count = document.getElementById('lost_count'); -let lost_message = document.getElementById('lost_message'); -function displayOnMap(trip, join) { - if (trip[1].includes('[')) { - let marker = L.marker(trip.slice(1,3).map(el => parseFloat(el.slice(1,-1)))).addTo(caveMap); - if (!join) { - marker._icon.classList.add('black'); - } - marker.bindPopup(`${trip[0]}`); - markers.push(marker); - } else { - lost_count.textContent = parseInt(lost_count.textContent)+1; - lost_message.style.display = 'block'; - } -} - -// Clear map of existing markers -function clearMap() { - markers.forEach(marker => caveMap.removeLayer(marker)); - markers = []; - lost_count.textContent = '0'; - lost_message.style.display = 'none'; -} - -// Move map to show all markers -let group = null; -function fitBounds() { - if ( markers.length > 0 ) { - group = new L.featureGroup(markers); - caveMap.flyToBounds(group.getBounds().pad(0.1), ); - } -} \ No newline at end of file +let deferredPrompt,views={derbyshire:{coords:[53.2,-1.78],zoom:9},scotland:{coords:[57.9,-5.17],zoom:7},yorkshire:{coords:[54.17,-2.19],zoom:10}};"serviceWorker"in navigator&&navigator.serviceWorker.register("/what-can-I-rig/sw.js",{scope:"/what-can-I-rig/"});let installBtn=document.querySelector("#install"),installP=document.querySelector(".install");function removeRope(e){e.target.parentNode.remove()}function addRope(e){let t=ropeInput.cloneNode(!0);t.lastChild.disabled=!1,t.firstChild.value="",t.firstChild.onkeydown=handleTabs,addButton.parentNode.insertBefore(t,e.target??e);for(let e of document.getElementsByClassName("rope"))e.lastChild.onclick=removeRope;return t}window.addEventListener("beforeinstallprompt",(e=>{e.preventDefault(),deferredPrompt=e,installP.style.display="block",installBtn.addEventListener("click",(e=>{deferredPrompt.prompt(),deferredPrompt.userChoice.then((e=>{deferredPrompt=null}))}))}));let addButton=document.getElementById("add");function handleTabs(e){if("Tab"==e.code){let t=addRope(e.target.parentNode.nextElementSibling);e.preventDefault(),t.firstChild.focus()}}addButton.onclick=addRope;let ropeInput=document.getElementsByClassName("rope")[0];function showMap(){let e=L.map("map").setView([54.17,-2.19],10);return L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png",{maxZoom:19,attribution:'© OpenStreetMap'}).addTo(e),e}ropeInput.onkeydown=handleTabs;let caveMap=showMap(),regionSel=document.getElementById("region"),region=new URLSearchParams(window.location.search).get("region")??"";function updateRegion(e){region=["yorkshire","derbyshire","scotland"].filter((t=>t==e.toLowerCase()))[0]??"yorkshire",regionSel.value=region,caveMap.flyTo(views[region].coords,views[region].zoom)}updateRegion(region),regionSel.addEventListener("change",(e=>updateRegion(e.target.value)));let tableData=document.getElementById("data"),resetButton=document.getElementById("reset");resetButton.onclick=function(){for(let e of document.querySelectorAll(".rope"))e.lastChild.disabled?e.firstChild.value="":e.remove();tableData.innerHTML="",clearMap(),document.getElementById("join").checked=!1};let goButton=document.getElementById("go");function join_combs(e){function t(e){let t=[];for(var n=0;n{e.length>1&&t(e).forEach((e=>{n.push(e.sort().reverse())}))})),n=Object.values(n.reduce(((e,t)=>(e[JSON.stringify(t)]=t,e)),{})),n}goButton.onclick=function(){let e=[];for(let t of document.getElementsByClassName("rope"))e.push(t.firstChild.value);e=e.map((e=>parseInt(e))).filter((e=>e)).sort(((e,t)=>ee.text())).then((function(n){n=n.split("\n").filter((e=>!(e.startsWith("#")|e.length<3)));for(let[o,r]of Object.entries(n.sort()))if(r=r.split(","),!(r.filter((e=>!e.includes("["))).length-1>e.length))if(pitches=r.slice(1).filter((e=>!e.includes("["))).map((e=>parseInt(e.trim()))).sort(((e,t)=>et<=e[n]))){let e=`${r[1].includes("[")?`${r[0]}`:r[0]}${r.slice(1).filter((e=>!e.includes("["))).join(", ")||"Ropes in-situ"}`;tableData.insertAdjacentHTML("beforeend",e),displayOnMap(r,!1)}else if(t)for(let[t,n]of Object.entries(join_combs(e)))if(pitches.every(((e,t)=>e<=n[t]))){let e=`${r[1].includes("[")?`${r[0]}`:r[0]}${r.slice(1).filter((e=>!e.includes("["))).join(", ")||"Ropes in-situ"}`;tableData.insertAdjacentHTML("beforeend",e),displayOnMap(r,!0);break}fitBounds()}))};let markers=[],lost_count=document.getElementById("lost_count"),lost_message=document.getElementById("lost_message");function displayOnMap(e,t){if(e[1].includes("[")){let n=L.marker(e.slice(1,3).map((e=>parseFloat(e.slice(1,-1))))).addTo(caveMap);t||n._icon.classList.add("black"),n.bindPopup(`${e[0]}`),markers.push(n)}else lost_count.textContent=parseInt(lost_count.textContent)+1,lost_message.style.display="block"}function clearMap(){markers.forEach((e=>caveMap.removeLayer(e))),markers=[],lost_count.textContent="0",lost_message.style.display="none"}let group=null;function fitBounds(){markers.length>0&&(group=new L.featureGroup(markers),caveMap.flyToBounds(group.getBounds().pad(.1)))} \ No newline at end of file diff --git a/sw.js b/sw.js index e1ffecd..04c41b9 100644 --- a/sw.js +++ b/sw.js @@ -1,47 +1 @@ -let cacheName = 'what-can-i-rig-v1'; -let cacheableContent = [ - '/what-can-I-rig/', - '/what-can-I-rig/index.html', - '/what-can-I-rig/icons/favicon.png', - '/what-can-I-rig/script.js', - '/what-can-I-rig/sw.js', - '/what-can-I-rig/css/style.css', - '/what-can-I-rig/pitchlengths/yorkshire.txt', - '/what-can-I-rig/pitchlengths/derbyshire.txt', - '/what-can-I-rig/pitchlengths/scotland.txt', -]; - -// On install cache required assets -self.addEventListener('install', (e) => { - e.waitUntil((async () => { - let cache = await caches.open(cacheName); - await cache.addAll(cacheableContent); - })()); -}); - -// Clear old caches when cacheName is updated (i.e. pitchlengths.txt is update) -self.addEventListener('activate', (e) => { - e.waitUntil(caches.keys().then((keyList) => { - return Promise.all(keyList.map((key) => { - if (key === cacheName) { return; } - return caches.delete(key); - })); - })); - }); - -// On fetch serve files from cache (if available) -self.addEventListener('fetch', (e) => { - e.responseWith((async () => { - let r = await caches.match(e.request, {ignoreSearch: true}); - // Serve cached file - if (r) { return r; } - // Else fetch remote, cache, and serve - let response = await fetch(e.request); - // Don't cache map tiles - if (!e.request.url.includes('openstreetmap')) { - let cache = await caches.open(cacheName) - cache.put(e.request, response.clone()); - } - return response; - })()); -}); \ No newline at end of file +let cacheName="what-can-i-rig-v1",cacheableContent=["/what-can-I-rig/","/what-can-I-rig/index.html","/what-can-I-rig/icons/favicon.png","/what-can-I-rig/script.js","/what-can-I-rig/sw.js","/what-can-I-rig/css/style.css","/what-can-I-rig/pitchlengths/yorkshire.txt","/what-can-I-rig/pitchlengths/derbyshire.txt","/what-can-I-rig/pitchlengths/scotland.txt"];self.addEventListener("install",(e=>{e.waitUntil((async()=>{let e=await caches.open(cacheName);await e.addAll(cacheableContent)})())})),self.addEventListener("activate",(e=>{e.waitUntil(caches.keys().then((e=>Promise.all(e.map((e=>{if(e!==cacheName)return caches.delete(e)}))))))})),self.addEventListener("fetch",(e=>{e.responseWith((async()=>{let t=await caches.match(e.request,{ignoreSearch:!0});if(t)return t;let a=await fetch(e.request);if(!e.request.url.includes("openstreetmap")){(await caches.open(cacheName)).put(e.request,a.clone())}return a})())})); \ No newline at end of file