diff options
| author | Martin Fischer <martin@push-f.com> | 2025-11-14 08:35:35 +0100 |
|---|---|---|
| committer | Martin Fischer <martin@push-f.com> | 2025-11-14 08:36:40 +0100 |
| commit | 067e52bc973931680da025b4f012f92f94bdbe53 (patch) | |
| tree | 77713dee7e54fa5b161178f8907fe3cf5cc70a84 /src | |
| parent | 036895f35ed6cb1b7832c0c1dc42c5d95a8b247f (diff) | |
The nix derivation src didn't use lib.cleanSource. Putting the source files
into a separate directory is even better since it avoids unnecessary rebuilds.
Diffstat (limited to 'src')
| -rw-r--r-- | src/index.html | 26 | ||||
| -rw-r--r-- | src/script.js | 117 |
2 files changed, 143 insertions, 0 deletions
diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..a3a747d --- /dev/null +++ b/src/index.html @@ -0,0 +1,26 @@ +<!doctype html> +<html> + <meta charset="utf-8"> + <title>Share a geographic position</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> + <body> + <div id="content"></div> + <noscript>This website requires JavaScript</noscript> + <script src="script.js"></script> + <style> + body { + max-width: 40ch; + margin: 1em auto; + padding: 0 0.5em; + font-size: 1.2em; + } + li { + padding: 1em; + } + .input-group { display: flex; gap: 0.2em; margin-bottom: 1em; } + .input-group input { flex-grow: 1; } + input, button { font-size: inherit; } + .error { color: darkred; } + </style> + </body> +</html> diff --git a/src/script.js b/src/script.js new file mode 100644 index 0000000..8e539d8 --- /dev/null +++ b/src/script.js @@ -0,0 +1,117 @@ +const LINKS = [ + { + label: 'Google Maps', + url: 'https://www.google.com/maps/search/?api=1&query={lat},{lon}', + }, + { + label: 'Apple Maps', + url: 'https://maps.apple.com/?q={lat},{lon}&t=m', + }, + { + label: 'Default app', + url: 'geo:{lat},{lon}', + }, + { + label: 'OpenStreetMap', + url: 'https://www.openstreetmap.org/?mlat={lat}&mlon={lon}&zoom=15&layers=M', + }, +]; + +const content = document.getElementById('content'); + +route(); +window.addEventListener('hashchange', route); + +function route() { + const latLon = parseLatLon(location.hash.slice(1)); + content.innerHTML = ''; + + if (!latLon) { + document.title = 'Share a location'; + content.append(createEl('h1', {}, [document.title])); + const input = createEl('input', { placeholder: '55.7, 12.5' }); + const shareInputButton = createEl('button', {}, ['Share']); + const inputGroup = createEl('div', { class: 'input-group' }, [input, shareInputButton]); + const errorDiv = createEl('div', { class: 'error' }); + content.append(createEl('label', {}, ['Enter latitude, longitude', inputGroup])); + content.append(errorDiv); + function shareInput() { + if (!input.value.trim()) { + return; + } + try { + const [lat, lon] = parseLatLon(input.value); + shareLocation(lat, lon); + } catch (error) { + errorDiv.textContent = error; + } + } + input.addEventListener('keydown', (e) => { + if (e.key == 'Enter') { + shareInput(); + } + }); + shareInputButton.addEventListener('click', shareInput); + if (navigator.geolocation) { + const shareCurPos = createEl('button', {}, ['Share current position']); + content.append('or ', shareCurPos, errorDiv); + shareCurPos.addEventListener('click', () => { + navigator.geolocation.getCurrentPosition( + (pos) => { + shareLocation(pos.coords.latitude, pos.coords.longitude); + }, + (err) => { + errorDiv.textContent = err.message; + }, + ); + }); + } + } else { + document.title = 'Open location'; + + content.append(createEl('p', {}, ['Open in'])); + + const linkList = createEl('ul'); + const [lat, lon] = latLon; + + for (const linkSpec of LINKS) { + const url = linkSpec.url.replace('{lat}', lat).replace('{lon}', lon); + const link = createEl('a', { href: url }, [linkSpec.label]); + linkList.append(createEl('li', {}, [link])); + } + content.append(linkList); + } +} + +function shareLocation(lat, lon) { + // TODO: use navigator.share if supported + window.location = `#${lat},${lon}`; +} + +function parseLatLon(text) { + if (text == '') { + return null; + } + + let [lat, lon] = text.split(','); + lat = parseFloat(lat); + lon = parseFloat(lon); + if (Number.isNaN(lat) || Number.isNaN(lon)) { + throw Error('failed to parse latitude or longitude'); + } + if (lat < -180 || lat > 180 || lon < -90 || lon > 90) { + throw Error('latitude or longitude are out of range'); + } + return [lat, lon]; +} + +function createEl(tag, attrs = {}, content = []) { + const el = document.createElement(tag); + for (const [name, value] of Object.entries(attrs)) { + el.setAttribute(name, value); + } + for (const child of content) { + el.append(child); + } + return el; +} |
