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