Compare commits
14
Commits
21c1d22859
...
ed25907545
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed25907545 | ||
|
|
476c2c17ef | ||
|
|
24ffa42499 | ||
|
|
04bcab2d55 | ||
|
|
1458c24aa3 | ||
|
|
ede5ce1914 | ||
|
|
0b73cf5048 | ||
|
|
10354a0760 | ||
|
|
632a3028fa | ||
|
|
53d873b4c7 | ||
|
|
a640294e9b | ||
|
|
f8a6b9eb96 | ||
|
|
5923ba431a | ||
|
|
75aaa61640 |
@@ -7,3 +7,4 @@
|
||||
/pages/02.post/*ui-test*/
|
||||
/config/plugins/git-sync.yaml
|
||||
/config/security.yaml
|
||||
/themes/intotheeast/node_modules/
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -330,9 +330,98 @@
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse one or more GPX files and compute aggregate cycling statistics.
|
||||
* urls: array of GPX file URL strings
|
||||
* callback: called once with { distance, eleGain, eleLoss, highest, lowest, movingTime, avgSpeed }
|
||||
* or { error: 'no files' } if urls is empty.
|
||||
*
|
||||
* distance/eleGain/eleLoss in raw units (km / metres).
|
||||
* movingTime: "H:MM" string. avgSpeed: km/h number.
|
||||
*/
|
||||
function parseGpxFiles(urls, callback) {
|
||||
var pending = urls.length;
|
||||
var fileResults = new Array(urls.length);
|
||||
if (pending === 0) { callback({ error: 'no files' }); return; }
|
||||
|
||||
urls.forEach(function (url, idx) {
|
||||
fetch(url)
|
||||
.then(function (r) { return r.text(); })
|
||||
.then(function (text) {
|
||||
var xml = new DOMParser().parseFromString(text, 'text/xml');
|
||||
var pts = [];
|
||||
xml.querySelectorAll('trkpt').forEach(function (pt) {
|
||||
var eleEl = pt.querySelector('ele');
|
||||
var timeEl = pt.querySelector('time');
|
||||
pts.push({
|
||||
lat: parseFloat(pt.getAttribute('lat')),
|
||||
lon: parseFloat(pt.getAttribute('lon')),
|
||||
ele: eleEl ? parseFloat(eleEl.textContent) : NaN,
|
||||
time: timeEl ? timeEl.textContent : null
|
||||
});
|
||||
});
|
||||
fileResults[idx] = pts;
|
||||
if (--pending === 0) computeAndCallback();
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.warn('GPX load failed:', url, err);
|
||||
fileResults[idx] = [];
|
||||
if (--pending === 0) computeAndCallback();
|
||||
});
|
||||
});
|
||||
|
||||
function computeAndCallback() {
|
||||
var totalDistance = 0, totalEleGain = 0, totalEleLoss = 0;
|
||||
var globalHighest = NaN, globalLowest = NaN, totalMovingTime = 0;
|
||||
|
||||
fileResults.forEach(function (pts) {
|
||||
if (!pts || pts.length < 2) return;
|
||||
/* Include first point of each file in elevation range */
|
||||
if (!isNaN(pts[0].ele)) {
|
||||
if (isNaN(globalHighest) || pts[0].ele > globalHighest) globalHighest = pts[0].ele;
|
||||
if (isNaN(globalLowest) || pts[0].ele < globalLowest) globalLowest = pts[0].ele;
|
||||
}
|
||||
for (var i = 1; i < pts.length; i++) {
|
||||
var p0 = pts[i - 1], p1 = pts[i];
|
||||
totalDistance += haversineKm(p0.lat, p0.lon, p1.lat, p1.lon);
|
||||
if (!isNaN(p0.ele) && !isNaN(p1.ele)) {
|
||||
var dEle = p1.ele - p0.ele;
|
||||
if (dEle > 0) totalEleGain += dEle;
|
||||
if (dEle < 0) totalEleLoss += (-dEle);
|
||||
if (isNaN(globalHighest) || p1.ele > globalHighest) globalHighest = p1.ele;
|
||||
if (isNaN(globalLowest) || p1.ele < globalLowest) globalLowest = p1.ele;
|
||||
}
|
||||
if (p0.time && p1.time) {
|
||||
var dtHrs = (Date.parse(p1.time) - Date.parse(p0.time)) / 3600000;
|
||||
if (dtHrs > 0 && (haversineKm(p0.lat, p0.lon, p1.lat, p1.lon) / dtHrs) >= 1) {
|
||||
totalMovingTime += dtHrs;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var avgSpeed = totalMovingTime > 0 ? totalDistance / totalMovingTime : 0;
|
||||
var movHours = Math.floor(totalMovingTime);
|
||||
var movMins = Math.round((totalMovingTime - movHours) * 60);
|
||||
if (movMins === 60) { movHours++; movMins = 0; }
|
||||
|
||||
callback({
|
||||
distance: totalDistance,
|
||||
eleGain: totalEleGain,
|
||||
eleLoss: totalEleLoss,
|
||||
highest: globalHighest,
|
||||
lowest: globalLowest,
|
||||
movingTime: movHours + ':' + (movMins < 10 ? '0' : '') + movMins,
|
||||
avgSpeed: avgSpeed
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
global.MapUtils = {
|
||||
MAP_STYLE: MAP_STYLE,
|
||||
ACCENT: ACCENT,
|
||||
haversineKm: haversineKm,
|
||||
parseGpxFiles: parseGpxFiles,
|
||||
addJourneyLine: addJourneyLine,
|
||||
addJourneySegments: addJourneySegments,
|
||||
buildJourneySegments: buildJourneySegments,
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
/* ── Fonts ───────────────────────────────────────────────── */
|
||||
import '@fontsource-variable/dm-sans';
|
||||
import '@fontsource/dm-serif-display/400.css';
|
||||
import '@fontsource/dm-serif-display/400-italic.css';
|
||||
|
||||
/* ── PhotoSwipe ──────────────────────────────────────────── */
|
||||
import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
||||
import PhotoSwipe from 'photoswipe';
|
||||
import 'photoswipe/style.css';
|
||||
|
||||
/* ── Scrollama (used by story.html.twig inline script) ────── */
|
||||
import scrollama from 'scrollama';
|
||||
window.scrollama = scrollama;
|
||||
|
||||
/* ── Photo strip: prev/next buttons + scroll-based dot sync ─ */
|
||||
function initPhotoStrip() {
|
||||
document.querySelectorAll('.journal-photo-strip').forEach(function (strip) {
|
||||
strip.setAttribute('role', 'region');
|
||||
strip.setAttribute('aria-label', 'Photo strip');
|
||||
strip.setAttribute('tabindex', '0');
|
||||
|
||||
var slideCount = parseInt(strip.dataset.slides, 10) || 1;
|
||||
var dots = strip.nextElementSibling;
|
||||
if (!dots || !dots.classList.contains('journal-photo-dots')) return;
|
||||
var dotEls = Array.from(dots.querySelectorAll('.journal-photo-dot'));
|
||||
|
||||
strip.addEventListener('scroll', function () {
|
||||
var idx = Math.round(strip.scrollLeft / strip.offsetWidth);
|
||||
dotEls.forEach(function (d, i) { d.classList.toggle('is-active', i === idx); });
|
||||
}, { passive: true });
|
||||
|
||||
if (slideCount < 2) return;
|
||||
|
||||
var prev = document.createElement('button');
|
||||
prev.className = 'strip-prev';
|
||||
prev.setAttribute('aria-label', 'Previous photo');
|
||||
prev.textContent = '‹';
|
||||
prev.addEventListener('click', function () {
|
||||
strip.scrollBy({ left: -strip.offsetWidth, behavior: 'smooth' });
|
||||
});
|
||||
|
||||
var next = document.createElement('button');
|
||||
next.className = 'strip-next';
|
||||
next.setAttribute('aria-label', 'Next photo');
|
||||
next.textContent = '›';
|
||||
next.addEventListener('click', function () {
|
||||
strip.scrollBy({ left: strip.offsetWidth, behavior: 'smooth' });
|
||||
});
|
||||
|
||||
var controls = document.createElement('div');
|
||||
controls.className = 'strip-controls';
|
||||
controls.appendChild(prev);
|
||||
controls.appendChild(next);
|
||||
var wrap = strip.closest('.journal-photo-wrap');
|
||||
(wrap || dots).insertAdjacentElement('afterend', controls);
|
||||
});
|
||||
}
|
||||
|
||||
/* ── PhotoSwipe lightbox + IntersectionObserver dot sync ──── */
|
||||
function initPhotoSwipe() {
|
||||
if (!document.querySelector('.pswp-gallery')) return;
|
||||
|
||||
var lightbox = new PhotoSwipeLightbox({
|
||||
gallery: '.pswp-gallery',
|
||||
children: 'a.journal-photo-slide',
|
||||
pswpModule: PhotoSwipe
|
||||
});
|
||||
|
||||
lightbox.on('afterOpen', function () {
|
||||
var pswp = lightbox.pswp;
|
||||
var keyDir = 0;
|
||||
var clearTimer = null;
|
||||
|
||||
function onKey(e) {
|
||||
if (e.key === 'ArrowRight') keyDir = 1;
|
||||
else if (e.key === 'ArrowLeft') keyDir = -1;
|
||||
else keyDir = 0;
|
||||
}
|
||||
document.addEventListener('keydown', onKey, true);
|
||||
|
||||
pswp.on('change', function () {
|
||||
if (!keyDir) return;
|
||||
var dir = keyDir;
|
||||
keyDir = 0;
|
||||
var el = pswp.currSlide && pswp.currSlide.container;
|
||||
if (!el) return;
|
||||
el.classList.remove('pswp-key-from-left', 'pswp-key-from-right');
|
||||
el.offsetWidth; /* force reflow */
|
||||
el.classList.add(dir > 0 ? 'pswp-key-from-right' : 'pswp-key-from-left');
|
||||
clearTimeout(clearTimer);
|
||||
clearTimer = setTimeout(function () {
|
||||
el.classList.remove('pswp-key-from-left', 'pswp-key-from-right');
|
||||
}, 400);
|
||||
});
|
||||
|
||||
pswp.on('close', function () {
|
||||
document.removeEventListener('keydown', onKey, true);
|
||||
clearTimeout(clearTimer);
|
||||
});
|
||||
});
|
||||
|
||||
lightbox.init();
|
||||
|
||||
/* Per-strip: IntersectionObserver dot sync + expand button */
|
||||
document.querySelectorAll('.journal-photo-wrap').forEach(function (wrap) {
|
||||
var strip = wrap.querySelector('.journal-photo-strip');
|
||||
if (!strip) return;
|
||||
var slides = Array.from(strip.querySelectorAll('a.journal-photo-slide'));
|
||||
var expandBtn = wrap.querySelector('.journal-photo-expand');
|
||||
var article = wrap.closest('article');
|
||||
var dots = article ? Array.from(article.querySelectorAll('.journal-photo-dot')) : [];
|
||||
var visibleIdx = 0;
|
||||
|
||||
var io = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (e) {
|
||||
if (!e.isIntersecting) return;
|
||||
visibleIdx = slides.indexOf(e.target);
|
||||
dots.forEach(function (d) { d.classList.remove('is-active'); });
|
||||
if (dots[visibleIdx]) dots[visibleIdx].classList.add('is-active');
|
||||
});
|
||||
}, { root: strip, threshold: 0.5 });
|
||||
slides.forEach(function (s) { io.observe(s); });
|
||||
|
||||
if (expandBtn && slides.length) {
|
||||
expandBtn.addEventListener('click', function () {
|
||||
slides[visibleIdx].dispatchEvent(
|
||||
new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* ── Sort button ─────────────────────────────────────────────
|
||||
btnId: element id of the sort toggle button
|
||||
containerSel: CSS selector for the list container
|
||||
itemSel: CSS selector for sortable items within container
|
||||
withLabel: true = button shows "↑ Oldest first" / "↓ Newest first"
|
||||
false = button shows "↑" / "↓" only
|
||||
──────────────────────────────────────────────────────────── */
|
||||
function initSortButton(btnId, containerSel, itemSel, withLabel) {
|
||||
var btn = document.getElementById(btnId);
|
||||
if (!btn) return;
|
||||
var container = document.querySelector(containerSel);
|
||||
if (!container) return;
|
||||
var sentinel = container.querySelector('#feed-filter-empty');
|
||||
var ascending = true;
|
||||
|
||||
btn.addEventListener('click', function () {
|
||||
ascending = !ascending;
|
||||
var items = Array.from(container.querySelectorAll(itemSel));
|
||||
items.reverse().forEach(function (el) {
|
||||
if (sentinel) container.insertBefore(el, sentinel);
|
||||
else container.appendChild(el);
|
||||
});
|
||||
btn.textContent = withLabel
|
||||
? (ascending ? '↑ Oldest first' : '↓ Newest first')
|
||||
: (ascending ? '↑' : '↓');
|
||||
btn.setAttribute('aria-label', ascending ? 'Sort: oldest first' : 'Sort: newest first');
|
||||
btn.classList.toggle('is-active', !ascending);
|
||||
});
|
||||
}
|
||||
|
||||
/* ── Filter bar (trip page: All / Journal / Stories) ─────── */
|
||||
function initFilterBar() {
|
||||
var filterBtns = document.querySelectorAll('.trip-filter-btn');
|
||||
if (!filterBtns.length) return;
|
||||
var cards = document.querySelectorAll('[data-type]');
|
||||
var filterEmpty = document.getElementById('feed-filter-empty');
|
||||
|
||||
filterBtns.forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
filterBtns.forEach(function (b) {
|
||||
b.classList.remove('is-active');
|
||||
b.setAttribute('aria-pressed', 'false');
|
||||
});
|
||||
btn.classList.add('is-active');
|
||||
btn.setAttribute('aria-pressed', 'true');
|
||||
|
||||
var filter = btn.getAttribute('data-filter');
|
||||
var visible = 0;
|
||||
cards.forEach(function (card) {
|
||||
var show = filter === 'all' || card.getAttribute('data-type') === filter;
|
||||
card.style.display = show ? '' : 'none';
|
||||
if (show) visible++;
|
||||
});
|
||||
|
||||
if (filterEmpty) {
|
||||
if (visible === 0) {
|
||||
filterEmpty.textContent = filter === 'story'
|
||||
? 'No stories yet for this trip.'
|
||||
: 'No entries yet.';
|
||||
filterEmpty.style.display = '';
|
||||
} else {
|
||||
filterEmpty.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* ── Back to top ─────────────────────────────────────────── */
|
||||
function initBackToTop(btnId) {
|
||||
var btn = document.getElementById(btnId);
|
||||
if (!btn) return;
|
||||
var threshold = window.innerHeight * 0.8;
|
||||
var shown = false;
|
||||
btn.addEventListener('click', function () {
|
||||
history.pushState(null, '', window.location.pathname + window.location.search);
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
});
|
||||
window.addEventListener('scroll', function () {
|
||||
var shouldShow = window.scrollY > threshold;
|
||||
if (shouldShow !== shown) {
|
||||
shown = shouldShow;
|
||||
btn.classList.toggle('is-visible', shown);
|
||||
}
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
/* ── Panel toggles (trip stats / cycling panels) ─────────── */
|
||||
function initPanelToggles() {
|
||||
document.querySelectorAll('.trip-panel-toggle').forEach(function (toggle) {
|
||||
var blockId = toggle.getAttribute('aria-controls');
|
||||
var block = blockId ? document.getElementById(blockId) : null;
|
||||
if (!block) return;
|
||||
toggle.addEventListener('click', function () {
|
||||
var isOpen = block.classList.contains('is-open');
|
||||
block.classList.toggle('is-open', !isOpen);
|
||||
toggle.classList.toggle('is-active', !isOpen);
|
||||
toggle.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.trip-panel-close').forEach(function (btn) {
|
||||
var toggleBtn = document.getElementById(btn.getAttribute('data-toggle'));
|
||||
if (toggleBtn) btn.addEventListener('click', function () { toggleBtn.click(); });
|
||||
});
|
||||
}
|
||||
|
||||
/* ── Boot ────────────────────────────────────────────────── */
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
initPhotoStrip();
|
||||
initPhotoSwipe();
|
||||
initFilterBar();
|
||||
/* Sort buttons — each call is silent if its button/container isn't on this page */
|
||||
initSortButton('trip-sort-toggle', '.feed', '[data-type]', false);
|
||||
initSortButton('feed-sort-toggle', '.feed', '[data-type]', true);
|
||||
initSortButton('feed-sort-toggle', '.stories-grid', '.story-card', true);
|
||||
initBackToTop('story-totop');
|
||||
initBackToTop('trip-totop');
|
||||
initPanelToggles();
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import 'maplibre-gl/dist/maplibre-gl.css';
|
||||
import toGeoJSON from '@mapbox/togeojson';
|
||||
|
||||
/* maplibre-utils.js attaches MapUtils to window as a side effect */
|
||||
import '../maplibre-utils.js';
|
||||
|
||||
window.maplibregl = maplibregl;
|
||||
window.toGeoJSON = toGeoJSON;
|
||||
Generated
+964
@@ -0,0 +1,964 @@
|
||||
{
|
||||
"name": "app",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@fontsource-variable/dm-sans": "latest",
|
||||
"@fontsource/dm-serif-display": "latest",
|
||||
"@mapbox/togeojson": "^0.16.2",
|
||||
"maplibre-gl": "^4",
|
||||
"photoswipe": "^5",
|
||||
"scrollama": "^3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.21"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
|
||||
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
|
||||
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
|
||||
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
|
||||
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
|
||||
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
|
||||
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
|
||||
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
|
||||
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
|
||||
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
|
||||
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource-variable/dm-sans": {
|
||||
"version": "5.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource-variable/dm-sans/-/dm-sans-5.2.8.tgz",
|
||||
"integrity": "sha512-AxkvMTvNWgfrmlyjiV05vlHYJa+nRQCf1EfvIrQAPBpFJW0O9VTz7oAFr9S3lvbWdmnFoBk7yFqQL86u64nl2g==",
|
||||
"license": "OFL-1.1",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ayuhito"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource/dm-serif-display": {
|
||||
"version": "5.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource/dm-serif-display/-/dm-serif-display-5.2.8.tgz",
|
||||
"integrity": "sha512-GYSDSlGU6vyhv9a5MwaiVNf9HCuSVpK8hEFRyG4NNDHCDeHiX7YHDAcWsaoLKKcfXLgWG9YkBkk9T3SxM4rAjQ==",
|
||||
"license": "OFL-1.1",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ayuhito"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/geojson-rewind": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz",
|
||||
"integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"get-stream": "^6.0.1",
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
"bin": {
|
||||
"geojson-rewind": "geojson-rewind"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/jsonlint-lines-primitives": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
|
||||
"integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/point-geometry": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
|
||||
"integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@mapbox/tiny-sdf": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.2.0.tgz",
|
||||
"integrity": "sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/@mapbox/togeojson": {
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/togeojson/-/togeojson-0.16.2.tgz",
|
||||
"integrity": "sha512-DcApudmw4g/grOrpM5gYPZfts6Kr8litBESN6n/27sDsjR2f+iJhx4BA0J2B+XrLlnHyJkKztYApe6oCUZpzFA==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@xmldom/xmldom": "^0.8.10",
|
||||
"concat-stream": "~2.0.0",
|
||||
"minimist": "1.2.8"
|
||||
},
|
||||
"bin": {
|
||||
"togeojson": "togeojson"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/unitbezier": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz",
|
||||
"integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/@mapbox/vector-tile": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz",
|
||||
"integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@mapbox/point-geometry": "~0.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/whoots-js": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
|
||||
"integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@maplibre/maplibre-gl-style-spec": {
|
||||
"version": "20.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.4.0.tgz",
|
||||
"integrity": "sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
|
||||
"@mapbox/unitbezier": "^0.0.1",
|
||||
"json-stringify-pretty-compact": "^4.0.0",
|
||||
"minimist": "^1.2.8",
|
||||
"quickselect": "^2.0.0",
|
||||
"rw": "^1.3.3",
|
||||
"tinyqueue": "^3.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"gl-style-format": "dist/gl-style-format.mjs",
|
||||
"gl-style-migrate": "dist/gl-style-migrate.mjs",
|
||||
"gl-style-validate": "dist/gl-style-validate.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@maplibre/maplibre-gl-style-spec/node_modules/quickselect": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
|
||||
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@types/geojson": {
|
||||
"version": "7946.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
|
||||
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/geojson-vt": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz",
|
||||
"integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mapbox__point-geometry": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz",
|
||||
"integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/mapbox__vector-tile": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz",
|
||||
"integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/geojson": "*",
|
||||
"@types/mapbox__point-geometry": "*",
|
||||
"@types/pbf": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pbf": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz",
|
||||
"integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/supercluster": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz",
|
||||
"integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@xmldom/xmldom": {
|
||||
"version": "0.8.13",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz",
|
||||
"integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/concat-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||
"engines": [
|
||||
"node >= 6.0"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.0.2",
|
||||
"typedarray": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/earcut": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz",
|
||||
"integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
|
||||
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.21.5",
|
||||
"@esbuild/android-arm": "0.21.5",
|
||||
"@esbuild/android-arm64": "0.21.5",
|
||||
"@esbuild/android-x64": "0.21.5",
|
||||
"@esbuild/darwin-arm64": "0.21.5",
|
||||
"@esbuild/darwin-x64": "0.21.5",
|
||||
"@esbuild/freebsd-arm64": "0.21.5",
|
||||
"@esbuild/freebsd-x64": "0.21.5",
|
||||
"@esbuild/linux-arm": "0.21.5",
|
||||
"@esbuild/linux-arm64": "0.21.5",
|
||||
"@esbuild/linux-ia32": "0.21.5",
|
||||
"@esbuild/linux-loong64": "0.21.5",
|
||||
"@esbuild/linux-mips64el": "0.21.5",
|
||||
"@esbuild/linux-ppc64": "0.21.5",
|
||||
"@esbuild/linux-riscv64": "0.21.5",
|
||||
"@esbuild/linux-s390x": "0.21.5",
|
||||
"@esbuild/linux-x64": "0.21.5",
|
||||
"@esbuild/netbsd-x64": "0.21.5",
|
||||
"@esbuild/openbsd-x64": "0.21.5",
|
||||
"@esbuild/sunos-x64": "0.21.5",
|
||||
"@esbuild/win32-arm64": "0.21.5",
|
||||
"@esbuild/win32-ia32": "0.21.5",
|
||||
"@esbuild/win32-x64": "0.21.5"
|
||||
}
|
||||
},
|
||||
"node_modules/geojson-vt": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.3.tgz",
|
||||
"integrity": "sha512-jR1MwkLaZGa8Zftct9ZFruyWFrdl9ZyD2OliXNy9Qq5bBPeg5wHVpBQF9p5GjnicSDQqvBVpysxTPKmWdsfWMA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/get-stream": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/gl-matrix": {
|
||||
"version": "3.4.4",
|
||||
"resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz",
|
||||
"integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/global-prefix": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz",
|
||||
"integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ini": "^4.1.3",
|
||||
"kind-of": "^6.0.3",
|
||||
"which": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ini": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz",
|
||||
"integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz",
|
||||
"integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/json-stringify-pretty-compact": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz",
|
||||
"integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/kdbush": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.1.0.tgz",
|
||||
"integrity": "sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/kind-of": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
||||
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/maplibre-gl": {
|
||||
"version": "4.7.1",
|
||||
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.7.1.tgz",
|
||||
"integrity": "sha512-lgL7XpIwsgICiL82ITplfS7IGwrB1OJIw/pCvprDp2dhmSSEBgmPzYRvwYYYvJGJD7fxUv1Tvpih4nZ6VrLuaA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@mapbox/geojson-rewind": "^0.5.2",
|
||||
"@mapbox/jsonlint-lines-primitives": "^2.0.2",
|
||||
"@mapbox/point-geometry": "^0.1.0",
|
||||
"@mapbox/tiny-sdf": "^2.0.6",
|
||||
"@mapbox/unitbezier": "^0.0.1",
|
||||
"@mapbox/vector-tile": "^1.3.1",
|
||||
"@mapbox/whoots-js": "^3.1.0",
|
||||
"@maplibre/maplibre-gl-style-spec": "^20.3.1",
|
||||
"@types/geojson": "^7946.0.14",
|
||||
"@types/geojson-vt": "3.2.5",
|
||||
"@types/mapbox__point-geometry": "^0.1.4",
|
||||
"@types/mapbox__vector-tile": "^1.3.4",
|
||||
"@types/pbf": "^3.0.5",
|
||||
"@types/supercluster": "^7.1.3",
|
||||
"earcut": "^3.0.0",
|
||||
"geojson-vt": "^4.0.2",
|
||||
"gl-matrix": "^3.4.3",
|
||||
"global-prefix": "^4.0.0",
|
||||
"kdbush": "^4.0.2",
|
||||
"murmurhash-js": "^1.0.0",
|
||||
"pbf": "^3.3.0",
|
||||
"potpack": "^2.0.0",
|
||||
"quickselect": "^3.0.0",
|
||||
"supercluster": "^8.0.1",
|
||||
"tinyqueue": "^3.0.0",
|
||||
"vt-pbf": "^3.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.14.0",
|
||||
"npm": ">=8.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/murmurhash-js": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
|
||||
"integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pbf": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz",
|
||||
"integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"ieee754": "^1.1.12",
|
||||
"resolve-protobuf-schema": "^2.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"pbf": "bin/pbf"
|
||||
}
|
||||
},
|
||||
"node_modules/photoswipe": {
|
||||
"version": "5.4.4",
|
||||
"resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.4.tgz",
|
||||
"integrity": "sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/potpack": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz",
|
||||
"integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/protocol-buffers-schema": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz",
|
||||
"integrity": "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/quickselect": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz",
|
||||
"integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-protobuf-schema": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
|
||||
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"protocol-buffers-schema": "^3.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/rw": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
|
||||
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/scrollama": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/scrollama/-/scrollama-3.2.0.tgz",
|
||||
"integrity": "sha512-PIPwB1kYBnbw/ezvPBJa5dCN5qEwokfpAkI3BmpZWAwcVID4nDf1qH6WV16A2fQaJmsKx0un5S/zhxN+PQeKDQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supercluster": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz",
|
||||
"integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"kdbush": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyqueue": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz",
|
||||
"integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vt-pbf": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz",
|
||||
"integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mapbox/point-geometry": "0.1.0",
|
||||
"@mapbox/vector-tile": "^1.3.1",
|
||||
"pbf": "^3.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
|
||||
"integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/which.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.13.0 || >=18.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "esbuild js/src/main.js --bundle --minify --format=iife --outfile=js/main.js --loader:.woff2=file --loader:.woff=file --asset-names=../fonts/[name] && esbuild js/src/map.js --bundle --minify --format=iife --outfile=js/map.js && mkdir -p css-compiled fonts && { mv js/main.css css-compiled/main.css 2>/dev/null || true; } && { mv js/map.css css-compiled/map.css 2>/dev/null || true; }"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource-variable/dm-sans": "latest",
|
||||
"@fontsource/dm-serif-display": "latest",
|
||||
"@mapbox/togeojson": "^0.16.2",
|
||||
"maplibre-gl": "^4",
|
||||
"photoswipe": "^5",
|
||||
"scrollama": "^3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.21"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
{% extends 'default.html.twig' %}
|
||||
|
||||
{% block map_assets %}
|
||||
{% do assets.addCss('theme://css-compiled/map.css') %}
|
||||
{% do assets.addJs('theme://js/map.js', {group: 'bottom'}) %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe.css">
|
||||
{% set journal_entries = page.collection() %}
|
||||
@@ -126,21 +131,4 @@ document.querySelectorAll('.journal-photo-wrap').forEach(function (wrap) {
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
(function() {
|
||||
var sortBtn = document.getElementById('feed-sort-toggle');
|
||||
if (!sortBtn) return;
|
||||
var feed = document.querySelector('.feed');
|
||||
var ascending = true;
|
||||
|
||||
sortBtn.addEventListener('click', function() {
|
||||
ascending = !ascending;
|
||||
var entries = Array.from(feed.querySelectorAll('[data-type]'));
|
||||
entries.reverse().forEach(function(el) { feed.appendChild(el); });
|
||||
sortBtn.textContent = ascending ? '↑ Oldest first' : '↓ Newest first';
|
||||
sortBtn.setAttribute('aria-label', ascending ? 'Sort: oldest first' : 'Sort: newest first');
|
||||
sortBtn.classList.toggle('is-active', !ascending);
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{% macro cycling_panel() %}
|
||||
<div id="trip-cycling-block" class="trip-cycling-block">
|
||||
<div class="trip-panel-inner">
|
||||
<div class="trip-cycling-header">
|
||||
<span class="trip-cycling-icon">🚴</span>
|
||||
<span class="trip-cycling-title">Cycling Stats</span>
|
||||
</div>
|
||||
<div class="trip-cycling-grid">
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-distance">—</span>
|
||||
<span class="stat-label">km distance</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-ele-gain">—</span>
|
||||
<span class="stat-label">m ↑ gain</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-ele-loss">—</span>
|
||||
<span class="stat-label">m ↓ loss</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-highest">—</span>
|
||||
<span class="stat-label">m highest</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-lowest">—</span>
|
||||
<span class="stat-label">m lowest</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-moving-time">—</span>
|
||||
<span class="stat-label">moving time</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-avg-speed">—</span>
|
||||
<span class="stat-label">km/h avg speed</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="trip-panel-close" data-toggle="trip-cycling-toggle">↑ Close cycling</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
@@ -0,0 +1,19 @@
|
||||
{%- macro format_date_range(start_date, end_date) -%}
|
||||
{%- if end_date is not empty and end_date|date('Y-m-d') != start_date|date('Y-m-d') -%}
|
||||
{%- set sd = start_date|date('d') -%}
|
||||
{%- set sm = start_date|date('M') -%}
|
||||
{%- set sy = start_date|date('Y') -%}
|
||||
{%- set ed = end_date|date('d') -%}
|
||||
{%- set em = end_date|date('M') -%}
|
||||
{%- set ey = end_date|date('Y') -%}
|
||||
{%- if sy == ey and sm == em -%}
|
||||
{{- sd ~ ' – ' ~ ed ~ ' ' ~ em ~ ' ' ~ ey -}}
|
||||
{%- elseif sy == ey -%}
|
||||
{{- sd ~ ' ' ~ sm ~ ' – ' ~ ed ~ ' ' ~ em ~ ' ' ~ ey -}}
|
||||
{%- else -%}
|
||||
{{- sd ~ ' ' ~ sm ~ ' ' ~ sy ~ ' – ' ~ ed ~ ' ' ~ em ~ ' ' ~ ey -}}
|
||||
{%- endif -%}
|
||||
{%- else -%}
|
||||
{{- start_date|date('d M Y') -}}
|
||||
{%- endif %}
|
||||
{%- endmacro -%}
|
||||
@@ -0,0 +1,93 @@
|
||||
{% macro stats_panel(journal_entries, page, journal_count, has_gpx) %}
|
||||
{% set days_on_road = 0 %}
|
||||
{% if page.header.date_end is not empty %}
|
||||
{% set start_ts = page.header.date_start|date('U') %}
|
||||
{% set end_ts = page.header.date_end|date('U') %}
|
||||
{% set days_on_road = ((end_ts - start_ts) / 86400)|round(0, 'ceil') %}
|
||||
{% else %}
|
||||
{% set first_ts = null %}
|
||||
{% for entry in journal_entries %}
|
||||
{% set ts = entry.date|date('U') %}
|
||||
{% if first_ts is null or ts < first_ts %}{% set first_ts = ts %}{% endif %}
|
||||
{% endfor %}
|
||||
{% if first_ts is not null %}
|
||||
{% set diff_seconds = "now"|date('U') - first_ts %}
|
||||
{% set days_raw = (diff_seconds / 86400)|round(0, 'floor') %}
|
||||
{% set days_on_road = days_raw < 1 ? 1 : days_raw %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% set seen_lower = [] %}
|
||||
{% set country_display = [] %}
|
||||
{% for entry in journal_entries %}
|
||||
{% if entry.header.location_country is not empty %}
|
||||
{% set lower = entry.header.location_country|trim|lower %}
|
||||
{% if lower not in seen_lower %}
|
||||
{% set seen_lower = seen_lower|merge([lower]) %}
|
||||
{% set country_display = country_display|merge([entry.header.location_country|trim]) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% set seen_city_lower = [] %}
|
||||
{% set city_display = [] %}
|
||||
{% for entry in journal_entries %}
|
||||
{% if entry.header.location_city is not empty %}
|
||||
{% set lower = entry.header.location_city|trim|lower %}
|
||||
{% if lower not in seen_city_lower %}
|
||||
{% set seen_city_lower = seen_city_lower|merge([lower]) %}
|
||||
{% set city_display = city_display|merge([entry.header.location_city|trim]) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% set temp_min = null %}
|
||||
{% set temp_max = null %}
|
||||
{% for entry in journal_entries %}
|
||||
{% if entry.header.weather_temp_c is defined and entry.header.weather_temp_c is not empty %}
|
||||
{% set t = entry.header.weather_temp_c %}
|
||||
{% if temp_min is null or t < temp_min %}{% set temp_min = t %}{% endif %}
|
||||
{% if temp_max is null or t > temp_max %}{% set temp_max = t %}{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<div id="trip-stats-block" class="trip-stats-block">
|
||||
<div class="trip-panel-inner">
|
||||
<div class="trip-stats-grid">
|
||||
<div class="stat-block">
|
||||
<span class="stat-value">{{ days_on_road }}</span>
|
||||
<span class="stat-label">{{ days_on_road == 1 ? 'day' : 'days' }} on the road</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value">{{ journal_count }}</span>
|
||||
<span class="stat-label">{{ journal_count == 1 ? 'entry' : 'entries' }} posted</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value">{{ country_display|length }}</span>
|
||||
<span class="stat-label">{{ country_display|length == 1 ? 'country' : 'countries' }} visited</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value">{{ city_display|length }}</span>
|
||||
<span class="stat-label">{{ city_display|length == 1 ? 'city' : 'cities' }} visited</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="stat-distance">—</span>
|
||||
<span class="stat-label">{{ has_gpx ? '🚴 km cycled' : '🧭 km roamed' }}</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
{% if temp_min is not null %}
|
||||
<span class="stat-value">{{ temp_min == temp_max ? temp_min : temp_min ~ ' → ' ~ temp_max }}</span>
|
||||
{% else %}
|
||||
<span class="stat-value">—</span>
|
||||
{% endif %}
|
||||
<span class="stat-label">°C range</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if country_display|length > 0 %}
|
||||
<p class="trip-stats-countries">{{ country_display|join(' · ') }}</p>
|
||||
{% endif %}
|
||||
<p class="trip-stats-note">{{ has_gpx ? 'Distance based on GPS track data.' : 'Distance is approximate — straight lines between entry locations.' }}</p>
|
||||
<button class="trip-panel-close" data-toggle="trip-stats-toggle">↑ Close stats</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
@@ -1,5 +1,10 @@
|
||||
{% extends 'partials/base.html.twig' %}
|
||||
|
||||
{% block map_assets %}
|
||||
{% do assets.addCss('theme://css-compiled/map.css') %}
|
||||
{% do assets.addJs('theme://js/map.js', {group: 'bottom'}) %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% set trip_page = page.parent() %}
|
||||
{% set tracker_page = grav.pages.find(page.parent().route ~ '/dailies') %}
|
||||
@@ -36,17 +41,13 @@
|
||||
|
||||
<div class="map-container" id="trip-map"></div>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@4/dist/maplibre-gl.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/maplibre-gl@4/dist/maplibre-gl.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@mapbox/togeojson@0.16.2/togeojson.min.js"></script>
|
||||
<script src="{{ url('theme://js/maplibre-utils.js') }}"></script>
|
||||
|
||||
<script>
|
||||
var ENTRIES = {{ map_entries|json_encode|raw }};
|
||||
var GPX_URLS = {{ gpx_urls|json_encode|raw }};
|
||||
var USE_GPX = {{ trip_page.header.use_gpx ?? true ? 'true' : 'false' }};
|
||||
var AUTOCONNECT = "{{ trip_page.header.autoconnect ?? 'on' }}";
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var map = new maplibregl.Map({
|
||||
container: 'trip-map',
|
||||
style: MapUtils.MAP_STYLE,
|
||||
@@ -96,5 +97,6 @@ map.on('load', function () {
|
||||
/* ── GPX tracks + journey segments ─────────────────────────── */
|
||||
MapUtils.renderGpxJourney(map, USE_GPX ? GPX_URLS : [], ENTRIES, 'gpx', 'journey', { connectMode: AUTOCONNECT });
|
||||
});
|
||||
}); // DOMContentLoaded
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% if page.title %}{{ page.title }} | {% endif %}{{ site.title }}</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;1,9..40,400&family=DM+Serif+Display:ital@0;1&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url('theme://css/tokens.css') }}">
|
||||
<link rel="stylesheet" href="{{ url('theme://css/style.css') }}">
|
||||
{% do assets.addCss('theme://css/tokens.css') %}
|
||||
{% do assets.addCss('theme://css/style.css') %}
|
||||
{% do assets.addCss('theme://css-compiled/main.css') %}
|
||||
{% do assets.addJs('theme://js/main.js', {group: 'bottom'}) %}
|
||||
{% block map_assets %}{% endblock %}
|
||||
{{ assets.css()|raw }}
|
||||
{{ assets.js()|raw }}
|
||||
</head>
|
||||
@@ -27,49 +27,5 @@
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
{{ assets.js('bottom')|raw }}
|
||||
<script>
|
||||
(function () {
|
||||
document.querySelectorAll('.journal-photo-strip').forEach(function (strip) {
|
||||
strip.setAttribute('role', 'region');
|
||||
strip.setAttribute('aria-label', 'Photo strip');
|
||||
strip.setAttribute('tabindex', '0');
|
||||
|
||||
var slideCount = parseInt(strip.dataset.slides, 10) || 1;
|
||||
var dots = strip.nextElementSibling;
|
||||
if (!dots || !dots.classList.contains('journal-photo-dots')) return;
|
||||
var dotEls = Array.from(dots.querySelectorAll('.journal-photo-dot'));
|
||||
|
||||
strip.addEventListener('scroll', function () {
|
||||
var idx = Math.round(strip.scrollLeft / strip.offsetWidth);
|
||||
dotEls.forEach(function (d, i) { d.classList.toggle('is-active', i === idx); });
|
||||
}, { passive: true });
|
||||
|
||||
if (slideCount < 2) return;
|
||||
|
||||
var prev = document.createElement('button');
|
||||
prev.className = 'strip-prev';
|
||||
prev.setAttribute('aria-label', 'Previous photo');
|
||||
prev.textContent = '‹';
|
||||
prev.addEventListener('click', function () {
|
||||
strip.scrollBy({ left: -strip.offsetWidth, behavior: 'smooth' });
|
||||
});
|
||||
|
||||
var next = document.createElement('button');
|
||||
next.className = 'strip-next';
|
||||
next.setAttribute('aria-label', 'Next photo');
|
||||
next.textContent = '›';
|
||||
next.addEventListener('click', function () {
|
||||
strip.scrollBy({ left: strip.offsetWidth, behavior: 'smooth' });
|
||||
});
|
||||
|
||||
var controls = document.createElement('div');
|
||||
controls.className = 'strip-controls';
|
||||
controls.appendChild(prev);
|
||||
controls.appendChild(next);
|
||||
var wrap = strip.closest('.journal-photo-wrap');
|
||||
(wrap || dots).insertAdjacentElement('afterend', controls);
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
card_prefix — string: prefix for scroll-to card IDs ('entry-' or 'story-')
|
||||
trip_page — Grav page: trip page for autoconnect setting (used when show_journey is true)
|
||||
show_journey — bool: whether to draw the route connector line between markers
|
||||
|
||||
Callers must register map assets via {% block map_assets %} in their own template.
|
||||
#}
|
||||
{% if map_entries|length > 0 %}
|
||||
<div class="feed-map-wrap">
|
||||
@@ -25,17 +27,17 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@4/dist/maplibre-gl.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/maplibre-gl@4/dist/maplibre-gl.js"></script>
|
||||
<script src="{{ url('theme://js/maplibre-utils.js') }}"></script>
|
||||
<script>
|
||||
{% set js_suffix = map_id|replace({'-': '_'})|upper %}
|
||||
var MAP_ENTRIES_{{ js_suffix }} = {{ map_entries|json_encode|raw }};
|
||||
{% if show_journey %}
|
||||
{% set _ac = trip_page ? (trip_page.header.autoconnect ?? 'on') : 'on' %}
|
||||
{% endif %}
|
||||
var MAP_ENTRIES_{{ js_suffix }} = {{ map_entries|json_encode|raw }};
|
||||
{% if show_journey %}
|
||||
var AUTOCONNECT_{{ js_suffix }} = "{{ _ac == 'intelligent_gpx' ? 'on' : _ac }}";
|
||||
{% endif %}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var {{ map_var }} = new maplibregl.Map({
|
||||
container: '{{ map_id }}',
|
||||
style: MapUtils.MAP_STYLE,
|
||||
@@ -100,8 +102,7 @@ var {{ map_var }} = new maplibregl.Map({
|
||||
MapUtils.addJourneySegments({{ map_var }}, segments, '{{ map_id }}-journey');
|
||||
{% endif %}
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
|
||||
(function() {
|
||||
var fsBtn = document.getElementById('{{ map_id }}-fullscreen');
|
||||
var mapWrap = document.querySelector('.feed-map-wrap');
|
||||
@@ -113,5 +114,6 @@ var {{ map_var }} = new maplibregl.Map({
|
||||
setTimeout(function() { typeof {{ map_var }} !== 'undefined' && {{ map_var }}.resize(); }, 50);
|
||||
});
|
||||
})();
|
||||
}); // DOMContentLoaded
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
{% extends 'partials/base.html.twig' %}
|
||||
|
||||
{% block map_assets %}
|
||||
{% do assets.addCss('theme://css-compiled/map.css') %}
|
||||
{% do assets.addJs('theme://js/map.js', {group: 'bottom'}) %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import 'macros/date-range.html.twig' as dr_m %}
|
||||
{% set stories = page.children.published().order('date', 'asc') %}
|
||||
|
||||
{# Collect stories that have coordinates for the mini-map #}
|
||||
@@ -46,10 +52,7 @@
|
||||
{% set hero = story.media[story.header.hero_image] %}
|
||||
{% endif %}
|
||||
|
||||
{% set date_str = story.date|date('d M Y') %}
|
||||
{% if story.header.end_date %}
|
||||
{% set date_str = story.date|date('d M') ~ '–' ~ story.header.end_date|date('d M Y') %}
|
||||
{% endif %}
|
||||
{% set date_str = dr_m.format_date_range(story.date, story.header.end_date ?? null) %}
|
||||
|
||||
<a class="story-card" id="story-{{ story.slug }}" href="{{ story.url }}">
|
||||
{% if hero %}
|
||||
@@ -74,22 +77,4 @@
|
||||
<p class="stories-empty">No stories yet — check back soon.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
var sortBtn = document.getElementById('feed-sort-toggle');
|
||||
if (!sortBtn) return;
|
||||
var grid = document.querySelector('.stories-grid');
|
||||
if (!grid) return;
|
||||
var ascending = true;
|
||||
|
||||
sortBtn.addEventListener('click', function() {
|
||||
ascending = !ascending;
|
||||
var cards = Array.from(grid.querySelectorAll('.story-card'));
|
||||
cards.reverse().forEach(function(el) { grid.appendChild(el); });
|
||||
sortBtn.textContent = ascending ? '↑ Oldest first' : '↓ Newest first';
|
||||
sortBtn.setAttribute('aria-label', ascending ? 'Sort: oldest first' : 'Sort: newest first');
|
||||
sortBtn.classList.toggle('is-active', !ascending);
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import 'macros/date-range.html.twig' as dr_m %}
|
||||
{% set hero_url = null %}
|
||||
{% if page.header.hero_image %}
|
||||
{% if page.media[page.header.hero_image] is defined %}
|
||||
@@ -16,22 +17,7 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% set date_str = page.date|date('d M Y') %}
|
||||
{% if page.header.end_date and page.header.end_date|date('Y-m-d') != page.date|date('Y-m-d') %}
|
||||
{% set sd = page.date|date('d') %}
|
||||
{% set sm = page.date|date('M') %}
|
||||
{% set sy = page.date|date('Y') %}
|
||||
{% set ed = page.header.end_date|date('d') %}
|
||||
{% set em = page.header.end_date|date('M') %}
|
||||
{% set ey = page.header.end_date|date('Y') %}
|
||||
{% if sy == ey and sm == em %}
|
||||
{% set date_str = sd ~ ' – ' ~ ed ~ ' ' ~ em ~ ' ' ~ ey %}
|
||||
{% elseif sy == ey %}
|
||||
{% set date_str = sd ~ ' ' ~ sm ~ ' – ' ~ ed ~ ' ' ~ em ~ ' ' ~ ey %}
|
||||
{% else %}
|
||||
{% set date_str = sd ~ ' ' ~ sm ~ ' ' ~ sy ~ ' – ' ~ ed ~ ' ' ~ em ~ ' ' ~ ey %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% set date_str = dr_m.format_date_range(page.date, page.header.end_date ?? null) %}
|
||||
|
||||
{% set location = page.header.location_name ?? '' %}
|
||||
|
||||
@@ -69,7 +55,6 @@
|
||||
|
||||
<button class="story-totop" id="story-totop" aria-label="Back to top">↑ Top</button>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/scrollama@3/build/scrollama.min.js"></script>
|
||||
<script>
|
||||
/* ── Hero scroll effect ──────────────────────────────────── */
|
||||
(function () {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
{% extends 'partials/base.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe.css">
|
||||
{% import 'macros/stats.html.twig' as stats_m %}
|
||||
{% import 'macros/cycling.html.twig' as cycling_m %}
|
||||
{% block map_assets %}
|
||||
{% do assets.addCss('theme://css-compiled/map.css') %}
|
||||
{% do assets.addJs('theme://js/map.js', {group: 'bottom'}) %}
|
||||
{% endblock %}
|
||||
{% set dailies_page = grav.pages.find(page.route ~ '/dailies') %}
|
||||
{% set stories_page = grav.pages.find(page.route ~ '/stories') %}
|
||||
{% set journal_entries = dailies_page ? dailies_page.children.published() : [] %}
|
||||
@@ -19,59 +24,6 @@
|
||||
{% set journal_count = journal_entries|length %}
|
||||
{% set story_count = story_entries|length %}
|
||||
|
||||
{# Stats computation #}
|
||||
{% set days_on_road = 0 %}
|
||||
{% if page.header.date_end is not empty %}
|
||||
{% set start_ts = page.header.date_start|date('U') %}
|
||||
{% set end_ts = page.header.date_end|date('U') %}
|
||||
{% set days_on_road = ((end_ts - start_ts) / 86400)|round(0, 'ceil') %}
|
||||
{% else %}
|
||||
{% set first_ts = null %}
|
||||
{% for entry in journal_entries %}
|
||||
{% set ts = entry.date|date('U') %}
|
||||
{% if first_ts is null or ts < first_ts %}{% set first_ts = ts %}{% endif %}
|
||||
{% endfor %}
|
||||
{% if first_ts is not null %}
|
||||
{% set diff_seconds = "now"|date('U') - first_ts %}
|
||||
{% set days_raw = (diff_seconds / 86400)|round(0, 'floor') %}
|
||||
{% set days_on_road = days_raw < 1 ? 1 : days_raw %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% set seen_lower = [] %}
|
||||
{% set country_display = [] %}
|
||||
{% for entry in journal_entries %}
|
||||
{% if entry.header.location_country is not empty %}
|
||||
{% set lower = entry.header.location_country|trim|lower %}
|
||||
{% if lower not in seen_lower %}
|
||||
{% set seen_lower = seen_lower|merge([lower]) %}
|
||||
{% set country_display = country_display|merge([entry.header.location_country|trim]) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% set seen_city_lower = [] %}
|
||||
{% set city_display = [] %}
|
||||
{% for entry in journal_entries %}
|
||||
{% if entry.header.location_city is not empty %}
|
||||
{% set lower = entry.header.location_city|trim|lower %}
|
||||
{% if lower not in seen_city_lower %}
|
||||
{% set seen_city_lower = seen_city_lower|merge([lower]) %}
|
||||
{% set city_display = city_display|merge([entry.header.location_city|trim]) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% set temp_min = null %}
|
||||
{% set temp_max = null %}
|
||||
{% for entry in journal_entries %}
|
||||
{% if entry.header.weather_temp_c is defined and entry.header.weather_temp_c is not empty %}
|
||||
{% set t = entry.header.weather_temp_c %}
|
||||
{% if temp_min is null or t < temp_min %}{% set temp_min = t %}{% endif %}
|
||||
{% if temp_max is null or t > temp_max %}{% set temp_max = t %}{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% set gps_points = [] %}
|
||||
{% for entry in journal_entries %}
|
||||
{% if entry.header.lat is not empty and entry.header.lng is not empty %}
|
||||
@@ -144,86 +96,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="trip-stats-block" class="trip-stats-block">
|
||||
<div class="trip-panel-inner">
|
||||
<div class="trip-stats-grid">
|
||||
<div class="stat-block">
|
||||
<span class="stat-value">{{ days_on_road }}</span>
|
||||
<span class="stat-label">{{ days_on_road == 1 ? 'day' : 'days' }} on the road</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value">{{ journal_count }}</span>
|
||||
<span class="stat-label">{{ journal_count == 1 ? 'entry' : 'entries' }} posted</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value">{{ country_display|length }}</span>
|
||||
<span class="stat-label">{{ country_display|length == 1 ? 'country' : 'countries' }} visited</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value">{{ city_display|length }}</span>
|
||||
<span class="stat-label">{{ city_display|length == 1 ? 'city' : 'cities' }} visited</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="stat-distance">—</span>
|
||||
<span class="stat-label">{{ has_gpx ? '🚴 km cycled' : '🧭 km roamed' }}</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
{% if temp_min is not null %}
|
||||
<span class="stat-value">{{ temp_min == temp_max ? temp_min : temp_min ~ ' → ' ~ temp_max }}</span>
|
||||
{% else %}
|
||||
<span class="stat-value">—</span>
|
||||
{% endif %}
|
||||
<span class="stat-label">°C range</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if country_display|length > 0 %}
|
||||
<p class="trip-stats-countries">{{ country_display|join(' · ') }}</p>
|
||||
{% endif %}
|
||||
<p class="trip-stats-note">{{ has_gpx ? 'Distance based on GPS track data.' : 'Distance is approximate — straight lines between entry locations.' }}</p>
|
||||
<button class="trip-panel-close" data-toggle="trip-stats-toggle">↑ Close stats</button>
|
||||
</div>
|
||||
</div>
|
||||
{{ stats_m.stats_panel(journal_entries, page, journal_count, has_gpx) }}
|
||||
|
||||
{% if has_gpx %}
|
||||
<div id="trip-cycling-block" class="trip-cycling-block">
|
||||
<div class="trip-panel-inner">
|
||||
<div class="trip-cycling-header">
|
||||
<span class="trip-cycling-icon">🚴</span>
|
||||
<span class="trip-cycling-title">Cycling Stats</span>
|
||||
</div>
|
||||
<div class="trip-cycling-grid">
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-distance">—</span>
|
||||
<span class="stat-label">km distance</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-ele-gain">—</span>
|
||||
<span class="stat-label">m ↑ gain</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-ele-loss">—</span>
|
||||
<span class="stat-label">m ↓ loss</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-highest">—</span>
|
||||
<span class="stat-label">m highest</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-lowest">—</span>
|
||||
<span class="stat-label">m lowest</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-moving-time">—</span>
|
||||
<span class="stat-label">moving time</span>
|
||||
</div>
|
||||
<div class="stat-block">
|
||||
<span class="stat-value" id="cyc-avg-speed">—</span>
|
||||
<span class="stat-label">km/h avg speed</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="trip-panel-close" data-toggle="trip-cycling-toggle">↑ Close cycling</button>
|
||||
</div>
|
||||
</div>
|
||||
{{ cycling_m.cycling_panel() }}
|
||||
{% endif %}
|
||||
|
||||
<div class="feed">
|
||||
@@ -244,16 +120,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@4/dist/maplibre-gl.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/maplibre-gl@4/dist/maplibre-gl.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@mapbox/togeojson@0.16.2/togeojson.min.js"></script>
|
||||
<script src="{{ url('theme://js/maplibre-utils.js') }}"></script>
|
||||
<script>
|
||||
var TRIP_ENTRIES = {{ map_entries|json_encode|raw }};
|
||||
var GPX_URLS = {{ gpx_urls|json_encode|raw }};
|
||||
var USE_GPX = {{ page.header.use_gpx ?? true ? 'true' : 'false' }};
|
||||
var AUTOCONNECT = "{{ page.header.autoconnect ?? 'on' }}";
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
var tripMap = new maplibregl.Map({
|
||||
container: 'trip-map',
|
||||
style: MapUtils.MAP_STYLE,
|
||||
@@ -336,164 +210,17 @@ setTimeout(function () { tripMap.resize(); }, 100);
|
||||
});
|
||||
})();
|
||||
|
||||
(function() {
|
||||
var filterBtns = document.querySelectorAll('.trip-filter-btn');
|
||||
var cards = document.querySelectorAll('[data-type]');
|
||||
var filterEmpty = document.getElementById('feed-filter-empty');
|
||||
|
||||
filterBtns.forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
filterBtns.forEach(function(b) { b.classList.remove('is-active'); b.setAttribute('aria-pressed', 'false'); });
|
||||
btn.classList.add('is-active');
|
||||
btn.setAttribute('aria-pressed', 'true');
|
||||
|
||||
var filter = btn.getAttribute('data-filter');
|
||||
var visible = 0;
|
||||
|
||||
cards.forEach(function(card) {
|
||||
var show = filter === 'all' || card.getAttribute('data-type') === filter;
|
||||
card.style.display = show ? '' : 'none';
|
||||
if (show) visible++;
|
||||
});
|
||||
|
||||
if (filterEmpty) {
|
||||
if (visible === 0) {
|
||||
filterEmpty.textContent = filter === 'story'
|
||||
? 'No stories yet for this trip.'
|
||||
: 'No entries yet.';
|
||||
filterEmpty.style.display = '';
|
||||
} else {
|
||||
filterEmpty.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
(function() {
|
||||
var sortBtn = document.getElementById('trip-sort-toggle');
|
||||
if (!sortBtn) return;
|
||||
var feed = document.querySelector('.feed');
|
||||
var emptyMsg = document.getElementById('feed-filter-empty');
|
||||
var ascending = true;
|
||||
|
||||
sortBtn.addEventListener('click', function() {
|
||||
ascending = !ascending;
|
||||
var entries = Array.from(feed.querySelectorAll('[data-type]'));
|
||||
entries.reverse().forEach(function(el) { feed.insertBefore(el, emptyMsg); });
|
||||
sortBtn.textContent = ascending ? '↑' : '↓';
|
||||
sortBtn.setAttribute('aria-label', ascending ? 'Sort: oldest first' : 'Sort: newest first');
|
||||
sortBtn.classList.toggle('is-active', !ascending);
|
||||
});
|
||||
})();
|
||||
|
||||
var STATS_GPS = {{ gps_points|json_encode|raw }};
|
||||
var HAS_GPX = {{ has_gpx ? 'true' : 'false' }};
|
||||
|
||||
function haversineKm(lat1, lng1, lat2, lng2) {
|
||||
var R = 6371;
|
||||
var dLat = (lat2 - lat1) * Math.PI / 180;
|
||||
var dLng = (lng2 - lng1) * Math.PI / 180;
|
||||
var a = Math.sin(dLat/2)*Math.sin(dLat/2) +
|
||||
Math.cos(lat1*Math.PI/180)*Math.cos(lat2*Math.PI/180)*
|
||||
Math.sin(dLng/2)*Math.sin(dLng/2);
|
||||
return R * 2 * Math.asin(Math.sqrt(a));
|
||||
}
|
||||
|
||||
function parseGpxFiles(urls, callback) {
|
||||
var pending = urls.length;
|
||||
var fileResults = new Array(urls.length);
|
||||
if (pending === 0) { callback({ error: 'no files' }); return; }
|
||||
urls.forEach(function(url, idx) {
|
||||
fetch(url)
|
||||
.then(function(r) { return r.text(); })
|
||||
.then(function(text) {
|
||||
var xml = new DOMParser().parseFromString(text, 'text/xml');
|
||||
var trkpts = xml.querySelectorAll('trkpt');
|
||||
var pts = [];
|
||||
trkpts.forEach(function(pt) {
|
||||
var eleEl = pt.querySelector('ele');
|
||||
var timeEl = pt.querySelector('time');
|
||||
pts.push({
|
||||
lat: parseFloat(pt.getAttribute('lat')),
|
||||
lon: parseFloat(pt.getAttribute('lon')),
|
||||
ele: eleEl ? parseFloat(eleEl.textContent) : NaN,
|
||||
time: timeEl ? timeEl.textContent : null
|
||||
});
|
||||
});
|
||||
fileResults[idx] = pts;
|
||||
pending--;
|
||||
if (pending === 0) { computeAndCallback(); }
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.warn('GPX load failed:', url, err);
|
||||
fileResults[idx] = [];
|
||||
pending--;
|
||||
if (pending === 0) { computeAndCallback(); }
|
||||
});
|
||||
});
|
||||
|
||||
function computeAndCallback() {
|
||||
var totalDistance = 0, totalEleGain = 0, totalEleLoss = 0;
|
||||
var globalHighest = NaN, globalLowest = NaN, totalMovingTime = 0;
|
||||
|
||||
fileResults.forEach(function(pts) {
|
||||
if (!pts || pts.length < 2) return;
|
||||
for (var i = 1; i < pts.length; i++) {
|
||||
var p0 = pts[i-1], p1 = pts[i];
|
||||
var d = haversineKm(p0.lat, p0.lon, p1.lat, p1.lon);
|
||||
totalDistance += d;
|
||||
|
||||
if (!isNaN(p0.ele) && !isNaN(p1.ele)) {
|
||||
var dEle = p1.ele - p0.ele;
|
||||
if (dEle > 0) totalEleGain += dEle;
|
||||
if (dEle < 0) totalEleLoss += (-dEle);
|
||||
if (isNaN(globalHighest) || p1.ele > globalHighest) globalHighest = p1.ele;
|
||||
if (isNaN(globalLowest) || p1.ele < globalLowest) globalLowest = p1.ele;
|
||||
}
|
||||
|
||||
if (p0.time && p1.time) {
|
||||
var dtHrs = (Date.parse(p1.time) - Date.parse(p0.time)) / 3600000;
|
||||
if (dtHrs > 0) {
|
||||
var speed = d / dtHrs;
|
||||
if (speed >= 1) totalMovingTime += dtHrs;
|
||||
}
|
||||
}
|
||||
}
|
||||
// include first point of each file in elevation range
|
||||
if (pts.length > 0 && !isNaN(pts[0].ele)) {
|
||||
if (isNaN(globalHighest) || pts[0].ele > globalHighest) globalHighest = pts[0].ele;
|
||||
if (isNaN(globalLowest) || pts[0].ele < globalLowest) globalLowest = pts[0].ele;
|
||||
}
|
||||
});
|
||||
|
||||
var avgSpeed = totalMovingTime > 0 ? totalDistance / totalMovingTime : 0;
|
||||
var movHours = Math.floor(totalMovingTime);
|
||||
var movMins = Math.round((totalMovingTime - movHours) * 60);
|
||||
if (movMins === 60) { movHours++; movMins = 0; }
|
||||
|
||||
callback({
|
||||
distance: totalDistance,
|
||||
eleGain: totalEleGain,
|
||||
eleLoss: totalEleLoss,
|
||||
highest: globalHighest,
|
||||
lowest: globalLowest,
|
||||
movingTime: movHours + ':' + (movMins < 10 ? '0' : '') + movMins,
|
||||
avgSpeed: avgSpeed
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
(function() {
|
||||
var distEl = document.getElementById('stat-distance');
|
||||
|
||||
if (HAS_GPX) {
|
||||
parseGpxFiles(GPX_URLS, function(result) {
|
||||
// Mode A: update distance stat
|
||||
MapUtils.parseGpxFiles(GPX_URLS, function(result) {
|
||||
if (distEl) {
|
||||
distEl.textContent = result.distance > 0 ? Math.round(result.distance).toLocaleString() : '—';
|
||||
}
|
||||
// Populate cycling panel
|
||||
function setText(id, val) {
|
||||
var el = document.getElementById(id);
|
||||
if (el) el.textContent = val;
|
||||
@@ -507,10 +234,9 @@ function parseGpxFiles(urls, callback) {
|
||||
setText('cyc-avg-speed', result.avgSpeed > 0 ? result.avgSpeed.toFixed(1) : '—');
|
||||
});
|
||||
} else {
|
||||
// Mode B: haversine between entry points
|
||||
var total = 0;
|
||||
for (var i = 1; i < STATS_GPS.length; i++) {
|
||||
total += haversineKm(
|
||||
total += MapUtils.haversineKm(
|
||||
parseFloat(STATS_GPS[i-1][0]), parseFloat(STATS_GPS[i-1][1]),
|
||||
parseFloat(STATS_GPS[i][0]), parseFloat(STATS_GPS[i][1])
|
||||
);
|
||||
@@ -520,108 +246,11 @@ function parseGpxFiles(urls, callback) {
|
||||
}
|
||||
}
|
||||
|
||||
function makePanelToggle(toggleId, blockId) {
|
||||
var toggle = document.getElementById(toggleId);
|
||||
var block = document.getElementById(blockId);
|
||||
if (!toggle || !block) return;
|
||||
toggle.addEventListener('click', function() {
|
||||
var isOpen = block.classList.contains('is-open');
|
||||
block.classList.toggle('is-open', !isOpen);
|
||||
toggle.classList.toggle('is-active', !isOpen);
|
||||
toggle.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
|
||||
});
|
||||
}
|
||||
makePanelToggle('trip-stats-toggle', 'trip-stats-block');
|
||||
makePanelToggle('trip-cycling-toggle', 'trip-cycling-block');
|
||||
|
||||
// Close buttons inside panels (mobile only via CSS)
|
||||
document.querySelectorAll('.trip-panel-close').forEach(function(btn) {
|
||||
var toggleBtn = document.getElementById(btn.getAttribute('data-toggle'));
|
||||
if (toggleBtn) btn.addEventListener('click', function() { toggleBtn.click(); });
|
||||
});
|
||||
})();
|
||||
|
||||
/* ── Back to top ─────────────────────────────────────────── */
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var btn = document.getElementById('trip-totop');
|
||||
if (!btn) return;
|
||||
var threshold = window.innerHeight * 0.8;
|
||||
var shown = false;
|
||||
btn.addEventListener('click', function () {
|
||||
history.pushState(null, '', window.location.pathname + window.location.search);
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
});
|
||||
window.addEventListener('scroll', function () {
|
||||
var shouldShow = window.scrollY > threshold;
|
||||
if (shouldShow !== shown) {
|
||||
shown = shouldShow;
|
||||
btn.classList.toggle('is-visible', shown);
|
||||
}
|
||||
}, { passive: true });
|
||||
});
|
||||
}); // DOMContentLoaded
|
||||
</script>
|
||||
|
||||
<button class="story-totop" id="trip-totop" aria-label="Back to top">↑ Top</button>
|
||||
|
||||
<script type="module">
|
||||
import PhotoSwipeLightbox from 'https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe-lightbox.esm.min.js';
|
||||
const lightbox = new PhotoSwipeLightbox({
|
||||
gallery: '.pswp-gallery',
|
||||
children: 'a.journal-photo-slide',
|
||||
pswpModule: () => import('https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe.esm.min.js')
|
||||
});
|
||||
lightbox.on('afterOpen', function () {
|
||||
var pswp = lightbox.pswp;
|
||||
var keyDir = 0;
|
||||
var clearTimer = null;
|
||||
function onKey(e) {
|
||||
if (e.key === 'ArrowRight') keyDir = 1;
|
||||
else if (e.key === 'ArrowLeft') keyDir = -1;
|
||||
else keyDir = 0;
|
||||
}
|
||||
document.addEventListener('keydown', onKey, true);
|
||||
pswp.on('change', function () {
|
||||
if (!keyDir) return;
|
||||
var dir = keyDir;
|
||||
keyDir = 0;
|
||||
var el = pswp.currSlide && pswp.currSlide.container;
|
||||
if (!el) return;
|
||||
el.classList.remove('pswp-key-from-left', 'pswp-key-from-right');
|
||||
el.offsetWidth;
|
||||
el.classList.add(dir > 0 ? 'pswp-key-from-right' : 'pswp-key-from-left');
|
||||
clearTimeout(clearTimer);
|
||||
clearTimer = setTimeout(function () { el.classList.remove('pswp-key-from-left', 'pswp-key-from-right'); }, 400);
|
||||
});
|
||||
pswp.on('close', function () {
|
||||
document.removeEventListener('keydown', onKey, true);
|
||||
clearTimeout(clearTimer);
|
||||
});
|
||||
});
|
||||
lightbox.init();
|
||||
|
||||
document.querySelectorAll('.journal-photo-wrap').forEach(function (wrap) {
|
||||
var strip = wrap.querySelector('.journal-photo-strip');
|
||||
var slides = Array.from(strip.querySelectorAll('a.journal-photo-slide'));
|
||||
var expandBtn = wrap.querySelector('.journal-photo-expand');
|
||||
var article = wrap.closest('article');
|
||||
var dots = article ? Array.from(article.querySelectorAll('.journal-photo-dot')) : [];
|
||||
var visibleIdx = 0;
|
||||
|
||||
var io = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (e) {
|
||||
if (!e.isIntersecting) return;
|
||||
visibleIdx = slides.indexOf(e.target);
|
||||
dots.forEach(function (d) { d.classList.remove('is-active'); });
|
||||
if (dots[visibleIdx]) dots[visibleIdx].classList.add('is-active');
|
||||
});
|
||||
}, { root: strip, threshold: 0.5 });
|
||||
slides.forEach(function (s) { io.observe(s); });
|
||||
|
||||
if (expandBtn && slides.length) {
|
||||
expandBtn.addEventListener('click', function () {
|
||||
slides[visibleIdx].dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user