Compare commits
No commits in common. "main" and "translation" have entirely different histories.
main
...
translatio
@ -1,7 +1,5 @@
|
|||||||
# RattenheimRechner
|
# RattenheimRechner
|
||||||
|
|
||||||
**[rechner.vdrd.de](https://rechner.vdrd.de)**
|
|
||||||
|
|
||||||
Ein Rechner für die minimale Größe eines Rattenkäfigs nach Standard des [VdRD e.V.](https://vdrd.de)
|
Ein Rechner für die minimale Größe eines Rattenkäfigs nach Standard des [VdRD e.V.](https://vdrd.de)
|
||||||
|
|
||||||
## CI
|
## CI
|
||||||
|
@ -62,7 +62,6 @@ ul {
|
|||||||
label {
|
label {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
font-size: x-large;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.slidecontainer {
|
.slidecontainer {
|
||||||
@ -200,31 +199,15 @@ input.measurement {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-navigation {
|
.navigation-sticky {
|
||||||
|
background-color: var(--secondary-light-one);
|
||||||
|
color: var(--primary-light-one);
|
||||||
|
padding: 16px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: none;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.homebutton {
|
|
||||||
background-color: var(--secondary-light-one);
|
|
||||||
color: var(--primary-light-one);
|
|
||||||
border-bottom-right-radius: 8px;
|
border-bottom-right-radius: 8px;
|
||||||
font-weight: bold;
|
|
||||||
padding: 16px;
|
|
||||||
width: 20%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.language-switcher {
|
|
||||||
background-color: var(--secondary-light-one);
|
|
||||||
color: var(--primary-light-one);
|
|
||||||
padding: 16px;
|
|
||||||
border-bottom-left-radius: 8px;
|
|
||||||
border: none;
|
border: none;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
width: 15%;
|
width: 20%;
|
||||||
margin-left: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -248,12 +231,4 @@ input.measurement {
|
|||||||
|
|
||||||
.tooltip:hover .tooltiptext {
|
.tooltip:hover .tooltiptext {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
|
||||||
|
|
||||||
select, option {
|
|
||||||
background-color: var(--primary-light-one);
|
|
||||||
border-radius: 0.2rem;
|
|
||||||
border: none;
|
|
||||||
color: var(--text-one);
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
85
public/assets/js/calculator.js
Normal file
85
public/assets/js/calculator.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
const MINIMUM_BASE_AREA = 0.5;
|
||||||
|
const MINIMUM_AREA_THREE_RATS = 1.5;
|
||||||
|
const AREA_PER_ADDITIONAL_RAT = 0.25;
|
||||||
|
const MAXIMUM_FALL_HEIGHT = 0.5;
|
||||||
|
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_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(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_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 {
|
||||||
|
constructor(width, depth, height) {
|
||||||
|
this.width = width;
|
||||||
|
this.depth = depth;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return `${this.width}x${this.depth}x${this.height}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromDict(data) {
|
||||||
|
const { width, depth, height } = data;
|
||||||
|
return new Dimensions(width, depth, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function overallAreaNeeded(numOfRats) {
|
||||||
|
if (numOfRats < 3 || numOfRats > 15) {
|
||||||
|
throw new Error("This formula works only from 3 to 15 rats");
|
||||||
|
}
|
||||||
|
return MINIMUM_AREA_THREE_RATS + (numOfRats - 3) * AREA_PER_ADDITIONAL_RAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cageCheck(dimensions, numRats, numFullFloors) {
|
||||||
|
let failedCriteria = {};
|
||||||
|
|
||||||
|
if (numRats < 2 || numRats > 15) {
|
||||||
|
failedCriteria[FAILED_NUM_RATS] = FAIL_CRITERIA[FAILED_NUM_RATS];
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseArea = dimensions.depth * dimensions.width;
|
||||||
|
if (baseArea < MINIMUM_BASE_AREA) {
|
||||||
|
failedCriteria[FAILED_BASE_AREA] = FAIL_CRITERIA[FAILED_BASE_AREA];
|
||||||
|
}
|
||||||
|
|
||||||
|
const areaNeeded = overallAreaNeeded(numRats);
|
||||||
|
if (baseArea * numFullFloors < areaNeeded) {
|
||||||
|
failedCriteria[FAILED_OVERALL_AREA] = FAIL_CRITERIA[FAILED_OVERALL_AREA];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dimensions.height / numFullFloors > MAXIMUM_FALL_HEIGHT) {
|
||||||
|
failedCriteria[FAILED_FALL_HEIGHT] = FAIL_CRITERIA[FAILED_FALL_HEIGHT];
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Käfigrechner</title>
|
<title>Käfigrechner</title>
|
||||||
<link rel="stylesheet" href="assets/css/style.css">
|
<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="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="32x32" href="assets/favicon/favicon-32x32.png">
|
||||||
@ -11,23 +12,18 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div class="top-navigation">
|
<div class="navigation-sticky">
|
||||||
<div class="homebutton">
|
<a href="https://notfellchen.org">
|
||||||
<a href="https://vdrd.de">
|
<b data-i18n-key="back-to-home">zurück zur Homepage</b>
|
||||||
<b data-i18n-key="back-to-home">zurück zur Homepage</b>
|
</a>
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="language-switcher">
|
|
||||||
<label aria-hidden="false" style="display: none" data-i18n-key="change-language"></label>
|
|
||||||
<select data-i18n-switcher class="locale-switcher" id="locale-switcher">
|
|
||||||
<option value="en">English</option>
|
|
||||||
<option value="de">Deutsch</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</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">
|
<div class="content">
|
||||||
<h1 data-i18n-key="app-name">Käfigrechner</h1>
|
<h1 data-i18n-key="app-name">Käfigrechner</h1>
|
||||||
<div class="container-form">
|
<div class="container-form">
|
||||||
@ -36,7 +32,7 @@
|
|||||||
<label for="SavicSuiteRoyaleXL">
|
<label for="SavicSuiteRoyaleXL">
|
||||||
<input type="checkbox" id="SavicSuiteRoyaleXL"/>
|
<input type="checkbox" id="SavicSuiteRoyaleXL"/>
|
||||||
<div class="card-photo">
|
<div class="card-photo">
|
||||||
<img alt-i18n-key="alt-savic-xl" src="assets/img/savic-xl.jpeg">
|
<img src="assets/img/savic-xl.jpeg">
|
||||||
</div>
|
</div>
|
||||||
<div class="info-container">
|
<div class="info-container">
|
||||||
<h4><b>Savic Suite Royale XL</b></h4>
|
<h4><b>Savic Suite Royale XL</b></h4>
|
||||||
@ -49,7 +45,7 @@
|
|||||||
<input type="checkbox" id="SavicSuiteRoyale95Double"/>
|
<input type="checkbox" id="SavicSuiteRoyale95Double"/>
|
||||||
<div class="card-photo">
|
<div class="card-photo">
|
||||||
<div>
|
<div>
|
||||||
<img alt-i18n-key="alt-savic-95-double" src="assets/img/savic-95-double.jpg">
|
<img src="assets/img/savic-95-double.jpg">
|
||||||
</div>
|
</div>
|
||||||
<div class="info-container">
|
<div class="info-container">
|
||||||
<h4><b>Savic Suite Royale 95 Double</b></h4>
|
<h4><b>Savic Suite Royale 95 Double</b></h4>
|
||||||
@ -62,7 +58,7 @@
|
|||||||
<label for="TiakiKleintierkäfigEtagere">
|
<label for="TiakiKleintierkäfigEtagere">
|
||||||
<input type="checkbox" id="TiakiKleintierkäfigEtagere"/>
|
<input type="checkbox" id="TiakiKleintierkäfigEtagere"/>
|
||||||
<div class="card-photo">
|
<div class="card-photo">
|
||||||
<img alt-i18n-key="alt-tiaki" src="assets/img/tiaki.jpeg">
|
<img src="assets/img/tiaki.jpeg">
|
||||||
</div>
|
</div>
|
||||||
<div class="info-container">
|
<div class="info-container">
|
||||||
<h4><b>TIAKI Kleintierkäfig Étagère</b></h4>
|
<h4><b>TIAKI Kleintierkäfig Étagère</b></h4>
|
||||||
@ -71,7 +67,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card" id="card-ManualMeasurements">
|
<div class="card" id="card-ManualMeasurements">
|
||||||
<label data-i18n-key="cage-measurements" for="form-cage-measurements">Käfigmaße</label>
|
<label for="form-cage-measurements">Käfigmaße</label>
|
||||||
<form id="form-cage-measurements" class="form-measurements">
|
<form id="form-cage-measurements" class="form-measurements">
|
||||||
<div class="input-measurement">
|
<div class="input-measurement">
|
||||||
<label for="width" data-i18n-key="width-cm">Breite (cm)</label>
|
<label for="width" data-i18n-key="width-cm">Breite (cm)</label>
|
||||||
@ -94,16 +90,7 @@
|
|||||||
<div class="input-element">
|
<div class="input-element">
|
||||||
<label for="numFullFloors" data-i18n-key="full-floors">Vollebenen</label>
|
<label for="numFullFloors" data-i18n-key="full-floors">Vollebenen</label>
|
||||||
<div class="tooltip">
|
<div class="tooltip">
|
||||||
<svg class="text-grey-dark" width="18" height="18" viewBox="0 0 18 18" fill="none"
|
<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>
|
||||||
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>
|
<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>
|
||||||
<div class="ncontainer">
|
<div class="ncontainer">
|
||||||
@ -117,17 +104,16 @@
|
|||||||
|
|
||||||
<div class="input-element">
|
<div class="input-element">
|
||||||
<div class="slidecontainer">
|
<div class="slidecontainer">
|
||||||
<label for="numRats" id="labelNumRats"></label>
|
<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">
|
<input type="range" min="3" max="15" value="4" class="slider" id="numRats">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container output-element">
|
<div class="container output-element" id="resultsDiv">
|
||||||
<strong data-i18n-key="result">Ergebnis</strong>
|
|
||||||
<div id="resultsDiv"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="./bundle.js"></script>
|
<script src="./bundle.js"></script>
|
||||||
|
|
||||||
|
@ -6,20 +6,5 @@
|
|||||||
"width-cm": "Breite (cm)",
|
"width-cm": "Breite (cm)",
|
||||||
"depth-cm": "Tiefe (cm)",
|
"depth-cm": "Tiefe (cm)",
|
||||||
"height-cm": "Höhe (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.",
|
"full-floors-tooltip": "Als Vollebenen zählen alle Ebenen die größer als 0.5m² sind, inklusive des Käfigbodens."
|
||||||
"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 }}cm betragen.",
|
|
||||||
"failed-floor-height": "Der Mindestabstand zwischen Ebenen muss {{ minimum_floor_height }}cm betragen.",
|
|
||||||
"failed-num-rats": "Es müssen mindestens 3 Ratten zusammenleben, Paarhaltung ist nicht artgerecht.",
|
|
||||||
"failed-minimum-length-long-side": "Die lange Seite des Käfig muss mindestens {{ minimum_length_long_side }}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 }}cm lang sein.",
|
|
||||||
"alt-savic-xl": "Bild des Käfigs Savic Suite Royal XL, ein Drahtkäfig mit vier Türen die erlauben die gesamte Vorderseite zu öffnen. Auf halber Höhe ist eine weitere Vollebene.",
|
|
||||||
"alt-savic-95-double": "Bild eines dekorierten Käfigs, des Savic Suite Royale 95 Double. Auf insgesamt vie Vollebenen sind Holz- und Papphäusschen, Hängematten, Futterschalen und mehr.",
|
|
||||||
"alt-tiaki": "Bild des Tiaki-Käfigs, ein Drahtkäfig mit vier Türen die erlauben die gesamte Vorderseite zu öffnen. Auf halber Höhe ist eine weitere Vollebene.",
|
|
||||||
"cage-measurements": "Käfigmaße",
|
|
||||||
"cage-complies-with-all-criteria": "Der Käfig erfüllt alle Kriterien!",
|
|
||||||
"cage-for-x-rats": "Käfig für {{ num_rats }} Ratten",
|
|
||||||
"change-language": "Sprache ändern",
|
|
||||||
"result": "Ergebnis"
|
|
||||||
}
|
}
|
@ -6,20 +6,5 @@
|
|||||||
"width-cm": "Width (cm)",
|
"width-cm": "Width (cm)",
|
||||||
"depth-cm": "Depth (cm)",
|
"depth-cm": "Depth (cm)",
|
||||||
"height-cm": "Height (cm)",
|
"height-cm": "Height (cm)",
|
||||||
"full-floors-tooltip": "A full floor is each floor with a area greater than XXm², including the bottom of the cage.",
|
"full-floors-tooltip": "A full floor is each floor with a area greater than 0.5m², including the bottom of the cage."
|
||||||
"failed-base-area": "The base area of the cage must not be below {{ MINIMUM_BASE_AREA }}m².",
|
|
||||||
"failed-overall-area": "The overall area in the cage is to small.",
|
|
||||||
"failed-fall-height": "The possible fall height between floors must not be above {{ maximum_fall_height }}cm.",
|
|
||||||
"failed-floor-height": "The height between floors must be above {{ minimum_floor_height }}cm.",
|
|
||||||
"failed-num-rats": "Rats must live in a group of at least three rats, pairs or lone rats are not species-appropriate.",
|
|
||||||
"failed-minimum-length-long-side": "The long side of the cage must be at least {{ minimum_length_long_side }}cm long to enable running.",
|
|
||||||
"failed-minimum-length-short-side": "The short side of the cage must be at least {{ minimum_length_short_side }}cm.",
|
|
||||||
"alt-savic-xl": "A picture of a wire bar cage with four doors that allow to open the full front of the cage. In the middle of the cage is a additional full floor.",
|
|
||||||
"alt-savic-95-double": "A picture of a decorated wire bar cage with four doors that allow to open the full front of the cage. Inside there are hammocks, toys and four full floors with lots of hides",
|
|
||||||
"alt-tiaki": "A picture of a decorated wire bar cage with four doors that allow to open the full front of the cage.",
|
|
||||||
"cage-measurements": "Cage measurements",
|
|
||||||
"cage-complies-with-all-criteria": "This cage complies with all criteria!",
|
|
||||||
"cage-for-x-rats": "Cage for {{ num_rats }} rats",
|
|
||||||
"change-language": "Change language",
|
|
||||||
"result": "Result"
|
|
||||||
}
|
}
|
239
src/index.js
239
src/index.js
@ -1,169 +1,3 @@
|
|||||||
import i18next from "i18next";
|
|
||||||
import LanguageDetector from "i18next-browser-languagedetector";
|
|
||||||
import HttpApi from "i18next-http-backend";
|
|
||||||
|
|
||||||
/////////////////
|
|
||||||
// TRANSLATION //
|
|
||||||
/////////////////
|
|
||||||
|
|
||||||
async function initI18next() {
|
|
||||||
await i18next
|
|
||||||
.use(HttpApi)
|
|
||||||
.use(LanguageDetector)
|
|
||||||
.init({
|
|
||||||
supportedLngs: ["en", "de"],
|
|
||||||
nonExplicitSupportedLngs: true,
|
|
||||||
fallbackLng: "en",
|
|
||||||
debug: true,
|
|
||||||
backend: {
|
|
||||||
loadPath: "/lang/{{lng}}.json",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function translatePageElements() {
|
|
||||||
// Translate content inside a tag
|
|
||||||
const translatableElements = document.querySelectorAll(
|
|
||||||
"[data-i18n-key]",
|
|
||||||
);
|
|
||||||
translatableElements.forEach((el) => {
|
|
||||||
const key = el.getAttribute("data-i18n-key");
|
|
||||||
el.innerHTML = i18next.t(key);
|
|
||||||
});
|
|
||||||
// Translate alt texts
|
|
||||||
const translatableAltTexts = document.querySelectorAll(
|
|
||||||
"[alt-i18n-key]",
|
|
||||||
);
|
|
||||||
translatableAltTexts.forEach((el) => {
|
|
||||||
const translation_key = el.getAttribute("alt-i18n-key");
|
|
||||||
el.setAttribute("alt", i18next.t(translation_key));
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function bindLocaleSwitcher(initialValue) {
|
|
||||||
const switcher = document.querySelector(
|
|
||||||
"[data-i18n-switcher]",
|
|
||||||
);
|
|
||||||
switcher.value = initialValue;
|
|
||||||
switcher.onchange = (e) => {
|
|
||||||
i18next
|
|
||||||
.changeLanguage(e.target.value)
|
|
||||||
.then(translatePageElements)
|
|
||||||
.then(update);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
(async function () {
|
|
||||||
await initI18next();
|
|
||||||
translatePageElements();
|
|
||||||
bindLocaleSwitcher(i18next.resolvedLanguage);
|
|
||||||
update();
|
|
||||||
})();
|
|
||||||
|
|
||||||
////////////////
|
|
||||||
// CALCULATOR //
|
|
||||||
////////////////
|
|
||||||
|
|
||||||
const MINIMUM_BASE_AREA = 0.5;
|
|
||||||
const MINIMUM_AREA_THREE_RATS = 1.5;
|
|
||||||
const AREA_PER_ADDITIONAL_RAT = 0.25;
|
|
||||||
const MAXIMUM_FALL_HEIGHT = 0.5;
|
|
||||||
const MINIMUM_LENGTH_LONG_SIDE = 0.8;
|
|
||||||
const MINIMUM_LENGTH_SHORT_SIDE = 0.5;
|
|
||||||
const MINIMUM_FLOOR_HEIGHT = 0.25;
|
|
||||||
|
|
||||||
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_MINIMUM_LENGTH_LONG_SIDE = "length_long_side";
|
|
||||||
const FAILED_MINIMUM_LENGTH_SHORT_SIDE= "length_short_side";
|
|
||||||
const FAILED_FLOOR_HEIGHT = "floor_height"
|
|
||||||
|
|
||||||
|
|
||||||
class Validator {
|
|
||||||
constructor() {
|
|
||||||
this.FAIL_CRITERIA = {
|
|
||||||
[FAILED_BASE_AREA]: i18next.t('failed-base-area', {"MINIMUM_BASE_AREA": MINIMUM_BASE_AREA}),
|
|
||||||
[FAILED_OVERALL_AREA]: i18next.t("failed-overall-area"),
|
|
||||||
[FAILED_FALL_HEIGHT]: i18next.t("failed-fall-height", {"maximum_fall_height": (MAXIMUM_FALL_HEIGHT * 100).toFixed(0)}),
|
|
||||||
[FAILED_FLOOR_HEIGHT]: i18next.t("failed-floor-height", {"minimum_floor_height": (MINIMUM_FLOOR_HEIGHT * 100).toFixed(0)}),
|
|
||||||
[FAILED_NUM_RATS]: i18next.t("failed-num-rats"),
|
|
||||||
[FAILED_MINIMUM_LENGTH_LONG_SIDE]: i18next.t("failed-minimum-length-long-side", {"minimum_length_long_side": (MINIMUM_LENGTH_LONG_SIDE * 100).toFixed(0)}),
|
|
||||||
[FAILED_MINIMUM_LENGTH_SHORT_SIDE]: i18next.t("failed-minimum-length-short-side", {"minimum_length_short_side": (MINIMUM_LENGTH_SHORT_SIDE * 100).toFixed(0)}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
overallAreaNeeded(numOfRats) {
|
|
||||||
if (numOfRats < 3 || numOfRats > 15) {
|
|
||||||
throw new Error("This formula works only from 3 to 15 rats");
|
|
||||||
}
|
|
||||||
return MINIMUM_AREA_THREE_RATS + (numOfRats - 3) * AREA_PER_ADDITIONAL_RAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
cageCheck(dimensions, numRats, numFullFloors) {
|
|
||||||
let failedCriteria = {};
|
|
||||||
|
|
||||||
if (numRats < 2 || numRats > 15) {
|
|
||||||
failedCriteria[FAILED_NUM_RATS] = this.FAIL_CRITERIA[FAILED_NUM_RATS];
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseArea = dimensions.depth * dimensions.width;
|
|
||||||
if (baseArea < MINIMUM_BASE_AREA) {
|
|
||||||
failedCriteria[FAILED_BASE_AREA] = this.FAIL_CRITERIA[FAILED_BASE_AREA];
|
|
||||||
}
|
|
||||||
|
|
||||||
const areaNeeded = this.overallAreaNeeded(numRats);
|
|
||||||
if (baseArea * numFullFloors < areaNeeded) {
|
|
||||||
failedCriteria[FAILED_OVERALL_AREA] = this.FAIL_CRITERIA[FAILED_OVERALL_AREA];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dimensions.height / numFullFloors > MAXIMUM_FALL_HEIGHT) {
|
|
||||||
failedCriteria[FAILED_FALL_HEIGHT] = this.FAIL_CRITERIA[FAILED_FALL_HEIGHT];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dimensions.width < MINIMUM_LENGTH_LONG_SIDE && dimensions.depth < MINIMUM_LENGTH_LONG_SIDE) {
|
|
||||||
failedCriteria[FAILED_MINIMUM_LENGTH_LONG_SIDE] = this.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] = this.FAIL_CRITERIA[FAILED_MINIMUM_LENGTH_SHORT_SIDE];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dimensions.height / numFullFloors < MINIMUM_FLOOR_HEIGHT) {
|
|
||||||
failedCriteria[FAILED_FLOOR_HEIGHT] = this.FAIL_CRITERIA[FAILED_FLOOR_HEIGHT];
|
|
||||||
}
|
|
||||||
|
|
||||||
return failedCriteria;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class Dimensions {
|
|
||||||
constructor(width, depth, height) {
|
|
||||||
this.width = width;
|
|
||||||
this.depth = depth;
|
|
||||||
this.height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() {
|
|
||||||
return `${this.width}x${this.depth}x${this.height}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromDict(data) {
|
|
||||||
const { width, depth, height } = data;
|
|
||||||
return new Dimensions(width, depth, height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////
|
|
||||||
// DOCUMENT INTERACTION //
|
|
||||||
//////////////////////////
|
|
||||||
|
|
||||||
const inputDecreaseFloorNum = document.getElementById("decreaseFloorNum");
|
const inputDecreaseFloorNum = document.getElementById("decreaseFloorNum");
|
||||||
inputDecreaseFloorNum.onclick = decreaseFloorNum;
|
inputDecreaseFloorNum.onclick = decreaseFloorNum;
|
||||||
|
|
||||||
@ -208,6 +42,7 @@ function updateCage(event) {
|
|||||||
selectedCage.checked = true;
|
selectedCage.checked = true;
|
||||||
const cageName = selectedCage.id;
|
const cageName = selectedCage.id;
|
||||||
|
|
||||||
|
console.log(cageName);
|
||||||
var dim = getCageDimensions(cageName);
|
var dim = getCageDimensions(cageName);
|
||||||
inputWidth.value = dim.width;
|
inputWidth.value = dim.width;
|
||||||
inputDepth.value = dim.depth;
|
inputDepth.value = dim.depth;
|
||||||
@ -227,7 +62,10 @@ var labelNumRats = document.getElementById("labelNumRats");
|
|||||||
|
|
||||||
var ratSlider = document.getElementById("numRats");
|
var ratSlider = document.getElementById("numRats");
|
||||||
|
|
||||||
|
labelNumRats.innerHTML = `Anzahl an Ratten: ` + ratSlider.value;
|
||||||
|
|
||||||
ratSlider.oninput = function () {
|
ratSlider.oninput = function () {
|
||||||
|
labelNumRats.innerHTML = `Anzahl an Ratten: ` + this.value;
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,6 +73,7 @@ ratSlider.oninput = function () {
|
|||||||
var fullFloorNum = document.getElementById("numFullFloors");
|
var fullFloorNum = document.getElementById("numFullFloors");
|
||||||
|
|
||||||
function getCageDimensions(cageName) {
|
function getCageDimensions(cageName) {
|
||||||
|
console.log(cageName);
|
||||||
if (cageName == "SavicSuiteRoyaleXL") {
|
if (cageName == "SavicSuiteRoyaleXL") {
|
||||||
return new Dimensions(115, 67.5, 153);
|
return new Dimensions(115, 67.5, 153);
|
||||||
}
|
}
|
||||||
@ -247,17 +86,18 @@ function getCageDimensions(cageName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getResultFromChecks(checks) {
|
function getResultFromChecks(checks) {
|
||||||
|
console.log(checks.length)
|
||||||
if (Object.keys(checks).length > 0) {
|
if (Object.keys(checks).length > 0) {
|
||||||
const ul = document.createElement('ul');
|
const ul = document.createElement('ul');
|
||||||
for (const key in checks) {
|
for (const key in checks) {
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
li.textContent = `❌ ` + checks[key];
|
li.textContent = `❌` + checks[key];
|
||||||
ul.appendChild(li);
|
ul.appendChild(li);
|
||||||
}
|
}
|
||||||
return ul;
|
return ul;
|
||||||
} else {
|
} else {
|
||||||
const p = document.createElement('p');
|
const p = document.createElement('p');
|
||||||
p.innerHTML = "✅ " + i18next.t("cage-complies-with-all-criteria")
|
p.innerHTML = "✅ Der Käfig erfüllt alle Kriterien!"
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,19 +109,17 @@ function updateViaManualMeasurements() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
labelNumRats.innerHTML = i18next.t("cage-for-x-rats", {"num_rats": ratSlider.value});
|
|
||||||
|
|
||||||
const width = inputWidth.value
|
const width = inputWidth.value
|
||||||
const depth = inputDepth.value
|
const depth = inputDepth.value
|
||||||
const height = inputHeight.value
|
const height = inputHeight.value
|
||||||
const dimensions = new Dimensions(width / 100, depth / 100, height / 100);
|
const dimensions = new Dimensions(width / 100, depth / 100, height / 100);
|
||||||
const validator = new Validator();
|
const failed_checks = cageCheck(dimensions, ratSlider.value, fullFloorNum.value);
|
||||||
const failed_checks = validator.cageCheck(dimensions, ratSlider.value, fullFloorNum.value);
|
console.log(failed_checks);
|
||||||
let resultsDiv = document.getElementById("resultsDiv");
|
let resultsDiv = document.getElementById("resultsDiv");
|
||||||
|
resultsDiv.innerHTML = `<strong>Ergebnis</strong>`;
|
||||||
|
|
||||||
const result = getResultFromChecks(failed_checks);
|
const result = getResultFromChecks(failed_checks);
|
||||||
|
|
||||||
resultsDiv.innerHTML = "";
|
|
||||||
resultsDiv.appendChild(result);
|
resultsDiv.appendChild(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,3 +139,58 @@ function increaseFloorNum() {
|
|||||||
input.value = value + 1;
|
input.value = value + 1;
|
||||||
update();
|
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);
|
||||||
|
})();
|
Loading…
Reference in New Issue
Block a user