2022-09-08 22:55:43 +02:00
|
|
|
const foodSearch = document.querySelector("#food-search");
|
|
|
|
const searchEntries = document.querySelector("#search-entries");
|
|
|
|
const foodMenu = document.querySelector("#food-menu");
|
2022-09-09 13:23:03 +02:00
|
|
|
const menuTable = document.querySelector("#menu-table");
|
|
|
|
|
|
|
|
const summaryEnergy = document.querySelector("#summary-energy");
|
|
|
|
const summaryFat = document.querySelector("#summary-fat");
|
|
|
|
const summaryCarbs = document.querySelector("#summary-carbs");
|
2022-09-09 15:25:08 +02:00
|
|
|
const summarySugar = document.querySelector("#summary-sugar");
|
2022-09-09 13:23:03 +02:00
|
|
|
const summaryProteins = document.querySelector("#summary-proteins");
|
2022-09-09 15:25:08 +02:00
|
|
|
const summaryAlc = document.querySelector("#summary-alc");
|
2022-09-09 13:23:03 +02:00
|
|
|
|
2022-09-09 15:25:08 +02:00
|
|
|
const alcDensity = 0.789;
|
2022-09-08 22:55:43 +02:00
|
|
|
|
|
|
|
updateSummary();
|
|
|
|
initFoodList();
|
|
|
|
foodSearch.addEventListener("input", onFoodSearched);
|
|
|
|
if (foodSearch.value != "") onFoodSearched();
|
|
|
|
|
|
|
|
function initFoodList() {
|
|
|
|
const dataList = document.querySelector("#food-list");
|
|
|
|
|
|
|
|
for (let food of foods) {
|
|
|
|
const option = document.createElement("option");
|
|
|
|
option.setAttribute("value", food.name);
|
|
|
|
dataList.appendChild(option);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function onFoodSearched() {
|
|
|
|
const food = foodSearch.value;
|
|
|
|
const foodLC = food.toLowerCase();
|
2022-09-09 17:22:52 +02:00
|
|
|
|
2022-09-08 22:55:43 +02:00
|
|
|
if (food == "") {
|
|
|
|
createSearchEntries([]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const foodsCopy = structuredClone(foods);
|
|
|
|
|
|
|
|
foodsCopy.sort((a, b) => {
|
|
|
|
const aNameLC = a.name.toLowerCase();
|
|
|
|
const bNameLC = b.name.toLowerCase();
|
|
|
|
|
|
|
|
const ainc = aNameLC.includes(foodLC);
|
|
|
|
const binc = bNameLC.includes(foodLC);
|
|
|
|
if (ainc && binc) {
|
|
|
|
return 0;
|
|
|
|
} else if (ainc) {
|
|
|
|
return -1;
|
|
|
|
} else if (binc) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const distA = levenshtein(foodLC, aNameLC);
|
|
|
|
const distB = levenshtein(foodLC, bNameLC);
|
|
|
|
return distA - distB;
|
|
|
|
})
|
|
|
|
|
|
|
|
const maxDist = 10;
|
|
|
|
let maxAmount = 10;
|
|
|
|
let amount = 0;
|
|
|
|
for (let f of foodsCopy) {
|
|
|
|
const fnameLC = f.name.toLowerCase();
|
|
|
|
if (fnameLC.includes(foodLC)) {
|
|
|
|
amount++;
|
|
|
|
maxAmount++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const dist = levenshtein(foodLC, fnameLC);
|
|
|
|
if (dist >= maxDist) break;
|
|
|
|
amount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
createSearchEntries(foodsCopy.slice(0, Math.min(amount, maxAmount, 30)));
|
|
|
|
}
|
|
|
|
|
|
|
|
async function createSearchEntries(foods) {
|
|
|
|
const nodes = [];
|
|
|
|
for (let food of foods) {
|
|
|
|
const node = await JST.load("search-entry");
|
|
|
|
node.querySelector(".search-entry-name").textContent = food.name;
|
|
|
|
node.querySelector(".search-entry").addEventListener("click", () => addMenuItem(food));
|
|
|
|
nodes.push(node);
|
|
|
|
}
|
|
|
|
searchEntries.replaceChildren(...nodes);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function addMenuItem(food) {
|
|
|
|
const alreadyAddedMenuItem = document.querySelector(`#food-menu .menu-item[data-food-name="${food.name}"]`);
|
|
|
|
if (alreadyAddedMenuItem !== null) {
|
|
|
|
alreadyAddedMenuItem.style.animation = "highlight 0.25s linear";
|
|
|
|
setTimeout(() => alreadyAddedMenuItem.style.animation = "", 250);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const node = await JST.load("menu-item");
|
2022-09-09 13:23:03 +02:00
|
|
|
|
2022-09-08 22:55:43 +02:00
|
|
|
const nodeRoot = node.querySelector(".menu-item");
|
2022-09-09 13:23:03 +02:00
|
|
|
const itemName = node.querySelector(".menu-item-name");
|
|
|
|
const itemKcal = node.querySelector(".menu-item-kcal");
|
|
|
|
const itemUnit = node.querySelector(".menu-item-unit");
|
2022-09-08 22:55:43 +02:00
|
|
|
const itemAmount = node.querySelector(".menu-item-amount");
|
2022-09-09 13:23:03 +02:00
|
|
|
const kcalAmount = node.querySelector(".menu-item-energy");
|
|
|
|
const fatAmount = node.querySelector(".menu-item-fat");
|
|
|
|
const carbsAmount = node.querySelector(".menu-item-carbs");
|
2022-09-09 15:25:08 +02:00
|
|
|
const sugarAmount = node.querySelector(".menu-item-sugar");
|
2022-09-09 13:23:03 +02:00
|
|
|
const proteinsAmount = node.querySelector(".menu-item-proteins");
|
2022-09-09 15:25:08 +02:00
|
|
|
const alcAmount = node.querySelector(".menu-item-alc");
|
2022-09-09 00:21:59 +02:00
|
|
|
const score = node.querySelector(".menu-item-score");
|
2022-09-09 13:23:03 +02:00
|
|
|
|
2022-09-09 15:25:08 +02:00
|
|
|
const nutritions100 = calculateKcal(food, 100);
|
|
|
|
|
2022-09-08 22:55:43 +02:00
|
|
|
nodeRoot.setAttribute("data-food-name", food.name);
|
2022-09-09 13:23:03 +02:00
|
|
|
|
|
|
|
itemName.textContent = food.name;
|
2022-09-09 17:44:41 +02:00
|
|
|
itemKcal.textContent = `${formatValue(nutritions100.kcal)} kcal/100${food.unit}`;
|
2022-09-09 13:23:03 +02:00
|
|
|
itemUnit.textContent = food.unit;
|
|
|
|
|
|
|
|
fatAmount.textContent = `${food.fat}g`;
|
|
|
|
carbsAmount.textContent = `${food.carbs}g`;
|
|
|
|
proteinsAmount.textContent = `${food.proteins}g`;
|
2022-09-09 00:21:59 +02:00
|
|
|
|
2022-09-09 15:25:08 +02:00
|
|
|
score.textContent = nutritions100.score.toFixed(1);
|
|
|
|
score.classList.add(getScoreColor(nutritions100.score, food.unit));
|
2022-09-09 00:21:59 +02:00
|
|
|
|
2022-09-08 22:55:43 +02:00
|
|
|
node.querySelector(".delete-menu-item").addEventListener("click", () => {
|
|
|
|
foodMenu.removeChild(nodeRoot);
|
|
|
|
updateSummary();
|
|
|
|
});
|
|
|
|
|
2022-09-09 15:25:08 +02:00
|
|
|
const updateFoodValues = () => {
|
2022-09-09 01:20:36 +02:00
|
|
|
if (!/^[0-9]+$/.test(itemAmount.value)) {
|
2022-09-08 22:55:43 +02:00
|
|
|
return;
|
|
|
|
}
|
2022-09-09 01:20:36 +02:00
|
|
|
|
2022-09-08 22:55:43 +02:00
|
|
|
const amount = parseInt(itemAmount.value);
|
2022-09-09 15:25:08 +02:00
|
|
|
updateFoodAmount(food, amount, kcalAmount, fatAmount, carbsAmount, sugarAmount, proteinsAmount, alcAmount);
|
2022-09-09 01:20:36 +02:00
|
|
|
};
|
|
|
|
|
2022-09-09 15:25:08 +02:00
|
|
|
const evaluateFoodValues = () => {
|
2022-09-09 01:20:36 +02:00
|
|
|
if (/^[0-9]+$/.test(itemAmount.value)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const evaluator = new Worker("evaluator.js");
|
|
|
|
evaluator.postMessage(itemAmount.value);
|
|
|
|
evaluator.onmessage = e => {
|
|
|
|
const amount = Math.max(parseInt(e.data), 0);
|
|
|
|
itemAmount.value = amount;
|
2022-09-09 15:25:08 +02:00
|
|
|
updateFoodAmount(food, amount, kcalAmount, fatAmount, carbsAmount, sugarAmount, proteinsAmount, alcAmount);
|
2022-09-09 01:20:36 +02:00
|
|
|
};
|
2022-09-08 22:55:43 +02:00
|
|
|
}
|
|
|
|
|
2022-09-09 00:55:16 +02:00
|
|
|
itemAmount.value = food.avg_amount ?? 100;
|
2022-09-09 15:25:08 +02:00
|
|
|
itemAmount.addEventListener("input", updateFoodValues);
|
|
|
|
itemAmount.addEventListener("change", evaluateFoodValues);
|
2022-09-08 22:55:43 +02:00
|
|
|
|
|
|
|
foodMenu.appendChild(node);
|
2022-09-09 15:25:08 +02:00
|
|
|
updateFoodValues();
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateFoodAmount(food, amount, kcalAmount, fatAmount, carbsAmount, sugarAmount, proteinsAmount, alcAmount) {
|
|
|
|
const {fat, carbs, sugar, proteins, alc, kcal} = calculateKcal(food, amount);
|
|
|
|
|
2022-09-09 17:44:41 +02:00
|
|
|
setAmount(kcalAmount, kcal, "kcal");
|
|
|
|
setAmount(fatAmount, fat, "g");
|
|
|
|
setAmount(carbsAmount, carbs, "g");
|
|
|
|
setAmount(sugarAmount, sugar, "g");
|
|
|
|
setAmount(proteinsAmount, proteins, "g");
|
|
|
|
setAmount(alcAmount, alc, "g");
|
|
|
|
|
2022-09-08 22:55:43 +02:00
|
|
|
updateSummary();
|
|
|
|
}
|
|
|
|
|
2022-09-09 17:44:41 +02:00
|
|
|
function setAmount(element, amount, unit) {
|
|
|
|
element.textContent = `${formatValue(amount)}${unit}`;
|
|
|
|
element.setAttribute("data-value", amount);
|
|
|
|
}
|
|
|
|
|
|
|
|
function formatValue(value) {
|
|
|
|
if (value > 0 && value < 1) {
|
|
|
|
return "<1"
|
|
|
|
}
|
|
|
|
|
|
|
|
return Math.round(value);
|
|
|
|
}
|
|
|
|
|
2022-09-09 15:25:08 +02:00
|
|
|
function calculateKcal(food, amount) {
|
2022-09-09 17:44:41 +02:00
|
|
|
const fat = food.fat / 100 * amount;
|
|
|
|
const carbs = food.carbs / 100 * amount;
|
|
|
|
const sugar = food.sugar / 100 * amount;
|
|
|
|
const proteins = food.proteins / 100 * amount;
|
|
|
|
const alc = amount * food.alc / 100;
|
|
|
|
const kcal = fat * 9 + carbs * 4 + proteins * 4 + alc * 7;
|
2022-09-09 15:25:08 +02:00
|
|
|
const score = kcal / amount;
|
|
|
|
return {fat, carbs, sugar, proteins, alc, kcal, score}
|
|
|
|
}
|
|
|
|
|
2022-09-08 22:55:43 +02:00
|
|
|
function updateSummary() {
|
2022-09-09 13:23:03 +02:00
|
|
|
const menuItems = foodMenu.querySelectorAll(".menu-item");
|
|
|
|
menuTable.style.display = menuItems.length > 0 ? "table" : "none";
|
|
|
|
|
|
|
|
let energy = 0;
|
|
|
|
let fat = 0;
|
|
|
|
let carbs = 0;
|
2022-09-09 15:25:08 +02:00
|
|
|
let sugar = 0;
|
2022-09-09 13:23:03 +02:00
|
|
|
let proteins = 0;
|
2022-09-09 15:25:08 +02:00
|
|
|
let alc = 0;
|
2022-09-09 13:23:03 +02:00
|
|
|
|
|
|
|
for (let menuItem of menuItems) {
|
2022-09-09 17:44:41 +02:00
|
|
|
energy += parseFloat(menuItem.querySelector(".menu-item-energy").getAttribute("data-value"));
|
|
|
|
fat += parseFloat(menuItem.querySelector(".menu-item-fat").getAttribute("data-value"));
|
|
|
|
carbs += parseFloat(menuItem.querySelector(".menu-item-carbs").getAttribute("data-value"));
|
|
|
|
sugar += parseFloat(menuItem.querySelector(".menu-item-sugar").getAttribute("data-value"));
|
|
|
|
proteins += parseFloat(menuItem.querySelector(".menu-item-proteins").getAttribute("data-value"));
|
|
|
|
alc += parseFloat(menuItem.querySelector(".menu-item-alc").getAttribute("data-value"));
|
2022-09-08 22:55:43 +02:00
|
|
|
}
|
|
|
|
|
2022-09-09 17:44:41 +02:00
|
|
|
summaryEnergy.textContent = `${formatValue(energy)}kcal`;
|
|
|
|
summaryFat.textContent = `${formatValue(fat)}g`;
|
|
|
|
summaryCarbs.textContent = `${formatValue(carbs)}g`;
|
|
|
|
summarySugar.textContent = `${formatValue(sugar)}g`;
|
|
|
|
summaryProteins.textContent = `${formatValue(proteins)}g`;
|
|
|
|
summaryAlc.textContent = `${formatValue(alc)}g`;
|
2022-09-09 00:21:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function getScoreColor(density, unit) {
|
|
|
|
switch (unit) {
|
|
|
|
case "g": {
|
|
|
|
if (density >= 2.4) {
|
|
|
|
return "red"
|
2022-09-09 15:25:08 +02:00
|
|
|
} else if (density >= 1.6) {
|
2022-09-09 00:21:59 +02:00
|
|
|
return "orange"
|
|
|
|
} else {
|
|
|
|
return "green";
|
|
|
|
}
|
|
|
|
}
|
2022-09-09 17:22:52 +02:00
|
|
|
|
2022-09-09 00:21:59 +02:00
|
|
|
case "ml": {
|
2022-09-09 15:25:08 +02:00
|
|
|
if (density >= 0.1) {
|
2022-09-09 00:21:59 +02:00
|
|
|
return "red"
|
|
|
|
} else {
|
|
|
|
return "green";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-09 17:22:52 +02:00
|
|
|
}
|