diff --git a/README.md b/README.md
index 6528771..a00b7d7 100644
--- a/README.md
+++ b/README.md
@@ -57,7 +57,7 @@
| π Timeline | Chronological photo browser |
| πΌ Lightbox | Full-size viewer with navigation and camera info |
| π Notes | Add notes to any pin |
-| π° Map styles | Light, Bright, Dark, Terrain, 3D Terrain, Satellite, Globe |
+| π° Map styles | Light, Bright, Dark, Terrain, 3D Terrain, Satellite, 3D Satellite, Globe |
| π Clustering | Pins cluster by zoom, expand on click |
| πΎ Auto-save | Background backup to disk via `serve.py` |
| π¦ Export / Import | Full dataset as compressed `.json.gz` |
diff --git a/css/styles.css b/css/styles.css
index 5195b6d..95c7c36 100644
--- a/css/styles.css
+++ b/css/styles.css
@@ -225,6 +225,15 @@ input,textarea,select{font-family:var(--font)}
.dest-item-name{font-weight:500;color:var(--text)}
.dest-item-detail{font-size:.66rem;color:var(--muted);margin-top:1px}
#dest-loading{padding:10px 12px;font-size:.73rem;color:var(--muted);display:none}
+#search-cat-wrap{position:relative}
+#search-cat-btn{background:var(--surface2);border:1px solid var(--border);color:var(--muted);font-size:.73rem;font-weight:500;padding:5px 10px;border-radius:20px;cursor:pointer;white-space:nowrap;transition:all .15s;line-height:1}
+#search-cat-btn:hover{background:var(--surface3);color:var(--text)}
+#search-cat-btn.active{border-color:var(--accent);color:var(--accent)}
+#search-cat-menu{position:absolute;top:calc(100% + 6px);right:0;background:var(--surface);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);display:none;z-index:100;min-width:130px;overflow:hidden}
+#search-cat-menu.open{display:block}
+.search-cat-item{padding:8px 14px;font-size:.73rem;cursor:pointer;transition:background .12s;color:var(--text)}
+.search-cat-item:hover{background:var(--surface2)}
+.search-cat-item.active{color:var(--accent);font-weight:500}
/* PIN MARKERS (canvas-rendered via symbol layer β no DOM positioning) */
/* Cluster markers still use DOM for click expand */
diff --git a/index.html b/index.html
index 81d99c1..a336c84 100644
--- a/index.html
+++ b/index.html
@@ -120,6 +120,15 @@
+
+
+
+
@@ -131,6 +140,7 @@
+
diff --git a/js/data.js b/js/data.js
index 6ccb80c..d96f35c 100644
--- a/js/data.js
+++ b/js/data.js
@@ -405,7 +405,7 @@ async function autoSave() {
if (!_autoSaveAvailable) return;
try {
// Upload new photo files that haven't been saved yet
- const uploads = photos.filter(p => !_savedPhotoDisk.has(p.id) && p.dataUrl && p.dataUrl.startsWith('data:'));
+ const uploads = photos.filter(p => !_savedPhotoDisk.has(p.id) && !p.isEmptyPin);
await Promise.all(uploads.map(p => uploadPhotoFile(p)));
// Build metadata-only payload with file paths instead of base64
@@ -704,9 +704,6 @@ window.addEventListener('offline', () => updateOfflineState(true));
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
if ('serviceWorker' in navigator && !isSafari) {
navigator.serviceWorker.register('/sw.js', { updateViaCache: 'none' }).catch(err => console.warn('SW registration failed:', err));
- navigator.serviceWorker.ready.then(reg => {
- if (reg.active) reg.active.postMessage({ type: 'set-port', port: location.port || '8765' });
- });
} else if (isSafari && navigator.serviceWorker) {
// Unregister any existing SW in Safari to clean up stale state
navigator.serviceWorker.getRegistrations().then(regs => regs.forEach(r => r.unregister()));
diff --git a/js/map.js b/js/map.js
index 39ef068..eaf88b3 100644
--- a/js/map.js
+++ b/js/map.js
@@ -2,7 +2,7 @@
// MAP
// βββββββββββββββββββββββββββββββββββββββ
function _styleUrl() {
- if (_mapStyle === 'satellite') return STYLE_SAT;
+ if (_mapStyle === 'satellite' || _mapStyle === 'satellite3d') return STYLE_SAT;
if (_mapStyle === 'dark') return STYLE_DARK;
if (_mapStyle === 'bright') return STYLE_BRIGHT;
return STYLE_STREET; // light, enriched, terrain3d, globe all use Liberty as base
@@ -236,7 +236,7 @@ function _scaledTextSize(orig) {
}
function applyLabelScale() {
- if (_mapStyle === 'satellite') return;
+ if (_mapStyle === 'satellite' || _mapStyle === 'satellite3d') return;
const style = map.getStyle();
if (!style || !style.layers) return;
for (const layer of style.layers) {
@@ -251,7 +251,7 @@ function applyLabelScale() {
}
function applyLabelVisibility() {
- if (_mapStyle === 'satellite') return;
+ if (_mapStyle === 'satellite' || _mapStyle === 'satellite3d') return;
const vis = labelsVisible ? 'visible' : 'none';
const style = map.getStyle();
if (!style || !style.layers) return;
@@ -276,7 +276,7 @@ function toggleLabels() {
// Apply vivid parks, airport runways, mountain peaks, and optional 3D buildings
function applyExtraLayers() {
const hasOmt = map.getSource && map.getSource('openmaptiles');
- if (!hasOmt || ['satellite', 'globe'].includes(_mapStyle)) return;
+ if (!hasOmt || ['satellite', 'satellite3d', 'globe'].includes(_mapStyle)) return;
// Vivid park fill β more saturated green over the base park layer
if (!map.getLayer('matrix-park-vivid')) {
@@ -357,7 +357,7 @@ function applyExtraLayers() {
function apply3DBuildings() {
if (!map.getSource || !map.getSource('openmaptiles')) return;
- const shouldShow = buildings3DVisible && !['satellite', 'terrain3d', 'globe'].includes(_mapStyle);
+ const shouldShow = buildings3DVisible && !['satellite', 'satellite3d', 'terrain3d', 'globe'].includes(_mapStyle);
if (shouldShow && !map.getLayer('matrix-buildings-3d')) {
// Insert below the first symbol layer so road/POI labels render on top of buildings
const firstSymbol = map.getStyle()?.layers?.find(l => l.type === 'symbol');
@@ -418,7 +418,7 @@ function addPinLayers() {
if (!alreadyThere) {
// In 3D Terrain (pitched view), offset the focal point downward in screen space
// so the pin appears in the visible ground area rather than drifting off-screen
- const pitchOffset = _mapStyle === 'terrain3d' ? [0, Math.round(map.transform?.height * 0.15 || 100)] : [0, 0];
+ const pitchOffset = (_mapStyle === 'terrain3d' || _mapStyle === 'satellite3d') ? [0, Math.round(map.transform?.height * 0.15 || 100)] : [0, 0];
map.flyTo({ center: [lng, lat], zoom: targetZoom, speed: 0.8, curve: 1.0, essential: true,
offset: pitchOffset,
easing: t => t < 0.5 ? 4*t*t*t : 1 - Math.pow(-2*t+2, 3)/2 });
@@ -446,7 +446,7 @@ function applyTheme() {
_tileTemplatesCache = null;
// Set initial button label
const btn = document.getElementById('tb-style-btn');
- const labels = { light: 'Light Map', bright: 'Bright Map', enriched: 'Terrain', dark: 'Dark Map', satellite: 'Satellite', terrain3d: '3D Terrain', globe: 'Globe' };
+ const labels = { light: 'Light Map', bright: 'Bright Map', enriched: 'Terrain', dark: 'Dark Map', satellite: 'Satellite', satellite3d: '3D Satellite', terrain3d: '3D Terrain', globe: 'Globe' };
if (btn) btn.textContent = (labels[_mapStyle] || _mapStyle) + ' βΎ';
// Set initial active state in menu
@@ -624,7 +624,7 @@ async function initMap() {
map.getCanvas().addEventListener('mouseout', () => { coordsEl.style.display = 'none'; });
const updateZoom = () => {
zoomEl.textContent = 'z' + map.getZoom().toFixed(2);
- const is3D = _mapStyle === 'terrain3d';
+ const is3D = _mapStyle === 'terrain3d' || _mapStyle === 'satellite3d';
if (is3D) {
pitchWrap.style.display = 'flex';
pitchEl.textContent = `p${map.getPitch().toFixed(0)}Β° b${map.getBearing().toFixed(0)}Β°`;
@@ -676,7 +676,7 @@ async function initMap() {
bldgBtn.innerHTML = '3D';
bldgBtn.addEventListener('click', toggle3DBuildings);
bldgWrap.appendChild(bldgBtn);
- if (['satellite', 'terrain3d', 'globe'].includes(_mapStyle)) bldgWrap.style.display = 'none';
+ if (['satellite', 'satellite3d', 'terrain3d', 'globe'].includes(_mapStyle)) bldgWrap.style.display = 'none';
navGroup.before(bldgWrap);
}
applyLabelScale();
@@ -701,7 +701,7 @@ async function initMap() {
// Water clicks allowed at any zoom; land clicks require zoom >= 5.
// Satellite/terrain3d/globe have no vector layers for water detection.
const allHits = map.queryRenderedFeatures(e.point);
- const noVectorLayers = ['satellite', 'terrain3d', 'globe'].includes(_mapStyle);
+ const noVectorLayers = ['satellite', 'satellite3d', 'terrain3d', 'globe'].includes(_mapStyle);
const isWater = !noVectorLayers && allHits.some(f => f.layer.type === 'fill' && /^(water|ocean)/.test(f.layer.id));
// If style is mid-transition (queryRenderedFeatures returns nothing), treat as land
if (!isWater && map.getZoom() < 5) return;
@@ -765,7 +765,7 @@ async function initMap() {
// Elevation β only in 3D Terrain mode
let elevationStr = '';
- if (_mapStyle === 'terrain3d') {
+ if (_mapStyle === 'terrain3d' || _mapStyle === 'satellite3d') {
try {
const elev = map.queryTerrainElevation([lng, lat]);
if (elev !== null && elev !== undefined) {
@@ -871,21 +871,21 @@ function toggleStyleMenu(e) {
function setMapStyle(mode) {
const wasDark = _mapStyle === 'dark';
_mapStyle = mode;
- // Persist style preference (satellite resets to previous on reload)
- if (mode !== 'satellite') localStorage.setItem('matrix-theme', mode);
+ // Persist style preference (satellite/satellite3d reset to previous on reload)
+ if (mode !== 'satellite' && mode !== 'satellite3d') localStorage.setItem('matrix-theme', mode);
// Defer dark-map CSS class removal until pin icons are re-added with correct
// compensation (otherwise pre-darkened images render without the CSS filter)
const mapEl = document.getElementById('map');
if (_mapStyle === 'dark') mapEl.classList.add('dark-map');
- mapEl.classList.toggle('sat-mode', ['satellite', 'terrain3d', 'globe'].includes(_mapStyle));
+ mapEl.classList.toggle('sat-mode', ['satellite', 'satellite3d', 'terrain3d', 'globe'].includes(_mapStyle));
// Labels toggle visibility
const labelsWrap = document.getElementById('labels-toggle-wrap');
- if (labelsWrap) labelsWrap.style.visibility = _mapStyle === 'satellite' ? 'hidden' : 'visible';
+ if (labelsWrap) labelsWrap.style.visibility = (_mapStyle === 'satellite' || _mapStyle === 'satellite3d') ? 'hidden' : 'visible';
// Hide 3D buildings toggle in modes where buildings don't make sense
const bldgWrap = document.getElementById('buildings-toggle-wrap');
- if (bldgWrap) bldgWrap.style.display = ['satellite', 'terrain3d', 'globe'].includes(_mapStyle) ? 'none' : '';
+ if (bldgWrap) bldgWrap.style.display = ['satellite', 'satellite3d', 'terrain3d', 'globe'].includes(_mapStyle) ? 'none' : '';
// Disable Export Video in Globe mode (flyTo animation doesn't translate to globe projection)
const exportBtn = document.getElementById('tb-export-video');
@@ -895,7 +895,7 @@ function setMapStyle(mode) {
}
// Update button label
- const labels = { light: 'Light Map', bright: 'Bright Map', enriched: 'Terrain', dark: 'Dark Map', satellite: 'Satellite', terrain3d: '3D Terrain', globe: 'Globe' };
+ const labels = { light: 'Light Map', bright: 'Bright Map', enriched: 'Terrain', dark: 'Dark Map', satellite: 'Satellite', satellite3d: '3D Satellite', terrain3d: '3D Terrain', globe: 'Globe' };
const btn = document.getElementById('tb-style-btn');
if (btn) btn.textContent = (labels[mode] || mode) + ' βΎ';
@@ -914,7 +914,7 @@ function setMapStyle(mode) {
}
function _applyTerrainAndProjection() {
- const is3D = _mapStyle === 'terrain3d';
+ const is3D = _mapStyle === 'terrain3d' || _mapStyle === 'satellite3d';
const isGlobe = _mapStyle === 'globe';
// Set projection FIRST β before any camera moves β so easeTo never runs under the wrong projection
@@ -956,7 +956,7 @@ function _applyTerrainAndProjection() {
if (map.getLayer('sky') === undefined) {
map.addLayer({ id: 'sky', type: 'sky', paint: { 'sky-type': 'gradient', 'sky-gradient': ['interpolate', ['linear'], ['sky-radial-progress'], 0, 'rgba(255,255,255,0)', 0.5, '#87cefa', 1, '#4682b4'] } });
}
- if (!map.getLayer('terrain-hillshade')) {
+ if (_mapStyle !== 'satellite3d' && !map.getLayer('terrain-hillshade')) {
// Insert below the first road/label layer so hillshading shows through
const firstSymbol = map.getStyle().layers.find(l => l.type === 'line' || l.type === 'symbol');
map.addLayer({
diff --git a/js/search.js b/js/search.js
index 2cbfd0f..8796602 100644
--- a/js/search.js
+++ b/js/search.js
@@ -9,12 +9,41 @@ const dClear = document.getElementById('dest-clear');
const _searchCache = {};
let _searchMoveTimer = null;
+let _searchCategory = 'all';
+
+const POI_CATEGORIES = {
+ hotel: { label: 'π¨ Hotel', tags: [['amenity','hotel'],['tourism','hotel']] },
+ restaurant: { label: 'π½ Restaurant', tags: [['amenity','restaurant'],['amenity','cafe'],['amenity','bar'],['amenity','pub'],['amenity','fast_food']] },
+ attraction: { label: 'π Attraction', tags: [['tourism','attraction'],['tourism','museum'],['tourism','viewpoint']] },
+};
+
+function toggleSearchCatMenu() {
+ document.getElementById('search-cat-menu').classList.toggle('open');
+}
+
+function setSearchCat(cat) {
+ _searchCategory = cat;
+ document.getElementById('search-cat-label').textContent = cat === 'all' ? 'All' : POI_CATEGORIES[cat].label;
+ document.getElementById('search-cat-btn').classList.toggle('active', cat !== 'all');
+ document.querySelectorAll('.search-cat-item').forEach(el => el.classList.toggle('active', el.dataset.cat === cat));
+ document.getElementById('search-cat-menu').classList.remove('open');
+ Object.keys(_searchCache).forEach(k => delete _searchCache[k]);
+ const q = dInput.value.trim();
+ if (q.length >= 2) {
+ dResults.style.display = 'block';
+ runDestSearch(q);
+ } else if (cat !== 'all') {
+ dResults.style.display = 'block';
+ dLoading.style.display = 'none';
+ dResults.innerHTML = 'Type to search nearby
';
+ }
+}
// Re-run visible search when the map viewport changes (pan/zoom)
function _onMapMoveForSearch() {
clearTimeout(_searchMoveTimer);
const q = dInput.value.trim();
- if (q.length < 2 || dResults.style.display === 'none' || destMarkerObj) return;
+ if ((q.length < 2 && _searchCategory === 'all') || dResults.style.display === 'none' || destMarkerObj) return;
_searchMoveTimer = setTimeout(() => runDestSearch(q), 600);
}
@@ -22,12 +51,13 @@ dInput.addEventListener('input', () => {
const q = dInput.value.trim();
dClear.style.display = q ? 'block' : 'none';
clearTimeout(searchTimer);
- if (q.length < 2) { dResults.style.display='none'; return; }
+ if (q.length < 2 && _searchCategory === 'all') { dResults.style.display='none'; return; }
searchTimer = setTimeout(() => runDestSearch(q), 380);
});
dInput.addEventListener('keydown', e => { if(e.key==='Escape'){clearDestSearch();dInput.blur();} });
document.addEventListener('click', e => {
if (!document.getElementById('dest-search-wrap').contains(e.target)) dResults.style.display='none';
+ if (!document.getElementById('search-cat-wrap').contains(e.target)) document.getElementById('search-cat-menu').classList.remove('open');
});
@@ -130,6 +160,69 @@ async function runReverseGeoSearch(lat, lng) {
}
}
+async function runCategorySearch(q, cat, cacheKey) {
+ if (!map || q.length < 2) {
+ dLoading.style.display = 'none';
+ dResults.innerHTML = 'Type to search nearby
';
+ return;
+ }
+ const b = map.getBounds();
+
+ // --- Attempt 1: Overpass via local proxy (precise tag-based POI search) ---
+ const bbox = `(${b.getSouth().toFixed(5)},${b.getWest().toFixed(5)},${b.getNorth().toFixed(5)},${b.getEast().toFixed(5)})`;
+ const safe = q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ const nameFilter = `["name"~"${safe}",i]`;
+ const stmts = POI_CATEGORIES[cat].tags.flatMap(([k, v]) => [
+ `node["${k}"="${v}"]${nameFilter}${bbox};`,
+ `way["${k}"="${v}"]${nameFilter}${bbox};`,
+ `relation["${k}"="${v}"]${nameFilter}${bbox};`,
+ ]).join('\n ');
+ const query = `[out:json][timeout:10];\n(\n ${stmts}\n);\nout center 20;`;
+ try {
+ const r = await fetch('/api/overpass', { method: 'POST', body: new URLSearchParams({ data: query }) });
+ if (r.ok) {
+ const data = await r.json();
+ const results = (data.elements || [])
+ .filter(el => el.tags?.name)
+ .map(el => {
+ const t = el.tags, lat = el.lat ?? el.center?.lat, lon = el.lon ?? el.center?.lon;
+ if (lat == null || lon == null) return null;
+ const city = t['addr:city'] || t['addr:town'] || t['addr:village'] || '';
+ const country = t['addr:country'] || '';
+ return {
+ display_name: [t.name, city, country].filter(Boolean).join(', '),
+ lat: String(lat), lon: String(lon),
+ address: { city, country }
+ };
+ })
+ .filter(Boolean);
+ _searchCache[cacheKey] = results;
+ renderSearchResults(results);
+ return;
+ }
+ } catch(_) { /* proxy unreachable β fall through to Nominatim */ }
+
+ // --- Fallback: Nominatim bounded=1 + layer=poi (works everywhere, less precise) ---
+ const viewbox = `&viewbox=${b.getWest()},${b.getNorth()},${b.getEast()},${b.getSouth()}&bounded=1&layer=poi`;
+ const searchWait = Math.max(0, 1100 - (Date.now() - _lastNominatimCall));
+ if (searchWait > 0) await new Promise(r => setTimeout(r, searchWait));
+ _lastNominatimCall = Date.now();
+ try {
+ const r = await fetch(
+ `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(q)}&format=json&limit=20&addressdetails=1${viewbox}`,
+ { headers: { 'Accept-Language': 'en' } }
+ );
+ if (!r.ok) throw new Error(`HTTP ${r.status}`);
+ const data = await r.json();
+ _searchCache[cacheKey] = data;
+ renderSearchResults(data);
+ } catch(err) {
+ console.warn('Category search failed:', err);
+ dLoading.style.display = 'none';
+ dResults.innerHTML = `Search failed β try again
`;
+ }
+}
+
async function runDestSearch(q) {
if (_isOffline) {
dResults.style.display='block'; dLoading.style.display='none';
@@ -153,8 +246,13 @@ async function runDestSearch(q) {
}
const zoom = map ? map.getZoom() : 0;
- const cacheKey = q + (zoom >= 3 ? `@${map.getCenter().lng.toFixed(1)},${map.getCenter().lat.toFixed(1)}` : '');
+ const cacheKey = q + (zoom >= 3 ? `@${map.getCenter().lng.toFixed(1)},${map.getCenter().lat.toFixed(1)}` : '') + (_searchCategory !== 'all' ? `#${_searchCategory}` : '');
if (_searchCache[cacheKey]) { renderSearchResults(_searchCache[cacheKey]); return; }
+
+ if (_searchCategory !== 'all') {
+ await runCategorySearch(q, _searchCategory, cacheKey);
+ return;
+ }
try {
const searchWait = Math.max(0, 1100 - (Date.now() - _lastNominatimCall));
if (searchWait > 0) await new Promise(r => setTimeout(r, searchWait));
diff --git a/serve.py b/serve.py
index 5a60c01..9329c2f 100644
--- a/serve.py
+++ b/serve.py
@@ -182,7 +182,7 @@ def setup_vendor():
PHOTO_RE = re.compile(r"^/api/photos/([a-zA-Z0-9_-]+)(/thumb)?$")
# Allowed tile origins for the proxy
-TILE_ALLOWED_HOSTS = {'tiles.openfreemap.org', 'server.arcgisonline.com'}
+TILE_ALLOWED_HOSTS = {'tiles.openfreemap.org', 'tiles.maps.eox.at'}
# Video export streaming state: session_id -> {path, file, mime}
_video_sessions = {}
@@ -306,6 +306,8 @@ def do_POST(self):
try:
if self.path == "/api/data":
self._save_data()
+ elif self.path == "/api/overpass":
+ self._overpass_query()
elif self.path.startswith("/api/tiles/cache?"):
self._cache_tile()
elif self.path == "/api/video/start":
@@ -453,6 +455,28 @@ def _send_tile(self, data, content_type):
except (ConnectionResetError, BrokenPipeError):
pass # Client disconnected (e.g. user panned away, SW timeout)
+ def _overpass_query(self):
+ """Proxy Overpass API queries through serve.py to bypass network-level browser restrictions."""
+ length = int(self.headers.get('Content-Length', 0))
+ body = self.rfile.read(length) if length else b''
+ try:
+ req = urllib.request.Request(
+ 'https://overpass-api.de/api/interpreter',
+ data=body,
+ headers={'Content-Type': 'application/x-www-form-urlencoded',
+ 'User-Agent': 'Matrix-travel-app/1.0'}
+ )
+ with _urlopen(req) as resp:
+ data = resp.read()
+ self.send_response(200)
+ self.send_header('Content-Type', 'application/json')
+ self.end_headers()
+ self.wfile.write(data)
+ except Exception as e:
+ self.send_response(502)
+ self.end_headers()
+ self.wfile.write(str(e).encode())
+
def _proxy_tile(self):
"""Serve tile from disk cache only. Returns 404 if not cached."""
from urllib.parse import urlparse, parse_qs
diff --git a/sw.js b/sw.js
index 1c2d39d..193cda0 100644
--- a/sw.js
+++ b/sw.js
@@ -1,5 +1,5 @@
// Matrix β Service Worker for offline support
-const CACHE_VERSION = 'matrix-v19';
+const CACHE_VERSION = 'matrix-v20';
const APP_CACHE = `${CACHE_VERSION}-app`;
const TILE_CACHE = `${CACHE_VERSION}-tiles`;
@@ -29,7 +29,7 @@ const APP_SHELL = [
// Tile URL patterns to cache (raster + vector tiles, sprites, glyphs)
const TILE_PATTERNS = [
/tiles\.openfreemap\.org\/planet\//, // vector tiles only (not style JSON)
- /server\.arcgisonline\.com/,
+ /tiles\.maps\.eox\.at/,
/\.pbf(\?|$)/, // vector tile protobuf files
/sprites?\//, // map sprites
/glyphs?\//, // map font glyphs
@@ -38,9 +38,6 @@ const TILE_PATTERNS = [
// Max cached tiles (LRU eviction when exceeded)
const MAX_TILES = 10000;
-// Local server port for disk-cached tile proxy
-let serverPort = 8765;
-
console.log(`SW: ${CACHE_VERSION} loaded`);
self.addEventListener('install', (event) => {
@@ -55,10 +52,6 @@ self.addEventListener('install', (event) => {
self.skipWaiting();
});
-self.addEventListener('message', (event) => {
- if (event.data?.type === 'set-port') serverPort = event.data.port;
-});
-
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) => {
@@ -159,7 +152,7 @@ async function tileStrategy(request) {
const cached = await caches.match(request, { ignoreVary: true });
if (cached) return cached;
- const proxyUrl = `http://localhost:${serverPort}/api/tiles/proxy?url=${encodeURIComponent(request.url)}`;
+ const proxyUrl = `${self.location.origin}/api/tiles/proxy?url=${encodeURIComponent(request.url)}`;
const cacheAndReturn = async (body, ct) => {
try {
@@ -184,7 +177,7 @@ async function tileStrategy(request) {
return r.arrayBuffer().then(body => {
const ct = r.headers.get('Content-Type') || 'application/octet-stream';
// Save to disk in background
- fetch(`http://localhost:${serverPort}/api/tiles/cache?url=${encodeURIComponent(request.url)}`, {
+ fetch(`${self.location.origin}/api/tiles/cache?url=${encodeURIComponent(request.url)}`, {
method: 'POST', body: body.slice(0)
}).catch(() => {});
return { body, ct };