diff --git a/iframe.html b/iframe.html index 49c6d15..a6801ec 100644 --- a/iframe.html +++ b/iframe.html @@ -906,6 +906,364 @@ color: #f5f3f7; } } + + /* Multi-Food Source Styles */ + .dog-calculator-food-sources { + display: flex; + flex-direction: column; + gap: 16px; + } + + .dog-calculator-food-source-card { + background: #ffffff; + border: 1px solid #e8e3ed; + border-radius: 8px; + padding: 20px; + position: relative; + transition: all 0.2s ease; + } + + .dog-calculator-food-source-card:hover { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + } + + .dog-calculator-food-source-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; + } + + .dog-calculator-food-source-title { + font-weight: 600; + color: #6f3f6d; + font-size: 1.1rem; + margin: 0; + } + + .dog-calculator-remove-food-btn { + background: #e87159; + color: white; + border: none; + border-radius: 50%; + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + font-size: 16px; + font-weight: 600; + transition: all 0.2s ease; + line-height: 1; + } + + .dog-calculator-remove-food-btn:hover { + background: #d65a47; + transform: scale(1.1); + } + + .dog-calculator-percentage-group { + margin-top: 16px; + padding-top: 16px; + border-top: 1px solid #e8e3ed; + } + + .dog-calculator-percentage-label { + display: block; + margin-bottom: 8px; + font-weight: 500; + color: #6f3f6d; + font-size: 1rem; + } + + .dog-calculator-percentage-input-group { + display: flex; + align-items: center; + gap: 12px; + } + + .dog-calculator-percentage-slider { + flex: 1; + height: 6px; + border-radius: 3px; + background: #e8e3ed; + outline: none; + transition: all 0.2s ease; + -webkit-appearance: none; + appearance: none; + } + + .dog-calculator-percentage-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 20px; + height: 20px; + border-radius: 50%; + background: #f19a5f; + cursor: pointer; + border: 2px solid white; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + transition: all 0.2s ease; + } + + .dog-calculator-percentage-slider::-webkit-slider-thumb:hover { + background: #e87741; + transform: scale(1.1); + } + + .dog-calculator-percentage-slider::-moz-range-thumb { + width: 20px; + height: 20px; + border-radius: 50%; + background: #f19a5f; + cursor: pointer; + border: 2px solid white; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + transition: all 0.2s ease; + } + + .dog-calculator-percentage-input { + width: 70px; + padding: 8px 12px; + border: 1px solid #e8e3ed; + border-radius: 6px; + font-size: 0.9rem; + text-align: center; + background-color: #ffffff; + color: #6f3f6d; + } + + .dog-calculator-percentage-input:focus { + outline: none; + border-color: #f19a5f; + box-shadow: 0 0 0 3px rgba(241, 154, 95, 0.1); + } + + .dog-calculator-add-food-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + width: 100%; + padding: 16px; + border: 2px dashed #e8e3ed; + border-radius: 8px; + background: transparent; + color: #635870; + font-size: 1rem; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + font-family: inherit; + margin-top: 16px; + } + + .dog-calculator-add-food-btn:hover { + border-color: #f19a5f; + color: #f19a5f; + background: rgba(241, 154, 95, 0.05); + } + + .dog-calculator-add-food-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + border-color: #e8e3ed; + color: #635870; + background: transparent; + } + + .dog-calculator-add-food-btn:disabled:hover { + border-color: #e8e3ed; + color: #635870; + background: transparent; + } + + .dog-calculator-food-results { + background: linear-gradient(135deg, rgba(241, 154, 95, 0.08) 0%, rgba(241, 154, 95, 0.04) 100%); + border: 1px solid rgba(241, 154, 95, 0.2); + border-radius: 6px; + padding: 16px; + margin-top: 20px; + } + + .dog-calculator-food-result-item { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + font-size: 0.9rem; + } + + .dog-calculator-food-result-item:last-child { + margin-bottom: 0; + } + + .dog-calculator-food-result-label { + font-weight: 500; + color: #6f3f6d; + } + + .dog-calculator-food-result-value { + font-weight: 600; + color: #6f3f6d; + padding: 2px 8px; + background: rgba(241, 154, 95, 0.15); + border-radius: 3px; + font-size: 0.85rem; + } + + /* Dark theme support for food sources */ + .dog-calculator-container.theme-dark .dog-calculator-food-source-card { + background: #312b3b; + border-color: #433c4f; + } + + .dog-calculator-container.theme-dark .dog-calculator-food-source-title { + color: #f5f3f7; + } + + .dog-calculator-container.theme-dark .dog-calculator-percentage-label { + color: #f5f3f7; + } + + .dog-calculator-container.theme-dark .dog-calculator-percentage-slider { + background: #433c4f; + } + + .dog-calculator-container.theme-dark .dog-calculator-percentage-input { + background: #433c4f; + border-color: #524a5f; + color: #f5f3f7; + } + + .dog-calculator-container.theme-dark .dog-calculator-percentage-group { + border-color: #433c4f; + } + + .dog-calculator-container.theme-dark .dog-calculator-add-food-btn { + border-color: #433c4f; + color: #b8b0c2; + } + + .dog-calculator-container.theme-dark .dog-calculator-add-food-btn:hover { + border-color: #f19a5f; + color: #f19a5f; + background: rgba(241, 154, 95, 0.1); + } + + .dog-calculator-container.theme-dark .dog-calculator-food-results { + background: linear-gradient(135deg, rgba(241, 154, 95, 0.15) 0%, rgba(241, 154, 95, 0.08) 100%); + border-color: rgba(241, 154, 95, 0.3); + } + + .dog-calculator-container.theme-dark .dog-calculator-food-result-label { + color: #f5f3f7; + } + + .dog-calculator-container.theme-dark .dog-calculator-food-result-value { + color: #f5f3f7; + background: rgba(241, 154, 95, 0.2); + } + + /* System theme support for food sources */ + @media (prefers-color-scheme: dark) { + .dog-calculator-container.theme-system .dog-calculator-food-source-card { + background: #312b3b; + border-color: #433c4f; + } + + .dog-calculator-container.theme-system .dog-calculator-food-source-title { + color: #f5f3f7; + } + + .dog-calculator-container.theme-system .dog-calculator-percentage-label { + color: #f5f3f7; + } + + .dog-calculator-container.theme-system .dog-calculator-percentage-slider { + background: #433c4f; + } + + .dog-calculator-container.theme-system .dog-calculator-percentage-input { + background: #433c4f; + border-color: #524a5f; + color: #f5f3f7; + } + + .dog-calculator-container.theme-system .dog-calculator-percentage-group { + border-color: #433c4f; + } + + .dog-calculator-container.theme-system .dog-calculator-add-food-btn { + border-color: #433c4f; + color: #b8b0c2; + } + + .dog-calculator-container.theme-system .dog-calculator-add-food-btn:hover { + border-color: #f19a5f; + color: #f19a5f; + background: rgba(241, 154, 95, 0.1); + } + + .dog-calculator-container.theme-system .dog-calculator-food-results { + background: linear-gradient(135deg, rgba(241, 154, 95, 0.15) 0%, rgba(241, 154, 95, 0.08) 100%); + border-color: rgba(241, 154, 95, 0.3); + } + + .dog-calculator-container.theme-system .dog-calculator-food-result-label { + color: #f5f3f7; + } + + .dog-calculator-container.theme-system .dog-calculator-food-result-value { + color: #f5f3f7; + background: rgba(241, 154, 95, 0.2); + } + } + + /* Mobile responsive design for food sources */ + @media (max-width: 576px) { + .dog-calculator-food-source-card { + padding: 16px; + } + + .dog-calculator-food-source-header { + flex-direction: column; + align-items: flex-start; + gap: 12px; + } + + .dog-calculator-remove-food-btn { + align-self: flex-end; + margin-top: -8px; + } + + .dog-calculator-percentage-input-group { + flex-direction: column; + gap: 8px; + align-items: stretch; + } + + .dog-calculator-percentage-input { + width: 100%; + } + + .dog-calculator-add-food-btn { + padding: 12px; + font-size: 0.9rem; + } + + .dog-calculator-food-result-item { + flex-direction: column; + align-items: flex-start; + gap: 4px; + } + + .dog-calculator-food-result-value { + align-self: stretch; + text-align: center; + } + } @@ -965,30 +1323,33 @@
-
-
- - -
Please enter a valid energy content
-
-
- - + +
+ +
+ + + + + + + +
@@ -1100,12 +1461,15 @@ this.isImperial = false; this.theme = this.getThemeFromURL() || 'system'; this.scale = this.getScaleFromURL() || 1.0; + this.foodSources = []; + this.maxFoodSources = 5; this.init(); } init() { this.applyTheme(); this.applyScale(); + this.initializeFoodSources(); this.bindEvents(); this.updateUnitLabels(); this.setupIframeResize(); @@ -1153,13 +1517,289 @@ } } + // Food Source Management Methods + initializeFoodSources() { + this.addFoodSource(); + this.updateAddButton(); + } + + addFoodSource() { + if (this.foodSources.length >= this.maxFoodSources) { + return; + } + + const id = this.generateFoodSourceId(); + const foodSource = { + id: id, + name: `Food Source ${this.foodSources.length + 1}`, + energy: '', + energyUnit: this.isImperial ? 'kcalcup' : 'kcal100g', + percentage: this.foodSources.length === 0 ? 100 : 0 + }; + + this.foodSources.push(foodSource); + this.redistributePercentages(); + this.renderFoodSource(foodSource); + this.updateAddButton(); + this.updateFoodCalculations(); + } + + removeFoodSource(id) { + if (this.foodSources.length <= 1) { + return; // Cannot remove the last food source + } + + const index = this.foodSources.findIndex(fs => fs.id === id); + if (index === -1) return; + + this.foodSources.splice(index, 1); + + // Remove the DOM element + const element = document.getElementById(`foodSource-${id}`); + if (element) { + element.remove(); + } + + // Redistribute percentages among remaining sources + this.redistributePercentages(); + this.updateFoodSourceNames(); + this.updateAddButton(); + this.updateFoodCalculations(); + } + + generateFoodSourceId() { + return 'fs_' + Date.now() + '_' + Math.random().toString(36).substr(2, 5); + } + + redistributePercentages() { + const count = this.foodSources.length; + if (count === 0) return; + + const equalPercentage = Math.floor(100 / count); + const remainder = 100 - (equalPercentage * count); + + this.foodSources.forEach((fs, index) => { + fs.percentage = equalPercentage + (index < remainder ? 1 : 0); + }); + + // Update the UI sliders and inputs + this.updatePercentageInputs(); + } + + updatePercentageInputs() { + this.foodSources.forEach(fs => { + const slider = document.getElementById(`percentage-slider-${fs.id}`); + const input = document.getElementById(`percentage-input-${fs.id}`); + + if (slider) slider.value = fs.percentage; + if (input) input.value = fs.percentage; + }); + } + + adjustPercentages(changedId, newPercentage) { + const changedIndex = this.foodSources.findIndex(fs => fs.id === changedId); + if (changedIndex === -1) return; + + const oldPercentage = this.foodSources[changedIndex].percentage; + const difference = newPercentage - oldPercentage; + + this.foodSources[changedIndex].percentage = newPercentage; + + // Distribute the difference among other sources proportionally + const otherSources = this.foodSources.filter((fs, index) => index !== changedIndex); + if (otherSources.length === 0) return; + + const totalOtherPercentage = otherSources.reduce((sum, fs) => sum + fs.percentage, 0); + + if (totalOtherPercentage === 0) { + // If all others are 0, distribute equally + const remainingPercentage = 100 - newPercentage; + const equalShare = Math.floor(remainingPercentage / otherSources.length); + const remainder = remainingPercentage - (equalShare * otherSources.length); + + otherSources.forEach((fs, index) => { + fs.percentage = equalShare + (index < remainder ? 1 : 0); + }); + } else { + // Distribute proportionally + const targetTotal = 100 - newPercentage; + const scale = targetTotal / totalOtherPercentage; + + let distributedTotal = 0; + otherSources.forEach((fs, index) => { + if (index === otherSources.length - 1) { + // Last item gets the remainder to ensure exact 100% + fs.percentage = targetTotal - distributedTotal; + } else { + fs.percentage = Math.round(fs.percentage * scale); + distributedTotal += fs.percentage; + } + }); + } + + this.updatePercentageInputs(); + this.updateFoodCalculations(); + } + + updateFoodSourceNames() { + this.foodSources.forEach((fs, index) => { + fs.name = `Food Source ${index + 1}`; + const titleElement = document.getElementById(`food-title-${fs.id}`); + if (titleElement) { + titleElement.textContent = fs.name; + } + }); + } + + updateAddButton() { + const addBtn = document.getElementById('addFoodBtn'); + const countSpan = document.getElementById('foodSourceCount'); + + if (addBtn) { + const remaining = this.maxFoodSources - this.foodSources.length; + addBtn.disabled = remaining <= 0; + + if (countSpan) { + if (remaining > 0) { + countSpan.textContent = `(${remaining} of ${this.maxFoodSources} remaining)`; + } else { + countSpan.textContent = `(maximum ${this.maxFoodSources} reached)`; + } + } + } + } + + renderFoodSource(foodSource) { + const container = document.getElementById('foodSources'); + if (!container) return; + + const cardHTML = ` +
+
+

${foodSource.name}

+ ${this.foodSources.length > 1 ? `` : ''} +
+ +
+
+ + +
Please enter a valid energy content
+
+
+ + +
+
+ +
+ +
+ + +
+
+
+ `; + + container.insertAdjacentHTML('beforeend', cardHTML); + + // Bind events for the new food source + this.bindFoodSourceEvents(foodSource.id); + } + + bindFoodSourceEvents(id) { + // Energy input events + const energyInput = document.getElementById(`energy-${id}`); + const energyUnitSelect = document.getElementById(`energy-unit-${id}`); + const percentageSlider = document.getElementById(`percentage-slider-${id}`); + const percentageInput = document.getElementById(`percentage-input-${id}`); + const removeBtn = document.getElementById(`remove-${id}`); + + if (energyInput) { + energyInput.addEventListener('input', () => { + this.updateFoodSourceData(id, 'energy', energyInput.value); + this.updateFoodCalculations(); + }); + energyInput.addEventListener('blur', () => this.validateFoodSourceEnergy(id)); + } + + if (energyUnitSelect) { + energyUnitSelect.addEventListener('change', () => { + this.updateFoodSourceData(id, 'energyUnit', energyUnitSelect.value); + this.updateFoodCalculations(); + }); + } + + if (percentageSlider) { + percentageSlider.addEventListener('input', () => { + const newPercentage = parseInt(percentageSlider.value); + this.adjustPercentages(id, newPercentage); + document.getElementById(`percentage-display-${id}`).textContent = `${newPercentage}%`; + }); + } + + if (percentageInput) { + percentageInput.addEventListener('change', () => { + const newPercentage = Math.max(0, Math.min(100, parseInt(percentageInput.value) || 0)); + this.adjustPercentages(id, newPercentage); + document.getElementById(`percentage-display-${id}`).textContent = `${newPercentage}%`; + }); + } + + if (removeBtn) { + removeBtn.addEventListener('click', () => this.removeFoodSource(id)); + } + } + + updateFoodSourceData(id, field, value) { + const foodSource = this.foodSources.find(fs => fs.id === id); + if (foodSource) { + foodSource[field] = value; + } + } + + validateFoodSourceEnergy(id) { + const energyInput = document.getElementById(`energy-${id}`); + const energyUnitSelect = document.getElementById(`energy-unit-${id}`); + const errorElement = document.getElementById(`energy-error-${id}`); + + if (!energyInput || !energyUnitSelect || !errorElement) return; + + const energy = parseFloat(energyInput.value); + const unit = energyUnitSelect.value; + + let minValue = 1; + switch (unit) { + case 'kcal100g': minValue = 1; break; + case 'kcalkg': minValue = 10; break; + case 'kcalcup': minValue = 50; break; + case 'kcalcan': minValue = 100; break; + } + + if (!this.validateInput(energy, minValue)) { + errorElement.classList.remove('dog-calculator-hidden'); + } else { + errorElement.classList.add('dog-calculator-hidden'); + } + } + bindEvents() { const weightInput = document.getElementById('weight'); const dogTypeSelect = document.getElementById('dogType'); - const foodEnergyInput = document.getElementById('foodEnergy'); const daysInput = document.getElementById('days'); const unitSelect = document.getElementById('unit'); const unitToggle = document.getElementById('unitToggle'); + const addFoodBtn = document.getElementById('addFoodBtn'); if (weightInput) { weightInput.addEventListener('input', () => this.updateCalorieCalculations()); @@ -1168,14 +1808,6 @@ if (dogTypeSelect) dogTypeSelect.addEventListener('change', () => this.updateCalorieCalculations()); - if (foodEnergyInput) { - foodEnergyInput.addEventListener('input', () => this.updateFoodCalculations()); - foodEnergyInput.addEventListener('blur', () => this.validateFoodEnergy()); - } - - const energyUnitSelect = document.getElementById('energyUnit'); - if (energyUnitSelect) energyUnitSelect.addEventListener('change', () => this.updateFoodCalculations()); - if (daysInput) { daysInput.addEventListener('input', () => this.updateFoodCalculations()); daysInput.addEventListener('blur', () => this.validateDays()); @@ -1185,6 +1817,8 @@ if (unitToggle) unitToggle.addEventListener('change', () => this.toggleUnits()); + if (addFoodBtn) addFoodBtn.addEventListener('click', () => this.addFoodSource()); + // Modal event listeners const shareBtn = document.getElementById('shareBtn'); const embedBtn = document.getElementById('embedBtn'); @@ -1248,7 +1882,6 @@ const weightLabel = document.getElementById('weightLabel'); const weightInput = document.getElementById('weight'); const unitSelect = document.getElementById('unit'); - const energyUnitSelect = document.getElementById('energyUnit'); if (metricLabel && imperialLabel) { metricLabel.classList.toggle('active', !this.isImperial); @@ -1268,10 +1901,17 @@ '' + ''; } - // Set energy unit to kcal/cup for imperial - if (energyUnitSelect && energyUnitSelect.value === 'kcal100g') { - energyUnitSelect.value = 'kcalcup'; - } + + // Update energy units for all food sources to kcal/cup for imperial + this.foodSources.forEach(fs => { + if (fs.energyUnit === 'kcal100g') { + fs.energyUnit = 'kcalcup'; + const energyUnitSelect = document.getElementById(`energy-unit-${fs.id}`); + if (energyUnitSelect) { + energyUnitSelect.value = 'kcalcup'; + } + } + }); } else { if (weightLabel) weightLabel.textContent = "Dog's Weight (kg):"; if (weightInput) { @@ -1285,10 +1925,17 @@ '' + ''; } - // Set energy unit to kcal/100g for metric - if (energyUnitSelect && energyUnitSelect.value === 'kcalcup') { - energyUnitSelect.value = 'kcal100g'; - } + + // Update energy units for all food sources to kcal/100g for metric + this.foodSources.forEach(fs => { + if (fs.energyUnit === 'kcalcup') { + fs.energyUnit = 'kcal100g'; + const energyUnitSelect = document.getElementById(`energy-unit-${fs.id}`); + if (energyUnitSelect) { + energyUnitSelect.value = 'kcal100g'; + } + } + }); } } @@ -1317,30 +1964,6 @@ return this.isImperial ? weight / 2.20462 : weight; } - getFoodEnergyPer100g() { - const foodEnergyInput = document.getElementById('foodEnergy'); - const energyUnitSelect = document.getElementById('energyUnit'); - if (!foodEnergyInput || !foodEnergyInput.value || !energyUnitSelect) return null; - - const energy = parseFloat(foodEnergyInput.value); - if (isNaN(energy)) return null; - - const unit = energyUnitSelect.value; - - // Convert all units to kcal/100g for internal calculations - switch (unit) { - case 'kcal100g': - return energy; - case 'kcalkg': - return energy / 10; // 1 kg = 10 × 100g - case 'kcalcup': - return energy / 1.2; // Assume 1 cup ≈ 120g for dry dog food - case 'kcalcan': - return energy / 4.5; // Assume 1 can ≈ 450g for wet dog food - default: - return energy; - } - } calculateRER(weightKg) { return 70 * Math.pow(weightKg, 0.75); @@ -1397,40 +2020,6 @@ } } - validateFoodEnergy() { - const energyInput = document.getElementById('foodEnergy'); - const energyUnitSelect = document.getElementById('energyUnit'); - - if (!energyInput || !energyInput.value) { - this.showError('foodEnergyError', false); - return; - } - - const energy = parseFloat(energyInput.value); - const unit = energyUnitSelect?.value || 'kcal100g'; - - let minValue = 1; - switch (unit) { - case 'kcal100g': - minValue = 1; - break; - case 'kcalkg': - minValue = 10; - break; - case 'kcalcup': - minValue = 50; - break; - case 'kcalcan': - minValue = 100; - break; - } - - if (!this.validateInput(energy, minValue)) { - this.showError('foodEnergyError', true); - } else { - this.showError('foodEnergyError', false); - } - } validateDays() { const days = document.getElementById('days')?.value; @@ -1491,40 +2080,93 @@ const dailyFoodResults = document.getElementById('dailyFoodResults'); const dailyFoodValue = document.getElementById('dailyFoodValue'); const totalFoodDisplay = document.getElementById('totalFoodDisplay'); + const foodBreakdownResults = document.getElementById('foodBreakdownResults'); + const foodBreakdownList = document.getElementById('foodBreakdownList'); if (!daysInput || !unitSelect || !dailyFoodResults || !dailyFoodValue || !totalFoodDisplay) { return; } - const energyPer100g = this.getFoodEnergyPer100g(); const days = daysInput.value; const unit = unitSelect.value; - this.showError('foodEnergyError', false); + // Clear all food source errors first + this.foodSources.forEach(fs => { + this.showError(`energy-error-${fs.id}`, false); + }); this.showError('daysError', false); - - if (!energyPer100g || energyPer100g < 0.1) { - const foodEnergyInput = document.getElementById('foodEnergy'); - if (foodEnergyInput && foodEnergyInput.value) this.showError('foodEnergyError', true); - dailyFoodResults.style.display = 'none'; - totalFoodDisplay.value = ''; - return; - } + // Validate days input if (!days || !this.validateInput(days, 1, true)) { if (days) this.showError('daysError', true); totalFoodDisplay.value = ''; + dailyFoodResults.style.display = 'none'; + if (foodBreakdownResults) foodBreakdownResults.style.display = 'none'; return; } const numDays = parseInt(days); - const dailyFoodGrams = (this.currentMER / energyPer100g) * 100; - const totalFoodGrams = dailyFoodGrams * numDays; - - dailyFoodValue.textContent = this.formatNumber(dailyFoodGrams, 1) + ' g/day'; + // Calculate per-food breakdown + const foodBreakdowns = []; + let totalDailyGrams = 0; + let hasValidFoods = false; + + this.foodSources.forEach(fs => { + const energyPer100g = this.getFoodSourceEnergyPer100g(fs); + + if (energyPer100g && energyPer100g > 0.1 && fs.percentage > 0) { + const dailyCaloriesForThisFood = (this.currentMER * fs.percentage) / 100; + const dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100; + + foodBreakdowns.push({ + name: fs.name, + percentage: fs.percentage, + dailyGrams: dailyGramsForThisFood, + calories: dailyCaloriesForThisFood + }); + + totalDailyGrams += dailyGramsForThisFood; + hasValidFoods = true; + } + }); + + if (!hasValidFoods) { + // Show errors for invalid food sources + this.foodSources.forEach(fs => { + const energyInput = document.getElementById(`energy-${fs.id}`); + if (energyInput && energyInput.value && (!this.getFoodSourceEnergyPer100g(fs) || this.getFoodSourceEnergyPer100g(fs) <= 0.1)) { + this.showError(`energy-error-${fs.id}`, true); + } + }); + + dailyFoodResults.style.display = 'none'; + if (foodBreakdownResults) foodBreakdownResults.style.display = 'none'; + totalFoodDisplay.value = ''; + return; + } + + // Update daily food results (total) + dailyFoodValue.textContent = this.formatNumber(totalDailyGrams, 1) + ' g/day'; dailyFoodResults.style.display = 'block'; - + + // Update per-food breakdown + if (foodBreakdownList && foodBreakdowns.length > 1) { + const breakdownHTML = foodBreakdowns.map(breakdown => ` +
+ ${breakdown.name} (${breakdown.percentage}%): + ${this.formatNumber(breakdown.dailyGrams, 1)} g/day +
+ `).join(''); + + foodBreakdownList.innerHTML = breakdownHTML; + if (foodBreakdownResults) foodBreakdownResults.style.display = 'block'; + } else { + if (foodBreakdownResults) foodBreakdownResults.style.display = 'none'; + } + + // Calculate total food amount for the specified days + const totalFoodGrams = totalDailyGrams * numDays; const convertedAmount = this.convertUnits(totalFoodGrams, unit); const unitLabel = unit === 'g' ? 'g' : unit === 'kg' ? 'kg' : unit === 'oz' ? 'oz' : 'lb'; const decimals = unit === 'g' ? 0 : unit === 'kg' ? 2 : 1; @@ -1534,6 +2176,29 @@ this.sendHeightToParent(); } + getFoodSourceEnergyPer100g(foodSource) { + if (!foodSource.energy || !foodSource.energyUnit) return null; + + const energy = parseFloat(foodSource.energy); + if (isNaN(energy)) return null; + + const unit = foodSource.energyUnit; + + // Convert all units to kcal/100g for internal calculations + switch (unit) { + case 'kcal100g': + return energy; + case 'kcalkg': + return energy / 10; // 1 kg = 10 × 100g + case 'kcalcup': + return energy / 1.2; // Assume 1 cup ≈ 120g for dry dog food + case 'kcalcan': + return energy / 4.5; // Assume 1 can ≈ 450g for wet dog food + default: + return energy; + } + } + setupIframeResize() { // Send height to parent window for iframe auto-resize this.sendHeightToParent();