diff --git a/README.md b/README.md index e97500a..9d2c504 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,16 @@ composer require a2nt/silverstripe-progressivewebapp - Add js to register the service worker (example can be found at client/src/app.js) ``` if ('serviceWorker' in navigator) { - var baseHref = (document.getElementsByTagName('base')[0] || {}).href; - if(baseHref){ - navigator.serviceWorker.register(baseHref + 'sw.js').then(function() { - console.log('Service Worker Registered'); - }); - } + var baseHref = (document.getElementsByTagName('base')[0] || {}).href; + var version = (document.querySelector('meta[name="swversion"]') || {}) + .content; + if (baseHref) { + navigator.serviceWorker + .register(baseHref + 'sw.js?v=' + version) + .then(() => { + console.log('SW: Registered'); + }); + } } ``` - Add the following tags to the head of your website diff --git a/client/dist/app.js b/client/dist/app.js index 2303995..8285cc6 100644 --- a/client/dist/app.js +++ b/client/dist/app.js @@ -1 +1 @@ -!function(e){var r={};function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:n})},t.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,r){if(1&r&&(e=t(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(t.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var o in e)t.d(n,o,function(r){return e[r]}.bind(null,o));return n},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},t.p="/mnt/data/srv/dist/web/nysca/vendor/a2nt/silverstripe-progressivewebapp/client/dist",t(t.s="./client/src/app.js")}({"./client/src/app.js":function(e,r){if("serviceWorker"in navigator){var t=(document.getElementsByTagName("base")[0]||{}).href;t&&navigator.serviceWorker.register("".concat(t,"sw.js")).then((function(){console.log("Service Worker Registered")}))}}}); \ No newline at end of file +!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="/mnt/data/srv/dist/web/nysca/vendor/a2nt/silverstripe-progressivewebapp/client/dist",r(r.s="./client/src/app.js")}({"./client/src/app.js":function(e,t){if("serviceWorker"in navigator){var r=(document.getElementsByTagName("base")[0]||{}).href,n=(document.querySelector('meta[name="swversion"]')||{}).content;r&&navigator.serviceWorker.register("".concat(r,"sw.js?v=").concat(n)).then((function(){console.log("SW: Registered")}))}}}); \ No newline at end of file diff --git a/client/dist/sw.js b/client/dist/sw.js index 562abb6..257fdf6 100644 --- a/client/dist/sw.js +++ b/client/dist/sw.js @@ -1 +1 @@ -!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/mnt/data/srv/dist/web/nysca/vendor/a2nt/silverstripe-progressivewebapp/client/dist",n(n.s="./client/src/sw.js")}({"./client/src/lib/log.js":function(e,t){e.exports=function(e){debug&&console.log(e)}},"./client/src/sw.js":function(e,t,n){var r=n("./client/src/lib/log.js"),o=n("./client/src/thirdparty/serviceworker-caches.js");if(debug&&(r("SW: debug is on"),r("SW: CACHE_NAME: ".concat(CACHE_NAME)),r("SW: appDomain: ".concat(appDomain)),r("SW: lang: ".concat(lang))),"string"!==typeof self.CACHE_NAME)throw new Error("Cache Name cannot be empty");self.addEventListener("fetch",(function(e){var t=e.request.clone(),n=e.request.clone();e.respondWith(fetch(t).then((function(t){var n=t.clone();return o.open(self.CACHE_NAME).then((function(t){var r=e.request.clone();t.put(r,n)})),t})).catch((function(e){return r("SW: fetch failed"),o.match(n)})))})),self.addEventListener("activate",(function(e){r("SW: activated: ".concat(version)),e.waitUntil(o.delete(self.CACHE_NAME))})),self.addEventListener("install",(function(e){r("SW: installing version: ".concat(version))}))},"./client/src/thirdparty/serviceworker-caches.js":function(e,t){Cache.prototype.add||(Cache.prototype.add=function(e){return this.addAll([e])}),Cache.prototype.addAll||(Cache.prototype.addAll=function(e){var t=this;function n(e){this.name="NetworkError",this.code=19,this.message=e}return n.prototype=Object.create(Error.prototype),Promise.resolve().then((function(){if(arguments.length<1)throw new TypeError;return e=e.map((function(e){return e instanceof Request?e:String(e)})),Promise.all(e.map((function(e){"string"===typeof e&&(e=new Request(e));var t=new URL(e.url).protocol;if("http:"!==t&&"https:"!==t)throw new n("Invalid scheme");return fetch(e.clone())})))})).then((function(n){return Promise.all(n.map((function(n,r){return t.put(e[r],n)})))})).then((function(){}))}),CacheStorage.prototype.match||(CacheStorage.prototype.match=function(e,t){var n=this;return this.keys().then((function(r){var o;return r.reduce((function(r,c){return r.then((function(){return o||n.open(c).then((function(n){return n.match(e,t)})).then((function(e){return o=e}))}))}),Promise.resolve())}))}),e.exports=self.caches}}); \ No newline at end of file +!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/mnt/data/srv/dist/web/nysca/vendor/a2nt/silverstripe-progressivewebapp/client/dist",n(n.s="./client/src/sw.js")}({"./client/src/lib/log.js":function(e,t){e.exports=function(e){debug&&console.log(e)}},"./client/src/sw.js":function(e,t,n){var r=n("./client/src/lib/log.js"),o=n("./client/src/thirdparty/serviceworker-caches.js");if(debug&&(r("SW: debug is on"),r("SW: CACHE_NAME: ".concat(CACHE_NAME)),r("SW: appDomain: ".concat(appDomain)),r("SW: lang: ".concat(lang))),"string"!==typeof self.CACHE_NAME)throw new Error("Cache Name cannot be empty");self.addEventListener("fetch",(function(e){if("GET"===e.request.method){var t=new URL(e.request.url);if(t.pathname.indexOf("admin")>=0||t.pathname.indexOf("Security")>=0||t.pathname.indexOf("dev")>=0)r("SW: skip admin ".concat(e.request.url));else{var n=e.request.clone(),c=e.request.clone();e.respondWith(fetch(n).then((function(t){var n=t.clone();return o.open(self.CACHE_NAME).then((function(t){var r=e.request.clone();t.put(r,n)})),t})).catch((function(e){return r("SW: fetch failed"),o.match(c)})))}}})),self.addEventListener("activate",(function(e){r("SW: activated: ".concat(version)),e.waitUntil(o.delete(self.CACHE_NAME))})),self.addEventListener("install",(function(e){r("SW: installing version: ".concat(version))}))},"./client/src/thirdparty/serviceworker-caches.js":function(e,t){Cache.prototype.add||(Cache.prototype.add=function(e){return this.addAll([e])}),Cache.prototype.addAll||(Cache.prototype.addAll=function(e){var t=this;function n(e){this.name="NetworkError",this.code=19,this.message=e}return n.prototype=Object.create(Error.prototype),Promise.resolve().then((function(){if(arguments.length<1)throw new TypeError;return e=e.map((function(e){return e instanceof Request?e:String(e)})),Promise.all(e.map((function(e){"string"===typeof e&&(e=new Request(e));var t=new URL(e.url).protocol;if("http:"!==t&&"https:"!==t)throw new n("Invalid scheme");return fetch(e.clone())})))})).then((function(n){return Promise.all(n.map((function(n,r){return t.put(e[r],n)})))})).then((function(){}))}),CacheStorage.prototype.match||(CacheStorage.prototype.match=function(e,t){var n=this;return this.keys().then((function(r){var o;return r.reduce((function(r,c){return r.then((function(){return o||n.open(c).then((function(n){return n.match(e,t)})).then((function(e){return o=e}))}))}),Promise.resolve())}))}),e.exports=self.caches}}); \ No newline at end of file diff --git a/client/src/app.js b/client/src/app.js index 8e59360..327313b 100644 --- a/client/src/app.js +++ b/client/src/app.js @@ -1,8 +1,13 @@ +// Register service worker if ('serviceWorker' in navigator) { - var baseHref = (document.getElementsByTagName('base')[0] || {}).href; + const baseHref = (document.getElementsByTagName('base')[0] || {}).href; + const version = (document.querySelector('meta[name="swversion"]') || {}) + .content; if (baseHref) { - navigator.serviceWorker.register(`${baseHref}sw.js`).then(() => { - console.log('Service Worker Registered'); - }); + navigator.serviceWorker + .register(`${baseHref}sw.js?v=${version}`) + .then(() => { + console.log('SW: Registered'); + }); } } diff --git a/client/src/sw.js b/client/src/sw.js index 3204e9e..9d7ce68 100644 --- a/client/src/sw.js +++ b/client/src/sw.js @@ -14,9 +14,40 @@ if (typeof self.CACHE_NAME !== 'string') { } self.addEventListener('fetch', (event) => { + // skip non-get + if (event.request.method !== 'GET') { + return; + } + + //Parse the url + const requestURL = new URL(event.request.url); + + //Check for our own urls + /*if (requestURL.origin !== location.origin) { + log('SW: skip external ' + event.request.url); + return; + }*/ + + //Skip admin url's + if ( + requestURL.pathname.indexOf('admin') >= 0 || + requestURL.pathname.indexOf('Security') >= 0 || + requestURL.pathname.indexOf('dev') >= 0 + ) { + log(`SW: skip admin ${ event.request.url}`); + return; + } + + //Test for images + /*if (/\.(jpg|jpeg|png|gif|webp)$/.test(requestURL.pathname)) { + log('SW: skip image ' + event.request.url); + //For now we skip images but change this later to maybe some caching and/or an offline fallback + return; + }*/ + // Clone the request for fetch and cache // A request is a stream and can be consumed only once. - var fetchRequest = event.request.clone(), + const fetchRequest = event.request.clone(), cacheRequest = event.request.clone(); // Respond with content from fetch or cache @@ -29,13 +60,13 @@ self.addEventListener('fetch', (event) => { // Because we want the browser to consume the response, // as well as cache to consume the response, we need to // clone it so we have 2 streams - var responseToCache = response.clone(); + const responseToCache = response.clone(); // and update the cache caches.open(self.CACHE_NAME).then((cache) => { // Clone the request again to use it // as the key for our cache - var cacheSaveRequest = event.request.clone(); + const cacheSaveRequest = event.request.clone(); cache.put(cacheSaveRequest, responseToCache); }); diff --git a/src/Templates/ServiceWorkerTemplateProvider.php b/src/Templates/ServiceWorkerTemplateProvider.php new file mode 100644 index 0000000..516c99e --- /dev/null +++ b/src/Templates/ServiceWorkerTemplateProvider.php @@ -0,0 +1,23 @@ + 'swVersion', + ]; + } + + public static function swVersion() + { + if(class_exists(ServiceWorkerController::class)) { + return ServiceWorkerController::Version(); + } + } +}