diff --git a/index.html b/index.html
index 355f2a809..a8042c894 100644
--- a/index.html
+++ b/index.html
@@ -4,13 +4,13 @@
Hextris
-
+
-
+
@@ -18,10 +18,10 @@
-
+
-
+
@@ -31,8 +31,9 @@
+
-
+
diff --git a/js/assets.json b/js/assets.json
new file mode 100644
index 000000000..4eae4f85b
--- /dev/null
+++ b/js/assets.json
@@ -0,0 +1,31 @@
+{
+"cache":
+ [
+ "index.html",
+ "vendor/hammer.min.js",
+ "vendor/js.cookie.js",
+ "vendor/jsonfn.min.js",
+ "vendor/keypress.min.js",
+ "vendor/jquery.js",
+ "js/save-state.js",
+ "js/view.js",
+ "js/wavegen.js",
+ "js/math.js",
+ "js/Block.js",
+ "js/Hex.js",
+ "js/Text.js",
+ "js/comboTimer.js",
+ "js/checking.js",
+ "js/update.js",
+ "js/render.js",
+ "js/input.js",
+ "js/main.js",
+ "js/initialization.js",
+ "https://fonts.googleapis.com/css?family=Exo+2",
+ "style/fa/css/font-awesome.min.css",
+ "style/style.css",
+ "style/rrssb.css",
+ "vendor/rrssb.min.js",
+ "vendor/sweet-alert.min.js"
+ ]
+}
\ No newline at end of file
diff --git a/js/main.js b/js/main.js
index 90101759b..9336755fa 100644
--- a/js/main.js
+++ b/js/main.js
@@ -374,3 +374,68 @@ function showHelp() {
$("#openSideBar").fadeIn(150,"linear");
$('#helpScreen').fadeToggle(150, "linear");
}
+
+// ask browser if it supports service workers, if it does, register it.
+if ('serviceWorker' in navigator) {
+ window.addEventListener('load', function() {
+ navigator.serviceWorker.register('./sw.js').then(function(reg) {
+ console.log('SW registered successfully');
+
+ if(!navigator.serviceWorker.controller) {
+ return;
+ }
+
+ if(reg.waiting) {
+ notifySWUpdates(reg.waiting);
+ }
+
+ if(reg.installing) {
+ trackSWStates(reg.installing);
+ }
+
+ reg.addEventListener('updatefound', function() {
+ trackSWStates(reg.installing);
+ })
+
+ var reloading;
+ navigator.serviceWorker.addEventListener('controllerchange', function() {
+ if(reloading) return;
+ window.location.reload();
+ reloading = true;
+ });
+
+ }, function(err) {
+ console.log('SW registration failed');
+ });
+
+ });
+}
+
+// notify user of game update available,
+// when user clicks button service worker updates with new cache and page reloads.
+function notifySWUpdates(reg) {
+ console.log('There is a new Service Worker available');
+ let SW_Button = document.createElement('button');
+ SW_Button.innerHTML = 'Update Available';
+ let doc_body = document.getElementsByTagName('body')[0];
+ doc_body.appendChild(SW_Button);
+ SW_Button.style.backgroundColor = 'blue';
+ SW_Button.style.color = '#fff';
+ SW_Button.style.position = 'fixed';
+ SW_Button.style.bottom = '0px';
+ SW_Button.style.right = '0px';
+ SW_Button.style.padding = '5px 10px';
+ SW_Button.addEventListener('click', function() {
+ console.log(reg);
+ reg.postMessage({activate: 'true'});
+ });
+}
+
+// tracks service worker state and activates notification function when installed.
+function trackSWStates(reg) {
+ reg.addEventListener('statechange', function() {
+ if(this.state == 'installed') {
+ notifySWUpdates(reg);
+ }
+ });
+}
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 000000000..1b14630b6
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,11 @@
+{
+ "background_color": "#ecf0f1",
+ "display": "standalone",
+ "icons": [],
+ "name": "Hextris",
+ "short_name": "Hextris",
+ "description": "Fast paced HTML5 puzzle game inspired by Tetris!",
+ "start_url": "/",
+ "scope": "/",
+ "theme_color": "#ecf0f1"
+}
\ No newline at end of file
diff --git a/sw.js b/sw.js
new file mode 100644
index 000000000..686f09ef0
--- /dev/null
+++ b/sw.js
@@ -0,0 +1,77 @@
+var CACHE_NAME = 'hextris-v3';
+
+// install service worker on first install and load cache assets.
+self.addEventListener('install', function(event) {
+ event.waitUntil(
+ caches.open(CACHE_NAME).then(function(cache) {
+ fetch('js/assets.json').then(function(response) {
+ return response.json();
+ }).catch(function(err) {
+ console.log('json fetch err:', err);
+ }).then(function(assetManifest) {
+ let cacheFiles = assetManifest.cache;
+ cache.addAll(cacheFiles);
+ }).catch(function(err) {
+ console.log('problem adding to cache:', err);
+ })
+ })
+ );
+});
+
+// respond to fetch requests by browser using service worker.
+self.addEventListener('fetch', function(event) {
+ const requestUrl = new URL(event.request.url);
+
+ if (requestUrl.origin === location.origin) {
+ event.respondWith(getNetworkResponse(event.request));
+ return;
+ }
+
+ event.respondWith(fetch(event.request));
+});
+
+// If asset exists in cache, respond with cache,
+// otherwise respond from network after putting asset in cache.
+function getNetworkResponse(request) {
+ return caches.open(CACHE_NAME).then(cache => {
+ return cache.match(request).then(response => {
+ if (response) return response;
+
+ if(request.cache === 'only-if-cached') {
+ return fetch(request, {mode: "same-origin"}).then(networkResponse => {
+ cache.put(request, networkResponse.clone());
+ return networkResponse;
+ })
+ } else {
+ return fetch(request).then(networkResponse => {
+ cache.put(request, networkResponse.clone());
+ return networkResponse;
+ })
+ }
+ });
+ });
+};
+
+// Delete any unused old caches when a new service worker is activated
+self.addEventListener('activate', function(event) {
+ event.waitUntil(
+ Promise.all([
+ clients.claim(),
+ caches.keys().then(function(cacheNames) {
+ cacheNames.filter(function(cacheName) {
+ if(cacheName.startsWith('hextris') && cacheName !== CACHE_NAME) {
+ caches.delete(cacheName);
+ }
+ })
+ })
+ ]
+ )
+ )
+});
+
+// listen to messages and skip waiting state of service worker
+// this basically activates service worker when notification recieved.
+self.addEventListener('message', function(event) {
+ if(event.data.activate == 'true');
+ self.skipWaiting();
+});
\ No newline at end of file