Files
RattenheimRechner/src/index.js
2024-09-15 18:06:38 +02:00

403 lines
13 KiB
JavaScript

import i18next from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import HttpApi from "i18next-http-backend";
///////////////
// CAGE DATA //
///////////////
const cages_dict = [
{
"name": "SavicSuiteRoyal95Double",
"dimensions": {
"width": 95,
"depth": 63,
"height": 120
},
"description": {
"en": "A european cage that (with added floors) is suitable for 4-7 rats",
"de": "Ein eurpäischer Käfig der mit zwei zusätzliche Ebenen für 4-7 Ratten geeignet ist. Oft lässt sich der Käfig auch gebraucht kaufen"
},
"images": {
"1": {
"path": "assets/img/savic-95-double.jpg",
"alt": "Bild eines Gitterkäfigs mit Türen die die gesamte Front öffnen."
}
}
},
{
"name": "SavicSuiteRoyalXL",
"dimensions": {
"width": 115,
"depth": 67.5,
"height": 153
},
"description": {
"en": "A european cage that (with added floors) is suitable for 4-9 rats",
"de": "Ein eurpäischer Käfig der mit zwei zusätzliche Ebenen für 4-9 Ratten geeignet ist. Oft lässt sich der Käfig auch gebraucht kaufen"
},
"images": {
"1": {
"path": "assets/img/savic-xl.jpeg",
"alt": "Bild eines Gitterkäfigs mit Türen die die gesamte Front öffnen."
}
}
}
]
/////////////////
// 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");
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;
function createCageCard(cageName, cageImages) {
console.log(cageImages);
// Create the card div
const cardDiv = document.createElement('div');
cardDiv.className = 'card';
cardDiv.id = 'card-'+cageName;
// Create the label element
const label = document.createElement('label');
label.setAttribute('for', cageName);
// Create the checkbox input element
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = cageName;
const cardImageDiv = document.createElement('div');
cardImageDiv.className = 'card-photo';
const img = document.createElement('img');
img.setAttribute("alt-i18n-key", 'alt-savic-xl');
img.src = cageImages[1].path;
// Append image to card-photo div
cardImageDiv.appendChild(img);
// Create the info-container div and heading
const infoContainerDiv = document.createElement('div');
infoContainerDiv.className = 'info-container';
const heading = document.createElement('h4');
heading.innerHTML = '<b>'+ cageName + '</b>';
// Append heading to info-container div
infoContainerDiv.appendChild(heading);
// Append elements to label
label.appendChild(checkbox);
label.appendChild(cardImageDiv);
label.appendChild(infoContainerDiv);
// Append label to cardDiv
cardDiv.appendChild(label);
// Append cardDiv to the body or a specific element
let containerForCages = document.getElementById("container-cages");
containerForCages.appendChild(cardDiv); // Change to a specific parent element if needed
}
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;
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");
ratSlider.oninput = function () {
update();
}
// Full floor functions
var fullFloorNum = document.getElementById("numFullFloors");
function getCageDimensions(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) {
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 = "✅ " + i18next.t("cage-complies-with-all-criteria")
return p;
}
}
function updateViaManualMeasurements() {
markActiveCage("ManualMeasurements");
update();
}
function update() {
labelNumRats.innerHTML = i18next.t("cage-for-x-rats", {"num_rats": ratSlider.value});
const width = inputWidth.value
const depth = inputDepth.value
const height = inputHeight.value
const dimensions = new Dimensions(width / 100, depth / 100, height / 100);
const validator = new Validator();
const failed_checks = validator.cageCheck(dimensions, ratSlider.value, fullFloorNum.value);
let resultsDiv = document.getElementById("resultsDiv");
const result = getResultFromChecks(failed_checks);
resultsDiv.innerHTML = "";
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();
}
cages_dict.forEach((cage) => {
console.log(cage.images);
createCageCard(cage.name, cage.images);
});