const foodSearch = document.querySelector("#food-search"); const searchEntries = document.querySelector("#search-entries"); const foodMenu = document.querySelector("#food-menu"); const menuTable = document.querySelector("#menu-table"); const summaryEnergy = document.querySelector("#summary-energy"); const summaryFat = document.querySelector("#summary-fat"); const summaryCarbs = document.querySelector("#summary-carbs"); const summarySugar = document.querySelector("#summary-sugar"); const summaryProteins = document.querySelector("#summary-proteins"); const summaryAlc = document.querySelector("#summary-alc"); const alcDensity = 0.789; 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(); 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"); const nodeRoot = node.querySelector(".menu-item"); const itemName = node.querySelector(".menu-item-name"); const itemKcal = node.querySelector(".menu-item-kcal"); const itemUnit = node.querySelector(".menu-item-unit"); const itemAmount = node.querySelector(".menu-item-amount"); const kcalAmount = node.querySelector(".menu-item-energy"); const fatAmount = node.querySelector(".menu-item-fat"); const carbsAmount = node.querySelector(".menu-item-carbs"); const sugarAmount = node.querySelector(".menu-item-sugar"); const proteinsAmount = node.querySelector(".menu-item-proteins"); const alcAmount = node.querySelector(".menu-item-alc"); const score = node.querySelector(".menu-item-score"); const nutritions100 = calculateKcal(food, 100); nodeRoot.setAttribute("data-food-name", food.name); itemName.textContent = food.name; itemKcal.textContent = `${formatValue(nutritions100.kcal)} kcal/100${food.unit}`; itemUnit.textContent = food.unit; fatAmount.textContent = `${food.fat}g`; carbsAmount.textContent = `${food.carbs}g`; proteinsAmount.textContent = `${food.proteins}g`; score.textContent = nutritions100.score.toFixed(1); score.classList.add(getScoreColor(nutritions100.score, food.unit)); node.querySelector(".delete-menu-item").addEventListener("click", () => { foodMenu.removeChild(nodeRoot); updateSummary(); }); const updateFoodValues = () => { if (!/^[0-9]+$/.test(itemAmount.value)) { return; } const amount = parseInt(itemAmount.value); updateFoodAmount(food, amount, kcalAmount, fatAmount, carbsAmount, sugarAmount, proteinsAmount, alcAmount); }; const evaluateFoodValues = () => { 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; updateFoodAmount(food, amount, kcalAmount, fatAmount, carbsAmount, sugarAmount, proteinsAmount, alcAmount); }; } itemAmount.value = food.avg_amount ?? 100; itemAmount.addEventListener("input", updateFoodValues); itemAmount.addEventListener("change", evaluateFoodValues); foodMenu.appendChild(node); updateFoodValues(); } function updateFoodAmount(food, amount, kcalAmount, fatAmount, carbsAmount, sugarAmount, proteinsAmount, alcAmount) { const {fat, carbs, sugar, proteins, alc, kcal} = calculateKcal(food, amount); setAmount(kcalAmount, kcal, "kcal"); setAmount(fatAmount, fat, "g"); setAmount(carbsAmount, carbs, "g"); setAmount(sugarAmount, sugar, "g"); setAmount(proteinsAmount, proteins, "g"); setAmount(alcAmount, alc, "g"); updateSummary(); } 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); } function calculateKcal(food, amount) { 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; const score = kcal / amount; return {fat, carbs, sugar, proteins, alc, kcal, score} } function updateSummary() { const menuItems = foodMenu.querySelectorAll(".menu-item"); menuTable.style.display = menuItems.length > 0 ? "table" : "none"; let energy = 0; let fat = 0; let carbs = 0; let sugar = 0; let proteins = 0; let alc = 0; for (let menuItem of menuItems) { 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")); } 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`; } function getScoreColor(density, unit) { switch (unit) { case "g": { if (density >= 2.4) { return "red" } else if (density >= 1.6) { return "orange" } else { return "green"; } } case "ml": { if (density >= 0.1) { return "red" } else { return "green"; } } } }