Compare commits

...

28 Commits

Author SHA1 Message Date
a886d6575f ci: Add npm bulid
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-31 15:05:42 +02:00
8124b08b88 feat: Exclude build JS files from git 2024-07-31 14:49:29 +02:00
e80902e965 refactor: move js files to dedicated folder 2024-07-31 14:48:59 +02:00
c078eaa6c5 feat: Add basic translation framework
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-30 20:02:37 +02:00
75a6df9d9d feat: add initial i18n tags
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-30 06:20:43 +00:00
9dfc567d5c feat: use min-width in px
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-23 21:52:10 +02:00
093d0c0997 feat: Add minimum short side length rule 2024-07-23 21:52:10 +02:00
c86ed21c1e fix: typo
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-22 06:50:19 +00:00
9ce58c9860 fix: Make list of failed checks not jump around
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-22 06:47:14 +00:00
355c3055cb feat: Add tooltip to vollebenen
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-21 23:37:27 +02:00
a5c3cacef9 fix: Add full names of cages
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-21 23:25:47 +02:00
40db2b814b feat: Add favicon
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-21 23:25:22 +02:00
3c689015ac feat: Add title to content
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-21 23:15:10 +02:00
8a8c24d7ac feat: Add floor height criterion
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-21 23:14:36 +02:00
07f999aeb2 feat: Add sticky navigation
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-21 23:12:39 +02:00
6012d48197 feat: reorder floors and num of rats in flexcontainer
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-21 23:05:08 +02:00
90f08bf454 fix: adjust path of logo for prod
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-21 23:00:20 +02:00
aafa70fc1f feat: improve formatting, limit to sensible number of rats
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-21 22:57:58 +02:00
e3e065af16 feat: Make active cage
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-21 22:55:10 +02:00
9341c58dba feat: restyle cage cards 2024-07-21 22:44:06 +02:00
ecd7027bdd feat: Update on change in input fields
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-21 11:36:33 +02:00
75c814c057 feat: Rename to Käfigrechner 2024-07-21 11:36:05 +02:00
1d41b0cfc7 fix: Use more descriptive depth
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-20 08:50:07 +02:00
04dc1e6b90 fix: cage selector
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-07-20 08:47:41 +02:00
6b964c81a8 feat: do checks with correct units 2024-07-20 08:21:25 +02:00
fb83a94d4b feat: Add basic cage selector logic 2024-07-20 08:20:43 +02:00
b17f21a04f feat: Add unit to input 2024-07-20 08:20:10 +02:00
da5dc1da3b refactor: Use more descriptive ids 2024-07-20 08:19:47 +02:00
29 changed files with 5797 additions and 225 deletions

2
.gitignore vendored
View File

@@ -0,0 +1,2 @@
node_modules/
public/bundle.js*

View File

@@ -3,7 +3,11 @@
steps:
build:
image: node:latest
commands:
- npm install
- npm run build
deploy:
image: appleboy/drone-scp
settings:
@@ -14,6 +18,6 @@ steps:
from_secret: ssh_user
target:
from_secret: path
source: src/
source: public/
key:
from_secret: ssh_key

10
i18next-parser.config.js Normal file
View File

@@ -0,0 +1,10 @@
module.exports = {
defaultNamespace: 'translation',
lexers: {
js: ['JsxLexer'], // we're writing jsx inside .js files
default: ['JavascriptLexer'],
},
locales: ['en', 'de'],
output: 'public/lang/$LOCALE.json',
input: [ 'src/*.js', 'public/*.html', ],
}

5269
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

31
package.json Normal file
View File

@@ -0,0 +1,31 @@
{
"name": "kaefigrechner",
"version": "0.0.1",
"description": "Ein Rechner für die minimale Größe eines Rattenkäfigs nach Standard des VdRD e.V.",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --config webpack.config.js",
"ex-trans": "i18next -c i18next-parser.config.js",
"build": "webpack --config webpack.config.js"
},
"repository": {
"type": "git",
"url": "https://git.hyteck.de/moanos/RattenheimRechner"
},
"keywords": [
"animal",
"welfare"
],
"author": "Julian-Samuel Gebühr",
"license": "AGPL-3.0-or-later",
"devDependencies": {
"webpack": "^5.93.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4"
},
"dependencies": {
"i18next": "^23.12.2",
"i18next-browser-languagedetector": "^8.0.0",
"i18next-http-backend": "^2.5.2"
}
}

View File

@@ -20,9 +20,13 @@
}
body {
margin: 0;
background: var(--background-one);
color: var(--text-two);
}
.content {
margin: 10px;
}
h1, h2 {
word-wrap: break-word;
@@ -30,6 +34,16 @@ h1, h2 {
text-shadow: 2px 2px var(--shadow-one);
}
a {
color: inherit;
text-decoration: none;
}
ul {
list-style: none;
padding-left: 0px;
}
.container-form {
background: var(--background-three);
color: var(--text-two);
@@ -47,8 +61,7 @@ h1, h2 {
label {
font-weight: bold;
word-break: break-word;;
word-break: break-word;
}
.slidecontainer {
@@ -77,7 +90,7 @@ label {
width: 23px;
height: 24px;
border: 0;
background: url('/src/assets/img/logo_transparent.png');
background: url('../img/logo_transparent.png');
background-size: 100% 100%;
cursor: pointer;
}
@@ -86,13 +99,12 @@ label {
width: 23px;
height: 24px;
border: 0;
background: url('/src/assets/img/logo_transparent.png');
background: url('../img/logo_transparent.png');
background-size: 100% 100%;
cursor: pointer;
}
.input-group {
display: flex;
align-items: center;
@@ -116,47 +128,46 @@ label {
height: 36px;
}
.cage-selector{
gap: 15px;
.cards {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.card {
text-align: center;
color: var(--text-two);
flex: 1 25%;
}
.card-photo {
height: 200px;
min-width: 200px;
background-color: var(--background-one);
box-shadow: 0 0 25px rgba(17, 1, 68, 0.08);
border-radius: 8px;
position: relative;
cursor: pointer;
}
.card-photo img {
width: 70%;
height: 95%;
position: absolute;
margin: auto;
left: 0;
right: 0;
top: 0;
bottom: 0;
cursor: pointer;
}
.card-photo label {
color: var(--text-one);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
transition: 0.3s;
margin: 0.4em;
min-width: 300px;
padding: 10px;
border-radius: 10px;
}
@media screen and (min-width: 800px) {
flex: 0 1 calc(25% - 0.5em);
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
}
.card-active {
border: 3px solid var(--highlight-two);
}
.info-container {
padding: 2px 16px;
}
img {
display: block;
margin-left: auto;
margin-right: auto;
max-height: 200px;
}
.measurement {
width: 100px;
}
@@ -167,32 +178,57 @@ label {
}
input[type="checkbox"] {
-webkit-appearance: none;
position: relative;
width: 100%;
cursor: pointer;
}
input[type="checkbox"]:after {
position: absolute;
font-weight: 400;
content: "0";
font-size: 18px;
color: #478bfb;
right: 10px;
top: -5px;
}
input[type="checkbox"]:checked:after {
font-weight: 900;
content: "X";
color: #478bfb;
display: none;
}
.form-measurements {
max-height: 90%;
}
.form-measurements input {
width: 70%;
}
input.measurement {
margin-top: 5%;
}
.container-inputs {
display: flex;
flex-wrap: wrap;
}
.navigation-sticky {
background-color: var(--secondary-light-one);
color: var(--primary-light-one);
padding: 16px;
margin: 0;
border-bottom-right-radius: 8px;
border: none;
font-weight: bold;
width: 20%;
}
.tooltip {
position: relative;
display: inline-block;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 200px;
background-color: var(--primary-dark-one);
color: #ffffff;
text-align: center;
padding: 10px;
border-radius: 5px;
position: absolute;
z-index: 1;
}
.tooltip:hover .tooltiptext {
visibility: visible;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1 @@
<svg class="text-grey-dark" width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor"><path d="M9.00026 12.6C9.00026 12.6 9.00026 12.1224 9.00026 11.5333V8.86666C9.00026 8.57211 8.76148 8.33333 8.46693 8.33333H7.93359" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M8.73346 5.26666C8.58619 5.26666 8.4668 5.38605 8.4668 5.53333C8.4668 5.68061 8.58619 5.8 8.73346 5.8C8.88074 5.8 9.00013 5.68061 9.00013 5.53333C9.00013 5.38605 8.88074 5.26666 8.73346 5.26666V5.26666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M9 17C13.4183 17 17 13.4183 17 9C17 4.58172 13.4183 1 9 1C4.58172 1 1 4.58172 1 9C1 13.4183 4.58172 17 9 17Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>

After

Width:  |  Height:  |  Size: 834 B

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 546 B

After

Width:  |  Height:  |  Size: 546 B

View File

Before

Width:  |  Height:  |  Size: 3.2 MiB

After

Width:  |  Height:  |  Size: 3.2 MiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

View File

@@ -1,21 +1,27 @@
const MINIMUM_BASE_AREA = 0.5;
const MINIMUM_AREA_THREE_RATS = 1.8;
const AREA_PER_ADDITIONAL_RAT = 0.2;
const MINIMUM_AREA_THREE_RATS = 1.5;
const AREA_PER_ADDITIONAL_RAT = 0.25;
const MAXIMUM_FALL_HEIGHT = 0.5;
const MINIMUM_LENGTH = 0.8;
const MINIMUM_LENGTH_LONG_SIDE = 0.8;
const MINIMUM_LENGTH_SHORT_SIDE = 0.5;
const MINIMUM_FLOOR_HEIGHT = 0.3;
const FAILED_BASE_AREA = "base_area";
const FAILED_OVERALL_AREA = "overall_area";
const FAILED_FALL_HEIGHT = "fall_height";
const FAILED_NUM_RATS = "num_rats";
const FAILED_LENGTH = "length";
const FAILED_MINIMUM_LENGTH_LONG_SIDE = "length_long_side";
const FAILED_MINIMUM_LENGTH_SHORT_SIDE= "length_short_side";
const FAILED_FLOOR_HEIGHT = "floor_height"
const FAIL_CRITERIA = {
[FAILED_BASE_AREA]: `Die Mindestgrundfläche des Käfigs muss ${MINIMUM_BASE_AREA}m² (also z.B. 100x50cm) betragen.`,
[FAILED_OVERALL_AREA]: "Die Gesamtfläche im Käfig ist zu klein.",
[FAILED_FALL_HEIGHT]: `Die mögliche Fallhöhe darf nicht mehr als ${(MAXIMUM_FALL_HEIGHT * 100).toFixed(3)}cm betragen.`,
[FAILED_FALL_HEIGHT]: `Die mögliche Fallhöhe darf nicht mehr als ${(MAXIMUM_FALL_HEIGHT * 100).toFixed(0)}cm betragen.`,
[FAILED_FLOOR_HEIGHT]: `Der Mindestabstand zwischen Ebenen muss ${(MINIMUM_FLOOR_HEIGHT * 100).toFixed(0)}cm betragen.`,
[FAILED_NUM_RATS]: "Es müssen mindestens 3 Ratten zusammenleben, Paarhaltung ist nicht artgerecht.",
[FAILED_LENGTH]: `Eine Seite des Käfig muss mindestens ${(MINIMUM_LENGTH * 100).toFixed(3)}cm lang sein um Rennen zu ermöglichen.`,
[FAILED_MINIMUM_LENGTH_LONG_SIDE]: `Die lange Seite des Käfig muss mindestens ${(MINIMUM_LENGTH_LONG_SIDE * 100).toFixed(0)}cm lang sein um Rennen zu ermöglichen.`,
[FAILED_MINIMUM_LENGTH_SHORT_SIDE]: `Die kurze Seite des Käfig muss mindestens ${(MINIMUM_LENGTH_SHORT_SIDE * 100).toFixed(0)}cm lang sein.`,
};
class Dimensions {
@@ -49,7 +55,7 @@ function cageCheck(dimensions, numRats, numFullFloors) {
failedCriteria[FAILED_NUM_RATS] = FAIL_CRITERIA[FAILED_NUM_RATS];
}
const baseArea = dimensions.length * dimensions.width;
const baseArea = dimensions.depth * dimensions.width;
if (baseArea < MINIMUM_BASE_AREA) {
failedCriteria[FAILED_BASE_AREA] = FAIL_CRITERIA[FAILED_BASE_AREA];
}
@@ -63,8 +69,16 @@ function cageCheck(dimensions, numRats, numFullFloors) {
failedCriteria[FAILED_FALL_HEIGHT] = FAIL_CRITERIA[FAILED_FALL_HEIGHT];
}
if (dimensions.width < MINIMUM_LENGTH && dimensions.length < MINIMUM_LENGTH) {
failedCriteria[FAILED_LENGTH] = FAIL_CRITERIA[FAILED_LENGTH];
if (dimensions.width < MINIMUM_LENGTH_LONG_SIDE && dimensions.depth < MINIMUM_LENGTH_LONG_SIDE) {
failedCriteria[FAILED_MINIMUM_LENGTH_LONG_SIDE] = FAIL_CRITERIA[FAILED_MINIMUM_LENGTH_LONG_SIDE];
}
if (dimensions.width < MINIMUM_LENGTH_SHORT_SIDE || dimensions.depth < MINIMUM_LENGTH_SHORT_SIDE) {
failedCriteria[FAILED_MINIMUM_LENGTH_SHORT_SIDE] = FAIL_CRITERIA[FAILED_MINIMUM_LENGTH_SHORT_SIDE];
}
if (dimensions.height / numFullFloors < MINIMUM_FLOOR_HEIGHT) {
failedCriteria[FAILED_FLOOR_HEIGHT] = FAIL_CRITERIA[FAILED_FLOOR_HEIGHT];
}
return failedCriteria;

121
public/index.html Normal file
View File

@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>Käfigrechner</title>
<link rel="stylesheet" href="assets/css/style.css">
<script src="assets/js/calculator.js"></script>
<link rel="apple-touch-icon" sizes="180x180" href="assets/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="assets/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="assets/favicon/favicon-16x16.png">
</head>
<body>
<div class="navigation-sticky">
<a href="https://notfellchen.org">
<b data-i18n-key="back-to-home">zurück zur Homepage</b>
</a>
</div>
<img src="img/translation-icon@2x.png" class="translation-icon" / >
<select data-i18n-switcher class="locale-switcher">
<option value="en">English</option>
<option value="de">Deutsch</option>
</select>
<div class="content">
<h1 data-i18n-key="app-name">Käfigrechner</h1>
<div class="container-form">
<div class="cards">
<div class="card" id="card-SavicSuiteRoyaleXL">
<label for="SavicSuiteRoyaleXL">
<input type="checkbox" id="SavicSuiteRoyaleXL"/>
<div class="card-photo">
<img src="assets/img/savic-xl.jpeg">
</div>
<div class="info-container">
<h4><b>Savic Suite Royale XL</b></h4>
</div>
</label>
</div>
<div class="card" id="card-SavicSuiteRoyale95Double">
<label for="SavicSuiteRoyale95Double">
<input type="checkbox" id="SavicSuiteRoyale95Double"/>
<div class="card-photo">
<div>
<img src="assets/img/savic-95-double.jpg">
</div>
<div class="info-container">
<h4><b>Savic Suite Royale 95 Double</b></h4>
</div>
</div>
</label>
</div>
<div class="card" id="card-TiakiKleintierkäfigEtagere">
<label for="TiakiKleintierkäfigEtagere">
<input type="checkbox" id="TiakiKleintierkäfigEtagere"/>
<div class="card-photo">
<img src="assets/img/tiaki.jpeg">
</div>
<div class="info-container">
<h4><b>TIAKI Kleintierkäfig Étagère</b></h4>
</div>
</label>
</div>
<div class="card" id="card-ManualMeasurements">
<label for="form-cage-measurements">Käfigmaße</label>
<form id="form-cage-measurements" class="form-measurements">
<div class="input-measurement">
<label for="width" data-i18n-key="width-cm">Breite (cm)</label>
<input class="measurement" type="number" id="width">
</div>
<div class="input-measurement">
<label for="depth" data-i18n-key="depth-cm">Tiefe (cm)</label>
<input class="measurement" type="number" id="depth">
</div>
<div class="input-measurement">
<label for="height" data-i18n-key="height-cm">Höhe (cm)</label>
<input class="measurement" type="number" id="height">
</div>
</form>
</div>
</div>
</div>
<div class="container-inputs">
<div class="input-element">
<label for="numFullFloors" data-i18n-key="full-floors">Vollebenen</label>
<div class="tooltip">
<svg class="text-grey-dark" width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor"><path d="M9.00026 12.6C9.00026 12.6 9.00026 12.1224 9.00026 11.5333V8.86666C9.00026 8.57211 8.76148 8.33333 8.46693 8.33333H7.93359" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M8.73346 5.26666C8.58619 5.26666 8.4668 5.38605 8.4668 5.53333C8.4668 5.68061 8.58619 5.8 8.73346 5.8C8.88074 5.8 9.00013 5.68061 9.00013 5.53333C9.00013 5.38605 8.88074 5.26666 8.73346 5.26666V5.26666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M9 17C13.4183 17 17 13.4183 17 9C17 4.58172 13.4183 1 9 1C4.58172 1 1 4.58172 1 9C1 13.4183 4.58172 17 9 17Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>
<span class="tooltiptext" data-i18n-key="full-floors-tooltip">Als Vollebenen zählen alle Ebenen die größer als 0.5m² sind, inklusive des Käfigbodens.</span>
</div>
<div class="ncontainer">
<div class="input-group">
<button id="decreaseFloorNum">-</button>
<input type="text" id="numFullFloors" value="3" readonly>
<button id="increaseFloorNum">+</button>
</div>
</div>
</div>
<div class="input-element">
<div class="slidecontainer">
<label for="numRats" id="labelNumRats" data-i18n-key="number-of-rats">Anzahl an Ratten ?</label>
<input type="range" min="3" max="15" value="4" class="slider" id="numRats">
</div>
</div>
</div>
<div class="container output-element" id="resultsDiv">
</div>
</div>
</div>
<script src="./bundle.js"></script>
</body>
</html>

10
public/lang/de.json Normal file
View File

@@ -0,0 +1,10 @@
{
"back-to-home": "Zurück zur Homepage",
"app-name": "Käfigrechner",
"number-of-rats": "Anzahl an Ratten",
"full-floors": "Vollebenen",
"width-cm": "Breite (cm)",
"depth-cm": "Tiefe (cm)",
"height-cm": "Höhe (cm)",
"full-floors-tooltip": "Als Vollebenen zählen alle Ebenen die größer als 0.5m² sind, inklusive des Käfigbodens."
}

10
public/lang/en.json Normal file
View File

@@ -0,0 +1,10 @@
{
"back-to-home": "Back to home",
"app-name": "Cage Calculator",
"number-of-rats": "Number of Rats",
"full-floors": "Full floors",
"width-cm": "Width (cm)",
"depth-cm": "Depth (cm)",
"height-cm": "Height (cm)",
"full-floors-tooltip": "A full floor is each floor with a area greater than 0.5m², including the bottom of the cage."
}

View File

@@ -1,158 +0,0 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>Knastrechner</title>
<link rel="stylesheet" href="assets/css/style.css">
<script src="assets/calculator.js"></script>
</head>
<body>
<h1>Käfigrechner</h1>
<div class="content">
<div class="container-form">
<div class="cage-selector">
<div class="card">
<div class="card-photo">
<input type="checkbox" id="savic-xl"/>
<div>
<img src="assets/img/savic-xl.jpeg">
</div>
</div>
<label for="savic-xl"> Savic Suite Royale XL</label>
</div>
<div class="card">
<div class="card-photo">
<input type="checkbox" id="savic-95"/>
<div>
<img src="assets/img/savic-95-double.jpg">
</div>
</div>
<label for="savic-95">Savic Suite Royale 95 Double</label>
</div>
<div class="card">
<div class="card-photo">
<input type="checkbox" id="tiaki"/>
<div>
<img src="assets/img/tiaki.jpeg">
</div>
</div>
<label>TIAKI Kleintierkäfig Étagère</label>
</div>
<div class="card">
<div class="card-photo">
<label for="form-cage-measurements">Käfigmaße</label>
<form id="form-cage-measurements" class="form-measurements">
<div class="input-measurement">
<label for="width">Breite</label>
<input class="measurement" type="number" id="width">
</div>
<div class="input-measurement">
<label for="depth">Tiefe</label>
<input class="measurement" type="number" id="depth">
</div>
<div class="input-measurement">
<label for="height">Höhe</label>
<input class="measurement" type="number" id="height">
</div>
</form>
</div>
</div>
</div>
<div class="input-element">
<div class="slidecontainer">
<label for="numRats" id="labelNumRats">Anzahl an Ratten ?</label>
<input type="range" min="1" max="20" value="4" class="slider" id="numRats">
</div>
</div>
<div class="input-element">
<label for="numFullFloors">Vollebenen</label>
<div class="ncontainer">
<div class="input-group">
<button onclick="decreaseFloorNum()">-</button>
<input type="text" id="numFullFloors" value="3" readonly>
<button onclick="increaseFloorNum()">+</button>
</div>
</div>
</div>
<div class="container output-element" id="resultsDiv">
</div>
</div>
</div>
<script>
var labelNumRats = document.getElementById("labelNumRats");
var ratSlider = document.getElementById("numRats");
labelNumRats.innerHTML = `Anzahl an Ratten: ` + ratSlider.value;
ratSlider.oninput = function () {
labelNumRats.innerHTML = `Anzahl an Ratten: ` + this.value;
update();
}
// Full floor functions
var fullFloorNum = document.getElementById("numFullFloors");
var savicSuiteRoyaleDim = new Dimensions(1, 0.5, 1);
function getResultFromChecks(checks) {
console.log(checks.length)
if (Object.keys(checks).length > 0) {
const ul = document.createElement('ul');
for (const key in checks) {
const li = document.createElement('li');
li.textContent = `` + checks[key];
ul.appendChild(li);
}
return ul;
} else {
const p = document.createElement('p');
p.innerHTML = "✅ Der Käfig erfüllt alle Kriterien!"
return p;
}
}
function update() {
var failed_checks = cageCheck(savicSuiteRoyaleDim, ratSlider.value, fullFloorNum.value);
var resultsDiv = document.getElementById("resultsDiv");
resultsDiv.innerHTML = `<strong>Ergebnis</strong>`;
const result = getResultFromChecks(failed_checks);
resultsDiv.appendChild(result);
}
function decreaseFloorNum() {
var input = document.getElementById('numFullFloors');
var value = parseInt(input.value);
if (value > 0) {
input.value = value - 1;
}
update();
}
function increaseFloorNum() {
var input = document.getElementById('numFullFloors');
var value = parseInt(input.value);
input.value = value + 1;
update();
}
update();
</script>
</body>
</html>

196
src/index.js Normal file
View File

@@ -0,0 +1,196 @@
const inputDecreaseFloorNum = document.getElementById("decreaseFloorNum");
inputDecreaseFloorNum.onclick = decreaseFloorNum;
const inputIncreaseFloorNum = document.getElementById("increaseFloorNum");
inputIncreaseFloorNum.onclick = increaseFloorNum;
const inputWidth = document.getElementById("width");
inputWidth.onchange = updateViaManualMeasurements;
const inputDepth = document.getElementById("depth");
inputDepth.onchange = updateViaManualMeasurements;
const inputHeight = document.getElementById("height");
inputHeight.onchange = updateViaManualMeasurements;
const selectSavicSuiteRoyaleXL = document.getElementById("SavicSuiteRoyaleXL");
const selectSavicSuiteRoyale95Double = document.getElementById("SavicSuiteRoyale95Double");
const selectTiakiKleintierkäfigEtagere = document.getElementById("TiakiKleintierkäfigEtagere");
const cardSavicSuiteRoyaleXL = document.getElementById("card-SavicSuiteRoyaleXL");
const cardSavicSuiteRoyale95Double = document.getElementById("card-SavicSuiteRoyale95Double");
const cardTiakiKleintierkäfigEtagere = document.getElementById("card-TiakiKleintierkäfigEtagere");
function markActiveCage(cageName) {
cardSavicSuiteRoyaleXL.classList.remove("card-active");
cardSavicSuiteRoyale95Double.classList.remove("card-active");
cardTiakiKleintierkäfigEtagere.classList.remove("card-active");
if (cageName != "") {
const activeCage = document.getElementById("card-" + cageName);
activeCage.classList.add("card-active")
}
}
function updateCage(event) {
selectSavicSuiteRoyaleXL.checked = false;
selectSavicSuiteRoyale95Double.checked = false;
selectTiakiKleintierkäfigEtagere.checked = false;
const selectedCage = event.currentTarget
selectedCage.checked = true;
const cageName = selectedCage.id;
console.log(cageName);
var dim = getCageDimensions(cageName);
inputWidth.value = dim.width;
inputDepth.value = dim.depth;
inputHeight.value = dim.height;
markActiveCage(cageName);
update();
}
selectSavicSuiteRoyaleXL.onchange = updateCage;
selectSavicSuiteRoyale95Double.onchange = updateCage;
selectTiakiKleintierkäfigEtagere.onchange = updateCage;
var labelNumRats = document.getElementById("labelNumRats");
var ratSlider = document.getElementById("numRats");
labelNumRats.innerHTML = `Anzahl an Ratten: ` + ratSlider.value;
ratSlider.oninput = function () {
labelNumRats.innerHTML = `Anzahl an Ratten: ` + this.value;
update();
}
// Full floor functions
var fullFloorNum = document.getElementById("numFullFloors");
function getCageDimensions(cageName) {
console.log(cageName);
if (cageName == "SavicSuiteRoyaleXL") {
return new Dimensions(115, 67.5, 153);
}
if (cageName == "SavicSuiteRoyale95Double") {
return new Dimensions(95, 63, 120);
}
if (cageName == "TiakiKleintierkäfigEtagere") {
return new Dimensions(93.5, 63, 141.2);
}
}
function getResultFromChecks(checks) {
console.log(checks.length)
if (Object.keys(checks).length > 0) {
const ul = document.createElement('ul');
for (const key in checks) {
const li = document.createElement('li');
li.textContent = `` + checks[key];
ul.appendChild(li);
}
return ul;
} else {
const p = document.createElement('p');
p.innerHTML = "✅ Der Käfig erfüllt alle Kriterien!"
return p;
}
}
function updateViaManualMeasurements() {
markActiveCage("ManualMeasurements");
update();
}
function update() {
const width = inputWidth.value
const depth = inputDepth.value
const height = inputHeight.value
const dimensions = new Dimensions(width / 100, depth / 100, height / 100);
const failed_checks = cageCheck(dimensions, ratSlider.value, fullFloorNum.value);
console.log(failed_checks);
let resultsDiv = document.getElementById("resultsDiv");
resultsDiv.innerHTML = `<strong>Ergebnis</strong>`;
const result = getResultFromChecks(failed_checks);
resultsDiv.appendChild(result);
}
function decreaseFloorNum() {
var input = document.getElementById('numFullFloors');
var value = parseInt(input.value);
if (value > 0) {
input.value = value - 1;
}
update();
}
function increaseFloorNum() {
var input = document.getElementById('numFullFloors');
var value = parseInt(input.value);
input.value = value + 1;
update();
}
update();
import i18next from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import HttpApi from "i18next-http-backend";
async function initI18next() {
// We use() the backend and await it to load
// the translations from the network
await i18next
.use(HttpApi)
.use(LanguageDetector)
.init({
lng: "en",
supportedLngs: ["en", "de"],
nonExplicitSupportedLngs: true,
fallbackLng: "en",
debug: true,
backend: {
loadPath: "/lang/{{lng}}.json",
},
});
}
function translatePageElements() {
const translatableElements = document.querySelectorAll(
"[data-i18n-key]",
);
translatableElements.forEach((el) => {
const key = el.getAttribute("data-i18n-key");
el.innerHTML = i18next.t(key);
});
}
// ...
function bindLocaleSwitcher(initialValue) {
const switcher = document.querySelector(
"[data-i18n-switcher]",
);
switcher.value = initialValue;
switcher.onchange = (e) => {
i18next
.changeLanguage(e.target.value)
.then(translatePageElements);
};
}
(async function () {
await initI18next();
translatePageElements();
bindLocaleSwitcher(i18next.resolvedLanguage);
})();

25
webpack.config.js Normal file
View File

@@ -0,0 +1,25 @@
const path = require("path");
const port = 3000;
const openBrowser = true;
module.exports = {
entry: {
app: ["./src/index.js"],
},
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "public"),
publicPath: "/",
},
mode: "development",
devtool: "source-map",
devServer: {
port: port,
open: openBrowser,
historyApiFallback: {
index: "index.html",
},
static: "public",
},
};