-
Notifications
You must be signed in to change notification settings - Fork 2
/
sw.js
153 lines (123 loc) · 5.29 KB
/
sw.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Choose a cache name
const cacheName = 'hitchmap-v1';
// List the files to precache
const TWM = 'https://tinyworldmap.com/dist/tiny-world-all-10000.json';
const precacheResources = ['/', '/light.html', '/icon.png', '/favicon.ico', 'https://a.tile.openstreetmap.org/0/0/0.png',TWM];
const REGEXP = /tile\.openstreetmap\.org\/(?<z>\d+)\/(?<x>\d+)\/(?<y>\d+)/
const TILESIZE = 256
// When the service worker is installing, open the cache and add the precache resources to it
self.addEventListener('install', (event) => {
console.log('Service worker install event!');
event.waitUntil(caches.open(cacheName).then((cache) => cache.addAll(precacheResources)));
});
function drawPlaces(tile, coords, places, opts) {
var ctx = tile.getContext('2d');
if (!places.path2ds)
places.path2ds = places.paths.map(p => [new Path2D(p[0]), p[1]])
let vwidth = 800, vheight = 800
var size = {x: tile.width, y: tile.height};
let margin1 = 2/size.x, margin2 = 35/size.x, margin3 = 50/size.x, N = Math.pow(2, coords.z);
let lbound = coords.x / N, rbound = lbound + 1/N, tbound = coords.y / N, bbound = tbound + 1/N
ctx.fillStyle = opts.backgroundColor || (places.path2ds.length ? "#aad3df" : 'white')
ctx.fillRect(0, 0, size.x, size.y)
ctx.translate(-size.x*coords.x, -size.x*coords.y)
ctx.scale(size.x*N/vwidth,size.y*N/vheight)
ctx.strokeStyle = opts.borderColor || 'black'
ctx.fillStyle = opts.borderFillColor || 'white'
ctx.lineWidth = 2/N
for (let [p, bounds] of places.path2ds) {
if (!(bounds[0] > rbound || bounds[2] < lbound || bounds[1] > bbound || bounds[3] < tbound)) {
ctx.fill(p)
ctx.stroke(p)
}
}
ctx.resetTransform()
let dotColor = opts.dotColor || (places.path2ds.length ? "transparent" : "red")
ctx.fillStyle = dotColor
if (dotColor != 'transparent')
for (let [yc, xc, name, zoom] of places.cities) {
let y = yc * N - coords.y, x = xc * N - coords.x
if (y > -margin1 && y < 1+margin1 && x > -margin1 && x < 1+margin1) {
let xS = size.x * x, yS = size.y * y
ctx.fillRect(xS-1,yS-1,2,2)
}
}
ctx.strokeStyle = opts.textStrokeColor || 'rgba(255,255,255,.9)'
ctx.lineWidth = 4
ctx.textAlign = 'center'
ctx.fillStyle = opts.textColor || "black";
ctx.font = opts.cityFont || '12px Arial, Helvetica, Ubuntu, sans-serif'
ctx.lineJoin = 'round'
for (let [yc, xc, name, zoom] of places.cities) {
if (zoom > coords.z) continue
let y = yc * N - coords.y, x = xc * N - coords.x
if (y > -margin2 && y < 1+margin2 && x > -margin2 && x < 1+margin2) {
let xS = size.x * x, yS = size.y * y
ctx.strokeText(name, xS, yS, 70)
ctx.fillText(name, xS, yS, 70)
}
}
ctx.font = opts.countryFont || 'bold 14px Arial, Helvetica, Ubuntu, sans-serif'
for (let [yc, xc, name, zoom] of places.countries) {
if (zoom > coords.z || coords.z > 8) continue
let y = yc * N - coords.y, x = xc * N - coords.x
if (y > -margin3 && y < 1+margin3 && x > -margin3 && x < 1+margin3) {
let xS = size.x * x, yS = size.y * y
ctx.strokeText(name, xS, yS, 100)
ctx.fillText(name, xS, yS, 100)
}
}
return tile;
}
let places
async function handleTileRequest(request, match) {
let cache = await caches.open(cacheName)
// Go to the network first
try {
let response = await fetch(request.url)
if (!response.ok) throw new Error('No 200')
return response
}
// If the network is unavailable, create a replacement tile locally
catch(e) {
try {
places = places || await (await cache.match(TWM)).json()
const canvas = new OffscreenCanvas(TILESIZE, TILESIZE)
let coords = {x: +match.groups.x, y: +match.groups.y, z: +match.groups.z}
drawPlaces(canvas, coords, places, {})
let blob = await canvas.convertToBlob({type: 'image/png'})
const headers = new Headers()
headers.set('content-type', blob.type)
headers.set('content-length', blob.size)
headers.set('access-control-allow-origin', '*')
return new Response(blob, {headers: headers})
}
catch (e) {
console.log(e)
}
}
}
self.addEventListener('fetch', (event) => {
if (event.request.method != 'GET')
return
let match = REGEXP.exec(event.request.url)
if (event.request.destination === 'image' && match) {
event.respondWith(handleTileRequest(event.request, match))
}
else {
// Open the cache
event.respondWith(caches.open(cacheName).then((cache) => {
// Go to the network first
return fetch(event.request).then((fetchedResponse) => {
// IMPORTANT: Tell the service worker what not to cache
if (!['image', 'video', 'audio'].includes(event.request.destination))
// Cache response
cache.put(event.request, fetchedResponse.clone());
return fetchedResponse;
}).catch(() => {
// If the network is unavailable, get
return cache.match(event.request.url);
});
}));
}
});