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