alpha
Login
or
Join now
lass.pds.lassul.us
/
superconfig
Star
0
Fork
1
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
This repository has no description
Star
0
Fork
1
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
1
Pipelines
where.is.lassul.us: add share buttons
author
lassulus
date
3 weeks ago
(Sep 28, 2026, 2:01 PM CEST)
commit
9751d81b
9751d81bd30addc94de501b048a80e9b22b9ba55
parent
6ff102f8
6ff102f809b8bdf56c42ae77d7644e88c4bbe27b
+444
-63
4 changed files
Expand all
Collapse all
Unified
Split
2configs
websites
where.is.lassul.us
at.html
default.nix
index.html
maps.js
+7
-55
2configs/websites/where.is.lassul.us/at.html
View file
Reviewed
···
5
5
<meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
<title>lassulus was here</title>
7
7
<link rel="stylesheet" href="/leaflet/leaflet.css" />
8
8
+
<link rel="stylesheet" href="/maps.css" />
8
9
<style>
9
10
html,
10
11
body {
···
33
34
user-select: all;
34
35
color: #aaa;
35
36
}
36
36
-
#open {
37
37
-
display: grid;
38
38
-
grid-template-columns: 1fr 1fr;
39
39
-
gap: 0.5em;
40
40
-
}
41
41
-
#open a {
42
42
-
display: block;
43
43
-
padding: 0.8em;
44
44
-
border-radius: 0.4em;
45
45
-
text-align: center;
46
46
-
text-decoration: none;
47
47
-
color: #fff;
48
48
-
background: #333;
49
49
-
}
50
50
-
#open a:active {
51
51
-
background: #39f;
52
52
-
}
53
37
#live {
54
38
display: block;
55
39
margin-top: 1em;
···
62
46
<div id="panel">
63
47
<p id="when"></p>
64
48
<p id="coords"></p>
65
65
-
<div id="open"></div>
49
49
+
<div id="open" class="open"></div>
66
50
<a id="live" href="/">live location →</a>
67
51
</div>
68
52
<script src="/leaflet/leaflet.js"></script>
53
53
+
<script src="/maps.js"></script>
69
54
<script>
70
55
(function () {
71
56
// /at#LAT,LON[@UNIXTIME]; the fragment never reaches the server
57
57
+
window.addEventListener("hashchange", function () {
58
58
+
location.reload();
59
59
+
});
72
60
var m = /^#(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?)(?:@(\d+))?$/.exec(
73
61
location.hash,
74
62
);
···
97
85
}
98
86
document.getElementById("coords").textContent = lat + ", " + lon;
99
87
100
100
-
// iOS has no handler for geo: (iPadOS reports itself as a Mac)
101
101
-
var ios =
102
102
-
/iPad|iPhone|iPod/.test(navigator.userAgent) ||
103
103
-
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
104
104
-
var ll = lat + "," + lon;
105
105
-
var links = [
106
106
-
[
107
107
-
"Google Maps",
108
108
-
"https://www.google.com/maps/search/?api=1&query=" +
109
109
-
encodeURIComponent(ll),
110
110
-
],
111
111
-
[
112
112
-
"Apple Maps",
113
113
-
"https://maps.apple.com/?ll=" + ll + "&q=lassulus",
114
114
-
],
115
115
-
// android: app chooser incl. OsmAnd, Organic Maps, ...
116
116
-
ios ? null : ["other app", "geo:" + ll + "?q=" + ll + "(lassulus)"],
117
117
-
[
118
118
-
"OpenStreetMap",
119
119
-
"https://www.openstreetmap.org/?mlat=" +
120
120
-
lat +
121
121
-
"&mlon=" +
122
122
-
lon +
123
123
-
"#map=16/" +
124
124
-
lat +
125
125
-
"/" +
126
126
-
lon,
127
127
-
],
128
128
-
];
129
129
-
var open = document.getElementById("open");
130
130
-
links.forEach(function (l) {
131
131
-
if (!l) return;
132
132
-
var a = document.createElement("a");
133
133
-
a.textContent = l[0];
134
134
-
a.href = l[1];
135
135
-
open.appendChild(a);
136
136
-
});
88
88
+
renderMapLinks(document.getElementById("open"), lat, lon);
137
89
})();
138
90
</script>
139
91
</body>
+5
2configs/websites/where.is.lassul.us/default.nix
View file
Reviewed
···
14
14
webroot = pkgs.runCommand "${domain}-webroot" { } ''
15
15
mkdir -p $out/leaflet
16
16
cp ${./index.html} $out/index.html
17
17
+
cp ${./at.html} $out/at.html
18
18
+
cp ${./maps.js} $out/maps.js
19
19
+
cp ${./maps.css} $out/maps.css
17
20
cp ${leaflet}/dist/leaflet.js ${leaflet}/dist/leaflet.css $out/leaflet/
18
21
cp -r ${leaflet}/dist/images $out/leaflet/images
19
22
'';
···
101
104
enableACME = true;
102
105
forceSSL = true;
103
106
root = webroot;
107
107
+
# share landing page: /at#LAT,LON[@UNIXTIME]
108
108
+
locations."= /at".tryFiles = "/at.html =404";
104
109
locations."= /location.json".extraConfig = ''
105
110
alias ${stateDir}/location.json;
106
111
default_type application/json;
+430
-7
2configs/websites/where.is.lassul.us/index.html
View file
Reviewed
···
5
5
<meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
<title>where is lassulus?</title>
7
7
<link rel="stylesheet" href="/leaflet/leaflet.css" />
8
8
+
<link rel="stylesheet" href="/maps.css" />
8
9
<style>
9
10
html,
10
11
body {
···
30
31
#info a {
31
32
color: #9cf;
32
33
}
34
34
+
.ctl a {
35
35
+
font-size: 18px;
36
36
+
text-decoration: none;
37
37
+
}
38
38
+
.ctl a.on {
39
39
+
background: #39f;
40
40
+
}
41
41
+
#compass {
42
42
+
display: none;
43
43
+
position: absolute;
44
44
+
z-index: 1000;
45
45
+
left: 50%;
46
46
+
top: 50%;
47
47
+
transform: translate(-50%, -50%);
48
48
+
width: 14em;
49
49
+
padding: 1em;
50
50
+
border-radius: 1em;
51
51
+
text-align: center;
52
52
+
font: 14px/1.4 monospace;
53
53
+
color: #eee;
54
54
+
background: rgba(0, 0, 0, 0.75);
55
55
+
pointer-events: none;
56
56
+
}
57
57
+
body.nav #compass {
58
58
+
display: block;
59
59
+
}
60
60
+
#arrow {
61
61
+
width: 10em;
62
62
+
height: 10em;
63
63
+
transition: transform 0.2s linear;
64
64
+
}
65
65
+
#dist {
66
66
+
font-size: 2em;
67
67
+
font-weight: bold;
68
68
+
}
69
69
+
#sheet {
70
70
+
display: none;
71
71
+
position: absolute;
72
72
+
z-index: 1001;
73
73
+
left: 0;
74
74
+
right: 0;
75
75
+
bottom: 0;
76
76
+
padding: 1em;
77
77
+
border-radius: 1em 1em 0 0;
78
78
+
font: 14px/1.4 monospace;
79
79
+
color: #eee;
80
80
+
background: #111;
81
81
+
}
82
82
+
body.sheet #sheet {
83
83
+
display: block;
84
84
+
}
85
85
+
#sheet-head {
86
86
+
display: flex;
87
87
+
justify-content: space-between;
88
88
+
align-items: center;
89
89
+
margin-bottom: 0.75em;
90
90
+
}
91
91
+
#sheet-close {
92
92
+
border: 0;
93
93
+
font: inherit;
94
94
+
font-size: 1.4em;
95
95
+
color: #eee;
96
96
+
background: none;
97
97
+
cursor: pointer;
98
98
+
}
99
99
+
#sheet-share {
100
100
+
margin-top: 0.5em;
101
101
+
}
33
102
</style>
34
103
</head>
35
104
<body>
36
105
<div id="map"></div>
106
106
+
<div id="compass">
107
107
+
<svg id="arrow" viewBox="-50 -50 100 100">
108
108
+
<circle r="46" fill="none" stroke="#666" stroke-width="2" />
109
109
+
<path d="M0,-42 L18,20 L0,10 L-18,20 Z" fill="#e33" />
110
110
+
</svg>
111
111
+
<div id="dist">…</div>
112
112
+
<div id="dir"></div>
113
113
+
</div>
37
114
<div id="info">loading…</div>
115
115
+
<div id="sheet">
116
116
+
<div id="sheet-head">
117
117
+
<span>open lassulus' location in…</span>
118
118
+
<button id="sheet-close" aria-label="close">✕</button>
119
119
+
</div>
120
120
+
<div id="sheet-open" class="open"></div>
121
121
+
<div id="sheet-share" class="open">
122
122
+
<button id="share-link">share link…</button>
123
123
+
<button id="copy-link">copy link</button>
124
124
+
</div>
125
125
+
</div>
38
126
<script src="/leaflet/leaflet.js"></script>
127
127
+
<script src="/maps.js"></script>
39
128
<script>
40
129
(function () {
41
130
var map = L.map("map", { zoomControl: true });
···
46
135
}).addTo(map);
47
136
48
137
var layer = L.layerGroup().addTo(map);
138
138
+
var meLayer = L.layerGroup().addTo(map);
49
139
var info = document.getElementById("info");
140
140
+
var arrow = document.getElementById("arrow");
141
141
+
var distEl = document.getElementById("dist");
142
142
+
var dirEl = document.getElementById("dir");
143
143
+
144
144
+
var target = null; // L.LatLng of lassulus' last point
145
145
+
var targetTime = null; // ISO timestamp of that point
146
146
+
var me = null; // L.LatLng of the viewer
147
147
+
var meError = null;
148
148
+
var heading = null; // device compass heading, degrees clockwise from north
149
149
+
var compassNote = "no compass"; // why heading is null
150
150
+
var navMode = false;
151
151
+
var trailBounds = null; // bounds of the first trail we loaded
152
152
+
var loaded = false; // first location.json fetched (even if empty)
153
153
+
var meFitted = false; // initial view already includes the viewer
154
154
+
155
155
+
// initial view: trail plus viewer, once each is first known
156
156
+
function initialView() {
157
157
+
if (!loaded) return;
158
158
+
var b = trailBounds ? L.latLngBounds([]).extend(trailBounds) : null;
159
159
+
if (me) {
160
160
+
b = b ? b.extend(me) : L.latLngBounds([me]);
161
161
+
meFitted = true;
162
162
+
}
163
163
+
if (b) map.fitBounds(b.pad(0.1), { maxZoom: 16 });
164
164
+
else map.setView([0, 0], 2);
165
165
+
}
50
166
51
167
function ago(iso) {
52
168
var s = Math.round((Date.now() - Date.parse(iso)) / 1000);
···
56
172
return (s / 86400).toFixed(1) + "d";
57
173
}
58
174
175
175
+
function fmtDist(m) {
176
176
+
if (m < 1000) return Math.round(m) + " m";
177
177
+
if (m < 10000) return (m / 1000).toFixed(1) + " km";
178
178
+
return Math.round(m / 1000) + " km";
179
179
+
}
180
180
+
181
181
+
// initial great-circle bearing from a to b, degrees clockwise from north
182
182
+
function bearing(a, b) {
183
183
+
var r = Math.PI / 180;
184
184
+
var p1 = a.lat * r,
185
185
+
p2 = b.lat * r,
186
186
+
dl = (b.lng - a.lng) * r;
187
187
+
var y = Math.sin(dl) * Math.cos(p2);
188
188
+
var x =
189
189
+
Math.cos(p1) * Math.sin(p2) -
190
190
+
Math.sin(p1) * Math.cos(p2) * Math.cos(dl);
191
191
+
return (Math.atan2(y, x) / r + 360) % 360;
192
192
+
}
193
193
+
194
194
+
var points = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"];
195
195
+
function cardinal(deg) {
196
196
+
return points[Math.round(deg / 45) % 8];
197
197
+
}
198
198
+
199
199
+
// rotate along the shortest path so the css transition never spins 350°
200
200
+
var shown = 0;
201
201
+
function rotateArrow(deg) {
202
202
+
var d = ((((deg - shown) % 360) + 540) % 360) - 180;
203
203
+
shown += d;
204
204
+
arrow.style.transform = "rotate(" + shown + "deg)";
205
205
+
}
206
206
+
207
207
+
// distance/direction between viewer and lassulus: info bar + compass
208
208
+
function updateRel() {
209
209
+
var rel = document.getElementById("rel");
210
210
+
if (!target) return;
211
211
+
if (!me) {
212
212
+
if (rel)
213
213
+
rel.textContent = meError ? " · your location: " + meError : "";
214
214
+
distEl.textContent = "…";
215
215
+
dirEl.textContent = meError || "waiting for your location";
216
216
+
return;
217
217
+
}
218
218
+
var d = me.distanceTo(target);
219
219
+
var b = bearing(me, target);
220
220
+
if (rel) {
221
221
+
rel.innerHTML =
222
222
+
" · " +
223
223
+
fmtDist(d) +
224
224
+
" " +
225
225
+
cardinal(b) +
226
226
+
' of you · <a href="https://www.openstreetmap.org/directions?route=' +
227
227
+
me.lat +
228
228
+
"%2C" +
229
229
+
me.lng +
230
230
+
"%3B" +
231
231
+
target.lat +
232
232
+
"%2C" +
233
233
+
target.lng +
234
234
+
'">route</a>';
235
235
+
}
236
236
+
distEl.textContent = fmtDist(d);
237
237
+
if (heading === null) {
238
238
+
// no compass: arrow is relative to north (map up)
239
239
+
rotateArrow(b);
240
240
+
dirEl.textContent =
241
241
+
Math.round(b) +
242
242
+
"° " +
243
243
+
cardinal(b) +
244
244
+
" (" +
245
245
+
compassNote +
246
246
+
", north up)";
247
247
+
} else {
248
248
+
rotateArrow(b - heading);
249
249
+
dirEl.textContent = Math.round(b) + "° " + cardinal(b);
250
250
+
}
251
251
+
}
252
252
+
59
253
// trail fades from transparent (24h ago) to solid (now); one segment per point pair
60
254
function render(data) {
61
255
layer.clearLayers();
62
256
var pts = data.points;
63
257
if (!pts.length) {
258
258
+
target = null;
64
259
info.textContent = "no location in the last 24h";
65
260
return;
66
261
}
···
83
278
).addTo(layer);
84
279
}
85
280
var last = pts[pts.length - 1];
281
281
+
target = L.latLng(last.lat, last.lon);
282
282
+
targetTime = last.t;
283
283
+
updateSheet();
86
284
if (last.acc) {
87
87
-
L.circle([last.lat, last.lon], {
285
285
+
L.circle(target, {
88
286
radius: last.acc,
89
287
color: "#39f",
90
288
weight: 1,
91
289
fillOpacity: 0.1,
92
290
}).addTo(layer);
93
291
}
94
94
-
L.marker([last.lat, last.lon])
292
292
+
L.marker(target)
95
293
.addTo(layer)
96
294
.bindPopup(
97
295
"last seen " +
···
116
314
last.lat +
117
315
"/" +
118
316
last.lon +
119
119
-
'">osm</a>';
317
317
+
'">osm</a><span id="rel"></span>';
318
318
+
updateRel();
120
319
return L.latLngBounds(
121
320
pts.map(function (p) {
122
321
return [p.lat, p.lon];
···
124
323
);
125
324
}
126
325
127
127
-
var first = true;
326
326
+
function fitBoth() {
327
327
+
if (!me || !target) return;
328
328
+
map.fitBounds(L.latLngBounds([me, target]).pad(0.2), {
329
329
+
maxZoom: 17,
330
330
+
});
331
331
+
}
332
332
+
333
333
+
// viewer position: blue dot + accuracy circle
334
334
+
var meDot = null,
335
335
+
meAcc = null;
336
336
+
function onPosition(pos) {
337
337
+
me = L.latLng(pos.coords.latitude, pos.coords.longitude);
338
338
+
meError = null;
339
339
+
if (!meDot) {
340
340
+
meAcc = L.circle(me, {
341
341
+
radius: pos.coords.accuracy,
342
342
+
color: "#3c3",
343
343
+
weight: 1,
344
344
+
fillOpacity: 0.1,
345
345
+
}).addTo(meLayer);
346
346
+
meDot = L.circleMarker(me, {
347
347
+
radius: 7,
348
348
+
color: "#fff",
349
349
+
weight: 2,
350
350
+
fillColor: "#3c3",
351
351
+
fillOpacity: 1,
352
352
+
})
353
353
+
.addTo(meLayer)
354
354
+
.bindPopup("you");
355
355
+
} else {
356
356
+
meDot.setLatLng(me);
357
357
+
meAcc.setLatLng(me).setRadius(pos.coords.accuracy);
358
358
+
}
359
359
+
if (!meFitted) initialView();
360
360
+
updateRel();
361
361
+
}
362
362
+
function onPositionError(e) {
363
363
+
meError = e.code === 1 ? "permission denied" : "unavailable";
364
364
+
updateRel();
365
365
+
}
366
366
+
var watchId = null;
367
367
+
function watchMe() {
368
368
+
if (!navigator.geolocation) {
369
369
+
meError = "not supported";
370
370
+
return;
371
371
+
}
372
372
+
if (watchId !== null) navigator.geolocation.clearWatch(watchId);
373
373
+
watchId = navigator.geolocation.watchPosition(
374
374
+
onPosition,
375
375
+
onPositionError,
376
376
+
{ enableHighAccuracy: true, maximumAge: 10000 },
377
377
+
);
378
378
+
}
379
379
+
380
380
+
// device compass. iOS exposes webkitCompassHeading; others give an
381
381
+
// absolute alpha (counter-clockwise from north) via deviceorientationabsolute.
382
382
+
function onOrientation(e) {
383
383
+
var h;
384
384
+
if (typeof e.webkitCompassHeading === "number") {
385
385
+
h = e.webkitCompassHeading;
386
386
+
} else if (e.absolute && e.alpha !== null) {
387
387
+
h = 360 - e.alpha;
388
388
+
} else {
389
389
+
return;
390
390
+
}
391
391
+
var screenAngle =
392
392
+
(screen.orientation && screen.orientation.angle) ||
393
393
+
window.orientation ||
394
394
+
0;
395
395
+
heading = (h + screenAngle + 360) % 360;
396
396
+
updateRel();
397
397
+
}
398
398
+
var orientationEvent =
399
399
+
"ondeviceorientationabsolute" in window
400
400
+
? "deviceorientationabsolute"
401
401
+
: "deviceorientation";
402
402
+
403
403
+
function setNav(on) {
404
404
+
navMode = on;
405
405
+
document.body.classList.toggle("nav", on);
406
406
+
navBtn.classList.toggle("on", on);
407
407
+
if (!on) {
408
408
+
window.removeEventListener(orientationEvent, onOrientation);
409
409
+
heading = null;
410
410
+
compassNote = "no compass";
411
411
+
return;
412
412
+
}
413
413
+
var listen = function () {
414
414
+
window.addEventListener(orientationEvent, onOrientation);
415
415
+
};
416
416
+
// iOS 13+ (and newer Chromium): permission must be requested from
417
417
+
// the click handler
418
418
+
if (
419
419
+
window.DeviceOrientationEvent &&
420
420
+
typeof DeviceOrientationEvent.requestPermission === "function"
421
421
+
) {
422
422
+
DeviceOrientationEvent.requestPermission()
423
423
+
.then(function (state) {
424
424
+
if (state === "granted") listen();
425
425
+
else compassNote = "compass " + state;
426
426
+
updateRel();
427
427
+
})
428
428
+
.catch(function () {
429
429
+
compassNote = "compass unavailable";
430
430
+
updateRel();
431
431
+
});
432
432
+
} else {
433
433
+
listen();
434
434
+
}
435
435
+
if (!me) watchMe();
436
436
+
fitBoth();
437
437
+
updateRel();
438
438
+
}
439
439
+
440
440
+
function button(container, text, title, onClick) {
441
441
+
var a = L.DomUtil.create("a", "", container);
442
442
+
a.href = "#";
443
443
+
a.setAttribute("role", "button");
444
444
+
a.title = title;
445
445
+
a.textContent = text;
446
446
+
L.DomEvent.on(a, "click", function (e) {
447
447
+
L.DomEvent.preventDefault(e);
448
448
+
onClick();
449
449
+
});
450
450
+
return a;
451
451
+
}
452
452
+
453
453
+
// snapshot link to the /at page, which offers the same "open in"
454
454
+
// choices to whoever receives it
455
455
+
function shareUrl() {
456
456
+
return (
457
457
+
location.origin +
458
458
+
"/at#" +
459
459
+
target.lat +
460
460
+
"," +
461
461
+
target.lng +
462
462
+
"@" +
463
463
+
Math.floor(Date.parse(targetTime) / 1000)
464
464
+
);
465
465
+
}
466
466
+
var sheetOpen = document.getElementById("sheet-open");
467
467
+
var shareLinkBtn = document.getElementById("share-link");
468
468
+
var copyLinkBtn = document.getElementById("copy-link");
469
469
+
// the share sheet only lists apps that accept shared text/urls, which
470
470
+
// is why the "open in" links above exist at all
471
471
+
if (!navigator.share) shareLinkBtn.remove();
472
472
+
function copyLink() {
473
473
+
var url = shareUrl();
474
474
+
var ask = function () {
475
475
+
window.prompt("copy this link", url);
476
476
+
};
477
477
+
if (!navigator.clipboard) return ask();
478
478
+
navigator.clipboard.writeText(url).then(function () {
479
479
+
copyLinkBtn.textContent = "copied ✓";
480
480
+
setTimeout(function () {
481
481
+
copyLinkBtn.textContent = "copy link";
482
482
+
}, 1500);
483
483
+
}, ask);
484
484
+
}
485
485
+
function shareLink() {
486
486
+
navigator
487
487
+
.share({
488
488
+
title: "where is lassulus?",
489
489
+
text:
490
490
+
"lassulus, last seen " + new Date(targetTime).toLocaleString(),
491
491
+
url: shareUrl(),
492
492
+
})
493
493
+
.catch(function (e) {
494
494
+
// AbortError: user closed the share sheet
495
495
+
if (e.name !== "AbortError") copyLink();
496
496
+
});
497
497
+
}
498
498
+
function updateSheet() {
499
499
+
if (target) renderMapLinks(sheetOpen, target.lat, target.lng);
500
500
+
}
501
501
+
function setSheet(on) {
502
502
+
if (on && !target) return;
503
503
+
document.body.classList.toggle("sheet", on);
504
504
+
shareBtn.classList.toggle("on", on);
505
505
+
if (on) updateSheet();
506
506
+
}
507
507
+
shareLinkBtn.addEventListener("click", shareLink);
508
508
+
copyLinkBtn.addEventListener("click", copyLink);
509
509
+
document
510
510
+
.getElementById("sheet-close")
511
511
+
.addEventListener("click", function () {
512
512
+
setSheet(false);
513
513
+
});
514
514
+
map.on("click", function () {
515
515
+
setSheet(false);
516
516
+
});
517
517
+
518
518
+
var navBtn, shareBtn;
519
519
+
var Controls = L.Control.extend({
520
520
+
options: { position: "topleft" },
521
521
+
onAdd: function () {
522
522
+
var div = L.DomUtil.create("div", "leaflet-bar ctl");
523
523
+
button(div, "◎", "show me and lassulus", function () {
524
524
+
if (!me) watchMe();
525
525
+
fitBoth();
526
526
+
});
527
527
+
navBtn = button(
528
528
+
div,
529
529
+
"➤",
530
530
+
"compass: point to lassulus",
531
531
+
function () {
532
532
+
setNav(!navMode);
533
533
+
},
534
534
+
);
535
535
+
shareBtn = button(
536
536
+
div,
537
537
+
"⤴",
538
538
+
"open in maps app / share lassulus' location",
539
539
+
function () {
540
540
+
setSheet(!document.body.classList.contains("sheet"));
541
541
+
},
542
542
+
);
543
543
+
L.DomEvent.disableClickPropagation(div);
544
544
+
return div;
545
545
+
},
546
546
+
});
547
547
+
new Controls().addTo(map);
548
548
+
128
549
function refresh() {
129
550
fetch("/location.json", { cache: "no-cache" })
130
551
.then(function (r) {
···
132
553
})
133
554
.then(function (data) {
134
555
var bounds = render(data);
135
135
-
if (first && bounds) {
136
136
-
map.fitBounds(bounds.pad(0.1), { maxZoom: 16 });
137
137
-
first = false;
556
556
+
if (!loaded) {
557
557
+
loaded = true;
558
558
+
trailBounds = bounds || null;
559
559
+
initialView();
138
560
}
139
561
})
140
562
.catch(function (e) {
···
143
565
}
144
566
refresh();
145
567
setInterval(refresh, 60 * 1000);
568
568
+
watchMe();
146
569
})();
147
570
</script>
148
571
</body>
+2
-1
2configs/websites/where.is.lassul.us/maps.js
View file
Reviewed
···
15
15
["Apple Maps", "https://maps.apple.com/?ll=" + ll + "&q=lassulus"],
16
16
];
17
17
// android: app chooser incl. Google Maps, OsmAnd, Organic Maps, ...
18
18
-
if (!ios) links.push(["choose app…", "geo:" + ll + "?q=" + ll + "(lassulus)"]);
18
18
+
if (!ios)
19
19
+
links.push(["choose app…", "geo:" + ll + "?q=" + ll + "(lassulus)"]);
19
20
links.push([
20
21
"OpenStreetMap",
21
22
"https://www.openstreetmap.org/?mlat=" +