Skip to content

Commit 0aaa932

Browse files
committed
Add PWA support
1 parent 547fc3d commit 0aaa932

14 files changed

+336
-4
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<h3 align="center">KeepAlive</h3>
1616

1717
<p align="center">
18-
A simple website to keep a screen awake and stop a device from sleeping.
18+
A simple website and PWA to keep a screen awake and stop a device from sleeping.
1919
</p>
2020
</div>
2121

android-chrome-192x192.png

1.53 KB
Loading

android-chrome-512x512.png

2.39 KB
Loading

apple-touch-icon.png

1.03 KB
Loading

browserconfig.xml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<browserconfig>
3+
<msapplication>
4+
<tile>
5+
<square150x150logo src="/mstile-150x150.png"/>
6+
<TileColor>#da532c</TileColor>
7+
</tile>
8+
</msapplication>
9+
</browserconfig>

favicon-16x16.png

523 Bytes
Loading

favicon-32x32.png

751 Bytes
Loading

favicon.ico

13.6 KB
Binary file not shown.

index.html

+8-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@
55
<title>KeepAlive</title>
66
<link rel='stylesheet' href='style.css'>
77
<meta name='viewport' content='width=device-width, initial-scale=1'>
8-
<link rel='shortcut icon' href='favicon.ico' type='image/x-icon'>
9-
<link rel='icon' href='favicon.ico' type='image/x-icon'>
8+
9+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
10+
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
11+
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
12+
<link rel="manifest" href="/site.webmanifest">
13+
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
14+
<meta name="msapplication-TileColor" content="#da532c">
15+
<meta name="theme-color" content="#ffffff">
1016
</head>
1117
<body>
1218
<div class='container'>

mstile-150x150.png

1.26 KB
Loading

safari-pinned-tab.svg

+25
Loading

script.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
if ('serviceWorker' in navigator)
2+
navigator.serviceWorker.register('service-worker.js');
3+
14
const html = document.querySelector('html');
25
const container = document.querySelector('.container');
36
const status = container.querySelector('.status');
@@ -53,4 +56,3 @@ document.addEventListener('click', async () => {
5356
});
5457

5558
html.click();
56-

service-worker.js

+268
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
/**
2+
* Copyright 2016 Google Inc. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
// DO NOT EDIT THIS GENERATED OUTPUT DIRECTLY!
18+
// This file should be overwritten as part of your build process.
19+
// If you need to extend the behavior of the generated service worker, the best approach is to write
20+
// additional code and include it using the importScripts option:
21+
// https://github.com/GoogleChrome/sw-precache#importscripts-arraystring
22+
//
23+
// Alternatively, it's possible to make changes to the underlying template file and then use that as the
24+
// new base for generating output, via the templateFilePath option:
25+
// https://github.com/GoogleChrome/sw-precache#templatefilepath-string
26+
//
27+
// If you go that route, make sure that whenever you update your sw-precache dependency, you reconcile any
28+
// changes made to this original template file with your modified copy.
29+
30+
// This generated service worker JavaScript will precache your site's resources.
31+
// The code needs to be saved in a .js file at the top-level of your site, and registered
32+
// from your pages in order to be used. See
33+
// https://github.com/googlechrome/sw-precache/blob/master/demo/app/js/service-worker-registration.js
34+
// for an example of how you can register this script and handle various service worker events.
35+
36+
/* eslint-env worker, serviceworker */
37+
/* eslint-disable indent, no-unused-vars, no-multiple-empty-lines, max-nested-callbacks, space-before-function-paren, quotes, comma-spacing */
38+
'use strict';
39+
40+
var precacheConfig = [["README.md","33e4e964ae2cda332040d94f1ce0c609"],["android-chrome-192x192.png","0cc3d586cc19f6d119bdfa5c657ccb2d"],["android-chrome-512x512.png","70dc3be3b6092676af7521b916ece0de"],["apple-touch-icon.png","26b984fc0d652fcc3762aa14d9e35374"],["browserconfig.xml","a493ba0aa0b8ec8068d786d7248bb92c"],["favicon-16x16.png","123dd170fde94db2d935e9e2b0a7339d"],["favicon-32x32.png","9eeb97ca191484472b879a685c33186a"],["favicon.ico","5bcf6583062eb8f283885568f6a4ceff"],["index.html","ce17cb8d68ede539d00f99886961c491"],["mstile-150x150.png","449d57936cacae98992f32ee74740f41"],["safari-pinned-tab.svg","d88e05e0f1513ade8c070cb88a3adc06"],["script.js","8265d077d332ef5944df9846f9e772ca"],["site.webmanifest","f6c1dfcc1478fae1f150f4a99a3054bd"],["style.css","f1c27a2dba06de52e1b852f9ba7d7812"]];
41+
var cacheName = 'sw-precache-v3-sw-precache-' + (self.registration ? self.registration.scope : '');
42+
43+
44+
var ignoreUrlParametersMatching = [/^utm_/];
45+
46+
47+
48+
var addDirectoryIndex = function(originalUrl, index) {
49+
var url = new URL(originalUrl);
50+
if (url.pathname.slice(-1) === '/') {
51+
url.pathname += index;
52+
}
53+
return url.toString();
54+
};
55+
56+
var cleanResponse = function(originalResponse) {
57+
// If this is not a redirected response, then we don't have to do anything.
58+
if (!originalResponse.redirected) {
59+
return Promise.resolve(originalResponse);
60+
}
61+
62+
// Firefox 50 and below doesn't support the Response.body stream, so we may
63+
// need to read the entire body to memory as a Blob.
64+
var bodyPromise = 'body' in originalResponse ?
65+
Promise.resolve(originalResponse.body) :
66+
originalResponse.blob();
67+
68+
return bodyPromise.then(function(body) {
69+
// new Response() is happy when passed either a stream or a Blob.
70+
return new Response(body, {
71+
headers: originalResponse.headers,
72+
status: originalResponse.status,
73+
statusText: originalResponse.statusText
74+
});
75+
});
76+
};
77+
78+
var createCacheKey = function(originalUrl, paramName, paramValue,
79+
dontCacheBustUrlsMatching) {
80+
// Create a new URL object to avoid modifying originalUrl.
81+
var url = new URL(originalUrl);
82+
83+
// If dontCacheBustUrlsMatching is not set, or if we don't have a match,
84+
// then add in the extra cache-busting URL parameter.
85+
if (!dontCacheBustUrlsMatching ||
86+
!(url.pathname.match(dontCacheBustUrlsMatching))) {
87+
url.search += (url.search ? '&' : '') +
88+
encodeURIComponent(paramName) + '=' + encodeURIComponent(paramValue);
89+
}
90+
91+
return url.toString();
92+
};
93+
94+
var isPathWhitelisted = function(whitelist, absoluteUrlString) {
95+
// If the whitelist is empty, then consider all URLs to be whitelisted.
96+
if (whitelist.length === 0) {
97+
return true;
98+
}
99+
100+
// Otherwise compare each path regex to the path of the URL passed in.
101+
var path = (new URL(absoluteUrlString)).pathname;
102+
return whitelist.some(function(whitelistedPathRegex) {
103+
return path.match(whitelistedPathRegex);
104+
});
105+
};
106+
107+
var stripIgnoredUrlParameters = function(originalUrl,
108+
ignoreUrlParametersMatching) {
109+
var url = new URL(originalUrl);
110+
// Remove the hash; see https://github.com/GoogleChrome/sw-precache/issues/290
111+
url.hash = '';
112+
113+
url.search = url.search.slice(1) // Exclude initial '?'
114+
.split('&') // Split into an array of 'key=value' strings
115+
.map(function(kv) {
116+
return kv.split('='); // Split each 'key=value' string into a [key, value] array
117+
})
118+
.filter(function(kv) {
119+
return ignoreUrlParametersMatching.every(function(ignoredRegex) {
120+
return !ignoredRegex.test(kv[0]); // Return true iff the key doesn't match any of the regexes.
121+
});
122+
})
123+
.map(function(kv) {
124+
return kv.join('='); // Join each [key, value] array into a 'key=value' string
125+
})
126+
.join('&'); // Join the array of 'key=value' strings into a string with '&' in between each
127+
128+
return url.toString();
129+
};
130+
131+
132+
var hashParamName = '_sw-precache';
133+
var urlsToCacheKeys = new Map(
134+
precacheConfig.map(function(item) {
135+
var relativeUrl = item[0];
136+
var hash = item[1];
137+
var absoluteUrl = new URL(relativeUrl, self.location);
138+
var cacheKey = createCacheKey(absoluteUrl, hashParamName, hash, false);
139+
return [absoluteUrl.toString(), cacheKey];
140+
})
141+
);
142+
143+
function setOfCachedUrls(cache) {
144+
return cache.keys().then(function(requests) {
145+
return requests.map(function(request) {
146+
return request.url;
147+
});
148+
}).then(function(urls) {
149+
return new Set(urls);
150+
});
151+
}
152+
153+
self.addEventListener('install', function(event) {
154+
event.waitUntil(
155+
caches.open(cacheName).then(function(cache) {
156+
return setOfCachedUrls(cache).then(function(cachedUrls) {
157+
return Promise.all(
158+
Array.from(urlsToCacheKeys.values()).map(function(cacheKey) {
159+
// If we don't have a key matching url in the cache already, add it.
160+
if (!cachedUrls.has(cacheKey)) {
161+
var request = new Request(cacheKey, {credentials: 'same-origin'});
162+
return fetch(request).then(function(response) {
163+
// Bail out of installation unless we get back a 200 OK for
164+
// every request.
165+
if (!response.ok) {
166+
throw new Error('Request for ' + cacheKey + ' returned a ' +
167+
'response with status ' + response.status);
168+
}
169+
170+
return cleanResponse(response).then(function(responseToCache) {
171+
return cache.put(cacheKey, responseToCache);
172+
});
173+
});
174+
}
175+
})
176+
);
177+
});
178+
}).then(function() {
179+
180+
// Force the SW to transition from installing -> active state
181+
return self.skipWaiting();
182+
183+
})
184+
);
185+
});
186+
187+
self.addEventListener('activate', function(event) {
188+
var setOfExpectedUrls = new Set(urlsToCacheKeys.values());
189+
190+
event.waitUntil(
191+
caches.open(cacheName).then(function(cache) {
192+
return cache.keys().then(function(existingRequests) {
193+
return Promise.all(
194+
existingRequests.map(function(existingRequest) {
195+
if (!setOfExpectedUrls.has(existingRequest.url)) {
196+
return cache.delete(existingRequest);
197+
}
198+
})
199+
);
200+
});
201+
}).then(function() {
202+
203+
return self.clients.claim();
204+
205+
})
206+
);
207+
});
208+
209+
210+
self.addEventListener('fetch', function(event) {
211+
if (event.request.method === 'GET') {
212+
// Should we call event.respondWith() inside this fetch event handler?
213+
// This needs to be determined synchronously, which will give other fetch
214+
// handlers a chance to handle the request if need be.
215+
var shouldRespond;
216+
217+
// First, remove all the ignored parameters and hash fragment, and see if we
218+
// have that URL in our cache. If so, great! shouldRespond will be true.
219+
var url = stripIgnoredUrlParameters(event.request.url, ignoreUrlParametersMatching);
220+
shouldRespond = urlsToCacheKeys.has(url);
221+
222+
// If shouldRespond is false, check again, this time with 'index.html'
223+
// (or whatever the directoryIndex option is set to) at the end.
224+
var directoryIndex = 'index.html';
225+
if (!shouldRespond && directoryIndex) {
226+
url = addDirectoryIndex(url, directoryIndex);
227+
shouldRespond = urlsToCacheKeys.has(url);
228+
}
229+
230+
// If shouldRespond is still false, check to see if this is a navigation
231+
// request, and if so, whether the URL matches navigateFallbackWhitelist.
232+
var navigateFallback = '';
233+
if (!shouldRespond &&
234+
navigateFallback &&
235+
(event.request.mode === 'navigate') &&
236+
isPathWhitelisted([], event.request.url)) {
237+
url = new URL(navigateFallback, self.location).toString();
238+
shouldRespond = urlsToCacheKeys.has(url);
239+
}
240+
241+
// If shouldRespond was set to true at any point, then call
242+
// event.respondWith(), using the appropriate cache key.
243+
if (shouldRespond) {
244+
event.respondWith(
245+
caches.open(cacheName).then(function(cache) {
246+
return cache.match(urlsToCacheKeys.get(url)).then(function(response) {
247+
if (response) {
248+
return response;
249+
}
250+
throw Error('The cached response that was expected is missing.');
251+
});
252+
}).catch(function(e) {
253+
// Fall back to just fetch()ing the request if some unexpected error
254+
// prevented the cached response from being valid.
255+
console.warn('Couldn\'t serve response for "%s" from cache: %O', event.request.url, e);
256+
return fetch(event.request);
257+
})
258+
);
259+
}
260+
}
261+
});
262+
263+
264+
265+
266+
267+
268+

site.webmanifest

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "Keepalive",
3+
"short_name": "ka",
4+
"start_url": "index.html",
5+
"scope": "/",
6+
"icons": [
7+
{
8+
"src": "/android-chrome-192x192.png",
9+
"sizes": "192x192",
10+
"type": "image/png"
11+
},
12+
{
13+
"src": "/android-chrome-512x512.png",
14+
"sizes": "512x512",
15+
"type": "image/png",
16+
"purpose": "any"
17+
}
18+
],
19+
"theme_color": "#ffffff",
20+
"background_color": "#ffffff",
21+
"display": "standalone"
22+
}

0 commit comments

Comments
 (0)