1const version = "v2:", // be sure to update 2 3 // Stuff to load on install 4 offline_page = "/offline/", 5 preinstall = [ 6 // images 7 "/static/img/favicon-16x16.png", 8 "/static/img/favicon-32x32.png", 9 "/static/img/favicon-96x96.png", 10 // CSS 11 // JavaScript 12 // Offline 13 offline_page 14 ], 15 16 // caches 17 sw_caches = { 18 static: { 19 name: `${version}static` 20 }, 21 pages: { 22 name: `${version}pages`, 23 limit: 5 24 }, 25 other: { 26 name: `${version}other`, 27 limit: 5 28 } 29 }, 30 31 // Never cache 32 ignore = [ 33 'thanks', 34 'submitted', 35 'chrome-extension' 36 ], 37 38 // How to decide what gets cached and 39 // what might not be left out 40 high_priority = [ 41 /webwewant\.fyi/, 42 /fonts\.googleapis\.com/ 43 ], 44 45 fetch_config = { 46 images: { 47 mode: 'no-cors' 48 } 49 }; 50 51let slow_connection = false, 52save_data; 53 54if ( 'connection' in navigator ) 55{ 56 slow_connection = ( navigator.connection.downlink < .5 ); 57 save_data = navigator.connection.saveData; 58} 59self.addEventListener( "activate", event => { 60 61 // console.log('WORKER: activate event in progress.'); 62 63 // clean up stale caches 64 event.waitUntil( 65 caches.keys() 66 .then( keys => { 67 return Promise.all( 68 keys 69 .filter( key => { 70 return ! key.startsWith( version ); 71 }) 72 .map( key => { 73 return caches.delete( key ); 74 }) 75 ); // end promise 76 }) // end then 77 ); // end event 78 79 80}); 81 82addEventListener("message", messageEvent => { 83 if (messageEvent.data == "clean up") 84 { 85 for ( let key in sw_caches ) 86 { 87 if ( sw_caches[key].limit != undefined ) 88 { 89 trimCache( sw_caches[key].name, sw_caches[key].limit ); 90 } 91 } 92 } 93}); 94 95function trimCache( cache_name, limit ) 96{ 97 caches.open( cache_name ) 98 .then( cache => { 99 cache.keys() 100 .then( items => { 101 if ( items.length > limit ) { 102 cache.delete( items[0] ) 103 .then( 104 trimCache( cache_name, limit) 105 ); // end delete 106 } // end if 107 }); // end keys 108 }); // end open 109} 110self.addEventListener( "fetch", event => { 111 112 // console.log( "WORKER: fetch event in progress." ); 113 114 const request = event.request, 115 url = request.url; 116 117 if ( request.method !== "GET" || shouldBeIgnored( url ) ) 118 { 119 // console.log( "ignoring " + url ); 120 return; 121 } 122 123 if ( save_data == undefined ) 124 { 125 save_data = request.headers.get("save-data"); 126 } 127 128 // console.log(request.url, request.headers); 129 130 // JSON & such 131 if ( /\.json$/.test( url ) || 132 /jsonp\=/.test( url ) ) 133 { 134 event.respondWith( 135 caches.match( request ) 136 .then( cached_result => { 137 // cached first 138 if ( cached_result ) 139 { 140 // Update the cache in the background, but only if weâre not trying to save data 141 if ( ! save_data && ! slow_connection ) 142 { 143 event.waitUntil( 144 refreshCachedCopy( request, sw_caches.other.name ) 145 ); 146 } 147 return cached_result; 148 } 149 // fallback to network 150 return fetch( request ) 151 .then( response => { 152 const copy = response.clone(); 153 event.waitUntil( 154 saveToCache( sw_caches.other.name, request, copy ) 155 ); 156 return response; 157 }) 158 // fallback to offline page 159 .catch( 160 respondWithServerOffline 161 ); 162 }) 163 ); 164 } 165 166 // JavaScript 167 else if ( /\.js$/.test( url ) && isHighPriority( url ) ) 168 { 169 event.respondWith( 170 caches.match( request ) 171 .then( cached_result => { 172 // cached first 173 if ( cached_result ) 174 { 175 // Update the cache in the background, but only if weâre not trying to save data 176 if ( ! save_data && ! slow_connection ) 177 { 178 event.waitUntil( 179 refreshCachedCopy( request, sw_caches.static.name ) 180 ); 181 } 182 return cached_result; 183 } 184 // fallback to network 185 return fetch( request ) 186 .then( response => { 187 const copy = response.clone(); 188 event.waitUntil( 189 saveToCache( sw_caches.static.name, request, copy ) 190 ); 191 return response; 192 }) 193 // fallback to offline page 194 .catch( 195 respondWithServerOffline 196 ); 197 }) 198 ); 199 } 200 201 // Wants 202 else if ( /wants/.test( url ) ) 203 { 204 event.respondWith( 205 fetch( request ) 206 .then( response => { 207 const copy = response.clone(); 208 event.waitUntil( 209 saveToCache( sw_caches.pages.name, request, copy ) 210 ); // end waitUntil 211 return response; 212 }) 213 // fallback to offline page 214 .catch( 215 caches.match( request ) 216 .then( cached_result => { 217 return cached_result; 218 }) 219 .catch( 220 respondWithOfflinePage 221 ) 222 ) 223 ); 224 } 225 226 // Other HTML 227 else if ( ( request.headers.get("Accept") && 228 request.headers.get("Accept").includes("text/html") ) || 229 requestIsLikelyForHTML( url ) ) 230 { 231 event.respondWith( 232 // check the cache first 233 caches.match( request ) 234 .then( cached_result => { 235 if ( cached_result ) 236 { 237 // Update the cache in the background, but only if weâre not trying to save data 238 if ( ! save_data && ! slow_connection ) 239 { 240 event.waitUntil( 241 refreshCachedCopy( request, sw_caches.pages.name ) 242 ); 243 } 244 return cached_result; 245 } 246 // fallback to network, but cache the result 247 return fetch( request ) 248 .then( response => { 249 const copy = response.clone(); 250 event.waitUntil( 251 saveToCache( sw_caches.pages.name, request, copy ) 252 ); // end waitUntil 253 return response; 254 }) 255 // fallback to offline page 256 .catch( 257 respondWithOfflinePage 258 ); 259 }) 260 ); 261 } 262 263 // images - cache first, then determine if we should request form the network & cache, fallbacks 264 else if ( request.headers.get("Accept").includes("image") ) 265 { 266 event.respondWith( 267 // check the cache first 268 caches.match( request ) 269 .then( cached_result => { 270 if ( cached_result ) 271 { 272 return cached_result; 273 } 274 275 // high priority imagery 276 if ( isHighPriority( url ) ) 277 { 278 return fetch( request, fetch_config.images ) 279 .then( response => { 280 const copy = response.clone(); 281 event.waitUntil( 282 saveToCache( sw_caches.static.name, request, copy ) 283 ); // end waitUntil 284 return response; 285 }); 286 } 287 // all others 288 else 289 { 290 // console.log('other images', url); 291 // save data? 292 if ( save_data || slow_connection ) 293 { 294 return new Response( "", { 295 status: 408, 296 statusText: "This request was ignored to save data." 297 }); 298 } 299 300 // normal operation 301 else 302 { 303 // console.log('fetching'); 304 return fetch( request, fetch_config.images ) 305 .then( response => { 306 const copy = response.clone(); 307 event.waitUntil( 308 saveToCache( sw_caches.static.name, request, copy ) 309 ); 310 return response; 311 }); 312 } 313 } 314 }) 315 ); 316 } 317 318 // everything else - cache first, then network 319 else 320 { 321 event.respondWith( 322 // check the cache first 323 caches.match( request ) 324 .then( cached_result => { 325 if ( cached_result ) 326 { 327 return cached_result; 328 } 329 330 // save data? 331 if ( save_data || slow_connection ) 332 { 333 return new Response( "", { 334 status: 408, 335 statusText: "This request was ignored to save data." 336 }); 337 } 338 339 // normal operation 340 else 341 { 342 return fetch( request ) 343 .then( response => { 344 const copy = response.clone(); 345 if ( isHighPriority( url ) ) 346 { 347 event.waitUntil( 348 saveToCache( sw_caches.static.name, request, copy ) 349 ); 350 } 351 else 352 { 353 event.waitUntil( 354 saveToCache( sw_caches.other.name, request, copy ) 355 ); 356 } 357 return response; 358 }) 359 // fallback to offline image 360 .catch( 361 respondWithServerOffline 362 ); 363 } 364 }) 365 ); 366 } 367 368}); 369 370self.addEventListener( "install", function( event ){ 371 372 // console.log( "WORKER: install event in progress." ); 373 374 // immediately take over
375 self.skipWaiting(); 376 377 event.waitUntil( 378 caches.open( sw_caches.static.name ) 379 .then(function( cache ){ 380 return cache.addAll( preinstall ); 381 }) 382 ); 383 384}); 385 386function saveToCache( cache_name, request, response ) 387{ 388 // console.log( 'saving a copy of', request.url ); 389 caches.open( cache_name ) 390 .then( cache => { 391 return cache.put( request, response ); 392 }); 393} 394 395function refreshCachedCopy( the_request, cache_name ) 396{ 397 fetch( the_request ) 398 .then( the_response => { 399 caches.open( cache_name ) 400 .then( the_cache => { 401 return the_cache.put( the_request, the_response ); 402 }); 403 }); 404} 405 406function shouldBeIgnored( url ) 407{ 408 let i = ignore.length; 409 while( i-- ) 410 { 411 if ( url.indexOf( ignore[i] ) > -1 ) 412 { 413 // console.log( "found", ignore[i], 'in', url ); 414 return true; 415 } 416 } 417 return false; 418} 419 420function isHighPriority( url ) 421{ 422 let i = high_priority.length; 423 while ( i-- ) 424 { 425 if ( high_priority[i].test( url ) ) 426 { 427 return true; 428 } 429 } 430 return false; 431} 432 433function respondWithOfflinePage() 434{ 435 return caches.match( offline_page ) 436 .catch( 437 respondWithServerOffline 438 ); 439} 440 441function respondWithServerOffline(){ 442 return new Response( "", { 443 status: 408, 444 statusText: "The server appears to be offline." 445 }); 446} 447 448function requestIsLikelyForHTML( url ) 449{ 450 const final_segment = url.split("/").pop(); 451 if ( final_segment == "" || 452 /.+\.html$/.test( final_segment ) || 453 ! (/\..+$/.test( final_segment ) ) ) 454 { 455 return true; 456 } 457 return false; 458}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.