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 = `${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); kcalAmount.textContent = `${parseInt(kcal)}kcal`; fatAmount.textContent = `${parseInt(fat)}g`; carbsAmount.textContent = `${parseInt(carbs)}g`; sugarAmount.textContent = `${parseInt(sugar)}g`; proteinsAmount.textContent = `${parseInt(proteins)}g`; alcAmount.textContent = `${parseInt(alc)}g`; updateSummary(); } function calculateKcal(food, amount) { const fat = Math.round(food.fat / 100 * amount) const carbs = Math.round(food.carbs / 100 * amount); const sugar = Math.round(food.sugar / 100 * amount); const proteins = Math.round(food.proteins / 100 * amount); const alc = Math.round(amount * food.alc / 100); const kcal = Math.round(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 += parseInt(menuItem.querySelector(".menu-item-energy").textContent); fat += parseInt(menuItem.querySelector(".menu-item-fat").textContent); carbs += parseInt(menuItem.querySelector(".menu-item-carbs").textContent); sugar += parseInt(menuItem.querySelector(".menu-item-sugar").textContent); proteins += parseInt(menuItem.querySelector(".menu-item-proteins").textContent); alc += parseInt(menuItem.querySelector(".menu-item-alc").textContent); } summaryEnergy.textContent = `${energy}kcal`; summaryFat.textContent = `${fat}g`; summaryCarbs.textContent = `${carbs}g`; summarySugar.textContent = `${sugar}g`; summaryProteins.textContent = `${proteins}g`; summaryAlc.textContent = `${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"; } } } }