- added service worker that keeps file in browser cache

- added webmanifest to make a progressive web app (installability not tested yet)
- dependencies (jQuery etc.) are now included
- reduced dependency size by importing Bootstrap's modal.js only instead of bootstrap.js
This commit is contained in:
Tobias 2023-03-07 22:17:34 +01:00
parent c6d3bd5370
commit 04375e7d49
8 changed files with 162 additions and 68 deletions

18
TODO.md
View File

@ -38,7 +38,9 @@
- x derzeit werden die icons im actions-menu nicht dunkler gemacht (.zero-ini o.Ä.), weil ich die img-Rules aus dem CSS rausgenommen habe
- ich könnte die Rules wieder reinnehmen
- x oder die Icons mit SVG nachbauen (edit und trash)
- x damage-monitor und actions-menu überlappen sich; die oberen drei phys-dmg-buttons lösen edit/clone/remove aus
- bug in validateCombatant: OK schließt das modal, auch wenn die Eingaben invalid sind
- wenn ich bei add combatant mit der Maus auf OK klicke, macht er das Modal zu, auch wenn die Eingaben invalid sind
- nach dem Laden passiert es manchmal, dass nach dem Einfügen von testCombatant das add Modal gleich wieder aufgeht
- schien die gleiche Sache zu sein wie mit dem hidden.bs.modal event
@ -76,7 +78,16 @@
- aber am Ende bringt das gar nichts, weil der Browser bei jedem <use> den Inhalt des <symbol>s aufs Neue ins DOM kopiert. Die HTML-Platzersparnis wäre also gleich Null.
- x kann die classes text-center, text-end etc aus den Tabellenzellen entfernen
- x CSS: maroon durch b3005f ersetzen
- x anonymous functions wo möglich durch arrow functions ersetzen
- x auf bootstrap.js verzichten?
- zumindest modal.js muss ich einzeln laden
- aber sonst brauch ich es glaube ich nicht
- x dependencies lokal einbinden
- progressive web app
- Service Worker einrichten, um die Dateien lokal zu cachen
- lief nicht mit Parcel pur, brauchte Paket "serve" -> npx serve dist/
- parcel bindet sw.js nicht automatisch mit ein, ich muss es nach dist/ hardverlinken
- und die File-list in sw.js darf nur Files enthalten, die auch geladen werden können; sobald einer 404 ergibt, schlägt der gesamte Cache-Vorgang fehl
- Seite auch mal im Chrome checken
- dafür sorgen, dass die Seite erst dann aufgebaut wird, wenn die CSS-Files geladen sind, damit man nicht den ungestylten Krams sieht
@ -92,11 +103,8 @@
- warum sind im dist/-Folder immer zwei Versionen der gleichen Datei?
- HTML soll nicht in eine Zeile umgedingst werden, das sieht doch nicht aus
- bootstrap, jquery, font auch lokal vorhalten
- nicetohave: Anzeige, wieviele Aktionen einer hat u.d wieviele davon schon verbraucht sind
- docstrings
- parcel soll aus dem HTML code nicht die Newlines rausnehmen -> macht er das überhaupt noch?
- falls ja: .htmlnanorc anlegen, s. https://parceljs.org/languages/html/#minification und https://htmlnano.netlify.app/modules#collapsewhitespace
- progressive web app draus machen? -> https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Introduction
- auf bootstrap verzichten? brauch es schließlich kaum noch
- könnte beim modal schwierig werden (weil auch js)
- nicetohave: Wenn ich rea editiere, könnte sich die ini automatisch anpassen -> da müsste ich aber die Würfelergebnisse für speichern
- nicetohave: Anzeige, wieviele Aktionen einer hat u.d wieviele davon schon verbraucht sind

View File

@ -1,6 +1,9 @@
{
"name": "",
"short_name": "",
"name": "Shadowrun 2e Ini Tracker",
"short_name": "sr2ini",
"start_url": "/",
"description": "A simple Initiative tracker for Shadowrun 2e",
"orientation": "portrait",
"icons": [
{
"src": "/icons/android-chrome-192x192.png",

View File

@ -30,7 +30,7 @@
"scripts": {
"start": "npx parcel serve src/index.html --public-url / --dist-dir dist",
"prebuild": "rm -rf dist/",
"build": "npx parcel build --public-url ./",
"build": "npx parcel build --no-optimize --public-url ./",
"test": "jest --coverage --env=jsdom",
"webhint": "hint http://localhost:1234"
},
@ -48,6 +48,11 @@
"./tools/setup-jest.js"
]
},
"type": "module"
"type": "module",
"dependencies": {
"@parcel/service-worker": "^2.8.3",
"augmented-ui": "^2.0.0",
"bootstrap": "^5.2.3",
"jquery": "^3.6.3"
}
}

View File

@ -269,17 +269,19 @@ header.navbar {
--aug-inlay-bg: rgba(0, 0, 0, .5);
--aug-border-opacity: .75;
opacity: 0;
padding-top: 10px;
padding-bottom: 10px;
position: absolute;
right: 10px;
top: calc(100% - 2px);
transition: opacity .25s ease-in-out;
transition: transform .25s ease-in-out;
z-index: 200;
}
.seen { opacity: 1; }
.damage-monitor { left: calc(100vw - 1em); }
.actions-menu { left: calc(100vw + 3.5em); }
.seen { transform: translateX(-100vw); }
.damage-monitor {
button {

View File

@ -7,16 +7,23 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Shadowrun 2e Ini Tracker</title>
<meta name="description" content="A simple Initiative tracker for Shadowrun 2e" />
<meta name="author" content="Eclipse729" />
<meta name="theme-color" content="" />
<link rel="manifest" href="/icons/sr2ini.webmanifest">
<!-- Style sheets -->
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@latest/dist/css/bootstrap.min.css" >
<link type="text/css" rel="stylesheet" href="https://unpkg.com/augmented-ui@latest/augmented-ui.min.css">
<!-- <link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@latest/dist/css/bootstrap.min.css" >
<link type="text/css" rel="stylesheet" href="https://unpkg.com/augmented-ui@latest/augmented-ui.min.css">-->
<link type="text/css" rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.css" >
<link type="text/css" rel="stylesheet" href="../node_modules/augmented-ui/augmented-ui.css">
<link type="text/css" rel="stylesheet" href="css/sr2ini.scss">
<!-- javascript files -->
<script type="module" src="https://cdn.jsdelivr.net/npm/bootstrap@latest/dist/js/bootstrap.min.js"></script>
<script type="module" src="https://code.jquery.com/jquery-latest.min.js"></script>
<script type="module" src="js/sr2ini.js"></script>
<!-- <script type="module" src="https://cdn.jsdelivr.net/npm/bootstrap@latest/dist/js/bootstrap.min.js"></script>
<script type="module" src="https://code.jquery.com/jquery-latest.min.js"></script> -->
<script type="module" src="js/sr2ini.js" defer></script>
<!-- favicon related info -->
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">

82
src/js/service-worker.js Normal file
View File

@ -0,0 +1,82 @@
import {manifest, version} from '@parcel/service-worker';
console.log("[Service Worker] Version:", version);
console.log("[Service Worker] Manifest:", manifest);
/*const cacheName = "sr2ini-v1"
const fileList = [
"/",
"/index.html",
"/index.a5b8bd92.js",
"/index.a5b8bd92.js.map",
"/index.af281647.css",
"/index.af281647.css.map",
"/index.7489b7a6.css",
"/index.7489b7a6.css.map",
"/index.9cea7766.css",
"/index.9cea7766.css.map",
"/Electrolize-Regular.ttf",
"/favicon.69e87188.ico",
"/bg.jpg",
"/add.png",
"/cross.png",
"/newround.png",
"/zzz.png",
"/android-chrome-192x192.473fd5a7.png",
"/android-chrome-512x512.3639671b.png",
"/apple-touch-icon.d8da5e1c.png",
"/favicon-16x16.fd160567.png",
"/favicon-32x32.c37c1049.png",
"/up_/icons/browserconfig.xml"
];*/
// Installing Service Worker
async function install() {
const cache = await caches.open(version); //cacheNAme
console.log("[Service Worker] Caching all: app shell and content");
await cache.addAll(manifest); // fileList
}
console.log("[Service Worker] Install");
self.addEventListener("install", e => e.waitUntil(install()));
// Fetching content using Service Worker
/*self.addEventListener('fetch', (e) => {
e.respondWith((async () => {
const r = await caches.match(e.request);
console.log(`[Service Worker] Fetching resource: ${e.request.url}`);
if (r) {
return r;
}
const response = await fetch(e.request);
const cache = await caches.open(cacheName);
console.log(`[Service Worker] Caching new resource: ${e.request.url}`);
cache.put(e.request, response.clone());
return response;
})());
});*/
async function activate() {
const keys = await caches.keys();
await Promise.all(keys.map(key => key !== version && caches.delete(key)));
};
console.log("[Service Worker] Activate");
self.addEventListener("activate", e => e.waitUntil(activate()));
/*
self.addEventListener("activate", (e) => {
e.waitUntil(
caches.keys().then((keyList) => {
return Promise.all(
keyList.map((key) => {
if (key === cacheName) {
return;
}
return caches.delete(key);
})
);
})
);
});*/

View File

@ -1,3 +1,23 @@
// Register Service Worker
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register(
new URL("service-worker.js", import.meta.url), { type: "module" }
).then( (registration) => {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, (err) => {
console.log('ServiceWorker registration failed: ', err);
});
} else {
console.error("Service workers are not supported.");
}
/*
* import libraries
*/
const bs = require("../../node_modules/bootstrap/js/dist/modal.js");
const $ = require("../../node_modules/jquery/dist/jquery.js");
/*
* constants definitions
*/
@ -86,9 +106,7 @@ function addTestCombatant() {
$("#combatant-modal-dice").val(3);
$("#combatant-modal-rea").val(6);
// $("#combatant-modal-ini").val(12);
setTimeout(function () {
$("#combatant-modal-add-ok-button").click();
}, 500);
setTimeout( () => $("#combatant-modal-add-ok-button").click(), 500);
}
/*
* Event handler functions
@ -112,11 +130,7 @@ function handleAddButtonClick(e) {
$("#combatant-modal-stun, #combatant-modal-physical").val("0");
// add handler for enter key
$("#combatant-modal input[id*='combatant-modal']").off("keydown");
$("#combatant-modal input[id*='combatant-modal']").on("keydown", function (e) {
if (e.which == 13 || e.which == 10) {
addCombatant(e);
}
});
$("#combatant-modal input[id*='combatant-modal']").on("keydown", (e) => { if (e.which == 13 || e.which == 10) { addCombatant(e); } });
}
// click handler for clone buttons -> like handleAddButtonClick but with a pre-filled modal
function handleCloneButtonClick(e) {
@ -137,11 +151,7 @@ function handleCloneButtonClick(e) {
$("#combatant-modal-physical").val($tr.attr("data-damage-physical") || "0");
// add handler for enter key
$("#combatant-modal input[id*='combatant-modal']").off("keydown");
$("#combatant-modal input[id*='combatant-modal']").on("keydown", function (e) {
if (e.which == 13 || e.which == 10) {
addCombatant(e);
}
});
$("#combatant-modal input[id*='combatant-modal']").on("keydown", (e) => { if (e.which == 13 || e.which == 10) { addCombatant(e); } });
}
// click handler for damage buttons; basically toggles visibility of table.damage-monitor
function handleDamageButtonClick(e) {
@ -174,11 +184,7 @@ function handleEditButtonClick(e) {
$("#combatant-modal").data("row", $(".combatant-row").index($tr)); // here it's okay to use .data() b/c HTML/CSS does not care about this value
// add handler for enter key
$("#combatant-modal input[id*='combatant-modal']").off("keydown");
$("#combatant-modal input[id*='combatant-modal']").on("keydown", function (e) {
if (e.which == 13 || e.which == 10) {
editCombatant(e);
}
});
$("#combatant-modal input[id*='combatant-modal']").on("keydown", (e) => { if (e.which == 13 || e.which == 10) { editCombatant(e); } });
}
// click handler for the more-actions menus
function handleMoreActionsButtonClick(e) {
@ -260,19 +266,14 @@ function applyDamage(e) {
if ( $btn.hasClass("active") ) {
damageLevel += 1;
}
let damageType = $btn.attr("class").split(" ").filter(function (cls) {
return cls.substr(0, 7) == "damage-" ? cls : false;
}).toString().substr(7);
let damageType = $btn.attr("class").split(" ").filter(cls => cls.substr(0, 7) == "damage-" ? cls : false).toString().substr(7);
console.log("damageType is", damageType);
// add damage level to table row as as data attribute
$btn.parents("tr.combatant-row").attr("data-damage-" + damageType, damageLevel);
// select/unselect damage buttons above/below
$btn.toggleClass("active");
$btn.parent().parent().prevAll().find("button.damage-" + damageType).removeClass("active");
$btn.parent().parent().nextAll().find("button.damage-" + damageType + ":not(.active)").addClass("active");
// resort
/* setTimeout(function(){
sortTable();
},250);*/
sortTable();
}
// edit combatant
@ -341,7 +342,7 @@ function sortTable() {
$(".combatant-row").find(".act-button").prop("disabled", true).attr("aria-disabled", "true");
$(".combatant-ini").empty();
// mark KO or death with class
$(".combatant-row").each(function () {
$(".combatant-row").each( () => {
if (parseInt($(this).attr("data-damage-stun")) == 10 || parseInt($(this).attr("data-damage-physical")) == 10) {
$(this).addClass("ko-or-dead");
}
@ -377,8 +378,6 @@ function sortTable() {
$(this).addClass("max-ini").find(".act-button").prop("disabled", false).removeAttr("aria-disabled");
return true;
}
/* // everything else
$(this).addClass("REGULAR_INI");*/
})
// sort rows and append them in new order
let $rows = $(".combatant-row").toArray().sort(whoGoesFirst);
@ -400,14 +399,10 @@ function validateCombatant() {
// do standard HTML5 form validation first
// (makes sure that name is not empty and that all other values are numbers within their individual ranges)
let valid = true;
Object.values(inputElements).forEach(function (input) {
if (!input.reportValidity()) {
valid = false;
}
Object.values(inputElements).forEach( input => {
if (!input.reportValidity()) { valid = false; }
})
if (!valid) {
return false;
}
if (!valid) { return false; }
// now for some custom validation; first we need to get the input values
let ini = inputElements["ini"].value.trim();
let dice = inputElements["dice"].value.trim();
@ -443,14 +438,14 @@ $(document).ready(function () {
$("#confirm-modal-remove-combatant-ok-button").on("click", removeCombatant);
// add event listeners to damage sliders in combatant modal
$("#combatant-modal-stun").on("change", function() {
$("#combatant-modal-stun").on("change", () => {
if ($("#combatant-modal-stun").val() == "10") {
$("#combatant-modal-penalty-stun").text("(K.O.)");
} else {
$("#combatant-modal-penalty-stun").text("(wound penalty -" + DAMAGE_PENALTY[$("#combatant-modal-stun").val()] + ")");
}
});
$("#combatant-modal-physical").on("change", function() {
$("#combatant-modal-physical").on("change", () => {
if ($("#combatant-modal-physical").val() == "10") {
$("#combatant-modal-penalty-physical").text("(dead)");
} else {
@ -459,15 +454,11 @@ $(document).ready(function () {
});
// always focus name input field when combatant modal appears
$('#combatant-modal').on('shown.bs.modal', function () {
$('#combatant-modal-name').focus();
});
$('#combatant-modal').on('shown.bs.modal', () => $('#combatant-modal-name').focus());
// always empty input fields when combatant modal disappears
$("#combatant-modal").on('hidden.bs.modal', function (e) {
$("#combatant-modal input[id*='combatant-modal']").val("");
});
$("#combatant-modal").on('hidden.bs.modal', () => $("input[id*='combatant-modal']").val(""));
// Hide damage monitors after click somewhere else
$("html").on("click", function (e) {
$("html").on("click", (e) => {
if ($(e.target).parents(".damage-monitor").length == 0) {
$(".damage-monitor.seen").removeClass("seen");
}

File diff suppressed because one or more lines are too long