Updates
This commit is contained in:
parent
da9fd20ffb
commit
73c4648978
267
iframe.html
267
iframe.html
@ -2171,11 +2171,12 @@ const CALCULATOR_CONFIG = {
|
||||
// Seed three sources for Kaya's transition
|
||||
const gc = {
|
||||
id: this.generateFoodSourceId(),
|
||||
name: 'Fred & Felia, gently cooked',
|
||||
name: 'Fred & Felia (Junior Huhn)',
|
||||
energy: '115',
|
||||
energyUnit: 'kcal100g',
|
||||
percentage: 5,
|
||||
isLocked: false
|
||||
isLocked: false,
|
||||
chartType: 'gc'
|
||||
};
|
||||
this.foodSources.push(gc);
|
||||
this.renderFoodSource(gc);
|
||||
@ -2183,11 +2184,12 @@ const CALCULATOR_CONFIG = {
|
||||
|
||||
const kibble = {
|
||||
id: this.generateFoodSourceId(),
|
||||
name: 'Eukanuba, kibble',
|
||||
name: 'Eukanuba (Large Breed Fresh Chicken)',
|
||||
energy: '372',
|
||||
energyUnit: 'kcal100g',
|
||||
percentage: 95,
|
||||
isLocked: false
|
||||
isLocked: false,
|
||||
chartType: 'kibble'
|
||||
};
|
||||
this.foodSources.push(kibble);
|
||||
this.renderFoodSource(kibble);
|
||||
@ -2199,7 +2201,8 @@ const CALCULATOR_CONFIG = {
|
||||
energy: '',
|
||||
energyUnit: 'kcal100g',
|
||||
percentage: 0,
|
||||
isLocked: false
|
||||
isLocked: false,
|
||||
chartType: null
|
||||
};
|
||||
this.foodSources.push(treats);
|
||||
this.renderFoodSource(treats);
|
||||
@ -3239,58 +3242,29 @@ const CALCULATOR_CONFIG = {
|
||||
}
|
||||
|
||||
updateCalorieCalculations() {
|
||||
// Kaya-specific: compute daily kcal target from age + kibble energy
|
||||
// Kaya-specific: only track age and trigger recompute
|
||||
const ageInput = document.getElementById('ageMonths');
|
||||
const ageClampNote = document.getElementById('ageClampNote');
|
||||
|
||||
// Default: hide clamp note
|
||||
if (ageClampNote) ageClampNote.classList.add('dog-calculator-hidden');
|
||||
|
||||
if (!ageInput || !ageInput.value) {
|
||||
this.currentMER = 0;
|
||||
this.currentMERMin = 0;
|
||||
this.currentMERMax = 0;
|
||||
if (!ageInput || ageInput.value === '') {
|
||||
this.currentAge = null;
|
||||
this.updateFoodCalculations();
|
||||
return;
|
||||
}
|
||||
|
||||
let age = parseFloat(ageInput.value);
|
||||
if (isNaN(age)) {
|
||||
this.currentMER = 0;
|
||||
this.currentMERMin = 0;
|
||||
this.currentMERMax = 0;
|
||||
this.currentAge = null;
|
||||
this.updateFoodCalculations();
|
||||
return;
|
||||
}
|
||||
|
||||
// Clamp age to [2, 12]
|
||||
if (age < 2) { age = 2; if (ageClampNote) ageClampNote.classList.remove('dog-calculator-hidden'); }
|
||||
if (age > 12) { age = 12; if (ageClampNote) ageClampNote.classList.remove('dog-calculator-hidden'); }
|
||||
|
||||
// Bucket pills removed
|
||||
|
||||
// Calculate interpolated kibble grams/day for 30 kg at this age
|
||||
const kibbleGrams = this.getKayaKibbleGramsForAge(age);
|
||||
|
||||
// Get kibble reference energy density in kcal/100g
|
||||
let kibbleEnergyPer100g = null;
|
||||
if (this.kibbleRefId) {
|
||||
const kibbleRef = this.foodSources.find(fs => fs.id === this.kibbleRefId);
|
||||
kibbleEnergyPer100g = kibbleRef ? this.getFoodSourceEnergyPer100g(kibbleRef) : null;
|
||||
}
|
||||
|
||||
if (!kibbleEnergyPer100g || kibbleEnergyPer100g <= 0) {
|
||||
// Cannot compute without kibble energy density
|
||||
this.currentMER = 0;
|
||||
this.currentMERMin = 0;
|
||||
this.currentMERMax = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const kcalPerGram = kibbleEnergyPer100g / 100.0;
|
||||
const dailyKcal = kibbleGrams * kcalPerGram;
|
||||
this.currentMER = dailyKcal;
|
||||
this.currentMERMin = dailyKcal;
|
||||
this.currentMERMax = dailyKcal;
|
||||
|
||||
this.currentAge = age;
|
||||
this.updateFoodCalculations();
|
||||
}
|
||||
|
||||
@ -3321,38 +3295,38 @@ const CALCULATOR_CONFIG = {
|
||||
return lowerVal + (upperVal - lowerVal) * t;
|
||||
}
|
||||
|
||||
|
||||
|
||||
updateCupsButtonState() {
|
||||
const cupsButton = document.getElementById('cupsButton');
|
||||
if (!cupsButton) return;
|
||||
|
||||
// Check if any food source has kcal/cup selected
|
||||
const hasKcalCup = this.foodSources.some(fs =>
|
||||
fs.energyUnit === 'kcalcup' && fs.energy && parseFloat(fs.energy) > 0
|
||||
);
|
||||
|
||||
if (hasKcalCup) {
|
||||
cupsButton.disabled = false;
|
||||
cupsButton.title = 'Show amounts in cups';
|
||||
// Kaya: GC chart interpolation across buckets
|
||||
getGCChartGramsForAge(ageMonths) {
|
||||
// Buckets per guideline with boundary rule (5.0 → later bucket, 7.0 → later bucket)
|
||||
// <5 mo (2.0–<5.0): 950→1350
|
||||
// 5–6 mo (5.0–6.0): 1250→1550; (6.0–<7.0): hold 1550
|
||||
// 7–12 mo (7.0–12.0): 1300→1500
|
||||
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
|
||||
const age = clamp(ageMonths, 2, 12);
|
||||
if (age < 5) {
|
||||
const t = (age - 2) / (5 - 2);
|
||||
return 950 + t * (1350 - 950);
|
||||
} else if (age < 6) {
|
||||
const t = (age - 5) / (6 - 5);
|
||||
return 1250 + t * (1550 - 1250);
|
||||
} else if (age < 7) {
|
||||
return 1550; // hold upper value until 7.0
|
||||
} else {
|
||||
cupsButton.disabled = true;
|
||||
cupsButton.title = 'Available when using kcal/cup measurement';
|
||||
|
||||
// If cups was selected, switch back to grams
|
||||
const unitSelect = document.getElementById('unit');
|
||||
if (unitSelect && unitSelect.value === 'cups') {
|
||||
unitSelect.value = 'g';
|
||||
this.setActiveUnitButton('g');
|
||||
}
|
||||
const t = (age - 7) / (12 - 7);
|
||||
return 1300 + t * (1500 - 1300);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
updateCupsButtonState() {
|
||||
// Cups UI is not used in this configuration
|
||||
return;
|
||||
}
|
||||
|
||||
updateFoodCalculations() {
|
||||
if (this.currentMER === 0) return;
|
||||
|
||||
// Check if we have a range
|
||||
const hasRange = this.currentMERMin !== this.currentMERMax;
|
||||
// Chart-first: no MER check
|
||||
const hasRange = false;
|
||||
|
||||
const daysInput = document.getElementById('days');
|
||||
const unitSelect = document.getElementById('unit');
|
||||
@ -3415,102 +3389,81 @@ const CALCULATOR_CONFIG = {
|
||||
}
|
||||
|
||||
const numDays = parseInt(days);
|
||||
|
||||
// Calculate per-food breakdown
|
||||
|
||||
// Require a valid age for chart-first outputs
|
||||
const ageInput = document.getElementById('ageMonths');
|
||||
let age = ageInput && ageInput.value !== '' ? parseFloat(ageInput.value) : null;
|
||||
if (age !== null) {
|
||||
if (isNaN(age)) age = null;
|
||||
if (age !== null) {
|
||||
if (age < 2) age = 2;
|
||||
if (age > 12) age = 12;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate per-food breakdown (chart-first)
|
||||
const foodBreakdowns = [];
|
||||
let totalDailyGrams = 0;
|
||||
let hasValidFoods = false;
|
||||
|
||||
// First pass: charted baseline
|
||||
let chartedKcal = 0;
|
||||
let chartedPercent = 0;
|
||||
const firstPass = [];
|
||||
this.foodSources.forEach(fs => {
|
||||
const energyPer100g = this.getFoodSourceEnergyPer100g(fs);
|
||||
|
||||
if (energyPer100g && energyPer100g > 0.1 && fs.percentage > 0) {
|
||||
const dailyCaloriesForThisFood = (this.currentMER * fs.percentage) / 100;
|
||||
// Calculate range values if applicable
|
||||
const dailyCaloriesMin = hasRange ? (this.currentMERMin * fs.percentage) / 100 : dailyCaloriesForThisFood;
|
||||
const dailyCaloriesMax = hasRange ? (this.currentMERMax * fs.percentage) / 100 : dailyCaloriesForThisFood;
|
||||
|
||||
let dailyGramsForThisFood;
|
||||
let dailyGramsMin, dailyGramsMax;
|
||||
let dailyCupsForThisFood = null;
|
||||
let dailyCupsMin, dailyCupsMax;
|
||||
|
||||
// For kcal/cup, calculate cups directly from calories
|
||||
if (fs.energyUnit === 'kcalcup' && fs.energy) {
|
||||
const caloriesPerCup = parseFloat(fs.energy);
|
||||
dailyCupsForThisFood = dailyCaloriesForThisFood / caloriesPerCup;
|
||||
dailyCupsMin = dailyCaloriesMin / caloriesPerCup;
|
||||
dailyCupsMax = dailyCaloriesMax / caloriesPerCup;
|
||||
// We still need grams for total calculation, use approximation
|
||||
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
||||
dailyGramsMin = (dailyCaloriesMin / energyPer100g) * 100;
|
||||
dailyGramsMax = (dailyCaloriesMax / energyPer100g) * 100;
|
||||
} else {
|
||||
// For other units, calculate grams normally
|
||||
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
||||
dailyGramsMin = (dailyCaloriesMin / energyPer100g) * 100;
|
||||
dailyGramsMax = (dailyCaloriesMax / energyPer100g) * 100;
|
||||
}
|
||||
|
||||
// Calculate per-meal amounts if needed
|
||||
const displayGrams = this.showPerMeal ? dailyGramsForThisFood / this.mealsPerDay : dailyGramsForThisFood;
|
||||
const displayGramsMin = this.showPerMeal ? dailyGramsMin / this.mealsPerDay : dailyGramsMin;
|
||||
const displayGramsMax = this.showPerMeal ? dailyGramsMax / this.mealsPerDay : dailyGramsMax;
|
||||
|
||||
const displayCups = dailyCupsForThisFood !== null ?
|
||||
(this.showPerMeal ? dailyCupsForThisFood / this.mealsPerDay : dailyCupsForThisFood) : null;
|
||||
const displayCupsMin = dailyCupsMin !== undefined ?
|
||||
(this.showPerMeal ? dailyCupsMin / this.mealsPerDay : dailyCupsMin) : null;
|
||||
const displayCupsMax = dailyCupsMax !== undefined ?
|
||||
(this.showPerMeal ? dailyCupsMax / this.mealsPerDay : dailyCupsMax) : null;
|
||||
|
||||
const displayCalories = this.showPerMeal ? dailyCaloriesForThisFood / this.mealsPerDay : dailyCaloriesForThisFood;
|
||||
const displayCaloriesMin = this.showPerMeal ? dailyCaloriesMin / this.mealsPerDay : dailyCaloriesMin;
|
||||
const displayCaloriesMax = this.showPerMeal ? dailyCaloriesMax / this.mealsPerDay : dailyCaloriesMax;
|
||||
|
||||
foodBreakdowns.push({
|
||||
name: fs.name,
|
||||
percentage: fs.percentage,
|
||||
dailyGrams: dailyGramsForThisFood,
|
||||
dailyGramsMin: dailyGramsMin,
|
||||
dailyGramsMax: dailyGramsMax,
|
||||
displayGrams: displayGrams,
|
||||
displayGramsMin: displayGramsMin,
|
||||
displayGramsMax: displayGramsMax,
|
||||
dailyCups: dailyCupsForThisFood,
|
||||
dailyCupsMin: dailyCupsMin,
|
||||
dailyCupsMax: dailyCupsMax,
|
||||
displayCups: displayCups,
|
||||
displayCupsMin: displayCupsMin,
|
||||
displayCupsMax: displayCupsMax,
|
||||
calories: dailyCaloriesForThisFood,
|
||||
displayCalories: displayCalories,
|
||||
displayCaloriesMin: displayCaloriesMin,
|
||||
displayCaloriesMax: displayCaloriesMax,
|
||||
isLocked: fs.isLocked,
|
||||
hasEnergyContent: true,
|
||||
hasRange: hasRange,
|
||||
foodSource: fs // Store reference for cups conversion
|
||||
});
|
||||
|
||||
totalDailyGrams += dailyGramsForThisFood;
|
||||
hasValidFoods = true;
|
||||
} else if (fs.percentage > 0) {
|
||||
// Include food sources without energy content but show them as needing energy content
|
||||
foodBreakdowns.push({
|
||||
name: fs.name,
|
||||
percentage: fs.percentage,
|
||||
dailyGrams: 0,
|
||||
displayGrams: 0,
|
||||
dailyCups: null,
|
||||
displayCups: null,
|
||||
calories: 0,
|
||||
displayCalories: 0,
|
||||
isLocked: fs.isLocked,
|
||||
hasEnergyContent: false,
|
||||
foodSource: fs // Store reference for cups conversion
|
||||
});
|
||||
let chartGrams = null;
|
||||
if (fs.chartType === 'gc') {
|
||||
chartGrams = age !== null ? this.getGCChartGramsForAge(age) : null;
|
||||
} else if (fs.chartType === 'kibble') {
|
||||
chartGrams = age !== null ? this.getKayaKibbleGramsForAge(age) : null;
|
||||
}
|
||||
const gramsPortion = (chartGrams !== null && chartGrams !== undefined) ? (chartGrams * (fs.percentage || 0) / 100) : null;
|
||||
if (gramsPortion !== null && energyPer100g && energyPer100g > 0) {
|
||||
chartedKcal += gramsPortion * (energyPer100g / 100);
|
||||
chartedPercent += (fs.percentage || 0);
|
||||
}
|
||||
firstPass.push({ fs, energyPer100g, gramsPortion });
|
||||
});
|
||||
|
||||
const kcalPerPercent = chartedPercent > 0 ? (chartedKcal / chartedPercent) : null;
|
||||
|
||||
// Second pass: finalize amounts
|
||||
firstPass.forEach(({ fs, energyPer100g, gramsPortion }) => {
|
||||
let dailyGramsForThisFood = 0;
|
||||
let hasEnergyContent = !!(energyPer100g && energyPer100g > 0);
|
||||
if ((fs.chartType === 'gc' || fs.chartType === 'kibble')) {
|
||||
if (gramsPortion !== null) {
|
||||
dailyGramsForThisFood = gramsPortion;
|
||||
}
|
||||
} else {
|
||||
if (hasEnergyContent && kcalPerPercent && (fs.percentage || 0) > 0) {
|
||||
const perGramKcal = energyPer100g / 100;
|
||||
const kcalForFood = (fs.percentage || 0) * kcalPerPercent;
|
||||
dailyGramsForThisFood = kcalForFood / perGramKcal;
|
||||
} else {
|
||||
hasEnergyContent = false;
|
||||
dailyGramsForThisFood = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const displayGrams = this.showPerMeal ? dailyGramsForThisFood / this.mealsPerDay : dailyGramsForThisFood;
|
||||
|
||||
foodBreakdowns.push({
|
||||
name: fs.name,
|
||||
percentage: fs.percentage,
|
||||
dailyGrams: dailyGramsForThisFood,
|
||||
displayGrams: displayGrams,
|
||||
dailyCups: null,
|
||||
displayCups: null,
|
||||
calories: hasEnergyContent ? (dailyGramsForThisFood * (energyPer100g / 100)) : 0,
|
||||
displayCalories: hasEnergyContent ? (this.showPerMeal ? (dailyGramsForThisFood * (energyPer100g / 100)) / this.mealsPerDay : (dailyGramsForThisFood * (energyPer100g / 100))) : 0,
|
||||
isLocked: fs.isLocked,
|
||||
hasEnergyContent: hasEnergyContent,
|
||||
foodSource: fs
|
||||
});
|
||||
totalDailyGrams += dailyGramsForThisFood;
|
||||
if (dailyGramsForThisFood > 0) hasValidFoods = true;
|
||||
});
|
||||
|
||||
if (!hasValidFoods) {
|
||||
|
||||
@ -75,11 +75,12 @@
|
||||
// Seed three sources for Kaya's transition
|
||||
const gc = {
|
||||
id: this.generateFoodSourceId(),
|
||||
name: 'Fred & Felia, gently cooked',
|
||||
name: 'Fred & Felia (Junior Huhn)',
|
||||
energy: '115',
|
||||
energyUnit: 'kcal100g',
|
||||
percentage: 5,
|
||||
isLocked: false
|
||||
isLocked: false,
|
||||
chartType: 'gc'
|
||||
};
|
||||
this.foodSources.push(gc);
|
||||
this.renderFoodSource(gc);
|
||||
@ -87,11 +88,12 @@
|
||||
|
||||
const kibble = {
|
||||
id: this.generateFoodSourceId(),
|
||||
name: 'Eukanuba, kibble',
|
||||
name: 'Eukanuba (Large Breed Fresh Chicken)',
|
||||
energy: '372',
|
||||
energyUnit: 'kcal100g',
|
||||
percentage: 95,
|
||||
isLocked: false
|
||||
isLocked: false,
|
||||
chartType: 'kibble'
|
||||
};
|
||||
this.foodSources.push(kibble);
|
||||
this.renderFoodSource(kibble);
|
||||
@ -103,7 +105,8 @@
|
||||
energy: '',
|
||||
energyUnit: 'kcal100g',
|
||||
percentage: 0,
|
||||
isLocked: false
|
||||
isLocked: false,
|
||||
chartType: null
|
||||
};
|
||||
this.foodSources.push(treats);
|
||||
this.renderFoodSource(treats);
|
||||
@ -1143,58 +1146,29 @@
|
||||
}
|
||||
|
||||
updateCalorieCalculations() {
|
||||
// Kaya-specific: compute daily kcal target from age + kibble energy
|
||||
// Kaya-specific: only track age and trigger recompute
|
||||
const ageInput = document.getElementById('ageMonths');
|
||||
const ageClampNote = document.getElementById('ageClampNote');
|
||||
|
||||
// Default: hide clamp note
|
||||
if (ageClampNote) ageClampNote.classList.add('dog-calculator-hidden');
|
||||
|
||||
if (!ageInput || !ageInput.value) {
|
||||
this.currentMER = 0;
|
||||
this.currentMERMin = 0;
|
||||
this.currentMERMax = 0;
|
||||
if (!ageInput || ageInput.value === '') {
|
||||
this.currentAge = null;
|
||||
this.updateFoodCalculations();
|
||||
return;
|
||||
}
|
||||
|
||||
let age = parseFloat(ageInput.value);
|
||||
if (isNaN(age)) {
|
||||
this.currentMER = 0;
|
||||
this.currentMERMin = 0;
|
||||
this.currentMERMax = 0;
|
||||
this.currentAge = null;
|
||||
this.updateFoodCalculations();
|
||||
return;
|
||||
}
|
||||
|
||||
// Clamp age to [2, 12]
|
||||
if (age < 2) { age = 2; if (ageClampNote) ageClampNote.classList.remove('dog-calculator-hidden'); }
|
||||
if (age > 12) { age = 12; if (ageClampNote) ageClampNote.classList.remove('dog-calculator-hidden'); }
|
||||
|
||||
// Bucket pills removed
|
||||
|
||||
// Calculate interpolated kibble grams/day for 30 kg at this age
|
||||
const kibbleGrams = this.getKayaKibbleGramsForAge(age);
|
||||
|
||||
// Get kibble reference energy density in kcal/100g
|
||||
let kibbleEnergyPer100g = null;
|
||||
if (this.kibbleRefId) {
|
||||
const kibbleRef = this.foodSources.find(fs => fs.id === this.kibbleRefId);
|
||||
kibbleEnergyPer100g = kibbleRef ? this.getFoodSourceEnergyPer100g(kibbleRef) : null;
|
||||
}
|
||||
|
||||
if (!kibbleEnergyPer100g || kibbleEnergyPer100g <= 0) {
|
||||
// Cannot compute without kibble energy density
|
||||
this.currentMER = 0;
|
||||
this.currentMERMin = 0;
|
||||
this.currentMERMax = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const kcalPerGram = kibbleEnergyPer100g / 100.0;
|
||||
const dailyKcal = kibbleGrams * kcalPerGram;
|
||||
this.currentMER = dailyKcal;
|
||||
this.currentMERMin = dailyKcal;
|
||||
this.currentMERMax = dailyKcal;
|
||||
|
||||
this.currentAge = age;
|
||||
this.updateFoodCalculations();
|
||||
}
|
||||
|
||||
@ -1225,38 +1199,38 @@
|
||||
return lowerVal + (upperVal - lowerVal) * t;
|
||||
}
|
||||
|
||||
|
||||
|
||||
updateCupsButtonState() {
|
||||
const cupsButton = document.getElementById('cupsButton');
|
||||
if (!cupsButton) return;
|
||||
|
||||
// Check if any food source has kcal/cup selected
|
||||
const hasKcalCup = this.foodSources.some(fs =>
|
||||
fs.energyUnit === 'kcalcup' && fs.energy && parseFloat(fs.energy) > 0
|
||||
);
|
||||
|
||||
if (hasKcalCup) {
|
||||
cupsButton.disabled = false;
|
||||
cupsButton.title = 'Show amounts in cups';
|
||||
// Kaya: GC chart interpolation across buckets
|
||||
getGCChartGramsForAge(ageMonths) {
|
||||
// Buckets per guideline with boundary rule (5.0 → later bucket, 7.0 → later bucket)
|
||||
// <5 mo (2.0–<5.0): 950→1350
|
||||
// 5–6 mo (5.0–6.0): 1250→1550; (6.0–<7.0): hold 1550
|
||||
// 7–12 mo (7.0–12.0): 1300→1500
|
||||
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
|
||||
const age = clamp(ageMonths, 2, 12);
|
||||
if (age < 5) {
|
||||
const t = (age - 2) / (5 - 2);
|
||||
return 950 + t * (1350 - 950);
|
||||
} else if (age < 6) {
|
||||
const t = (age - 5) / (6 - 5);
|
||||
return 1250 + t * (1550 - 1250);
|
||||
} else if (age < 7) {
|
||||
return 1550; // hold upper value until 7.0
|
||||
} else {
|
||||
cupsButton.disabled = true;
|
||||
cupsButton.title = 'Available when using kcal/cup measurement';
|
||||
|
||||
// If cups was selected, switch back to grams
|
||||
const unitSelect = document.getElementById('unit');
|
||||
if (unitSelect && unitSelect.value === 'cups') {
|
||||
unitSelect.value = 'g';
|
||||
this.setActiveUnitButton('g');
|
||||
}
|
||||
const t = (age - 7) / (12 - 7);
|
||||
return 1300 + t * (1500 - 1300);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
updateCupsButtonState() {
|
||||
// Cups UI is not used in this configuration
|
||||
return;
|
||||
}
|
||||
|
||||
updateFoodCalculations() {
|
||||
if (this.currentMER === 0) return;
|
||||
|
||||
// Check if we have a range
|
||||
const hasRange = this.currentMERMin !== this.currentMERMax;
|
||||
// Chart-first: no MER check
|
||||
const hasRange = false;
|
||||
|
||||
const daysInput = document.getElementById('days');
|
||||
const unitSelect = document.getElementById('unit');
|
||||
@ -1319,102 +1293,81 @@
|
||||
}
|
||||
|
||||
const numDays = parseInt(days);
|
||||
|
||||
// Calculate per-food breakdown
|
||||
|
||||
// Require a valid age for chart-first outputs
|
||||
const ageInput = document.getElementById('ageMonths');
|
||||
let age = ageInput && ageInput.value !== '' ? parseFloat(ageInput.value) : null;
|
||||
if (age !== null) {
|
||||
if (isNaN(age)) age = null;
|
||||
if (age !== null) {
|
||||
if (age < 2) age = 2;
|
||||
if (age > 12) age = 12;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate per-food breakdown (chart-first)
|
||||
const foodBreakdowns = [];
|
||||
let totalDailyGrams = 0;
|
||||
let hasValidFoods = false;
|
||||
|
||||
// First pass: charted baseline
|
||||
let chartedKcal = 0;
|
||||
let chartedPercent = 0;
|
||||
const firstPass = [];
|
||||
this.foodSources.forEach(fs => {
|
||||
const energyPer100g = this.getFoodSourceEnergyPer100g(fs);
|
||||
|
||||
if (energyPer100g && energyPer100g > 0.1 && fs.percentage > 0) {
|
||||
const dailyCaloriesForThisFood = (this.currentMER * fs.percentage) / 100;
|
||||
// Calculate range values if applicable
|
||||
const dailyCaloriesMin = hasRange ? (this.currentMERMin * fs.percentage) / 100 : dailyCaloriesForThisFood;
|
||||
const dailyCaloriesMax = hasRange ? (this.currentMERMax * fs.percentage) / 100 : dailyCaloriesForThisFood;
|
||||
|
||||
let dailyGramsForThisFood;
|
||||
let dailyGramsMin, dailyGramsMax;
|
||||
let dailyCupsForThisFood = null;
|
||||
let dailyCupsMin, dailyCupsMax;
|
||||
|
||||
// For kcal/cup, calculate cups directly from calories
|
||||
if (fs.energyUnit === 'kcalcup' && fs.energy) {
|
||||
const caloriesPerCup = parseFloat(fs.energy);
|
||||
dailyCupsForThisFood = dailyCaloriesForThisFood / caloriesPerCup;
|
||||
dailyCupsMin = dailyCaloriesMin / caloriesPerCup;
|
||||
dailyCupsMax = dailyCaloriesMax / caloriesPerCup;
|
||||
// We still need grams for total calculation, use approximation
|
||||
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
||||
dailyGramsMin = (dailyCaloriesMin / energyPer100g) * 100;
|
||||
dailyGramsMax = (dailyCaloriesMax / energyPer100g) * 100;
|
||||
} else {
|
||||
// For other units, calculate grams normally
|
||||
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
||||
dailyGramsMin = (dailyCaloriesMin / energyPer100g) * 100;
|
||||
dailyGramsMax = (dailyCaloriesMax / energyPer100g) * 100;
|
||||
}
|
||||
|
||||
// Calculate per-meal amounts if needed
|
||||
const displayGrams = this.showPerMeal ? dailyGramsForThisFood / this.mealsPerDay : dailyGramsForThisFood;
|
||||
const displayGramsMin = this.showPerMeal ? dailyGramsMin / this.mealsPerDay : dailyGramsMin;
|
||||
const displayGramsMax = this.showPerMeal ? dailyGramsMax / this.mealsPerDay : dailyGramsMax;
|
||||
|
||||
const displayCups = dailyCupsForThisFood !== null ?
|
||||
(this.showPerMeal ? dailyCupsForThisFood / this.mealsPerDay : dailyCupsForThisFood) : null;
|
||||
const displayCupsMin = dailyCupsMin !== undefined ?
|
||||
(this.showPerMeal ? dailyCupsMin / this.mealsPerDay : dailyCupsMin) : null;
|
||||
const displayCupsMax = dailyCupsMax !== undefined ?
|
||||
(this.showPerMeal ? dailyCupsMax / this.mealsPerDay : dailyCupsMax) : null;
|
||||
|
||||
const displayCalories = this.showPerMeal ? dailyCaloriesForThisFood / this.mealsPerDay : dailyCaloriesForThisFood;
|
||||
const displayCaloriesMin = this.showPerMeal ? dailyCaloriesMin / this.mealsPerDay : dailyCaloriesMin;
|
||||
const displayCaloriesMax = this.showPerMeal ? dailyCaloriesMax / this.mealsPerDay : dailyCaloriesMax;
|
||||
|
||||
foodBreakdowns.push({
|
||||
name: fs.name,
|
||||
percentage: fs.percentage,
|
||||
dailyGrams: dailyGramsForThisFood,
|
||||
dailyGramsMin: dailyGramsMin,
|
||||
dailyGramsMax: dailyGramsMax,
|
||||
displayGrams: displayGrams,
|
||||
displayGramsMin: displayGramsMin,
|
||||
displayGramsMax: displayGramsMax,
|
||||
dailyCups: dailyCupsForThisFood,
|
||||
dailyCupsMin: dailyCupsMin,
|
||||
dailyCupsMax: dailyCupsMax,
|
||||
displayCups: displayCups,
|
||||
displayCupsMin: displayCupsMin,
|
||||
displayCupsMax: displayCupsMax,
|
||||
calories: dailyCaloriesForThisFood,
|
||||
displayCalories: displayCalories,
|
||||
displayCaloriesMin: displayCaloriesMin,
|
||||
displayCaloriesMax: displayCaloriesMax,
|
||||
isLocked: fs.isLocked,
|
||||
hasEnergyContent: true,
|
||||
hasRange: hasRange,
|
||||
foodSource: fs // Store reference for cups conversion
|
||||
});
|
||||
|
||||
totalDailyGrams += dailyGramsForThisFood;
|
||||
hasValidFoods = true;
|
||||
} else if (fs.percentage > 0) {
|
||||
// Include food sources without energy content but show them as needing energy content
|
||||
foodBreakdowns.push({
|
||||
name: fs.name,
|
||||
percentage: fs.percentage,
|
||||
dailyGrams: 0,
|
||||
displayGrams: 0,
|
||||
dailyCups: null,
|
||||
displayCups: null,
|
||||
calories: 0,
|
||||
displayCalories: 0,
|
||||
isLocked: fs.isLocked,
|
||||
hasEnergyContent: false,
|
||||
foodSource: fs // Store reference for cups conversion
|
||||
});
|
||||
let chartGrams = null;
|
||||
if (fs.chartType === 'gc') {
|
||||
chartGrams = age !== null ? this.getGCChartGramsForAge(age) : null;
|
||||
} else if (fs.chartType === 'kibble') {
|
||||
chartGrams = age !== null ? this.getKayaKibbleGramsForAge(age) : null;
|
||||
}
|
||||
const gramsPortion = (chartGrams !== null && chartGrams !== undefined) ? (chartGrams * (fs.percentage || 0) / 100) : null;
|
||||
if (gramsPortion !== null && energyPer100g && energyPer100g > 0) {
|
||||
chartedKcal += gramsPortion * (energyPer100g / 100);
|
||||
chartedPercent += (fs.percentage || 0);
|
||||
}
|
||||
firstPass.push({ fs, energyPer100g, gramsPortion });
|
||||
});
|
||||
|
||||
const kcalPerPercent = chartedPercent > 0 ? (chartedKcal / chartedPercent) : null;
|
||||
|
||||
// Second pass: finalize amounts
|
||||
firstPass.forEach(({ fs, energyPer100g, gramsPortion }) => {
|
||||
let dailyGramsForThisFood = 0;
|
||||
let hasEnergyContent = !!(energyPer100g && energyPer100g > 0);
|
||||
if ((fs.chartType === 'gc' || fs.chartType === 'kibble')) {
|
||||
if (gramsPortion !== null) {
|
||||
dailyGramsForThisFood = gramsPortion;
|
||||
}
|
||||
} else {
|
||||
if (hasEnergyContent && kcalPerPercent && (fs.percentage || 0) > 0) {
|
||||
const perGramKcal = energyPer100g / 100;
|
||||
const kcalForFood = (fs.percentage || 0) * kcalPerPercent;
|
||||
dailyGramsForThisFood = kcalForFood / perGramKcal;
|
||||
} else {
|
||||
hasEnergyContent = false;
|
||||
dailyGramsForThisFood = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const displayGrams = this.showPerMeal ? dailyGramsForThisFood / this.mealsPerDay : dailyGramsForThisFood;
|
||||
|
||||
foodBreakdowns.push({
|
||||
name: fs.name,
|
||||
percentage: fs.percentage,
|
||||
dailyGrams: dailyGramsForThisFood,
|
||||
displayGrams: displayGrams,
|
||||
dailyCups: null,
|
||||
displayCups: null,
|
||||
calories: hasEnergyContent ? (dailyGramsForThisFood * (energyPer100g / 100)) : 0,
|
||||
displayCalories: hasEnergyContent ? (this.showPerMeal ? (dailyGramsForThisFood * (energyPer100g / 100)) / this.mealsPerDay : (dailyGramsForThisFood * (energyPer100g / 100))) : 0,
|
||||
isLocked: fs.isLocked,
|
||||
hasEnergyContent: hasEnergyContent,
|
||||
foodSource: fs
|
||||
});
|
||||
totalDailyGrams += dailyGramsForThisFood;
|
||||
if (dailyGramsForThisFood > 0) hasValidFoods = true;
|
||||
});
|
||||
|
||||
if (!hasValidFoods) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user