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 @@ + + + + + + + + + + + + + + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <CustomData Count="3"> + <Item0 Name="Pas2JSProject" Value="1"/> + <Item1 Name="PasJSPort" Value="3001"/> + <Item2 Name="PasJSWebBrowserProject" Value="1"/> + </CustomData> + <BuildModes> + <Item Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + <UseFileFilters Value="True"/> + </PublishOptions> + <RunParams> + <FormatVersion Value="2"/> + </RunParams> + <Units> + <Unit> + <Filename Value="ServiceWorker.lpr"/> + <IsPartOfProject Value="True"/> + </Unit> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <Target FileExt=".js"> + <Filename Value="www/ServiceWorker.js"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="js"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <AllowLabel Value="False"/> + <CPPInline Value="False"/> + <UseAnsiStrings Value="False"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <TargetOS Value="browser"/> + </CodeGeneration> + <Linking> + <Debugging> + <GenerateDebugInfo Value="False"/> + <UseLineInfoUnit Value="False"/> + </Debugging> + </Linking> + <Other> + <CustomOptions Value="-Jeutf-8 -Jirtl.js -Jc -Jminclude"/> + <CompilerPath Value="$(pas2js)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions> + <Item> + <Name Value="EAbort"/> + </Item> + <Item> + <Name Value="ECodetoolError"/> + </Item> + <Item> + <Name Value="EFOpenError"/> + </Item> + </Exceptions> + </Debugging> +</CONFIG> 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="12"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + <MainUnitHasScaledStatement Value="False"/> + <Runnable Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <Title Value="SimplePWA1"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <CustomData Count="4"> + <Item0 Name="MaintainHTML" Value="1"/> + <Item1 Name="Pas2JSProject" Value="1"/> + <Item2 Name="PasJSPort" Value="3001"/> + <Item3 Name="PasJSWebBrowserProject" Value="1"/> + </CustomData> + <BuildModes> + <Item Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + <UseFileFilters Value="True"/> + </PublishOptions> + <RunParams> + <FormatVersion Value="2"/> + </RunParams> + <Units> + <Unit> + <Filename Value="SimplePWA1.lpr"/> + <IsPartOfProject Value="True"/> + </Unit> + <Unit> + <Filename Value="www/index.html"/> + <IsPartOfProject Value="True"/> + <CustomData Count="1"> + <Item0 Name="PasJSIsProjectHTMLFile" Value="1"/> + </CustomData> + </Unit> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <Target> + <Filename Value="www/SimplePWA1.js"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="js"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <AllowLabel Value="False"/> + <CPPInline Value="False"/> + <UseAnsiStrings Value="False"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <TargetOS Value="browser"/> + </CodeGeneration> + <Linking> + <Debugging> + <GenerateDebugInfo Value="False"/> + <UseLineInfoUnit Value="False"/> + </Debugging> + </Linking> + <Other> + <CustomOptions Value="-Jeutf-8 -Jirtl.js -Jc -Jminclude"/> + <CompilerPath Value="$(pas2js)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions> + <Item> + <Name Value="EAbort"/> + </Item> + <Item> + <Name Value="ECodetoolError"/> + </Item> + <Item> + <Name Value="EFOpenError"/> + </Item> + </Exceptions> + </Debugging> +</CONFIG> 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+'<div class="card">'#10 + +' <img class="card--image" src="/images/'+Letter+'.png"/>'#10 + +' <h1 class="card--title">'+Letter+'</h1>'#10 + +' <a class="card--link" href="#">Click</a>'#10 + +'</div>'#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 @@ +<!doctype html> +<html lang="en"> +<head> + <meta http-equiv="Content-type" content="text/html; charset=utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel="stylesheet" href="css/style.css" /> + <link rel="manifest" href="manifest.json"> + <title>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" + } + ] +}