many small client improvements
This commit is contained in:
		
							
								
								
									
										15
									
								
								js_client/app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								js_client/app.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| window.addEventListener('load', e => { | ||||
|     registerSW(); | ||||
| }); | ||||
|  | ||||
| async function registerSW() { | ||||
|     if ('serviceWorker' in navigator) { | ||||
|         try { | ||||
|             await navigator.serviceWorker.register('./sw.js'); | ||||
|         } catch (e) { | ||||
|             alert('ServiceWorker registration failed. Sorry about that.'); | ||||
|         } | ||||
|     } else { | ||||
|         document.querySelector('.alert').removeAttribute('hidden'); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								js_client/big_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								js_client/big_icon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 9.5 KiB | 
| @ -2,7 +2,7 @@ export function setCookie(cname, cvalue, exdays) { | ||||
|     var d = new Date(); | ||||
|     d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); | ||||
|     var expires = "expires=" + d.toUTCString(); | ||||
|     document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; | ||||
|     document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/; SameSite=Strict"; | ||||
| } | ||||
|  | ||||
| export function getCookie(cname) { | ||||
|  | ||||
| @ -2,39 +2,46 @@ | ||||
| <html> | ||||
|  | ||||
| <head> | ||||
|     <!-- Polyfills only needed for Firefox and Edge. --> | ||||
|     <script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script> | ||||
|     <!-- Works only on browsers that support Javascript modules like | ||||
|   <link rel="icon" type="image/png" href="./favicon.png" /> | ||||
|   <link rel="manifest" href="./manifest.json"> | ||||
|   <script type="module" src="./app.js"></script> | ||||
|   <script type="module" src="./sw.js"></script> | ||||
|  | ||||
|   <!-- Polyfills only needed for Firefox and Edge. --> | ||||
|   <script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script> | ||||
|   <!-- Works only on browsers that support Javascript modules like | ||||
|        Chrome, Safari, Firefox 60, Edge 17 --> | ||||
|  | ||||
|     <style> | ||||
|         body { | ||||
|             background-color: black; | ||||
|             font-size: 1.5vh; | ||||
|         } | ||||
|   <style> | ||||
|     body { | ||||
|       background-color: black; | ||||
|        | ||||
|       font-size: 1.5vh; | ||||
|     } | ||||
|  | ||||
|         @media (max-aspect-ratio: 3/2) { | ||||
|             body { | ||||
|                 font-size: 1.5vh; | ||||
|             } | ||||
|         } | ||||
|     </style> | ||||
|     // Original viewport definition, note the "id" that I use to modify the viewport later on: | ||||
|     <meta name="viewport" id="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/> | ||||
|      | ||||
|     <script> | ||||
|     @media (max-aspect-ratio: 3/2) { | ||||
|       body { | ||||
|         font-size: 1.5vh; | ||||
|       } | ||||
|     } | ||||
|   </style> | ||||
|   // Original viewport definition, note the "id" that I use to modify the viewport later on: | ||||
|   <meta name="viewport" id="viewport" | ||||
|     content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" /> | ||||
|  | ||||
|   <script> | ||||
|     // Global boolean variable that holds the current orientation | ||||
|     var pageInPortraintMode; | ||||
|      | ||||
|  | ||||
|     // Listen for window resizes to detect orientation changes | ||||
|     window.addEventListener("resize", windowSizeChanged); | ||||
|      | ||||
|  | ||||
|     // Set the global orientation variable as soon as the page loads | ||||
|     addEventListener("load", function() { | ||||
|     addEventListener("load", function () { | ||||
|       pageInPortraintMode = window.innerHeight > window.innerWidth; | ||||
|       document.getElementById("viewport").setAttribute("content", "width=" + window.innerWidth + ", height=" + window.innerHeight + ", initial-scale=1.0, maximum-scale=1.0, user-scalable=0"); | ||||
|     }) | ||||
|      | ||||
|  | ||||
|     // Adjust viewport values only if orientation has changed (not on every window resize) | ||||
|     function windowSizeChanged() { | ||||
|       if (((pageInPortraintMode === true) && (window.innerHeight < window.innerWidth)) || ((pageInPortraintMode === false) && (window.innerHeight > window.innerWidth))) { | ||||
| @ -42,15 +49,16 @@ | ||||
|         document.getElementById("viewport").setAttribute("content", "width=" + window.innerWidth + ", height=" + window.innerHeight + ", initial-scale=1.0, maximum-scale=1.0, user-scalable=0"); | ||||
|       } | ||||
|     } | ||||
|     </script> | ||||
|      | ||||
|   </script> | ||||
|  | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|     <info-box id="infobox"></info-box> | ||||
|     <server-connection id="server-connection" lang="en" grid_id="grid" url="wss://the-cake-is-a-lie.net:8765"></server-connection> | ||||
|     <crossword-grid id="grid" infobox_id="infobox" width="10" height="10"></crossword-grid> | ||||
|     <script type="module" src="./main.js"></script> | ||||
|   <info-box id="infobox"></info-box> | ||||
|   <server-connection id="server-connection" lang="en" grid_id="grid" url="wss://the-cake-is-a-lie.net:8765"> | ||||
|   </server-connection> | ||||
|   <crossword-grid id="grid" infobox_id="infobox" width="10" height="10"></crossword-grid> | ||||
|   <script type="module" src="./main.js"></script> | ||||
|  | ||||
| </body> | ||||
|  | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								js_client/favicon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								js_client/favicon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.3 KiB | 
| @ -2,39 +2,44 @@ | ||||
| <html> | ||||
|  | ||||
| <head> | ||||
|     <!-- Polyfills only needed for Firefox and Edge. --> | ||||
|     <script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script> | ||||
|     <!-- Works only on browsers that support Javascript modules like | ||||
|   <link rel="icon" type="image/png" href="./favicon.png" /> | ||||
|   <link rel="manifest" href="./manifest.json"> | ||||
|   <script type="module" src="./app.js"></script> | ||||
|   <script type="module" src="./sw.js"></script> | ||||
|   <!-- Polyfills only needed for Firefox and Edge. --> | ||||
|   <script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script> | ||||
|   <!-- Works only on browsers that support Javascript modules like | ||||
|        Chrome, Safari, Firefox 60, Edge 17 --> | ||||
|  | ||||
|     <style> | ||||
|         body { | ||||
|             background-color: black; | ||||
|             font-size: 1.5vh; | ||||
|         } | ||||
|   <style> | ||||
|     body { | ||||
|       background-color: black; | ||||
|       font-size: 1.5vh; | ||||
|     } | ||||
|  | ||||
|         @media (max-aspect-ratio: 3/2) { | ||||
|             body { | ||||
|                 font-size: 1.5vh; | ||||
|             } | ||||
|         } | ||||
|     </style> | ||||
|     // Original viewport definition, note the "id" that I use to modify the viewport later on: | ||||
|     <meta name="viewport" id="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/> | ||||
|      | ||||
|     <script> | ||||
|     @media (max-aspect-ratio: 3/2) { | ||||
|       body { | ||||
|         font-size: 1.5vh; | ||||
|       } | ||||
|     } | ||||
|   </style> | ||||
|   // Original viewport definition, note the "id" that I use to modify the viewport later on: | ||||
|   <meta name="viewport" id="viewport" | ||||
|     content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" /> | ||||
|  | ||||
|   <script> | ||||
|     // Global boolean variable that holds the current orientation | ||||
|     var pageInPortraintMode; | ||||
|      | ||||
|  | ||||
|     // Listen for window resizes to detect orientation changes | ||||
|     window.addEventListener("resize", windowSizeChanged); | ||||
|      | ||||
|  | ||||
|     // Set the global orientation variable as soon as the page loads | ||||
|     addEventListener("load", function() { | ||||
|     addEventListener("load", function () { | ||||
|       pageInPortraintMode = window.innerHeight > window.innerWidth; | ||||
|       document.getElementById("viewport").setAttribute("content", "width=" + window.innerWidth + ", height=" + window.innerHeight + ", initial-scale=1.0, maximum-scale=1.0, user-scalable=0"); | ||||
|     }) | ||||
|      | ||||
|  | ||||
|     // Adjust viewport values only if orientation has changed (not on every window resize) | ||||
|     function windowSizeChanged() { | ||||
|       if (((pageInPortraintMode === true) && (window.innerHeight < window.innerWidth)) || ((pageInPortraintMode === false) && (window.innerHeight > window.innerWidth))) { | ||||
| @ -42,15 +47,15 @@ | ||||
|         document.getElementById("viewport").setAttribute("content", "width=" + window.innerWidth + ", height=" + window.innerHeight + ", initial-scale=1.0, maximum-scale=1.0, user-scalable=0"); | ||||
|       } | ||||
|     } | ||||
|     </script> | ||||
|      | ||||
|   </script> | ||||
|  | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|     <info-box id="infobox"></info-box> | ||||
|     <server-connection id="server-connection" lang="de" grid_id="grid" url="ws://localhost:8765"></server-connection> | ||||
|     <crossword-grid id="grid" infobox_id="infobox" width="10" height="10"></crossword-grid> | ||||
|     <script type="module" src="./main.js"></script> | ||||
|   <info-box id="infobox"></info-box> | ||||
|   <server-connection id="server-connection" lang="de" grid_id="grid" url="ws://localhost:8765"></server-connection> | ||||
|   <crossword-grid id="grid" infobox_id="infobox" width="10" height="10"></crossword-grid> | ||||
|   <script type="module" src="./main.js"></script> | ||||
|  | ||||
| </body> | ||||
|  | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import { html, css, LitElement, unsafeCSS } from 'https://unpkg.com/lit-element/lit-element.js?module'; | ||||
| import './infoBox.js' | ||||
| import {GridElement, GridLetter, GridBox, gridType} from './gridBoxes.js' | ||||
| import { GridElement, GridLetter, GridBox, gridType } from './gridBoxes.js' | ||||
| import './solutionBox.js' | ||||
|  | ||||
| export class CrosswordGrid extends LitElement { | ||||
| @ -88,10 +88,11 @@ export class CrosswordGrid extends LitElement { | ||||
|  | ||||
|         this.solution_locations = json_grid.solution | ||||
|  | ||||
|         if (this.solutionBox) | ||||
|         if (this.solutionBox) { | ||||
|             this.solutionBox.length = this.solution_locations.length; | ||||
|             this.solutionBox.requestUpdate(); | ||||
|             console.log("update box"); | ||||
|         } | ||||
|  | ||||
|         var y; | ||||
|         var x; | ||||
| @ -126,14 +127,19 @@ export class CrosswordGrid extends LitElement { | ||||
|  | ||||
|         this.requestUpdate(); | ||||
|  | ||||
|          | ||||
|  | ||||
|     } | ||||
|  | ||||
|     focusNextCellHorizontal(x, y) { | ||||
|     focusNextCellHorizontal(x, y, skip_revealed = false) { | ||||
|         if (x + 1 < this.width && this.grid[y][x + 1].getGridType() === gridType.LETTER) { | ||||
|             if (this.grid[y][x].getGridType() === gridType.LETTER) { | ||||
|                 this.grid[y][x].getGridLetter().blur(); | ||||
|                 //this.grid[y][x].getGridLetter().blur(); | ||||
|             } | ||||
|  | ||||
|             if (skip_revealed && this.grid[y][x + 1].getGridLetter().revealed) { | ||||
|                 return this.focusNextCellHorizontal(x + 1, y, true); | ||||
|             } | ||||
|  | ||||
|             this.grid[y][x + 1].getGridLetter().focus(x, y); | ||||
|  | ||||
|  | ||||
| @ -143,11 +149,16 @@ export class CrosswordGrid extends LitElement { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     focusPrevCellHorizontal(x, y) { | ||||
|     focusPrevCellHorizontal(x, y, skip_revealed = false) { | ||||
|         if (x - 1 >= 0 && this.grid[y][x - 1].getGridType() === gridType.LETTER) { | ||||
|             if (this.grid[y][x].getGridType() === gridType.LETTER) { | ||||
|                 this.grid[y][x].getGridLetter().blur(); | ||||
|                 //this.grid[y][x].getGridLetter().blur(); | ||||
|             } | ||||
|  | ||||
|             if (skip_revealed && this.grid[y][x - 1].getGridLetter().revealed) { | ||||
|                 return this.focusPrevCellHorizontal(x - 1, y, true); | ||||
|             } | ||||
|  | ||||
|             this.grid[y][x - 1].getGridLetter().focus(); | ||||
|  | ||||
|  | ||||
| @ -157,10 +168,13 @@ export class CrosswordGrid extends LitElement { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     focusNextCellVertical(x, y) { | ||||
|     focusNextCellVertical(x, y, skip_revealed = false) { | ||||
|         if (y + 1 < this.height && this.grid[y + 1][x].getGridType() === gridType.LETTER) { | ||||
|             if (this.grid[y][x].getGridType() === gridType.LETTER) { | ||||
|                 this.grid[y][x].getGridLetter().blur(); | ||||
|                 //this.grid[y][x].getGridLetter().blur(); | ||||
|             } | ||||
|             if (skip_revealed && this.grid[y + 1][x].getGridLetter().revealed) { | ||||
|                 return this.focusNextCellVertical(x, y + 1, true); | ||||
|             } | ||||
|             this.grid[y + 1][x].getGridLetter().focus(x, y); | ||||
|  | ||||
| @ -170,10 +184,13 @@ export class CrosswordGrid extends LitElement { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     focusPrevCellVertical(x, y) { | ||||
|     focusPrevCellVertical(x, y, skip_revealed = false) { | ||||
|         if (y - 1 >= 0 && this.grid[y - 1][x].getGridType() === gridType.LETTER) { | ||||
|             if (this.grid[y][x].getGridType() === gridType.LETTER) { | ||||
|                 this.grid[y][x].getGridLetter().blur(); | ||||
|                 //this.grid[y][x].getGridLetter().blur(); | ||||
|             } | ||||
|             if (skip_revealed && this.grid[y - 1][x].getGridLetter().revealed) { | ||||
|                 return this.focusPrevCellVertical(x, y - 1, true); | ||||
|             } | ||||
|             this.grid[y - 1][x].getGridLetter().focus(); | ||||
|  | ||||
| @ -186,18 +203,18 @@ export class CrosswordGrid extends LitElement { | ||||
|  | ||||
|     focusNextCell(x, y) { | ||||
|         if (this.lastMoveVertical) { | ||||
|             if (this.focusNextCellVertical(x, y)) { | ||||
|             if (this.focusNextCellVertical(x, y, true)) { | ||||
|                 return; | ||||
|             } | ||||
|             if (this.focusNextCellHorizontal(x, y)) { | ||||
|             if (this.focusNextCellHorizontal(x, y, true)) { | ||||
|                 this.lastMoveVertical = false; | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             if (this.focusNextCellHorizontal(x, y)) { | ||||
|             if (this.focusNextCellHorizontal(x, y, true)) { | ||||
|                 return; | ||||
|             } | ||||
|             if (this.focusNextCellVertical(x, y)) { | ||||
|             if (this.focusNextCellVertical(x, y, true)) { | ||||
|                 this.lastMoveVertical = true; | ||||
|             } | ||||
|         } | ||||
| @ -209,18 +226,18 @@ export class CrosswordGrid extends LitElement { | ||||
|             return; | ||||
|         } | ||||
|         if (this.lastMoveVertical) { | ||||
|             if (this.focusPrevCellVertical(x, y)) { | ||||
|             if (this.focusPrevCellVertical(x, y, true)) { | ||||
|                 return; | ||||
|             } | ||||
|             if (this.focusPrevCellHorizontal(x, y)) { | ||||
|             if (this.focusPrevCellHorizontal(x, y, true)) { | ||||
|                 this.lastMoveVertical = false; | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             if (this.focusPrevCellHorizontal(x, y)) { | ||||
|             if (this.focusPrevCellHorizontal(x, y, true)) { | ||||
|                 return; | ||||
|             } | ||||
|             if (this.focusPrevCellVertical(x, y)) { | ||||
|             if (this.focusPrevCellVertical(x, y, true)) { | ||||
|                 this.lastMoveVertical = true; | ||||
|             } | ||||
|         } | ||||
| @ -335,23 +352,33 @@ export class CrosswordGrid extends LitElement { | ||||
|         this.serverConnection.sendMessage(msg) | ||||
|     } | ||||
|  | ||||
|     setSolutionLetter(solutionNumber, letter, revealed = false){ | ||||
|     setSolutionLetter(solutionNumber, letter, revealed = false) { | ||||
|         this.solutionBox.setLetter(solutionNumber, letter, revealed); | ||||
|         const x = this.solution_locations[solutionNumber][1]; | ||||
|         const y = this.solution_locations[solutionNumber][0]; | ||||
|         console.log("update solution letter"); | ||||
|         var old_val = this.grid[y][x].getGridLetter().value; | ||||
|         this.grid[y][x].getGridLetter().value = letter; | ||||
|  | ||||
|         if (old_val != letter && !revealed) { | ||||
|             this.sendMessage({ | ||||
|                 'type': 'update', | ||||
|                 'x': x, | ||||
|                 'y': y, | ||||
|                 'letter': letter | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|      | ||||
|  | ||||
|  | ||||
|     render() { | ||||
|         this.num_hints = 0; | ||||
|         console.log("refreshing grid"); | ||||
|  | ||||
|         var solution_length = 0; | ||||
|         if (this.solution_locations){ | ||||
|         if (this.solution_locations) { | ||||
|             solution_length = this.solution_locations.length; | ||||
|         } | ||||
|  | ||||
|  | ||||
| @ -397,6 +397,8 @@ export class GridLetter extends GridBox { | ||||
|  | ||||
|     updateLetter(letter, revealed) { | ||||
|         this.value = letter; | ||||
|         var element = this.shadowRoot.getElementById(`${this.x}-${this.y}`); | ||||
|         element.value = letter; | ||||
|         if (revealed) { | ||||
|             console.log("rec") | ||||
|             this.shadowRoot.getElementById(`${this.x}-${this.y}`).readOnly = true; | ||||
|  | ||||
| @ -2,6 +2,11 @@ | ||||
| <html> | ||||
|  | ||||
| <head> | ||||
|     <link rel="icon" type="image/png" href="./favicon.png" /> | ||||
|     <link rel="manifest" href="./manifest.json"> | ||||
|     <script type="module" src="./app.js"></script> | ||||
|     <script type="module" src="./sw.js"></script> | ||||
|  | ||||
|     <!-- Polyfills only needed for Firefox and Edge. --> | ||||
|     <script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script> | ||||
|     <!-- Works only on browsers that support Javascript modules like | ||||
| @ -72,8 +77,12 @@ | ||||
|  | ||||
|     <div class="centerbox"> | ||||
|         <h3>Select the language:</h3> | ||||
|         <a href="./german.html"> <div class="box"> German </div> </a> | ||||
|         <a href="./english.html"> <div class="box"> English </div> </a> | ||||
|         <a href="./german.html"> | ||||
|             <div class="box"> German </div> | ||||
|         </a> | ||||
|         <a href="./english.html"> | ||||
|             <div class="box"> English </div> | ||||
|         </a> | ||||
|     </div> | ||||
|  | ||||
| </body> | ||||
|  | ||||
							
								
								
									
										16
									
								
								js_client/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								js_client/manifest.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| { | ||||
|     "name": "Crosswords", | ||||
|     "short_name": "crosswords", | ||||
|     "start_url": "./", | ||||
|     "display": "standalone", | ||||
|     "background_color": "#000000", | ||||
|     "description": "Simple Collaborative Crossword", | ||||
|     "theme_color": "#000000", | ||||
|     "icons": [ | ||||
|       { | ||||
|         "src": "./big_icon.png", | ||||
|         "sizes": "256x256", | ||||
|         "type": "image/png" | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
| @ -73,7 +73,7 @@ export class ServerConnection extends WebsocketConnection { | ||||
|         console.log("stored session", sessionId, "as cookie") | ||||
|         setCookie("session", sessionId, 2); | ||||
|         const urlparams = new URLSearchParams(window.location.search); | ||||
|         if (urlparams.has('session') && this.sessionId === urlparams.get('session')){ | ||||
|         if (urlparams.has('session') && this.sessionId === urlparams.get('session')) { | ||||
|             return; | ||||
|         } | ||||
|         urlparams.set('session', sessionId); | ||||
|  | ||||
							
								
								
									
										51
									
								
								js_client/sw.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								js_client/sw.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| const cacheName = 'pwa-conf-v4'; | ||||
| const staticAssets = [ | ||||
|     './app.js ', | ||||
|     './big_icon.png', | ||||
|     './english.html', | ||||
|     './german.html', | ||||
|     './grid.js', | ||||
|     './infoBox.js', | ||||
|     './manifest.json', | ||||
|     './serverConnection.js', | ||||
|     './cookie.js', | ||||
|     './favicon.png', | ||||
|     './gridBoxes.js', | ||||
|     './index.html', | ||||
|     './main.js', | ||||
|     './solutionBox.js', | ||||
|     './websocket.js' | ||||
| ]; | ||||
|  | ||||
|  | ||||
| self.addEventListener('install', async event => { | ||||
|     const cache = await caches.open(cacheName); | ||||
|  | ||||
|  | ||||
|     await cache.addAll(staticAssets); | ||||
|  | ||||
|  | ||||
| }); | ||||
|  | ||||
| self.addEventListener('fetch', event => { | ||||
|     const req = event.request; | ||||
|     event.respondWith(networkFirst(req)); | ||||
| }); | ||||
|  | ||||
| async function cacheFirst(req) { | ||||
|     const cache = await caches.open(cacheName); | ||||
|  | ||||
|  | ||||
|     const cachedResponse = await cache.match(req); | ||||
|  | ||||
|  | ||||
|     return cachedResponse || fetch(req); | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| async function networkFirst(req) { | ||||
|     return fetch(req).catch(function() { | ||||
|         return caches.match(req); | ||||
|     })  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user