demo: progressive web app
82
demo/pwa/ServiceWorker.lpi
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?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="ServiceWorker"/>
|
||||||
|
<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>
|
113
demo/pwa/ServiceWorker.lpr
Normal file
@ -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.
|
90
demo/pwa/SimplePWA1.lpi
Normal file
@ -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>
|
52
demo/pwa/SimplePWA1.lpr
Normal file
@ -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.
|
84
demo/pwa/www/css/style.css
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
BIN
demo/pwa/www/favicon.ico
Normal file
After Width: | Height: | Size: 130 KiB |
BIN
demo/pwa/www/images/Alpha.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
demo/pwa/www/images/Beta.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
demo/pwa/www/images/Delta.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
demo/pwa/www/images/Epsilon.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
demo/pwa/www/images/Eta.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
demo/pwa/www/images/Gamma.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
demo/pwa/www/images/Iota.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
demo/pwa/www/images/Theta.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
demo/pwa/www/images/Zeta.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
demo/pwa/www/images/error.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
demo/pwa/www/images/icons/icon-128x128.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
demo/pwa/www/images/icons/icon-144x144.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
demo/pwa/www/images/icons/icon-152x152.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
demo/pwa/www/images/icons/icon-16x16.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
demo/pwa/www/images/icons/icon-192x192.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
demo/pwa/www/images/icons/icon-24x24.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
demo/pwa/www/images/icons/icon-256x256.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
demo/pwa/www/images/icons/icon-32x32.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
demo/pwa/www/images/icons/icon-384x384.png
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
demo/pwa/www/images/icons/icon-48x48.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
demo/pwa/www/images/icons/icon-512x512.png
Normal file
After Width: | Height: | Size: 122 KiB |
BIN
demo/pwa/www/images/icons/icon-72x72.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
demo/pwa/www/images/icons/icon-96x96.png
Normal file
After Width: | Height: | Size: 20 KiB |
39
demo/pwa/www/index.html
Normal file
@ -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</title>
|
||||||
|
<meta name="apple-mobile-web-app-status-bar" content="#d04010" />
|
||||||
|
<meta name="theme-color" content="#d04010" />
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" href="images/icons/icon-32x32.png" />
|
||||||
|
<link rel="apple-touch-icon" href="images/icons/icon-48x48.png" />
|
||||||
|
<link rel="apple-touch-icon" href="images/icons/icon-72x72.png" />
|
||||||
|
<link rel="apple-touch-icon" href="images/icons/icon-96x96.png" />
|
||||||
|
<link rel="apple-touch-icon" href="images/icons/icon-128x128.png" />
|
||||||
|
<link rel="apple-touch-icon" href="images/icons/icon-144x144.png" />
|
||||||
|
<link rel="apple-touch-icon" href="images/icons/icon-152x152.png" />
|
||||||
|
<link rel="apple-touch-icon" href="images/icons/icon-192x192.png" />
|
||||||
|
<link rel="apple-touch-icon" href="images/icons/icon-256x256.png" />
|
||||||
|
|
||||||
|
<script src="SimplePWA1.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<nav>
|
||||||
|
<h1>Greek Letters</h1>
|
||||||
|
<ul>
|
||||||
|
<li>Home</li>
|
||||||
|
<li>About</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<div class="container"></div>
|
||||||
|
</main>
|
||||||
|
<script>
|
||||||
|
rtl.run();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
43
demo/pwa/www/manifest.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|