diff --git a/demo/pwa/ServiceWorker.lpi b/demo/pwa/ServiceWorker.lpi
new file mode 100644
index 0000000..e1cbbc2
--- /dev/null
+++ b/demo/pwa/ServiceWorker.lpi
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
diff --git a/demo/pwa/ServiceWorker.lpr b/demo/pwa/ServiceWorker.lpr
new file mode 100644
index 0000000..79489dc
--- /dev/null
+++ b/demo/pwa/ServiceWorker.lpr
@@ -0,0 +1,113 @@
+program ServiceWorker;
+
+{$mode objfpc}
+
+uses
+ JS, Web;
+
+const
+ CacheName = 'v4';
+
+ FallbackURL = '/images/error.png';
+
+ Resources: array[0..12] of string = (
+ '/index.html',
+ '/css/style.css',
+ '/SimplePWA1.js',
+ '/images/Alpha.png',
+ '/images/Beta.png',
+ '/images/Gamma.png',
+ '/images/Delta.png',
+ '/images/Epsilon.png',
+ '/images/Zeta.png',
+ '/images/Eta.png',
+ '/images/Theta.png',
+ '/images/Iota.png',
+ '/images/error.png'
+ );
+
+procedure PutInCache(Request: TJSRequest; Response: TJSResponse); async;
+var
+ Cache: TJSCache;
+begin
+ Cache := await(TJSCache,Caches.open(CacheName));
+ await(TJSCache,Cache.put(Request, Response));
+end;
+
+function CacheFirst(Request: TJSRequest; PreloadResponsePromise: TJSPromise;
+ FallbackUrl: string): jsvalue; async;
+var
+ ResponseFromCache, PreloadResponse, ResponseFromNetwork, FallbackResponse: TJSResponse;
+begin
+ Result:=nil;
+
+ // First try to get the resource from the cache
+ ResponseFromCache := await(TJSResponse,caches.match(Request));
+ if Assigned(ResponseFromCache) then
+ exit(ResponseFromCache);
+
+ // Next try to use (and cache) the preloaded response, if it's there
+ PreloadResponse := await(TJSResponse,PreloadResponsePromise);
+ if Assigned(PreloadResponse) then
+ begin
+ console.info('using preload response: '+String(JSValue(PreloadResponse)));
+ putInCache(Request, PreloadResponse.clone());
+ exit(PreloadResponse);
+ end;
+
+ // Next try to get the resource from the network
+ try
+ ResponseFromNetwork := await(TJSResponse,window.fetch(Request));
+ // response may be used only once
+ // we need to save clone to put one copy in cache
+ // and serve second one
+ PutInCache(Request, ResponseFromNetwork.clone());
+ exit(ResponseFromNetwork);
+ except
+ FallbackResponse := await(TJSResponse,caches.match(FallbackUrl));
+ if Assigned(FallbackResponse) then
+ exit(FallbackResponse);
+
+ // when even the fallback response is not available,
+ // there is nothing we can do, but we must always
+ // return a Response object
+ Result:=TJSResponse.new('Network error happened', js.new([
+ 'status', 408,
+ 'headers',
+ js.new(['Content-Type', 'text/plain' ])
+ ]) );
+ end;
+end;
+
+// Enable navigation preload
+function EnableNavigationPreload: jsvalue; async;
+begin
+ Result:=nil;
+ if jsvalue(serviceWorker.registration.navigationPreload) then
+ // Enable navigation preloads!
+ await(serviceWorker.registration.navigationPreload.enable());
+end;
+
+begin
+ serviceWorker.addEventListener('activate', procedure(Event: TJSExtendableEvent)
+ begin
+ Event.waitUntil(EnableNavigationPreload());
+ end);
+
+ ServiceWorker.addEventListener('install', procedure(Event: TJSExtendableEvent)
+ begin
+ Event.waitUntil(
+ Caches.Open(CacheName)._then(
+ TJSPromiseResolver(procedure(Cache: TJSCache)
+ begin
+ Cache.addAll(Resources);
+ end))
+ );
+ end);
+
+ ServiceWorker.addEventListener('fetch', procedure(FetchEvent: TJSFetchEvent)
+ begin
+ FetchEvent.RespondWith(CacheFirst(FetchEvent.request,
+ FetchEvent.PreloadResponse,FallbackURL) );
+ end);
+end.
diff --git a/demo/pwa/SimplePWA1.lpi b/demo/pwa/SimplePWA1.lpi
new file mode 100644
index 0000000..c155a44
--- /dev/null
+++ b/demo/pwa/SimplePWA1.lpi
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
diff --git a/demo/pwa/SimplePWA1.lpr b/demo/pwa/SimplePWA1.lpr
new file mode 100644
index 0000000..f1d9493
--- /dev/null
+++ b/demo/pwa/SimplePWA1.lpr
@@ -0,0 +1,52 @@
+program SimplePWA1;
+
+{$mode objfpc}
+
+uses
+ JS, Classes, SysUtils, Web;
+
+const
+ GreekLetters: array[1..9] of string = (
+ 'Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon', 'Zeta', 'Eta', 'Theta', 'Iota'
+ );
+
+procedure ShowLetters;
+var
+ h, Letter: String;
+ container: TJSElement;
+begin
+ h:='';
+ for Letter in GreekLetters do
+ begin
+ h:=h+'
'#10
+ +'

'#10
+ +'
'+Letter+'
'#10
+ +'
Click'#10
+ +'
'#10;
+ end;
+ container:=document.querySelector('.container');
+ container.innerHTML := h;
+end;
+
+begin
+ // Your code here
+ document.addEventListener('DOMContentLoaded', @ShowLetters);
+
+ // register service worker
+ if IsServiceWorker then
+ Window.addEventListener('load',
+ procedure()
+ begin
+ Window.navigator.serviceWorker
+ .register('/ServiceWorker.js')
+ ._then(TJSPromiseResolver(procedure(Registration: TJSServiceWorkerRegistration)
+ begin
+ console.log('service worker registered');
+ if IsDefined(Registration.installing) then ;
+ end))
+ .catch(TJSPromiseResolver(procedure(err: JSValue)
+ begin
+ console.log('service worker not registered: '+String(err));
+ end));
+ end);
+end.
diff --git a/demo/pwa/www/css/style.css b/demo/pwa/www/css/style.css
new file mode 100644
index 0000000..2353d6a
--- /dev/null
+++ b/demo/pwa/www/css/style.css
@@ -0,0 +1,84 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ background: #f0f0f0;
+ font-family: "Arial";
+ font-size: 1rem;
+}
+
+main {
+ max-width: 800px;
+ margin: auto;
+ padding: 0.5rem;
+ text-align: center;
+}
+
+nav {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+ul {
+ list-style: none;
+ display: flex;
+}
+
+li {
+ margin-right: 1rem;
+}
+
+h1 {
+ color: #e04030;
+ margin-bottom: 0.5rem;
+}
+
+.container {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
+ grid-gap: 1rem;
+ justify-content: center;
+ align-items: center;
+ margin: auto;
+ padding: 1rem 0;
+}
+
+.card {
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ width: 16rem auto;
+ height: 18rem;
+ background: #fff;
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
+ border-radius: 10px;
+ margin: auto;
+ overflow: hidden;
+}
+
+.card--image {
+ width: 100%;
+ height: 10rem;
+ object-fit: cover;
+}
+
+.card--title {
+ color: #222;
+ font-weight: 700;
+ text-transform: capitalize;
+ font-size: 1.1rem;
+ margin-top: 0.5rem;
+}
+
+.card--link {
+ text-decoration: none;
+ background: #d04030;
+ color: #fff;
+ padding: 0.3rem 1rem;
+ border-radius: 20px;
+}
+
diff --git a/demo/pwa/www/favicon.ico b/demo/pwa/www/favicon.ico
new file mode 100644
index 0000000..10c5fc1
Binary files /dev/null and b/demo/pwa/www/favicon.ico differ
diff --git a/demo/pwa/www/images/Alpha.png b/demo/pwa/www/images/Alpha.png
new file mode 100644
index 0000000..460ae7b
Binary files /dev/null and b/demo/pwa/www/images/Alpha.png differ
diff --git a/demo/pwa/www/images/Beta.png b/demo/pwa/www/images/Beta.png
new file mode 100644
index 0000000..6ee35e9
Binary files /dev/null and b/demo/pwa/www/images/Beta.png differ
diff --git a/demo/pwa/www/images/Delta.png b/demo/pwa/www/images/Delta.png
new file mode 100644
index 0000000..c4e9070
Binary files /dev/null and b/demo/pwa/www/images/Delta.png differ
diff --git a/demo/pwa/www/images/Epsilon.png b/demo/pwa/www/images/Epsilon.png
new file mode 100644
index 0000000..4882928
Binary files /dev/null and b/demo/pwa/www/images/Epsilon.png differ
diff --git a/demo/pwa/www/images/Eta.png b/demo/pwa/www/images/Eta.png
new file mode 100644
index 0000000..2dee86b
Binary files /dev/null and b/demo/pwa/www/images/Eta.png differ
diff --git a/demo/pwa/www/images/Gamma.png b/demo/pwa/www/images/Gamma.png
new file mode 100644
index 0000000..585e1b6
Binary files /dev/null and b/demo/pwa/www/images/Gamma.png differ
diff --git a/demo/pwa/www/images/Iota.png b/demo/pwa/www/images/Iota.png
new file mode 100644
index 0000000..ddca7f3
Binary files /dev/null and b/demo/pwa/www/images/Iota.png differ
diff --git a/demo/pwa/www/images/Theta.png b/demo/pwa/www/images/Theta.png
new file mode 100644
index 0000000..e5c516b
Binary files /dev/null and b/demo/pwa/www/images/Theta.png differ
diff --git a/demo/pwa/www/images/Zeta.png b/demo/pwa/www/images/Zeta.png
new file mode 100644
index 0000000..efa199a
Binary files /dev/null and b/demo/pwa/www/images/Zeta.png differ
diff --git a/demo/pwa/www/images/error.png b/demo/pwa/www/images/error.png
new file mode 100644
index 0000000..7339fe0
Binary files /dev/null and b/demo/pwa/www/images/error.png differ
diff --git a/demo/pwa/www/images/icons/icon-128x128.png b/demo/pwa/www/images/icons/icon-128x128.png
new file mode 100644
index 0000000..87338e1
Binary files /dev/null and b/demo/pwa/www/images/icons/icon-128x128.png differ
diff --git a/demo/pwa/www/images/icons/icon-144x144.png b/demo/pwa/www/images/icons/icon-144x144.png
new file mode 100644
index 0000000..557d256
Binary files /dev/null and b/demo/pwa/www/images/icons/icon-144x144.png differ
diff --git a/demo/pwa/www/images/icons/icon-152x152.png b/demo/pwa/www/images/icons/icon-152x152.png
new file mode 100644
index 0000000..c981e96
Binary files /dev/null and b/demo/pwa/www/images/icons/icon-152x152.png differ
diff --git a/demo/pwa/www/images/icons/icon-16x16.png b/demo/pwa/www/images/icons/icon-16x16.png
new file mode 100644
index 0000000..8c89d02
Binary files /dev/null and b/demo/pwa/www/images/icons/icon-16x16.png differ
diff --git a/demo/pwa/www/images/icons/icon-192x192.png b/demo/pwa/www/images/icons/icon-192x192.png
new file mode 100644
index 0000000..60881e8
Binary files /dev/null and b/demo/pwa/www/images/icons/icon-192x192.png differ
diff --git a/demo/pwa/www/images/icons/icon-24x24.png b/demo/pwa/www/images/icons/icon-24x24.png
new file mode 100644
index 0000000..71252ea
Binary files /dev/null and b/demo/pwa/www/images/icons/icon-24x24.png differ
diff --git a/demo/pwa/www/images/icons/icon-256x256.png b/demo/pwa/www/images/icons/icon-256x256.png
new file mode 100644
index 0000000..b0cb6b4
Binary files /dev/null and b/demo/pwa/www/images/icons/icon-256x256.png differ
diff --git a/demo/pwa/www/images/icons/icon-32x32.png b/demo/pwa/www/images/icons/icon-32x32.png
new file mode 100644
index 0000000..f4dc503
Binary files /dev/null and b/demo/pwa/www/images/icons/icon-32x32.png differ
diff --git a/demo/pwa/www/images/icons/icon-384x384.png b/demo/pwa/www/images/icons/icon-384x384.png
new file mode 100644
index 0000000..283ef14
Binary files /dev/null and b/demo/pwa/www/images/icons/icon-384x384.png differ
diff --git a/demo/pwa/www/images/icons/icon-48x48.png b/demo/pwa/www/images/icons/icon-48x48.png
new file mode 100644
index 0000000..7d877b1
Binary files /dev/null and b/demo/pwa/www/images/icons/icon-48x48.png differ
diff --git a/demo/pwa/www/images/icons/icon-512x512.png b/demo/pwa/www/images/icons/icon-512x512.png
new file mode 100644
index 0000000..f466ad5
Binary files /dev/null and b/demo/pwa/www/images/icons/icon-512x512.png differ
diff --git a/demo/pwa/www/images/icons/icon-72x72.png b/demo/pwa/www/images/icons/icon-72x72.png
new file mode 100644
index 0000000..fdd168a
Binary files /dev/null and b/demo/pwa/www/images/icons/icon-72x72.png differ
diff --git a/demo/pwa/www/images/icons/icon-96x96.png b/demo/pwa/www/images/icons/icon-96x96.png
new file mode 100644
index 0000000..28b5abc
Binary files /dev/null and b/demo/pwa/www/images/icons/icon-96x96.png differ
diff --git a/demo/pwa/www/index.html b/demo/pwa/www/index.html
new file mode 100644
index 0000000..5a57f74
--- /dev/null
+++ b/demo/pwa/www/index.html
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+ Simple Progressive Web App
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/pwa/www/manifest.json b/demo/pwa/www/manifest.json
new file mode 100644
index 0000000..e3dfcc8
--- /dev/null
+++ b/demo/pwa/www/manifest.json
@@ -0,0 +1,43 @@
+{
+ "name": "Greek Letters",
+ "short_name": "GreekLetters",
+ "start_url": "index.html",
+ "display": "standalone",
+ "background_color": "#f0f0f0",
+ "theme_color": "#d04030",
+ "orientation": "portrait-primary",
+ "icons": [
+ {
+ "src": "/images/icons/icon-72x72.png",
+ "type": "image/png", "sizes": "72x72"
+ },
+ {
+ "src": "/images/icons/icon-96x96.png",
+ "type": "image/png", "sizes": "96x96"
+ },
+ {
+ "src": "/images/icons/icon-128x128.png",
+ "type": "image/png","sizes": "128x128"
+ },
+ {
+ "src": "/images/icons/icon-144x144.png",
+ "type": "image/png", "sizes": "144x144"
+ },
+ {
+ "src": "/images/icons/icon-152x152.png",
+ "type": "image/png", "sizes": "152x152"
+ },
+ {
+ "src": "/images/icons/icon-192x192.png",
+ "type": "image/png", "sizes": "192x192"
+ },
+ {
+ "src": "/images/icons/icon-384x384.png",
+ "type": "image/png", "sizes": "384x384"
+ },
+ {
+ "src": "/images/icons/icon-512x512.png",
+ "type": "image/png", "sizes": "512x512"
+ }
+ ]
+}