Compare commits

...

47 Commits

Author SHA1 Message Date
0d14525305 added geany project file 2023-09-27 12:23:45 +02:00
f71cfbf857 upgraded to version 0.9.5 2023-09-23 23:26:56 +02:00
07fb35b6c5 Merge branch 'main' of git.unterdemradar.de:tobias/sr2ini_ynh 2023-09-23 22:47:59 +02:00
47d03ffbe9 updated to sr2ini-0.9.4 2023-09-23 22:47:27 +02:00
a4aad58b9c updated to sr2ini-0.9.4 2023-09-23 22:35:51 +02:00
d6fc3fd5e8 upgraded to version sr2ini-0.9.3 2023-09-21 09:21:59 +02:00
c6b2cd11bb Merge branch 'main' of git.unterdemradar.de:tobias/sr2ini_ynh 2023-09-20 23:08:36 +02:00
baa8cd3d9e updated package for release 0.9.2 2023-09-20 22:58:53 +02:00
2d6ff0d33d updated package for release 0.9.2 2023-09-20 22:49:15 +02:00
0367ffff2e created 2023-07-29 18:06:53 +02:00
46fc2bb78c created 2023-07-29 18:06:40 +02:00
d2edfb2c5c not auto-generated anymore 2023-07-29 17:41:10 +02:00
825148d229 removed French README (LIT-MOI?) 2023-07-29 17:40:06 +02:00
847188a731 created 2023-07-29 17:30:52 +02:00
79478338d7 automatically generated 2023-07-29 17:29:47 +02:00
9dfd2d0c1a added filename 2023-07-29 17:29:12 +02:00
28ca30fbc5 changed version number 2023-07-29 17:28:55 +02:00
c34e9dc6ed removed heading 2023-07-29 16:37:24 +02:00
84666cfb39 removed todo file from index 2023-07-29 16:34:57 +02:00
f07ca6fc4c added screenshot 2023-07-29 16:33:14 +02:00
346147eae3 added the todo file 2023-07-29 16:24:16 +02:00
fce6508c72 removed quotes around "false" 2023-07-29 16:22:57 +02:00
ebf1f1119f created description 2023-07-29 16:00:01 +02:00
512f8eb550 created 2023-07-27 22:26:43 +02:00
f9c1baa355 commented out the yunohost tile b/c it seems to break the web layout 2023-07-27 22:26:06 +02:00
1d72e74ce9 tarball lives here now 2023-07-27 22:22:26 +02:00
2ed4a3689b after cleaning up the repository, there's no need to ignore anything anymore 2023-07-27 22:21:15 +02:00
3594b777fd cleaned up repository 2023-07-27 22:17:46 +02:00
80bc4c30c2 used script from hellworld_ynh 2023-07-27 22:02:18 +02:00
cc72748cc8 using script from helloworld 2023-07-27 22:01:47 +02:00
3b617ded37 uodated rewrite rule 2023-07-27 21:47:23 +02:00
f8a2a32cf4 copied file from helloworld_ynh 2023-07-27 21:44:31 +02:00
6088ab2741 minor changes 2023-07-27 21:41:18 +02:00
20f277fb32 added initial rewrite rule 2023-07-27 21:36:19 +02:00
46b3d1731e install now adds an NGINX conf file 2023-07-27 21:28:19 +02:00
1e4b37d990 added nginx configuration 2023-07-27 21:21:10 +02:00
7e4ff716f4 corrected tarball download URL 2023-07-27 21:05:02 +02:00
620c7dc08a updated tarball filename 2023-07-27 20:56:08 +02:00
7a550233ef add dist tarball 2023-07-27 20:54:19 +02:00
6f5f246a00 Merge branch 'main' of git.unterdemradar.de:tobias/sr2ini_ynh 2023-07-27 20:53:01 +02:00
1cc31bda50 Delete tools/sr2ini_ynh-o.1~ynh1.tar.gz 2023-07-27 20:50:08 +02:00
9c67f91e83 removed after fork 2023-07-27 20:46:34 +02:00
1a36c462f7 changes reflect fork 2023-07-27 20:46:17 +02:00
3c87d3e9b1 changes reflect fork 2023-07-27 20:45:36 +02:00
65fc936888 created folder and first version for the install script 2023-07-27 20:40:54 +02:00
28b7245e04 Created app manifest 2023-07-27 20:40:15 +02:00
e243e74453 uploaded test-release as gzipped tarball for the Yunohost installation script 2023-07-27 20:19:26 +02:00
46 changed files with 272 additions and 1534 deletions

13
.gitignore vendored
View File

@ -1,10 +1,3 @@
.cache/
.parcel-cache/
package-lock.json
.eslintrc.json
.parcelrc
dist/
node_modules/
hint-report/
.csslintrc
coverage/
TODO.md
clean-after-make_readme.sh
*.geany

View File

@ -1,3 +1,22 @@
# sr2ini
# SR2 Initiative Tracker for YunoHost
Simple Initiative tracker for Shadowrun 2e.
## Overview
sr2ini is a lightweight, single-page initiative tracker for the TTRPG Shadowrun in its 2nd edition. The app helps DMs and players to manage fights by tracking each combatant's initiative, order of action, damage (stun and physical), and wound modifiers. It was written specifically for mobile use and can be installed as a web app.
This package allows you to install sr2ini quickly and simply on a YunoHost server. If you don't have YunoHost, please consult the guide to learn how to install it.
## YunoHost specific features
* supports multi-install
* defaut visibility is public
**Shipped version:** 0.9.5~ynh1
## Screenshots
![Screenshot of SR2 Initiative Tracker](./doc/screenshots/sr2ini-screenshot.jpg)
## Documentation and resources
* App source: <https://git.unterdemradar.de/tobias/sr2ini>

118
TODO.md
View File

@ -1,118 +0,0 @@
# sr2ini: Bugs and Features Tracker
## Bugs
- x Bug: KO/dead buttons fkt. nicht
- applyDamage, ersetze erste Zeile: let $btn = $(e.target).is("button") ? $(e.target) : $(e.target).parents("button")[0];
- x Bug: Kann nicht weit genug runterscrollen für remove bzw die unteren schadenskastchen
- Footer hatte viel zu großen z-index
- x Bug: resort after remove -> einfach eingefügt
- x background-image wiederholt sich -> hat sich nach Redesign erledigt
- x Habe alle $("…").modal("hide|show") eliminiert; die Sichtbarkeit der Modals wird jetzt ausschließlich durch data-bs-Attribute gesteuert
- x add-Button leert die inputs vom Modal nicht mehr
- event hidden.bs.modal feuert nicht mehr
- zuerst dachte ich, es liegt daran, dass ich das bootstrap js zu Klasse Modal nicht laden konnte. Mittlerweile habe ich es hingekriegr, doch das event feuert trotzdem nicht
- hab's hingekriegt, indem ich jquery und bootstrap nun doch wieder in der index.html lade und nicht mehr als import.
- x in großer Darstellung bleiben u.U. links und rechts Ränder frei, wo das Hintergrundbild nicht skaliert wird -> background-size: cover
- x bei Benutzung von augmented-ui: der Text von .combatant-name ist nach oben verschoben und links+rechts abgeschnitten
- grundlegendes Problem: augmented-ui nutzt ::before und ::after, um die Styles zu erzeugen; mein eigenes ::before kollidiert damit
- füge den .combatant-name jetzt wieder direkt ein, nicht mehr per Datenattribut
- x damit nirgendwo ein Stück border fehlt, müssen die Ecken von zwei benachbarten combatant tablerows nicht gegeneinander verschoben sein
- d.h. die beiden linken und die beiden rechten Ecken einer Tablerow müssen jeweils die gleiche X-Position haben
- x alles, was vom damage monitor über den unteren Tabellenrand rüberragt, wird abgeschnitten
- musste den clip-path von tr und td auf none setzen
- x jetzt kann ich den damage-monitor zwar aufklappen, aber die Buttons der weiter unten liegenden combatant-rows überdecken ihn jedesmal.
- x wenn ich einen Damage-Button anklicke, beginnt die Transition des damage-monitor von Neuem
- x wenn die Navbar borders hat, sollte sie nicht direkt am oberen Bildrand anfangen, sondern ein paar Pixel weiter unten
- x Unterschied zwischen enabled und disabled button nicht gut erkennbar
- x Output vom HTML Validator:
- img elements müssen alt attributes haben
- The form attribute must refer to a form element. (bei den Input Elementen im combatant-modal) -> muss nachsehen, was ich dann genau da eintragen muss
- x minusten.svg benutzt die falsche Schriftart
- x manchmal spinnt das actions-menu, dann taucht es einfach nicht mehr auf
- Bsp: more -> clone -> cancel
- 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
- x bei tot/KO wird die Klasse ko-or-dead nicht gesetzt
- es werden gar keine contextual classes gesetzt
- musste die entspr. arrow function in sortTable() wieder zu einer regular function machen
- x combatant modal hat auf einmal zwei OK buttons
- Fehler beim sichtbar/unsichtbar-Setzen der modal buttons in handleEditButtonClick()
- x Änderungen im edit modal werden nicht mehr übernommen
- war ein fehlendes let in editCombatant() -> wo das nur wieder herkam …
- im FP3T Tor Browser kann ich rauszoomen, bis ich die ganzen damage monitors und action menus sehe -> verhindern!
- 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
- jetzt kommt es aber trotzdem manchmal wieder
- vllt. ein timeout-Problem?
- add modal geht bei enter key nicht zu -> liegt daran, dass ich nicht mehr .modal("hide") verwende
- ich könnt's umstellen, aber will ich das?
- wenn ein damage monitor offen ist und ich auf add combatant clicke, springt der Fokus nicht zuverlässig ins erste input feld
## Feature Requests
- x nochmal wg. Daten wie name, dice, rea, true-ini und damage-x:
- Verwalte sie jetzt komplett mit der data-* API; verwende dafür ausschließlich .attr() als Getter/Setter
- Füge die Werte aus dem Attribut per CSS direkt ins Element ein (::after und content).
- x prettify code: alle HTML class names von camelCale zu dash-case komvertieren
- x Design cyberpunkig machen
- x im modal soll man die damage levels einstellen/verändern können
- x Im modal, wenn ich die wound penalties anzeige, die Fälle KO und Tod gesondert behandeln
- x clone button
- x und im combatant-modal ein weiterer OK-Button, der das Modal offenlässt
- x Design: favicon
- x imput[type=range] schicker machen
- x input elements styling anpassen für :focus. :focus-visible, :valid, :invalid
- x contextual classes hübsch machen
- x neue Icons
- x dmg-mon/actions-menu: tabindex auf -1 setzen, wenn nicht sichtbar
- x dmg-mon/actions-menu direkt unter dem jeweiligen Button ausrichten (X-Achse)
- ich richte die Buttons jetzt rechtsbündig aus, dann weiß ich immer die (ungefähre) Position des Buttons
- x SVG Icons alle in eine externe Datei als <symbol>s mit ID packen. Diese kann ich dann in anderen Files referenzieren: <svg><use href="icons.svg#icon1" /></svg>
- https://stackoverflow.com/questions/34225008/how-to-reuse-an-embedded-svg-element-in-the-same-page
- leider geht das nicht: addCombatant() fügt HTML dynamisch ins Dokument ein, und wenn in diesem HTML SVG enthalten ist, werden die <use>-Elemente darin nicht ausgewertet (also duch das referenzierte <symbol> ersetzt). Das SVG wird daher nicht angezeigt.
- um das zu beheben. müsste ich die jQuery-Methode parseHTML() modifizieren und alle Aufrufe von createElement() durch createElementNS() ersetzen
- 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
- x progressive web app
- x Service Worker einrichten, um die Dateien lokal zu cachen
- x lief nicht mit Parcel pur, brauchte Paket "serve" -> npx serve dist/
-> mittlerweile geht's doch
- x 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
- geht
- x dafür sorgen, dass die Seite erst dann aufgebaut wird, wenn die CSS-Files geladen sind, damit man nicht den ungestylten Krams sieht -> passt schon
- x CSS aufräumen
- Variablen für Farben, Filter etc.
- installability (PWA) auf android noch ungetestet
- Seite auch mal im Chrome checken
- -moz-… mit -webkit-… ergänzen
- noch mehr Design
- Seite für größere Screens anpassen
- Schrift, Buttons, Icons skalieren
- em und % statt px
- Tabellenbreite begrenzen
- Animationen? Transitions?
- deployment: dist/* soll direkt auf hermes hochgeladen werden
- 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
- 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
- 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,16 +0,0 @@
const s = require("../src/js/sr2ini.js");
describe ("test function rollForInitiative()", () => {
test("function should accept numbes as integers as well as strings", () => {
expect(typeof s.rollForInitiative("1", "5")).toBe("number");
expect(typeof s.rollForInitiative(1, 5)).toBe("number");
});
test("result should be greater than zero", () => {
expect(s.rollForInitiative("1", "5")).toBeGreaterThan(0);
});
test("should return zero for bad parameters", () => {
expect(s.rollForInitiative("a", 2)).toBe(0);
});
});

11
conf/nginx.conf Normal file
View File

@ -0,0 +1,11 @@
#sub_path_only rewrite ^__PATH__$ __PATH__/ permanent;
location __PATH__/ {
# Path to source
alias __INSTALL_DIR__/;
index index.html;
# Include SSOWAT user panel
# include conf.d/yunohost_panel.conf.inc;
}

BIN
dist/sr2ini_ynh-0.1~ynh1.tar.gz vendored Normal file

Binary file not shown.

8
doc/DESCRIPTION.md Normal file
View File

@ -0,0 +1,8 @@
sr2ini is a lightweight, single-page initiative tracker for the TTRPG Shadowrun in its 2nd edition. The app helps DMs and players to manage fights by tracking each combatant's initiative, order of action, damage (stun and physical), and wound modifiers. It was written specifically for mobile use and can be installed as a web app.
This package allows you to install sr2ini quickly and simply on a YunoHost server. If you don't have YunoHost, please consult the guide to learn how to install it.
## YunoHost specific features
* supports multi-install
* defaut visibility is public

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/icons/mstile-150x150.png"/>
<TileColor>#004aa5</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,22 +0,0 @@
{
"name": "Shadowrun 2e Ini Tracker",
"short_name": "sr2ini",
"start_url": "https://unterdemradar.de/sr2ini",
"description": "A simple Initiative tracker for Shadowrun 2e",
"orientation": "portrait",
"icons": [
{
"src": "/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "deeppink",
"background_color": "#004aa5",
"display": "standalone"
}

51
manifest.toml Normal file
View File

@ -0,0 +1,51 @@
packaging_format = 2
id = "sr2ini"
name = "SR2 Initiative Tracker"
description.en = "A simple initiative tracker for Shaodwrun 2e."
description.de = "Ein einfacher Initiative-Tracker für Shaodwrun, 2. Edition."
version = "0.9.5~ynh1"
maintainers = ["Eclipse729"]
[upstream]
license = "ISC"
[integration]
yunohost = ">= 11.1"
architectures = "all"
multi_instance = true
ldap = "not_relevant"
sso = "not_relevant"
disk = "1M"
ram.build = "1M"
ram.runtime = "1M"
[install]
[install.domain]
# this is a generic question - ask strings are automatically handled by Yunohost's core
type = "domain"
[install.path]
# this is a generic question - ask strings are automatically handled by Yunohost's core
type = "path"
default = "/sr2ini"
[install.init_main_permission]
type = "group"
default = "visitors"
[resources]
[resources.sources.main]
url = "https://git.unterdemradar.de/tobias/sr2ini/releases/download/0.9.5/sr2ini-0.9.5.tar.gz"
sha256 = "7dd22080f8768eeda1b080e293f8646a33f60d9b9bd2352c3de572efd454064b"
format = "tar.gz"
in_subdir = false
[resources.system_user]
[resources.install_dir]
[resources.permissions]
main.url = "/"

View File

@ -1,58 +0,0 @@
{
"name": "sr2ini",
"version": "1.0.0",
"description": "Simple Initiative tracker for Shadowrun 2e",
"private": true,
"author": {
"name": "Eclipse729",
"email": "eclipse@unterdemradar.de",
"url": "https://www.unterdemradar.de"
},
"homepage": "https://www.unterdemradar.de/sr2ini/",
"license": "ISC",
"source": "src/index.html",
"devDependencies": {
"@parcel/packager-raw-url": "^2.8.3",
"@parcel/packager-xml": "^2.8.3",
"@parcel/transformer-sass": "^2.8.3",
"@parcel/transformer-webmanifest": "^2.8.3",
"@parcel/transformer-xml": "^2.8.3",
"hint": "^7.1.3",
"jest": "^29.4.3",
"jest-environment-jsdom": "^29.4.3",
"jsdom": "^21.1.0",
"parcel": "^2.8.3",
"parcel-reporter-static-files-copy": "^1.5.0"
},
"staticFiles": {
"staticPath": "src/img"
},
"scripts": {
"start": "npx parcel serve src/index.html --public-url / --dist-dir dist",
"prebuild": "rm -rf dist/",
"build": "npx parcel build --no-optimize --public-url ./",
"test": "jest --coverage --env=jsdom",
"webhint": "hint http://localhost:1234"
},
"repository": {
"type": "git",
"url": "git@git.unterdemradar.de:tobias/sr2ini.git"
},
"keywords": [
"Shadowrun",
"Initiative tracker",
"sr2e"
],
"jest": {
"setupFiles": [
"./tools/setup-jest.js"
]
},
"type": "module",
"dependencies": {
"@parcel/service-worker": "^2.8.3",
"augmented-ui": "^2.0.0",
"bootstrap": "^5.2.3",
"jquery": "^3.6.3"
}
}

32
scripts/backup Normal file
View File

@ -0,0 +1,32 @@
#!/bin/bash
#=================================================
# GENERIC START
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
source /usr/share/yunohost/helpers
#=================================================
# DECLARE DATA AND CONF FILES TO BACKUP
#=================================================
ynh_print_info --message="Declaring files to be backed up..."
#=================================================
# BACKUP THE APP MAIN DIR
#=================================================
ynh_backup --src_path="$install_dir"
#=================================================
# BACKUP THE NGINX CONFIGURATION
#=================================================
ynh_backup --src_path="/etc/nginx/conf.d/$domain.d/$app.conf"
#=================================================
# END OF SCRIPT
#=================================================
ynh_print_info --message="Backup script completed for $app. (YunoHost will then actually copy those files to the archive)."

24
scripts/change_url Normal file
View File

@ -0,0 +1,24 @@
#!/bin/bash
#=================================================
# GENERIC STARTING
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
source /usr/share/yunohost/helpers
#=================================================
# STANDARD MODIFICATIONS
#=================================================
# MODIFY URL IN NGINX CONF
#=================================================
ynh_script_progression --message="Updating NGINX web server configuration..." --weight=2
ynh_change_url_nginx_config
#=================================================
# END OF SCRIPT
#=================================================
ynh_script_progression --message="Change of URL completed for $app" --last

22
scripts/install Normal file
View File

@ -0,0 +1,22 @@
#!/bin/bash
source /usr/share/yunohost/helpers
#=================================================
# DOWNLOAD, CHECK AND UNPACK SOURCE
#=================================================
ynh_script_progression --message="Setting up source files…" --weight=1
ynh_setup_source --dest_dir="$install_dir"
chown -R $app:www-data "$install_dir"
chmod -R o-rwx "$install_dir"
#=================================================
# WEBSERVER CONFIGURATION
#=================================================
ynh_script_progression --message="Adding nginx configuration …" --weight=1
ynh_add_nginx_config

10
scripts/remove Normal file
View File

@ -0,0 +1,10 @@
#!/bin/bash
source /usr/share/yunohost/helpers
#=================================================
# REMOVE THE NGINX CONFIGURATION
#=================================================
ynh_script_progression --message="Removing nginx web server configuration..." --weight=1
ynh_remove_nginx_config

40
scripts/restore Normal file
View File

@ -0,0 +1,40 @@
#!/bin/bash
#=================================================
# GENERIC START
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
source /usr/share/yunohost/helpers
#=================================================
# RESTORE THE APP MAIN DIR
#=================================================
ynh_script_progression --message="Restoring the app main directory..." --weight=1
ynh_restore_file --origin_path="$install_dir"
# Restore permissions on app files
chown -R $app:www-data $install_dir
chmod -R o-rwx $install_dir
#=================================================
# RESTORE THE NGINX CONFIGURATION
#=================================================
ynh_script_progression --message="Restoring the NGINX configuration..." --weight=1
ynh_restore_file --origin_path="/etc/nginx/conf.d/$domain.d/$app.conf"
#=================================================
# RELOAD NGINX
#=================================================
ynh_script_progression --message="Reloading NGINX web server..." --weight=2
ynh_systemd_action --service_name=nginx --action=reload
#=================================================
# END OF SCRIPT
#=================================================
ynh_script_progression --message="Restoration completed for $app" --last

47
scripts/upgrade Normal file
View File

@ -0,0 +1,47 @@
#!/bin/bash
#=================================================
# GENERIC START
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
source /usr/share/yunohost/helpers
#=================================================
# CHECK VERSION
#=================================================
upgrade_type=$(ynh_check_app_version_changed)
#=================================================
# STANDARD UPGRADE STEPS
#=================================================
# DOWNLOAD, CHECK AND UNPACK SOURCE
#=================================================
if [ "$upgrade_type" == "UPGRADE_APP" ]
then
ynh_script_progression --message="Upgrading source files …" --weight=2
# Download, check integrity, uncompress and patch the source from app.src
ynh_setup_source --dest_dir="$install_dir"
fi
chown -R $app:www-data "$install_dir"
chmod -R o-rwx "$install_dir"
#=================================================
# REAPPLY SYSTEM CONFIGURATIONS
#=================================================
ynh_script_progression --message="Upgrading nginx configuration …" --weight=1
ynh_add_nginx_config
#=================================================
# END OF SCRIPT
#=================================================
ynh_script_progression --message="Upgrade of $app completed" --last

View File

@ -1,506 +0,0 @@
$fg: deeppink;
$fg-bright: lightpink;
$fg-dark: #b3005f;
$bg: cyan;
$bg-bright: lightcyan;
$bg-dark: darkcyan;
@font-face {
font-family: "Electrolize";
src: local("Electrolize"), url("../img/Electrolize-Regular.ttf") format("truetype"), url("https://fonts.googleapis.com/css2?family=Electrolize&display=swap");
}
@mixin aug() {
--aug-b: 5px;
--aug-bl: 5px;
--aug-br: 5px;
--aug-l: 5px;
--aug-r: 5px;
--aug-tl: 5px;
--aug-tr: 5px;
}
@mixin border() {
--aug-border-all: 2px;
--aug-border-bg: cyan; // variables don't work in this specific instance
--aug-border-opacity: .5;
}
@mixin inlay() {
--aug-inlay-bg: rgba(0, 0, 0, .5);
--aug-inlay-y: 10%;
}
@mixin button() {
background: transparent;
border: 1px solid $bg;
border-radius: 1px;
box-shadow: 0 0 2px $bg-bright, 0 0 4px $bg, 0 0 8px $bg-dark;
color: $fg;
padding-inline: 0px;
}
html {
font-family: 'Electrolize', sans-serif;
height: 100%;
margin: 0;
overflow-x: hidden;
user-select: none;
}
body {
background-color: darkslategray;
background-image: url("../img/bg.jpg");
background-repeat: no-repeat;
background-position: center center;
background-size: cover;
}
.icon { fill: $fg; }
.sr2-button:focus-visible {
border: 1px solid $bg !important;
box-shadow: 0 0 4px $bg-bright, 0 0 8px $bg, 0 0 16px $bg-dark !important;
outline: none;
}
.container {
padding: 4px;
position: relative;
}
header.navbar {
@include border;
@include inlay;
--aug-b: 7px;
--aug-bl: 7px;
--aug-br: 7px;
--aug-inlay-bg: rgba(0, 0, 0, .75);
--aug-l: 7px;
--aug-r: 7px;
--aug-tl: 7px;
--aug-tr: 7px;
padding-right: .6rem;;
.navbar-brand {
color: $bg;
text-shadow: 0 0 3px, 0 0 6px, 0 0 12px, 0 0 24px;
}
button {
@include button;
height: 38px;
margin-left: 3px;
margin-right: 3px;
width: 38px;
img {
width: 32px;
height: 32px;
filter: invert(34%) sepia(78%) saturate(7014%) hue-rotate(316deg) brightness(95%) contrast(105%);
}
}
}
.table-responsive { margin-bottom: 1px; }
.combatants-table {
margin-top: .5rem;
tr {
@include aug;
@include border;
@include inlay;
--aug-border-bottom: 0px;
--aug-border-right: 0px;
}
.combatant-row {
clip-path: none;
vertical-align: middle !important;
}
th:not(:first-of-type) { text-align: center; }
.th-ini { min-width: 3rem; }
.th-dice-and-rea { min-width: 3.75rem; }
.th-actions { min-width: 6.5rem; }
th,
td {
@include aug;
@include border;
@include inlay;
--aug-border-bottom: 0px;
--aug-border-right: 0px;
background: none !important;
}
th {
color: $bg;
text-transform: uppercase;
}
td {
clip-path: none;
color: $fg;
font-weight: bold;
}
th:first-of-type,
td:first-of-type {
padding-left: 1rem !important;
}
th:last-of-type,
td:last-of-type {
--aug-border-right: 2px;
padding-right: .75rem;
}
tr:last-of-type td,
tr:last-of-type th {
--aug-border-bottom: 2px;
}
.combatant-actions {
display: flex;
justify-content: flex-end;
}
.sr2-button {
@include button;
height: 24px;
margin-left: 3px;
margin-right: 3px;
width: 24px;
img,
.icon {
bottom: 3px;
height: 16px;
position: relative;
width: 16px;
}
&:disabled {
box-shadow: none;
border-color: $bg-dark;
.icon { fill: $fg-dark; }
}
}
.combatant-ini {
padding-right: 1rem !important;
text-align: center;
}
.combatant-dice-and-rea { text-align: center; }
.combatant-dice::before { content: attr(data-combatant-dice); }
.combatant-rea::before { content: attr(data-combatant-rea); }
.actions-menu {
display: flex;
flex-flow: column;
padding: 6px;
button {
@include button;
height: 24px;
margin: 4px;
width: 24px;
}
}
}
.max-ini td {
text-shadow: 0 0 .15em $fg;
}
.zero-ini td {
color: $fg-dark !important;
.icon { fill: $fg-dark !important; }
}
.ko-or-dead td {
background-color: rgba(0, 0, 0, .5);
color: $fg-dark !important;
text-decoration: line-through .1em $fg;
.icon { fill: $fg-dark !important; }
}
.badge.bg-warning {
background: radial-gradient(circle at center, $bg, $bg-dark);
bottom: -4px;
left: 12px;
width: 20px;
}
.badge.bg-danger {
background: radial-gradient(circle at center, $fg, $fg-dark);
left: 12px;
top: 12px;
width: 20px;
}
.damage-dropdown,
.actions-dropdown { display: inline-block; }
.damage-monitor,
.actions-menu {
@include aug;
@include border;
--aug-inlay-bg: rgba(0, 0, 0, .5);
--aug-border-opacity: .75;
padding-top: 10px;
padding-bottom: 10px;
position: absolute;
top: calc(100% - 2px);
transition: transform .25s ease-in-out;
z-index: 200;
}
.damage-monitor { left: calc(100vw - 1em); }
.actions-menu { left: calc(100vw + 3.5em); }
.seen { transform: translateX(-100vw); }
.damage-monitor {
button {
border: none;
border-radius: 0;
font-size: smaller;
height: 24px;
margin: 0px 2px;
width: 24px;
&:focus-visible {
filter: brightness(150%) !important;
outline: none;
}
}
.damage-stun {
background: radial-gradient(circle at center, $bg, $bg-dark);
box-shadow: none;
transition: background .5s, box-shadow .5s;
}
.damage-stun.active {
background: radial-gradient(circle at center, $bg-bright, $bg);
box-shadow: 0 0 3px $bg-bright, 0 0 6px $bg, 0 0 12px $bg-dark;
}
.damage-physical {
background: radial-gradient(circle at center, $fg, $fg-dark);
border-radius: 50%;;
box-shadow: none;
transition: background .5s, box-shadow .5s;
&:focus-visible { outline: $fg !important; }
}
.damage-physical.active {
background: radial-gradient(circle at center, $fg-bright, $fg);
box-shadow: 0 0 3px $fg-bright, 0 0 6px $fg, 0 0 12px $fg-dark;
}
}
.footer-container {
bottom: 0px;
left: 0px;
position: fixed;
right: 0px;
}
footer {
@include aug;
@include inlay;
--aug-border-all: 2px !important;
--aug-border-bg: cyan !important; // vars don't work here
--aug-border-opacity: .5 !important;
--aug-inlay-bg: rgba(0, 0, 0, .75) !important;
--aug-tl: 10px;
--aug-tr: 10px;
height: 2.5em;
p {
color: $bg;
font-size: xx-small;
margin: .25rem;
padding-top: .65em;
text-align: center;
user-select: auto;
a {
color: hotpink;
text-decoration: none;
}
}
}
.sr2-modal {
@include border;
@include aug;
color: $bg;
pointer-events: auto;
button { @include button; }
.modal-header {
@include inlay;
@include border;
--aug-inlay-bottom: 0;
border-bottom: none;
text-transform: uppercase;
button { width: 30px; }
}
.modal-body {
@include inlay;
@include border;
--aug-inlay-y: 0;
}
.modal-footer {
@include inlay;
@include border;
--aug-inlay-top: 0;
border-top: none;
button { width: 4rem; }
}
label { margin: 0; }
.label-swap {
display: flex;
flex-flow: column;
label { order: 2; }
input { order: 1; }
}
input {
background-color: transparent;
color: $fg;
user-select: text;
&::selection {
background-color: $fg;
color: black;
}
&:focus {
background-color: transparent;
color: $fg;
}
&:focus-visible {
background-color: transparent;
color: $fg;
}
&:not([type=range]):valid {
background-image: none !important;
border: 1px solid $bg !important ;
}
&:invalid {
background-image: none !important;
border: 1px solid $fg;
box-shadow: 0 0 3px $fg-bright, 0 0 6px $fg, 0 0 12px $fg-dark;
}
&[type=number]::-webkit-inner-spin-button,
&[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
&[type=number] {
-moz-appearance: textfield;
appearance: textfield;
margin: 0;
}
&[type=range] {
// -webkit-appearance: none;
margin-left: calc(4.5% - 2px);
width: 91%;
+ datalist {
display: block;
width: 100%;
option {
display: inline-block;
margin: 0;
text-align: center;
width: 9%;
}
}
&::-moz-range-track {
border: 1px;
border-radius: 1px;
height: 2px;
}
&::-moz-range-thumb {
height: 20px;
width: 4px;
}
}
}
.input-group-text {
background-color: transparent;
border: none;
color: $fg;
}
hr {
border-top: 2px solid $bg;
opacity: .5;
}
}
#combatant-modal-stun {
&::-moz-range-track { background-color: $bg-dark; }
&::-moz-range-thumb { background-color: $bg; }
}
#combatant-modal-physical {
&::-moz-range-track { background-color: $fg-dark; }
&::-moz-range-thumb { background-color: $fg; }
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 620 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,130 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<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">
<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">
<script type="module" src="js/sr2ini.js" defer></script>
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png">
<link rel="manifest" href="/icons/sr2ini.webmanifest">
<link rel="shortcut icon" href="/icons/favicon.ico">
<meta name="msapplication-TileColor" content="#004aa5">
<meta name="msapplication-config" content="/icons/browserconfig.xml">
<meta name="theme-color" content="#004aa5">
</head>
<body>
<div class="container">
<header class="navbar navbar-expand" data-augmented-ui="tl-2-clip-x tr-clip-y bl-clip-y br-2-clip-x b-scoop-x both">
<span class="navbar-brand ps-4">SR2 Initiative Tracker</span>
<nav class="container-fluid justify-content-end" aria-label="Main navigation">
<button type="submit" class="sr2-button" id="add-combatant-button" title="Add combatant" data-bs-toggle="modal" data-bs-target="#combatant-modal"><img src="img/add.png" alt="user outline with a plus sign"></button>
<button type="submit" class="sr2-button" id="new-round-button" title="Start new round" data-bs-toggle="modal" data-bs-target="#confirm-modal"><img src="img/newround.png" alt="a die being rolled"></button>
</nav>
</header>
</div>
<div class="container">
<main class="table-responsive overflow-visible">
<table class="table table-sm table-borderless combatants-table">
<thead>
<tr data-augmented-ui="tl-2-clip-y r-clip-y">
<th class="col th-name" data-augmented-ui="tl-2-clip-y both" title="Name">Name</th>
<th class="col-2 th-ini" data-augmented-ui="both" title="Initiative">Ini</th>
<th class="col-2 th-dice-and-rea" data-augmented-ui="both" title="Initiative Dice and Reaction">D+R</th>
<th class="col-3 th-actions" data-augmented-ui="r-clip-y both" title="Actions">Actions</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</main>
</div>
<div class="footer-container">
<footer data-augmented-ui="tl-2-clip-x tr-2-clip-x both">
<p>code & design by <a href="https://tobias-radloff.de/" tabindex="-1" title="Eclipse">Eclipse</a> | Shadowrun trademarked by <a href="https://www.topps.com/" tabindex="-1" title="Topps">Topps</a><br>
icons by <a href="https://www.freepik.com" tabindex="-1" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" tabindex="-1" title="Flaticon">www.flaticon.com</a> | background by <a href="https://www.deviantart.com/xxaries1970xx" tabindex="-1" title="xxAries1970xx on DeviantArt">xxAries1970xx</a></p>
</footer>
</div>
<div class="modal fade" id="confirm-modal" tabindex="-2" role="dialog">
<div class="modal-dialog modal-sm" role="document">
<div class="sr2-modal" data-augmented-ui="tl-2-clip-x tr-clip-y bl-clip-y br-2-clip-x b-scoop-x border">
<div class="modal-header" data-augmented-ui="inlay">
<h5 class="modal-title">Start New Round</h5>
<button type="button" class="sr2-button" data-bs-dismiss="modal" aria-label="Close">&#10006;</button>
</div>
<div class="modal-body" data-augmented-ui="inlay">
<p>Are you sure?</p>
</div>
<div class="modal-footer" data-augmented-ui="inlay">
<button type="button" class="sr2-button" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="sr2-button" id="confirm-modal-new-round-ok-button" data-bs-dismiss="modal">OK</button>
<button type="submit" class="sr2-button d-none" id="confirm-modal-remove-combatant-ok-button" data-bs-dismiss="modal">OK</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="combatant-modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-sm" role="document">
<div class="sr2-modal" data-augmented-ui="tl-2-clip-x tr-clip-y bl-clip-y br-2-clip-x b-scoop-x border">
<div class="modal-header" data-augmented-ui="inlay">
<h5 class="modal-title">Add New Combatant</h5>
<button type="button" class="sr2-button" data-bs-dismiss="modal" aria-label="Close">&#10006;</button>
</div>
<div class="modal-body" data-augmented-ui="inlay">
<form id="combatant-form" name="combatant-modal-form" class="was-validated" onsubmit="return false;">
<div class="my-2">
<input type="text" maxlength="40" class="form-control form-control-sm" id="combatant-modal-name" form="combatant-form" placeholder="Name" required>
</div>
<div class="input-group input-group-sm my-2">
<input type="number" min="1" max="5" class="form-control form-control-sm" id="combatant-modal-dice" form="combatant-form" placeholder="Dice">
<span class="input-group-text">D+</span>
<input type="number" min="1" max="25" class="form-control form-control-sm" id="combatant-modal-rea" form="combatant-form" placeholder="REA">
<span class="input-group-text">&nbsp;&nbsp;&nbsp;</span>
<input type="number" min="0" max="55" class="form-control form-control-sm" id="combatant-modal-ini" form="combatant-form" placeholder="Ini">
</div>
<div class="my-2">
<label for="combatant-modal-stun" class="form-label">Stun Damage <span id="combatant-modal-penalty-stun"></span></label>
<input type="range" class="form-range" min="0" max="10" value="0" id="combatant-modal-stun" list="damage">
<datalist id="damage">
<option>-</option><option>L</option><option>.</option><option>M</option><option>.</option><option>.</option><option>S</option><option>.</option><option>.</option><option>.</option><option>D</option>
</datalist>
<div class="label-swap">
<label for="combatant-modal-physical" class="form-label">Physical Damage <span id="combatant-modal-penalty-physical"></span></label>
<input type="range" class="form-range" min="0" max="10" value="0" id="combatant-modal-physical" list="damage">
</div>
</div>
</form>
</div>
<div class="modal-footer" data-augmented-ui="inlay">
<button type="button" class="sr2-button" id="combatant-modal-cancel-button" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="sr2-button" id="combatant-modal-add-apply-button">Apply</button>
<button type="submit" class="sr2-button" id="combatant-modal-add-ok-button" data-bs-dismiss="modal">OK</button>
<button type="submit" class="sr2-button d-none" id="combatant-modal-edit-ok-button" data-bs-dismiss="modal">OK</button>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,38 +0,0 @@
import {manifest, version} from '@parcel/service-worker';
console.log("[Service Worker] Version:", version);
console.log("[Service Worker] Manifest:", manifest);
// install 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()));
// fetch content
/*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;
})());
});*/
// activate: clear outdated files from cache
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()));

View File

@ -1,506 +0,0 @@
// 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
*/
const DAMAGE_PENALTY = [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4];
const DAMAGE_NIVEAU = ["", "L", "M", "S", "D"];
const COMBATANT_TABLE_ROW = [
'<tr class="combatant-row" data-true-ini="" data-augmented-ui="tl-scoop bl-clip-y tr-clip-y br-scoop">\n', //TODO: add data-damage-* attributes with initial damage levels
'<td class="combatant-name" title="Combatant\'s name" data-bs-toggle="modal" data-bs-target="#combatant-modal" data-augmented-ui="tl-scoop bl-clip-y both"></td>\n',
'<td class="combatant-ini" title="Effective initiative (w/ wound penalties)" data-bs-toggle="modal" data-bs-target="#combatant-modal" data-augmented-ui="both"></td>\n',
'<td class="combatant-dice-and-rea" title="Iniative dice and reaction" data-bs-toggle="modal" data-bs-target="#combatant-modal" data-augmented-ui="both"><span class="combatant-dice"></span>D+<span class="combatant-rea"></span></td>\n',
'<td class="combatant-actions" data-augmented-ui="tr-clip-y br-scoop both">\n',
'<button type="button" class="sr2-button act-button" title="Act and reduce ini by 10"><svg class="icon" viewBox="0 0 512 512"><path d="M 0 272 h 96 v 64 h -96 Z" /><path d="M 160 64 h 64 v 384 h -64 v -296 l -64 64 l -40 -40 Z" /><path d="M 352 64 h 96 l 64 64 v 256 l -64 64 h -96 l -64 -64 v -256 l 64 -64 l 32 64 h 32 l 32 32 v 192 l -32 32 h -32 l -32 -32 v -192 l 32 -32" fill-rule="evenodd" /></svg></button>\n',
'<div class="damage-dropdown">\n',
'<button type="button" class="sr2-button damage-button" title="Take damage"><svg class="icon" viewBox="0 0 512 512"><path d="M 0 288 L 144 224 L 64 32 L 224 128 L 272 0 L 336 144 L 480 96 L 400 224 L 512 304 L 384 352 L 432 512 L 272 416 L 128 512 L 160 352 L 0 288 L 166 267 L 222 290 L 211 346 L 262 312 L 318 346 L 301 290 L 346 273 L 306 245 L 334 200 L 284 217 L 262 166 L 245 211 L 189 178 L 217 245 L 166 267" fill-rule="evenodd" /></svg></button>\n',
'</div>\n',
'<div class="actions-dropdown">\n',
'<button type="button" class="sr2-button actions-button" title="More actions"><svg class="icon" viewBox="0 0 512 512"><polygon points="32 32 480 32 256 480" /></svg></button>\n',
'<div class="actions-menu" data-augmented-ui="tl-scoop bl-clip-y tr-clip-y br-scoop both">\n',
'<button type="button" class="sr2-button edit-button" title="Edit combatant" data-bs-toggle="modal" data-bs-target="#combatant-modal" tabindex="-1"><svg class="icon" viewBox="0 0 512 512"><polygon points="0 512 0 352 224 128 384 288 160 512" /><polygon points="352 0 512 160 416 256 256 96" /></svg></button>\n',
'<button type="button" class="sr2-button clone-button" title="Clone combatant" data-bs-toggle="modal" data-bs-target="#combatant-modal" tabindex="-1"><svg class="icon" viewBox="0 0 512 512"><rect x="0" y="0" width="512" height="128" /><rect x="0" y="384" width="512" height="128" /><polygon points="128 192 384 192 256 320" />/svg></button>\n',
'<button type="button" class="sr2-button remove-button" title="Remove combatant" data-bs-toggle="modal" data-bs-target="#confirm-modal" tabindex="-1"><svg class="icon" viewBox="0 0 512 512"><polygon points="96 0 416 0 416 64 480 64 480 128 32 128 32 64 96 64" /><path d="M 64 512 H 448 V 160 H 64 V 512 L 128 480 V 192 H 192 V 480 H 224 V 192 H 288 V 480 H 320 V 192 H 384 V 480 H 128" fill-rule="evenodd" /></svg></button>\n',
'</div>\n',
'</div>\n',
'</td>\n',
'</tr>'].join("");
const DAMAGE_MONITOR_HTML = [
'<div class="damage-monitor" data-augmented-ui="tl-scoop bl-clip-y tr-clip-y br-scoop both">\n',
'<table>\n',
'<tr><td><button type="button" class="damage-stun active" title="Light stun damage" tabindex="-1">L</button></td><td><button type="button" class="damage-physical active" title="Light physical damage" tabindex="-1">L</button></td></tr>\n',
'<tr><td><button type="button" class="damage-stun active" tabindex="-1"></button></td><td><button type="button" class="damage-physical active" tabindex="-1"></button></td></tr>\n',
'<tr><td><button type="button" class="damage-stun active" title="Medium stun damage" tabindex="-1">M</button></td><td><button type="button" class="damage-physical active" title="Medium physical damage" tabindex="-1">M</button></td></tr>\n',
'<tr><td><button type="button" class="damage-stun active" tabindex="-1"></button></td><td><button type="button" class="damage-physical active" tabindex="-1"></button></td></tr>\n',
'<tr><td><button type="button" class="damage-stun active" tabindex="-1"></button></td><td><button type="button" class="damage-physical active" tabindex="-1"></button></td></tr>\n',
'<tr><td><button type="button" class="damage-stun active" title="Severe stun damage" tabindex="-1">S</button></td><td><button type="button" class="damage-physical active" title="Severe physical damage" tabindex="-1">S</button></td></tr>\n',
'<tr><td><button type="button" class="damage-stun active" tabindex="-1"></button></td><td><button type="button" class="damage-physical active" tabindex="-1"></button></td></tr>\n',
'<tr><td><button type="button" class="damage-stun active" tabindex="-1"></button></td><td><button type="button" class="damage-physical active" tabindex="-1"></button></td></tr>\n',
'<tr><td><button type="button" class="damage-stun active" tabindex="-1"></button></td><td><button type="button" class="damage-physical active" tabindex="-1"></button></td></tr>\n',
'<tr><td><button type="button" class="damage-stun active" title="K.O." tabindex="-1"><img src="zzz.png" height="16" /></button></td><td><button type="button" class="damage-physical active" title="dead" tabindex="-1" ><img src="cross.png" height="16"/></button></td></tr>\n',
'</table>\n',
'</div>'].join("");
const STUN_BADGE_HTML = '<sup><span class="badge bg-warning position-absolute translate-middle stun-badge" title="Stun damage niveau"></span></sup>';
const PHYSICAL_BADGE_HTML = '<sub><span class="badge bg-danger position-absolute translate-middle physical-badge" title="Physical damage niveau"></span></sub>';
/*
* helper functions
*/
// roll for initiative with the given reaction and number of ini dice
function rollForInitiative(dice, rea) {
let diceRolls = Array.from({ length: parseInt(dice) }, () => Math.ceil(Math.random() * 6));
return diceRolls.reduce((a, b) => a + b, 0) + parseInt(rea);
}
// figure out whose action comes first out of two combatants a and b
function whoGoesFirst(a, b) {
// check for K.O./death
let tmpA = $(a).hasClass("ko-or-dead") ? -1 : parseInt($(a).find(".combatant-ini").text()) || 0;
let tmpB = $(b).hasClass("ko-or-dead") ? -1 : parseInt($(b).find(".combatant-ini").text()) || 0;
// compare ini
let compIni = tmpB - tmpA;
if (compIni != 0) {
return compIni;
}
// tie; compare reaction
else {
if ($(a).find(".combatant-rea").text() == "" || $(b).find(".combatant-rea").text() == "") {
return 0;
} else {
return parseInt($(b).find(".combatant-rea").text()) - parseInt($(a).find(".combatant-rea").text());
}
}
}
// compute a combatant's effective ini value (modified by wound penalties)
function getEffectiveIni(tr) {
// return -1 if combatant is K.O. or dead
if ($(tr).hasClass("ko-or-dead")) {
return -1;
}
// otherwise compute effective ini (true ini minus wound penalties)
let effectiveIni = parseInt($(tr).attr("data-true-ini")) - DAMAGE_PENALTY[parseInt($(tr).attr("data-damage-stun")) || 0] - DAMAGE_PENALTY[parseInt($(tr).attr("data-damage-physical")) || 0];
return Math.max(effectiveIni, 0);
}
// add test combatant for testing purposes (duh)
function addTestCombatant() {
// Eclipse
$("#add-combatant-button").click();
$("#combatant-modal-name").val("Eclipse");
$("#combatant-modal-dice").val(3);
$("#combatant-modal-rea").val(6);
// $("#combatant-modal-ini").val(12);
addCombatant();
// setTimeout( () => $("#combatant-modal-add-ok-button").click(), 500);
}
/*
* Event handler functions
*/
// click handler for act buttons; reduces ini by 10
function handleActButtonClick(e) {
// reduce ini by 10 but not lower than 0
let ini = Math.max(parseInt($(e.target).parents(".combatant-row").attr("data-true-ini")) - 10, 0);
// set new ini value
$(e.target).parents(".combatant-row").attr("data-true-ini", ini);
// resort table
sortTable();
}
// click handler for add buttons
function handleAddButtonClick(e) {
// restyle modal
$("#combatant-modal .modal-title").text("Add Combatant");
$("#combatant-modal-add-ok-button, #combatant-modal-add-apply-button").removeClass("d-none");
$("#combatant-modal-edit-ok-button").addClass("d-none");
// set default values
$("#combatant-modal-name").val("Goon 1");
$("#combatant-modal-dice").val("2");
$("#combatant-modal-rea").val("7");
// set damage sliders to zero
$("#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", (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) {
// find current table row
let $tr = $(e.target).parents(".combatant-row");
// hide actions menu
$tr.find(".actions-menu").removeClass("seen");
// restyle modal
$("#combatant-modal .modal-title").text("Clone Combatant");
$("#combatant-modal-add-ok-button, #combatant-modal-add-apply-button").removeClass("d-none");
$("#combatant-modal-edit-ok-button").addClass("d-none");
// populate modal with values from row
$("#combatant-modal-name").val($tr.find(".combatant-name").text());
$("#combatant-modal-dice").val($tr.find(".combatant-dice").attr("data-combatant-dice"));
$("#combatant-modal-rea").val($tr.find(".combatant-rea").attr("data-combatant-rea"));
$("#combatant-modal-ini").val($tr.attr("data-true-ini"));
$("#combatant-modal-stun").val($tr.attr("data-damage-stun") || "0");
$("#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", (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) {
// get visibility status at click time
let seenAtClick = $(e.target).parents(".damage-dropdown").find(".damage-monitor").hasClass("seen");
// hide all damage monitors and actions menus
$(".damage-monitor.seen, .actions-menu.seen").removeClass("seen").find("button").attr("tabindex", "-1");
// if targeted dm was hidden before, show it now
if (! seenAtClick) {
$(e.target).parents(".damage-dropdown").find(".damage-monitor").addClass("seen").find("button").attr("tabindex", "0");
}
return false;
}
// click handler for edit buttons
function handleEditButtonClick(e) {
// find current table row
let $tr = $(e.target).parents(".combatant-row");
// restyle modal
$("#combatant-modal .modal-title").text("Edit Combatant");
$("#combatant-modal-edit-ok-button").removeClass("d-none");
$("#combatant-modal-add-ok-button, #combatant-modal-add-apply-button").addClass("d-none");
// populate modal with values from row
$("#combatant-modal-name").val($tr.find(".combatant-name").text());
$("#combatant-modal-dice").val($tr.find(".combatant-dice").attr("data-combatant-dice"));
$("#combatant-modal-rea").val($tr.find(".combatant-rea").attr("data-combatant-rea"));
$("#combatant-modal-ini").val($tr.attr("data-true-ini"));
$("#combatant-modal-stun").val($tr.attr("data-damage-stun") || "0");
$("#combatant-modal-physical").val($tr.attr("data-damage-physical") || "0");
// mark which row is being edited
$("#combatant-modal").data("row", $(".combatant-row").index($tr)); // here it's okay to use the jQuery data() function (which is not the same as using a data attribute) b/c this value is used only in this script and not via HTML or CSS
// add handler for enter key
$("#combatant-modal input[id*='combatant-modal']").off("keydown");
$("#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) {
// get visibility status at click time
let seenAtClick = $(e.target).parents(".actions-dropdown").find(".actions-menu").hasClass("seen");
// hide all damage monitors
$(".actions-menu.seen, .damage-monitor.seen").removeClass("seen").find("button").attr("tabindex", "-1");
// if targeted dm was seen before, show it now
if (! seenAtClick) {
$(e.target).parents(".actions-dropdown").find(".actions-menu").addClass("seen").find("button").attr("tabindex", "0");
}
return false;
}
function handleNewRoundButton(e) {
// restyle modal
$("#confirm-modal .modal-title").text("Start new Round");
$("#confirm-modal-new-round-ok-button").removeClass("d-none");
$("#confirm-modal-remove-combatant-ok-button").addClass("d-none");
}
// click handler for remove buttons
function handleRemoveButtonClick(e) {
// restyle modal
$("#confirm-modal .modal-title").text("Remove Combatant");
$("#confirm-modal-remove-combatant-ok-button").removeClass("d-none");
$("#confirm-modal-new-round-ok-button").addClass("d-none");
// mark which row is being removed
$("#confirm-modal").data("row", $(".combatant-row").index($(e.target).parents(".combatant-row"))); // here it's okay to use .data() b/c HTML/CSS does not care about this value
}
/*
* Main functions
*/
// add new combatant
function addCombatant(e) {
// e.preventDefault();
// validate form
if (!validateCombatant()) {
return false;
}
// roll for initiative if necessary
let ini = $("#combatant-modal-ini").val().trim();
ini = (ini != "") ? ini : rollForInitiative($("#combatant-modal-dice").val(), $("#combatant-modal-rea").val());
// construct jQuery object for table row
let $tr = $($.parseHTML(COMBATANT_TABLE_ROW));
$tr.find(".damage-dropdown").append($.parseHTML(DAMAGE_MONITOR_HTML));
// populate table row with values from modal
$tr.attr("data-true-ini", ini);
$tr.find(".combatant-name").text($("#combatant-modal-name").val().trim());
$tr.find(".combatant-dice").attr("data-combatant-dice", $("#combatant-modal-dice").val().trim());
$tr.find(".combatant-rea").attr("data-combatant-rea", $("#combatant-modal-rea").val().trim());
// retrieve initial damage levels
$tr.attr("data-damage-stun", $("#combatant-modal-stun").val() || "0");
$tr.find(".damage-stun").addClass("active").slice(0, parseInt($tr.attr("data-damage-stun")) || 0).removeClass("active");
$tr.attr("data-damage-physical", $("#combatant-modal-physical").val() || "0");
$tr.find(".damage-physical").addClass("active").slice(0, parseInt($tr.attr("data-damage-physical")) || 0).removeClass("active");
// add event handlers
$tr.find("button.act-button").on("click", handleActButtonClick);
$tr.find("button.damage-button").on("click", handleDamageButtonClick);
$tr.find("button.actions-button").on("click", handleMoreActionsButtonClick);
$tr.find("button.edit-button, .combatant-name, .combatant-ini, .combatant-dice-and-rea").on("click", handleEditButtonClick);
$tr.find("button.clone-button").on("click", handleCloneButtonClick);
$tr.find("button.remove-button").on("click", handleRemoveButtonClick);
// add event handler to damage monitor
$tr.find(".damage-stun, .damage-physical").on("click", applyDamage);
// append row to table and sort
$(".combatants-table").append($tr);
sortTable();
}
// apply damage to combatant
function applyDamage(e) {
let $btn = $(e.target).is("button") ? $(e.target) : $(e.target).parent();
// retrieve new damage level and type from button position and "damage-[type]" class
let damageLevel = $btn.parent().parent().index();
if ( $btn.hasClass("active") ) {
damageLevel += 1;
}
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");
sortTable();
}
// edit combatant
function editCombatant(e) {
console.log("editing combatant …")
// e.preventDefault();
// validate form
if (!validateCombatant()) {
return false;
}
// get values
let name = $("#combatant-modal-name").val().trim();
let ini = $("#combatant-modal-ini").val().trim();
let dice = $("#combatant-modal-dice").val().trim();
let rea = $("#combatant-modal-rea").val().trim();
// roll for initiative if ini is empty
ini = (ini != "") ? ini : rollForInitiative(dice, rea);
// get correct row
let index = parseInt($("#combatant-modal").data("row"));
console.log("row index is", index);
let $tr = $("tr.combatant-row").eq(index);
console.log("row is", $tr);
// set new values
$tr.attr("data-true-ini", ini);
$tr.find(".combatant-name").text(name);
$tr.find(".combatant-dice").attr("data-combatant-dice", dice);
$tr.find(".combatant-rea").attr("data-combatant-rea", rea);
$tr.attr("data-damage-stun", $("#combatant-modal-stun").val() || "0");
$tr.find(".damage-stun").addClass("active").slice(0, parseInt($tr.attr("data-damage-stun")) || 0).removeClass("active");
$tr.attr("data-damage-physical", $("#combatant-modal-physical").val() || "0");
$tr.find(".damage-physical").addClass("active").slice(0, parseInt($tr.attr("data-damage-physical")) || 0).removeClass("active");
// sort table
sortTable();
// clean up
$("#combatant-modal").data("row", "");
}
// remove combatant
function removeCombatant(e) {
e.preventDefault();
// remove correct row
let index = parseInt($("#confirm-modal").data("row"));
$(".combatant-row").eq(index).remove();
sortTable();
// clean up
$("#confirm-modal").data("row", "");
}
// start a new combat round
function startNewRound(e) {
e.preventDefault();
// are there rows at all?
if ($(".combatant-row").length == 0) {
return;
}
// reset ini values
$(".combatant-row").each(function () {
if ($(this).find(".combatant-dice").attr("data-combatant-dice") == "") {
$(this).attr("data-true-ini", 1);
} else {
$(this).attr("data-true-ini", rollForInitiative(parseInt($(this).find(".combatant-dice").attr("data-combatant-dice")), parseInt($(this).find(".combatant-rea").attr("data-combatant-rea"))));
}
});
// resort table
sortTable();
}
// sort combatants by ini value and add contextual classes
function sortTable() {
// do some clean up: remove previous classes from rows, disable act buttons, remove effective ini and damage badges
$(".combatant-row").removeClass("ko-or-dead max-ini zero-ini"); //REGULAR_INI
$(".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() {
if (parseInt($(this).attr("data-damage-stun")) == 10 || parseInt($(this).attr("data-damage-physical")) == 10) {
$(this).addClass("ko-or-dead");
}
});
// compute highest effective ini
let iniMax = Math.max.apply(null, $.map($(".combatant-row"), function (tr, i) {
// write current effective ini to table row
$(tr).find(".combatant-ini").text($(tr).hasClass("ko-or-dead") ? 0 : getEffectiveIni($(tr)));
return $(tr).find(".combatant-ini").text();
}));
// add damage badges and contextual classes
$(".combatant-row").each(function () {
// damage badges
if ($(this).attr("data-damage-stun") && $(this).attr("data-damage-stun") != "0") {
$(this).find(".combatant-ini").append($.parseHTML(STUN_BADGE_HTML));
$(this).find(".stun-badge").append(DAMAGE_NIVEAU[DAMAGE_PENALTY[$(this).attr("data-damage-stun")]]);
}
if ($(this).attr("data-damage-physical") && $(this).attr("data-damage-physical") != "0") {
$(this).find(".combatant-ini").append($.parseHTML(PHYSICAL_BADGE_HTML));
$(this).find(".physical-badge").append(DAMAGE_NIVEAU[DAMAGE_PENALTY[$(this).attr("data-damage-physical")]]);
}
// K.O./dead -> don't add anything
if ($(this).hasClass("ko-or-dead")) {
return true;
}
// ini = zero
if (parseInt($(this).find(".combatant-ini").text()) == 0) {
$(this).addClass("zero-ini");
return true;
}
// ini = max and non-zero
if (parseInt($(this).find(".combatant-ini").text()) == iniMax && iniMax > 0) {
$(this).addClass("max-ini").find(".act-button").prop("disabled", false).removeAttr("aria-disabled");
return true;
}
})
// sort rows and append them in new order
let $rows = $(".combatant-row").toArray().sort(whoGoesFirst);
for (let i = 0; i < $rows.length; i++) {
$(".combatants-table").append($rows[i]);
$($rows[i]).css("z-index", 50-i).css("position", "relative");
}
return;
}
// validate a combatant row form by checking for all conditions, including regular HTML5 validation
function validateCombatant() {
// get input elements
let inputElements = {
name: $("#combatant-modal-name").get(0),
ini: $("#combatant-modal-ini").get(0),
dice: $("#combatant-modal-dice").get(0),
rea: $("#combatant-modal-rea").get(0)
};
// 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( input => {
if (!input.reportValidity()) { valid = 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();
let rea = inputElements["rea"].value.trim();
// invalidate if ini, dice and rea are all empty
if (ini == "" && (dice == "" || rea == "")) {
inputElements["ini"].setCustomValidity("Requiring values for ini dice and reaction, or initiative, or all three");
inputElements["ini"].reportValidity();
inputElements["ini"].setCustomValidity("");
return false;
}
// invalidate if dice or rea is empty but not both
if ((dice == "") != (rea == "")) {
inputElements["dice"].setCustomValidity("Values required for both dice and reaction, or none (in which case ini is required)");
inputElements["dice"].reportValidity();
inputElements["dice"].setCustomValidity("");
return false;
}
// ok then
return true;
}
/*
* Initialize document
*/
$(document).ready(function () {
// add event handlers to navbar buttons
$("#add-combatant-button").on("click", handleAddButtonClick);
$("#new-round-button").on("click", handleNewRoundButton);
// add event handlers to modal buttons
$("#combatant-modal-add-ok-button, #combatant-modal-add-apply-button").on("click", addCombatant);
$("#combatant-modal-edit-ok-button").on("click", editCombatant);
$("#confirm-modal-new-round-ok-button").on("click", startNewRound);
$("#confirm-modal-remove-combatant-ok-button").on("click", removeCombatant);
// add event listeners to damage sliders in combatant modal
$("#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", () => {
if ($("#combatant-modal-physical").val() == "10") {
$("#combatant-modal-penalty-physical").text("(dead)");
} else {
$("#combatant-modal-penalty-physical").text("(wound penalty -" + DAMAGE_PENALTY[$("#combatant-modal-physical").val()] + ")");
}
});
// always focus name input field when combatant modal appears
$('#combatant-modal').on('shown.bs.modal', () => $('#combatant-modal-name').focus());
// always empty input fields when combatant modal disappears
$("#combatant-modal").on('hidden.bs.modal', () => $("input[id*='combatant-modal']").val(""));
// Hide damage monitors after click somewhere else
$("html").on("click", (e) => {
if ($(e.target).parents(".damage-monitor").length == 0) {
$(".damage-monitor.seen").removeClass("seen");
}
});
addTestCombatant();
});
module.exports = { rollForInitiative, validateCombatant };

3
tests.toml Normal file
View File

@ -0,0 +1,3 @@
test_format = 1.0
[default]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,31 +0,0 @@
body {
margin: 0;
height: 100%;
background-color: #004aa5;
}
div {
width: 60%;
margin: auto;
}
button {
position: relative;
top: 100px;
border: 8px solid cyan;
border-radius: 16px;
padding: 16px;
background-color: rgba(0, 0, 0, .75);
box-shadow: 0 0 16px lightcyan, 0 0 32px lightcyan, 0 0 32px cyan, 0 0 64px cyan, 0 0 64px darkcyan, 0 0 128px darkcyan;
}
img {
width: 384px;
height: 384px;
filter: invert(34%) sepia(78%) saturate(7014%) hue-rotate(316deg) brightness(125%) contrast(105%);
}

View File

@ -1,4 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head><link type="text/css" rel="stylesheet" href="tmp.css"></head>
<body><div><button><img src="dice512.png"></button></div></body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -1 +0,0 @@
{"result":{"status":"success"},"favicon":{"package_url":"https://realfavicongenerator.net/files/d925f2495c2cc5046691e320cba3357e995bcbb8/favicon_package_v0.16.zip","files_urls":["https://realfavicongenerator.net/files/d925f2495c2cc5046691e320cba3357e995bcbb8/package_files/android-chrome-192x192.png","https://realfavicongenerator.net/files/d925f2495c2cc5046691e320cba3357e995bcbb8/package_files/android-chrome-512x512.png","https://realfavicongenerator.net/files/d925f2495c2cc5046691e320cba3357e995bcbb8/package_files/apple-touch-icon.png","https://realfavicongenerator.net/files/d925f2495c2cc5046691e320cba3357e995bcbb8/package_files/browserconfig.xml","https://realfavicongenerator.net/files/d925f2495c2cc5046691e320cba3357e995bcbb8/package_files/favicon-16x16.png","https://realfavicongenerator.net/files/d925f2495c2cc5046691e320cba3357e995bcbb8/package_files/favicon-32x32.png","https://realfavicongenerator.net/files/d925f2495c2cc5046691e320cba3357e995bcbb8/package_files/favicon.ico","https://realfavicongenerator.net/files/d925f2495c2cc5046691e320cba3357e995bcbb8/package_files/mstile-150x150.png","https://realfavicongenerator.net/files/d925f2495c2cc5046691e320cba3357e995bcbb8/package_files/site.webmanifest"],"html_code":"<link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/icons/apple-touch-icon.png\">\n<link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/icons/favicon-32x32.png\">\n<link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/icons/favicon-16x16.png\">\n<link rel=\"manifest\" href=\"/icons/site.webmanifest\">\n<link rel=\"shortcut icon\" href=\"/icons/favicon.ico\">\n<meta name=\"msapplication-TileColor\" content=\"#004aa5\">\n<meta name=\"msapplication-config\" content=\"/icons/browserconfig.xml\">\n<meta name=\"theme-color\" content=\"#004aa5\">","compression":"false","overlapping_markups":["link[rel=\"apple-touch-icon\"]","link[rel=\"shortcut\"]","link[rel=\"shortcut icon\"]","link[rel=\"icon\",sizes=\"16x16\"]","link[rel=\"icon\",sizes=\"32x32\"]","meta[name=\"msapplication-TileColor\"]","meta[name=\"msapplication-config\"]","link[rel=\"manifest\"]","meta[name=\"theme-color\"]"]},"files_location":{"type":"path","path":"/icons"},"preview_picture_url":"https://realfavicongenerator.net/files/d925f2495c2cc5046691e320cba3357e995bcbb8/favicon_preview.png","version":"0.16"}

View File

@ -1,59 +0,0 @@
{
"masterPicture": "tools/favicon-master-image.png",
"iconsPath": "/icons",
"design": {
"ios": {
"pictureAspect": "backgroundAndMargin",
"backgroundColor": "#004aa5",
"margin": "11%",
"assets": {
"ios6AndPriorIcons": false,
"ios7AndLaterIcons": false,
"precomposedIcons": false,
"declareOnlyDefaultIcon": true
}
},
"desktopBrowser": {
"design": "raw"
},
"windows": {
"pictureAspect": "noChange",
"backgroundColor": "#004aa5",
"onConflict": "override",
"assets": {
"windows80Ie10Tile": false,
"windows10Ie11EdgeTiles": {
"small": false,
"medium": true,
"big": false,
"rectangle": false
}
}
},
"androidChrome": {
"pictureAspect": "backgroundAndMargin",
"margin": "13%",
"backgroundColor": "#004aa5",
"themeColor": "#004aa5",
"manifest": {
"display": "standalone",
"orientation": "notSet",
"onConflict": "override",
"declared": true
},
"assets": {
"legacyIcon": false,
"lowResolutionIcons": false
}
}
},
"settings": {
"scalingAlgorithm": "Mitchell",
"errorOnImageTooSmall": false,
"readmeFile": false,
"htmlCodeFile": false,
"usePathAsIs": false
}
}

View File

@ -1 +0,0 @@
global.jQuery = global.$ = require('./jquery-latest.min.js');

View File

@ -1,23 +0,0 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
<defs>
<clipPath id="clip-headtop">
<path d="M 0 144 v -144 h 216 v 112 h 80 v -112 h 216 v 144 Z" />
</clipPath>
<clipPath id="clip-coattop">
<path d="M 100 264 a 144 132 0 0 0 312 0 h 100 v 256 h -512 v -256 Z" />
</clipPath>
</defs>
<rect x="0" y="0" width="512" height="512" fill="green" stroke="none"/>
<path d="M 112 164 h 288 v 72 l -24 24 h -88 q -32 -128 -64 0 h -88 l -24 -24 v -72 Z" fill-rule="evenodd" /> <!-- glasses -->
<path d="M 136 280 a 132 164 0 0 0 240 0 Z" /> <!-- bottom of head -->
<path d="M 128 144 a 144 128 0 0 1 256 0 Z" clip-path="url(#clip-headtop)"/> <!-- top of head -->
<path d="M 232 0 h 48 v 96 h -48 Z" /> <!-- mohawk -->
<path d="M 64 304 h 384 v 48 l -64 176 h -256 l -64 -192 Z" clip-path="url(#clip-coattop)" /> <!-- coat -->
<path d="M 128 512 h -128 v -112 l 48 -32 l 48 144 Z" />
<path d="M 384 512 h 128 v -112 l -48 -32 l -48 144 Z" />
<!-- <path d="M 32 96 h 448 v 96 l -32 32 h -144 q -56 -160 -96 0 h -144 l -32 -32 v -96 Z" fill-rule="evenodd" /> glasses in big -->
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB