1const CACHE_VERSION = '1.7.80'; 2 3const BASE_CACHE_FILES = [ 4 'https://arcopoli.org/wp-content/uploads/2024/01/favicon-app-192.jpg', 5'', 6'https://arcopoli.org/wp-content/uploads/2024/01/favicon-app-512.jpg', 7'', 8'https://arcopoli.org/wp-content/uploads/2024/01/favicon-app-512.jpg', 9 10]; 11 12const OFFLINE_CACHE_FILES = [ 13 'https://arcopoli.org/', 14]; 15 16const NOT_FOUND_CACHE_FILES = [ 17 'https://arcopoli.org/', 18]; 19 20const OFFLINE_PAGE = 'https://arcopoli.org/'; 21const NOT_FOUND_PAGE = 'https://arcopoli.org/'; 22 23const CACHE_VERSIONS = { 24 content: 'content-v' + CACHE_VERSION, 25 notFound: '404-v' + CACHE_VERSION, 26 offline: 'offline-v' + CACHE_VERSION, 27}; 28 29// Define MAX_TTL's in SECONDS for specific file extensions 30const MAX_TTL = { 31 '/': 3600, 32 html: 3600, 33 json: 86400, 34 js: 86400, 35 css: 86400, 36 png: 86400, 37 jpg: 86400, 38}; 39 40const CACHE_STRATEGY = { 41 default: 'cacheFirst', 42 css_js: 'cacheFirst', 43 images: 'cacheFirst', 44 fonts: 'cacheFirst', 45}; 46 47const CACHE_BLACKLIST = [ 48// (str) => { 49// return !str.includes('/wp-admin/') || !str.startsWith('https://arcopoli.org//wp-admin/'); 50// }, 51]; 52const neverCacheUrls = [/\/wp-admin/,/\/wp-login/,/preview=true/,/\/cart/,/ajax/,/login/,]; 53 54 55const SUPPORTED_METHODS = [ 56 'GET', 57]; 58// Check if current url is in the neverCacheUrls list 59function pwaForWpcheckNeverCacheList(url) { 60 if ( this.match(url) ) { 61 return false; 62 } 63 return true; 64} 65/** 66 * pwaForWpisBlackListed 67 * @param {string} url 68 * @returns {boolean} 69 */ 70function pwaForWpisBlackListed(url) { 71 return (CACHE_BLACKLIST.length > 0) ? !CACHE_BLACKLIST.filter((rule) => { 72 if(typeof rule === 'function') { 73 return !rule(url); 74 } else { 75 return false; 76 } 77 }).length : false 78} 79 80/** 81 * pwaForWpgetFileExtension 82 * @param {string} url 83 * @returns {string} 84 */ 85function pwaForWpgetFileExtension(url) { 86 87 if (typeof url === 'string') { 88 89 let split_two = url.split('?'); 90 let split_url = split_two[0]; 91 92 let extension = split_url.split('.').reverse()[0].split('?')[0]; 93 return (extension.endsWith('/')) ? '/' : extension; 94 95 }else{ 96 return null; 97 } 98} 99/** 100 * pwaForWpgetTTL 101 * @param {string} url 102 */ 103function pwaForWpgetTTL(url) { 104 if (typeof url === 'string') { 105 let extension = pwaForWpgetFileExtension(url); 106 if (typeof MAX_TTL[extension] === 'number') { 107 return MAX_TTL[extension]; 108 } else { 109 return MAX_TTL["/"]; 110 } 111 } else { 112 return MAX_TTL["/"]; 113 } 114} 115 116/** 117 * pwaForWpinstallServiceWorker 118 * @returns {Promise} 119 */ 120function pwaForWpinstallServiceWorker() { 121 return Promise.all( 122 [ 123 caches.open(CACHE_VERSIONS.content) 124 .then( 125 (cache) => { 126 127 if(BASE_CACHE_FILES.length >0){ 128 129 for (var i = 0; i < BASE_CACHE_FILES.length; i++) { 130 131 pwaForWpprecacheUrl(BASE_CACHE_FILES[i]) 132 133 } 134 135 } 136 137 //return cache.addAll(BASE_CACHE_FILES); 138 } 139 ), 140 caches.open(CACHE_VERSIONS.offline) 141 .then( 142 (cache) => { 143 return cache.addAll(OFFLINE_CACHE_FILES); 144 } 145 ), 146 caches.open(CACHE_VERSIONS.notFound) 147 .then( 148 (cache) => { 149 return cache.addAll(NOT_FOUND_CACHE_FILES); 150 } 151 ) 152 ] 153 ) 154 .then(() => { 155 return self.skipWaiting(); 156 }); 157} 158 159/** 160 * pwaForWpcleanupLegacyCache 161 * @returns {Promise} 162 */ 163function pwaForWpcleanupLegacyCache() { 164
165 let currentCaches = Object.keys(CACHE_VERSIONS) 166 .map( 167 (key) => { 168 return CACHE_VERSIONS[key]; 169 } 170 ); 171 172 return new Promise( 173 (resolve, reject) => { 174 175 caches.keys() 176 .then( 177 (keys) => { 178 return legacyKeys = keys.filter( 179 (key) => { 180 return !~currentCaches.indexOf(key); 181 } 182 ); 183 } 184 ) 185 .then( 186 (legacy) => { 187 if (legacy.length) { 188 Promise.all( 189 legacy.map( 190 (legacyKey) => { 191 return caches.delete(legacyKey) 192 } 193 ) 194 ) 195 .then( 196 () => { 197 resolve() 198 } 199 ) 200 .catch( 201 (err) => { 202 reject(err); 203 } 204 ); 205 } else { 206 resolve(); 207 } 208 } 209 ) 210 .catch( 211 () => { 212 reject(); 213 } 214 ); 215 216 } 217 ); 218} 219 220function pwaForWpprecacheUrl(url) { 221 222 if(!pwaForWpisBlackListed(url)) { 223 caches.open(CACHE_VERSIONS.content) 224 .then((cache) => { 225 cache.match(url) 226 .then((response) => { 227 if(!response) { 228 return fetch(url) 229 } else { 230 // already in cache, nothing to do. 231 return null 232 } 233 }) 234 .then((response) => { 235 if(response) { 236 fetch(url).then(dataWrappedByPromise => dataWrappedByPromise.text()) 237 .then(data => { 238 if(data){ 239 const regex = /<img[^>]+src="(https:\/\/[^">]+)"/g; 240 let m; 241 while ((m = regex.exec(data)) !== null) { 242 if (m.index === regex.lastIndex) { 243 regex.lastIndex++; 244 } 245 m.forEach((match, groupIndex) => { 246 if(groupIndex == 1){ 247 if(new URL(match).origin == location.origin){ 248 fetch(match). 249 then((imagedata) => { 250 //console.log(imagedata); 251 cache.put(match, imagedata.clone()); 252 253 }); 254 } 255 } 256 }); 257 } 258 259 260 } 261 262 263 }); 264 265 return cache.put(url, response.clone()); 266 } else { 267 return null; 268 } 269 }); 270 }) 271 } 272} 273 274var fetchRengeData = function(event){ 275 var pos = Number(/^bytes\=(\d+)\-$/g.exec(event.request.headers.get('range'))[1]); 276 console.log('Range request for', event.request.url, ', starting position:', pos); 277 event.respondWith( 278 caches.open(CACHE_VERSIONS.content) 279 .then(function(cache) { 280 return cache.match(event.request.url); 281 }).then(function(res) { 282 if (!res) { 283 return fetch(event.request) 284 .then(res => { 285 return res.arrayBuffer(); 286 }); 287 } 288 return res.arrayBuffer(); 289 }).then(function(ab) { 290 return new Response( 291 ab.slice(pos), 292 { 293 status: 206, 294 statusText: 'Partial Content', 295 headers: [ 296 // ['Content-Type', 'video/webm'], 297 ['Content-Range', 'bytes ' + pos + '-' + 298 (ab.byteLength - 1) + '/' + ab.byteLength]] 299 }); 300 })); 301} 302 303let cachingStrategy = { 304 notGetMethods: function(event){ 305 // If non-GET request, try the network first, fall back to the offline page 306 if (event.request.method !== 'GET') { 307 event.respondWith( 308 fetch(event.request) 309 .catch(error => { 310 return caches.match(offlinePage); 311 }) 312 ); 313 return false; 314 } 315 }, 316 317 fetchFromCache: function(event){ 318 /* return new Promise( 319 (resolve) => {*/ 320 return caches.open(CACHE_VERSIONS.content) 321 .then( 322 (cache) => { 323 324 return cache.match(event.request) 325 .then( 326 (response) => { 327 328 if (response) { 329 330 let headers = response.headers.entries(); 331 let date = null; 332 333 for (let pair of headers) { 334 if (pair[0] === 'date') { 335 date = new Date(pair[1]); 336 } 337 } 338 339 if (date) { 340 let age = parseInt((new Date().getTime() - date.getTime())/1000); 341 let ttl = pwaForWpgetTTL(event.request.url); 342 343 if (age > ttl) { 344 345 return new Promise( 346 (resolve) => { 347 348 return fetch(event.request.clone()) 349 .then( 350 (updatedResponse) => { 351 if (updatedResponse) { 352 cache.put(event.request, updatedResponse.clone()); 353 resolve(updatedResponse); 354 } else { 355 resolve(response) 356 } 357 } 358 ) 359 .catch( 360 () => { 361 resolve(response); 362 } 363 ); 364 365 } 366 ) 367 .catch( 368 (err) => { 369 return response; 370 } 371 ); 372 } else { 373 return response; 374 } 375 376 } else { 377 return response; 378 } 379 380 } else { 381 return null; 382 } 383 } 384 ) 385 .then( 386 (response) => { 387 if (response) { 388 return response; 389 } else { 390 return fetch(event.request.clone(), {cache: "no-store"}) 391 .then( 392 (response) => { 393 394 if(response.status < 300) { 395 if (~SUPPORTED_METHODS.indexOf(event.request.method) && !pwaForWpisBlackListed(event.request.url)) { 396 cache.put(event.request, response.clone()); 397 } 398 return response; 399 } else { 400 return caches.open(CACHE_VERSIONS.notFound).then((cache) => { 401 return cache.match(NOT_FOUND_PAGE); 402 }) 403 } 404 } 405 ) 406 .then((response) => { 407 if(response) { 408 return response; 409 } 410 }) 411 .catch( 412 () => { 413 414 return caches.open(CACHE_VERSIONS.offline) 415 .then( 416 (offlineCache) => { 417 return offlineCache.match(OFFLINE_PAGE) 418 } 419 ) 420 421 } 422 ); 423 } 424 } 425 ) 426 .catch( 427 (error) => { 428 console.error(' Error in fetch handler:', error); 429 throw error; 430 } 431 ); 432 } 433 ) 434 /*})*/ 435 436 }, 437 fetchnetwork: function(event){ 438 return caches.open(CACHE_VERSIONS.content) 439 .then( 440 (cache) => { 441 return fetch(event.request.clone()).then(function (response) { 442 443 if(response.status < 300) { 444 if (~SUPPORTED_METHODS.indexOf(event.request.method) && !pwaForWpisBlackListed(event.request.url)) { 445 cache.put(event.request, response.clone()); 446 } 447 return response; 448 }else if(response.status==404){ 449 return cachingStrategy.Notfoundpage(); 450 } else if( cache.match(event.request) ){
451 return cache.match(event.request); 452 }else { 453 return cachingStrategy.Offlinepage(); 454 } 455 }).catch( 456 (err) => { 457 return cache.match(event.request) 458 } 459 ).catch( 460 (err) => { 461 return cachingStrategy.Offlinepage(); 462 } 463 ) 464 } 465 ).catch( 466 (err) => { 467 return cachingStrategy.Offlinepage(); 468 } 469 ) 470 }, 471 addCache: function(event,updatedResponse){ 472 cache.put(event.request, updatedResponse.clone()); 473 resolve(updatedResponse); 474 }, 475 Offlinepage: function(){ 476 return caches.open(CACHE_VERSIONS.offline).then((cache) => { 477 return cache.match(OFFLINE_PAGE); 478 }) 479 }, 480 Notfoundpage: function(){ 481 return caches.open(CACHE_VERSIONS.notFound).then((cache) => { 482 return cache.match(NOT_FOUND_PAGE); 483 }) 484 }, 485 /*Strategies*/ 486 networkOnlyStrategy: function(event){ 487 return caches.open(CACHE_VERSIONS.content) 488 .then( 489 (cache) => { 490 return fetch(event.request.clone()).then(function (response) { 491 if(response.status < 300) { 492 if (~SUPPORTED_METHODS.indexOf(event.request.method) && !pwaForWpisBlackListed(event.request.url)) { 493 cache.put(event.request, response.clone()); 494 } 495 return response; 496 }else if(response.status==404){ 497 return cachingStrategy.Notfoundpage(); 498 } else if(cache.match(event.request)){ 499 return cache.match(event.request) 500 } else { 501 return cachingStrategy.Offlinepage(); 502 } 503 }).catch( 504 (err) => { 505 return cachingStrategy.Offlinepage(); 506 } 507 ) 508 } 509 ).catch( 510 (err) => { 511 return cachingStrategy.Offlinepage() 512 } 513 ); 514 }, 515 cacheFirstStrategy: function(events){ 516 return cachingStrategy.fetchFromCache(events).catch( 517 (err) => { 518 return cachingStrategy.Offlinepage() 519 } 520 ); 521 }, 522 NeworkFirstStrategy: function(events){ 523 return cachingStrategy.fetchnetwork(events).catch( 524 (err) => { 525 return cachingStrategy.fetchFromCache(events) 526 } 527 ).catch( 528 (err) => { 529 return cachingStrategy.Offlinepage() 530 } 531 ); 532 } 533 534 535} 536 537 538self.addEventListener( 539 'install', event => { 540 event.waitUntil( 541 Promise.all([ 542 pwaForWpinstallServiceWorker(), 543 self.skipWaiting(), 544 ]) 545 ); 546 } 547); 548 549// The activate handler takes care of cleaning up old caches. 550self.addEventListener( 551 'activate', event => { 552 event.waitUntil( 553 Promise.all( 554 [ 555 pwaForWpcleanupLegacyCache(), 556 self.clients.claim(),
557 self.skipWaiting(), 558 ] 559 ) 560 .catch( 561 (err) => { 562 self.skipWaiting(); 563 } 564 ) 565 ); 566 } 567); 568self.addEventListener('online', event => { 569 if (navigator.onLine && navigator.standalone === true) { 570 isReachable(event.request.url).then(function(online) { 571 if (online) { 572 //handle online status 573 caches.delete(event.request.url); 574 console.log('online'); 575 } else { 576 console.log('no connectivity'); 577 } 578 }); 579 } else { 580 //handle offline status 581 console.log('offline'); 582 } 583}); 584 585function isReachable(url) { 586 /** 587 * Note: fetch() still "succeeds" for 404s on subdirectories, 588 * which is ok when only testing for domain reachability. 589 */ 590 return fetch(url, { method: 'HEAD', mode: 'no-cors' }) 591 .then(function(resp) { 592 return resp && (resp.ok || resp.type === 'opaque'); 593 }) 594 .catch(function(err) { 595 console.warn('[conn test failure]:', err); 596 }); 597} 598 599self.addEventListener( 600 'fetch', event => { 601 // Return if the current request url is in the never cache list 602 if ( ! neverCacheUrls.every(pwaForWpcheckNeverCacheList, event.request.url) ) { 603 //console.log( 'PWA ServiceWorker: URL exists in excluded list of cache.' + event.request.url); 604 return; 605 } 606 if(! neverCacheUrls.every(pwaForWpcheckNeverCacheList, event.request.referrer) ){ 607 //console.log( 'PWA ServiceWorker: Ref-URL exists in excluded list of cache.' + event.request.referrer); 608 return; 609 } 610 if(pwaForWpisBlackListed(event.request.url)){ 611 return; 612 } 613 614 const url = new URL(event.request.url); 615 616 617 path_array = url.pathname.split('/') 618 if (event.request.method === 'POST' && path_array.includes('activity')) { 619 return event.respondWith((async () => { 620 const formData = await event.request.formData(); 621 const title = formData.get('title'); 622 const text = formData.get('text'); 623 const link = formData.get('url'); 624 const image = formData.get('externalMedia'); 625 const reader = new FileReader(); 626 627 reader.onload = async (event) => { 628 const imageData = event.target.result; 629 630 const db = await openDatabase(); 631 await saveData(db, { 632 id: 'sharedData', 633 title: title, 634 text: text, 635 url: link, 636 image: imageData 637 }); 638 639 console.log('Data saved to IndexedDB'); 640 641 // return Response.redirect('/?share-target', 303); 642 return Response.redirect(url.pathname+'/?share-target', 303); 643 }; 644 645 reader.readAsDataURL(image); 646 return Response.redirect(url.pathname+'/?share-target', 303); 647 // return Response.redirect('/?share-target', 303); 648 })()); 649 } 650 651 // Return if request url protocal isn't http or https 652 if ( ! event.request.url.match(/^(http|https):\/\//i) ) 653 return; 654 if ( event.request.referrer.match(/^(wp-admin):\/\//i) ) 655 return; 656 657 if ( new URL(event.request.url).origin !== location.origin ) 658 return; 659 660 if(event.request.method !== 'GET' ){ 661 event.respondWith( 662 fetch(event.request) 663 .catch(error => { 664 return caches.open(CACHE_VERSIONS.offline).then(function(cache) { 665 return cache.match(OFFLINE_PAGE); 666 }); 667 }) 668 ); 669 return false; 670 } 671 const destination = event.request.destination; 672 switch (destination) { 673 case 'style': 674 case 'script': 675 cachingStrategyType = CACHE_STRATEGY.css_js; 676 break; 677 case 'document': 678 cachingStrategyType = CACHE_STRATEGY.default 679 break; 680 case 'image': 681 cachingStrategyType = CACHE_STRATEGY.images; 682 break; 683 case 'font': 684 cachingStrategyType = CACHE_STRATEGY.fonts; 685 break; 686 // All `XMLHttpRequest` or `fetch()` calls where 687 // `Request.destination` is the empty string default value 688 default: 689 cachingStrategyType = CACHE_STRATEGY.default 690 } 691 var cache = null; 692 switch(cachingStrategyType){ 693 case "networkFirst": 694 cache = cachingStrategy.NeworkFirstStrategy(event) 695 break; 696 case "networkOnly": 697 cache = cachingStrategy.networkOnlyStrategy(event) 698 break; 699 //break;
700 case "cacheFirst": 701 case "staleWhileRevalidate": 702 default: 703 cache = cachingStrategy.cacheFirstStrategy(event) 704 break; 705 } 706 event.respondWith(cache); 707 708 } 709); 710 711function openDatabase() { 712 return new Promise((resolve, reject) => { 713 const request = indexedDB.open('shareTargetDB', 1); 714 715 request.onupgradeneeded = (event) => { 716 const db = event.target.result; 717 db.createObjectStore('sharedData', { keyPath: 'id' }); 718 }; 719 720 request.onsuccess = (event) => { 721 resolve(event.target.result); 722 }; 723 724 request.onerror = (event) => { 725 reject(event.target.error); 726 }; 727 }); 728 } 729 730 function saveData(db, data) { 731 return new Promise((resolve, reject) => { 732 const transaction = db.transaction('sharedData', 'readwrite'); 733 const store = transaction.objectStore('sharedData'); 734 735 const request = store.put(data); 736 737 request.onsuccess = () => { 738 resolve(); 739 }; 740 741 request.onerror = (event) => { 742 reject(event.target.error); 743 }; 744 }); 745 } 746 747 748self.addEventListener('message', (event) => { 749 750 if( 751 typeof event.data === 'object' && 752 typeof event.data.action === 'string' 753 ) { 754 switch(event.data.action) { 755 case 'cache' : 756 pwaForWpprecacheUrl(event.data.url); 757 break; 758 759 default : 760 console.log('Unknown action: ' + event.data.action); 761 break; 762 } 763 } 764 765}); 766 767
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.