Add range calculations for dog food amounts based on life stage
- Implement range multipliers for different life stages (e.g., 1.6-1.8x for intact adults) - Display MER and food amounts as ranges (e.g., '2042-2298 cal/day') - Add CSS to prevent value wrapping with white-space: nowrap - Increase calculator max-width from 600px to 640px for better text layout - Based on veterinary RER multiplier ranges for more accurate feeding recommendations
This commit is contained in:
parent
c4770d5ee6
commit
99b516d087
149
iframe.html
149
iframe.html
@ -32,7 +32,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dog-calculator-container {
|
.dog-calculator-container {
|
||||||
max-width: 600px;
|
max-width: 640px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -213,6 +213,7 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
gap: 10px; /* Add gap between label and value */
|
||||||
}
|
}
|
||||||
|
|
||||||
.dog-calculator-result-item:last-child {
|
.dog-calculator-result-item:last-child {
|
||||||
@ -232,6 +233,7 @@
|
|||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
background: rgba(241, 154, 95, 0.15);
|
background: rgba(241, 154, 95, 0.15);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
white-space: nowrap; /* Prevent text from wrapping to multiple lines */
|
||||||
}
|
}
|
||||||
|
|
||||||
.dog-calculator-collapsible {
|
.dog-calculator-collapsible {
|
||||||
@ -543,6 +545,24 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stack result items vertically on small screens */
|
||||||
|
.dog-calculator-result-item {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-result-label {
|
||||||
|
margin-right: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-result-value {
|
||||||
|
font-size: 1rem;
|
||||||
|
align-self: stretch;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark theme - manual override */
|
/* Dark theme - manual override */
|
||||||
@ -2288,6 +2308,8 @@ const CALCULATOR_CONFIG = {
|
|||||||
class DogCalorieCalculator {
|
class DogCalorieCalculator {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.currentMER = 0;
|
this.currentMER = 0;
|
||||||
|
this.currentMERMin = 0; // For range calculations
|
||||||
|
this.currentMERMax = 0; // For range calculations
|
||||||
this.isImperial = false;
|
this.isImperial = false;
|
||||||
this.theme = this.getThemeFromURL() || CALCULATOR_CONFIG.defaultTheme;
|
this.theme = this.getThemeFromURL() || CALCULATOR_CONFIG.defaultTheme;
|
||||||
this.scale = this.getScaleFromURL() || CALCULATOR_CONFIG.defaultScale;
|
this.scale = this.getScaleFromURL() || CALCULATOR_CONFIG.defaultScale;
|
||||||
@ -3335,6 +3357,25 @@ const CALCULATOR_CONFIG = {
|
|||||||
return rer * factor;
|
return rer * factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the range multipliers for each life stage
|
||||||
|
getLifeStageRange(factor) {
|
||||||
|
// Define ranges based on the reference image
|
||||||
|
const ranges = {
|
||||||
|
'3.0': { min: 3.0, max: 3.0 }, // Puppy 0-4 months (no range)
|
||||||
|
'2.0': { min: 2.0, max: 2.0 }, // Puppy 4m-adult OR Working light (no range for puppies)
|
||||||
|
'1.2': { min: 1.2, max: 1.4 }, // Adult inactive/obese
|
||||||
|
'1.6': { min: 1.4, max: 1.6 }, // Adult neutered/spayed
|
||||||
|
'1.8': { min: 1.6, max: 1.8 }, // Adult intact
|
||||||
|
'1.0': { min: 1.0, max: 1.0 }, // Weight loss (fixed)
|
||||||
|
'1.7': { min: 1.2, max: 1.8 }, // Weight gain (wide range)
|
||||||
|
'5.0': { min: 5.0, max: 5.0 }, // Working heavy (upper bound)
|
||||||
|
'1.1': { min: 1.1, max: 1.1 } // Senior (no range)
|
||||||
|
};
|
||||||
|
|
||||||
|
const key = factor.toFixed(1);
|
||||||
|
return ranges[key] || { min: factor, max: factor };
|
||||||
|
}
|
||||||
|
|
||||||
validateInput(value, min = 0, isInteger = false) {
|
validateInput(value, min = 0, isInteger = false) {
|
||||||
const num = parseFloat(value);
|
const num = parseFloat(value);
|
||||||
if (isNaN(num) || num < min) return false;
|
if (isNaN(num) || num < min) return false;
|
||||||
@ -3466,10 +3507,21 @@ const CALCULATOR_CONFIG = {
|
|||||||
const rer = this.calculateRER(weightKg);
|
const rer = this.calculateRER(weightKg);
|
||||||
const mer = this.calculateMER(rer, factor);
|
const mer = this.calculateMER(rer, factor);
|
||||||
|
|
||||||
this.currentMER = mer;
|
// Calculate range for MER
|
||||||
|
const range = this.getLifeStageRange(factor);
|
||||||
|
this.currentMERMin = this.calculateMER(rer, range.min);
|
||||||
|
this.currentMERMax = this.calculateMER(rer, range.max);
|
||||||
|
this.currentMER = mer; // Keep middle/selected value for compatibility
|
||||||
|
|
||||||
rerValue.textContent = this.formatNumber(rer, 0) + ' cal/day';
|
rerValue.textContent = this.formatNumber(rer, 0) + ' cal/day';
|
||||||
|
|
||||||
|
// Show MER as range if applicable
|
||||||
|
if (range.min !== range.max) {
|
||||||
|
merValue.textContent = this.formatNumber(this.currentMERMin, 0) + '-' +
|
||||||
|
this.formatNumber(this.currentMERMax, 0) + ' cal/day';
|
||||||
|
} else {
|
||||||
merValue.textContent = this.formatNumber(mer, 0) + ' cal/day';
|
merValue.textContent = this.formatNumber(mer, 0) + ' cal/day';
|
||||||
|
}
|
||||||
calorieResults.style.display = 'block';
|
calorieResults.style.display = 'block';
|
||||||
|
|
||||||
this.updateFoodCalculations();
|
this.updateFoodCalculations();
|
||||||
@ -3504,6 +3556,9 @@ const CALCULATOR_CONFIG = {
|
|||||||
updateFoodCalculations() {
|
updateFoodCalculations() {
|
||||||
if (this.currentMER === 0) return;
|
if (this.currentMER === 0) return;
|
||||||
|
|
||||||
|
// Check if we have a range
|
||||||
|
const hasRange = this.currentMERMin !== this.currentMERMax;
|
||||||
|
|
||||||
const daysInput = document.getElementById('days');
|
const daysInput = document.getElementById('days');
|
||||||
const unitSelect = document.getElementById('unit');
|
const unitSelect = document.getElementById('unit');
|
||||||
const dailyFoodResults = document.getElementById('dailyFoodResults');
|
const dailyFoodResults = document.getElementById('dailyFoodResults');
|
||||||
@ -3576,43 +3631,70 @@ const CALCULATOR_CONFIG = {
|
|||||||
|
|
||||||
if (energyPer100g && energyPer100g > 0.1 && fs.percentage > 0) {
|
if (energyPer100g && energyPer100g > 0.1 && fs.percentage > 0) {
|
||||||
const dailyCaloriesForThisFood = (this.currentMER * fs.percentage) / 100;
|
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 dailyGramsForThisFood;
|
||||||
|
let dailyGramsMin, dailyGramsMax;
|
||||||
let dailyCupsForThisFood = null;
|
let dailyCupsForThisFood = null;
|
||||||
|
let dailyCupsMin, dailyCupsMax;
|
||||||
|
|
||||||
// For kcal/cup, calculate cups directly from calories
|
// For kcal/cup, calculate cups directly from calories
|
||||||
if (fs.energyUnit === 'kcalcup' && fs.energy) {
|
if (fs.energyUnit === 'kcalcup' && fs.energy) {
|
||||||
const caloriesPerCup = parseFloat(fs.energy);
|
const caloriesPerCup = parseFloat(fs.energy);
|
||||||
dailyCupsForThisFood = dailyCaloriesForThisFood / caloriesPerCup;
|
dailyCupsForThisFood = dailyCaloriesForThisFood / caloriesPerCup;
|
||||||
|
dailyCupsMin = dailyCaloriesMin / caloriesPerCup;
|
||||||
|
dailyCupsMax = dailyCaloriesMax / caloriesPerCup;
|
||||||
// We still need grams for total calculation, use approximation
|
// We still need grams for total calculation, use approximation
|
||||||
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
||||||
console.log('Cups calculation:', {
|
dailyGramsMin = (dailyCaloriesMin / energyPer100g) * 100;
|
||||||
caloriesPerCup,
|
dailyGramsMax = (dailyCaloriesMax / energyPer100g) * 100;
|
||||||
dailyCaloriesForThisFood,
|
|
||||||
dailyCupsForThisFood,
|
|
||||||
dailyGramsForThisFood
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// For other units, calculate grams normally
|
// For other units, calculate grams normally
|
||||||
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
||||||
|
dailyGramsMin = (dailyCaloriesMin / energyPer100g) * 100;
|
||||||
|
dailyGramsMax = (dailyCaloriesMax / energyPer100g) * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate per-meal amounts if needed
|
// Calculate per-meal amounts if needed
|
||||||
const displayGrams = this.showPerMeal ? dailyGramsForThisFood / this.mealsPerDay : dailyGramsForThisFood;
|
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 ?
|
const displayCups = dailyCupsForThisFood !== null ?
|
||||||
(this.showPerMeal ? dailyCupsForThisFood / this.mealsPerDay : 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 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({
|
foodBreakdowns.push({
|
||||||
name: fs.name,
|
name: fs.name,
|
||||||
percentage: fs.percentage,
|
percentage: fs.percentage,
|
||||||
dailyGrams: dailyGramsForThisFood,
|
dailyGrams: dailyGramsForThisFood,
|
||||||
|
dailyGramsMin: dailyGramsMin,
|
||||||
|
dailyGramsMax: dailyGramsMax,
|
||||||
displayGrams: displayGrams,
|
displayGrams: displayGrams,
|
||||||
|
displayGramsMin: displayGramsMin,
|
||||||
|
displayGramsMax: displayGramsMax,
|
||||||
dailyCups: dailyCupsForThisFood,
|
dailyCups: dailyCupsForThisFood,
|
||||||
|
dailyCupsMin: dailyCupsMin,
|
||||||
|
dailyCupsMax: dailyCupsMax,
|
||||||
displayCups: displayCups,
|
displayCups: displayCups,
|
||||||
|
displayCupsMin: displayCupsMin,
|
||||||
|
displayCupsMax: displayCupsMax,
|
||||||
calories: dailyCaloriesForThisFood,
|
calories: dailyCaloriesForThisFood,
|
||||||
displayCalories: displayCalories,
|
displayCalories: displayCalories,
|
||||||
|
displayCaloriesMin: displayCaloriesMin,
|
||||||
|
displayCaloriesMax: displayCaloriesMax,
|
||||||
isLocked: fs.isLocked,
|
isLocked: fs.isLocked,
|
||||||
hasEnergyContent: true,
|
hasEnergyContent: true,
|
||||||
|
hasRange: hasRange,
|
||||||
foodSource: fs // Store reference for cups conversion
|
foodSource: fs // Store reference for cups conversion
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -3717,13 +3799,24 @@ const CALCULATOR_CONFIG = {
|
|||||||
if (unit === 'cups') {
|
if (unit === 'cups') {
|
||||||
// For cups, use the pre-calculated cups value if available
|
// For cups, use the pre-calculated cups value if available
|
||||||
if (breakdown.displayCups !== null) {
|
if (breakdown.displayCups !== null) {
|
||||||
|
if (breakdown.hasRange && breakdown.displayCupsMin !== breakdown.displayCupsMax) {
|
||||||
|
valueContent = `${this.formatNumber(breakdown.displayCupsMin, decimals)}-${this.formatNumber(breakdown.displayCupsMax, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
|
} else {
|
||||||
valueContent = `${this.formatNumber(breakdown.displayCups, decimals)} ${unitLabel}${frequencySuffix}`;
|
valueContent = `${this.formatNumber(breakdown.displayCups, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
valueContent = `<span class="dog-calculator-warning" title="Cups only available for foods with kcal/cup measurement">N/A</span>`;
|
valueContent = `<span class="dog-calculator-warning" title="Cups only available for foods with kcal/cup measurement">N/A</span>`;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// For other units (g, kg, oz, lb)
|
||||||
|
if (breakdown.hasRange && breakdown.displayGramsMin !== breakdown.displayGramsMax) {
|
||||||
|
const minConverted = this.convertUnits(breakdown.displayGramsMin, unit);
|
||||||
|
const maxConverted = this.convertUnits(breakdown.displayGramsMax, unit);
|
||||||
|
valueContent = `${this.formatNumber(minConverted, decimals)}-${this.formatNumber(maxConverted, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
} else {
|
} else {
|
||||||
valueContent = `${this.formatNumber(this.convertUnits(breakdown.displayGrams, unit), decimals)} ${unitLabel}${frequencySuffix}`;
|
valueContent = `${this.formatNumber(this.convertUnits(breakdown.displayGrams, unit), decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
valueContent = `<span class="dog-calculator-warning" title="Enter energy content to calculate amount">⚠️</span>`;
|
valueContent = `<span class="dog-calculator-warning" title="Enter energy content to calculate amount">⚠️</span>`;
|
||||||
}
|
}
|
||||||
@ -3760,24 +3853,54 @@ const CALCULATOR_CONFIG = {
|
|||||||
if (validForCups) {
|
if (validForCups) {
|
||||||
// Calculate total cups using pre-calculated values
|
// Calculate total cups using pre-calculated values
|
||||||
let totalCups = 0;
|
let totalCups = 0;
|
||||||
|
let totalCupsMin = 0;
|
||||||
|
let totalCupsMax = 0;
|
||||||
foodBreakdowns.forEach(breakdown => {
|
foodBreakdowns.forEach(breakdown => {
|
||||||
if (breakdown.percentage > 0 && breakdown.displayCups !== null) {
|
if (breakdown.percentage > 0 && breakdown.displayCups !== null) {
|
||||||
totalCups += breakdown.displayCups;
|
totalCups += breakdown.displayCups;
|
||||||
|
if (breakdown.hasRange) {
|
||||||
|
totalCupsMin += breakdown.displayCupsMin || breakdown.displayCups;
|
||||||
|
totalCupsMax += breakdown.displayCupsMax || breakdown.displayCups;
|
||||||
|
} else {
|
||||||
|
totalCupsMin += breakdown.displayCups;
|
||||||
|
totalCupsMax += breakdown.displayCups;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log('Total cups display:', {
|
|
||||||
totalCups,
|
if (hasRange && totalCupsMin !== totalCupsMax) {
|
||||||
displayTotal,
|
totalDisplayText = `${this.formatNumber(totalCupsMin, decimals)}-${this.formatNumber(totalCupsMax, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
foodBreakdowns: foodBreakdowns.map(b => ({ name: b.name, displayCups: b.displayCups }))
|
} else {
|
||||||
});
|
|
||||||
totalDisplayText = this.formatNumber(totalCups, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
totalDisplayText = this.formatNumber(totalCups, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
totalDisplayText = 'Mixed units - see breakdown';
|
totalDisplayText = 'Mixed units - see breakdown';
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Calculate totals for ranges
|
||||||
|
if (hasRange) {
|
||||||
|
let totalGramsMin = 0;
|
||||||
|
let totalGramsMax = 0;
|
||||||
|
foodBreakdowns.forEach(breakdown => {
|
||||||
|
if (breakdown.percentage > 0 && breakdown.hasEnergyContent) {
|
||||||
|
totalGramsMin += breakdown.displayGramsMin || breakdown.displayGrams;
|
||||||
|
totalGramsMax += breakdown.displayGramsMax || breakdown.displayGrams;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const convertedMin = this.convertUnits(totalGramsMin, unit);
|
||||||
|
const convertedMax = this.convertUnits(totalGramsMax, unit);
|
||||||
|
|
||||||
|
if (totalGramsMin !== totalGramsMax) {
|
||||||
|
totalDisplayText = `${this.formatNumber(convertedMin, decimals)}-${this.formatNumber(convertedMax, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
|
} else {
|
||||||
|
totalDisplayText = this.formatNumber(convertedMin, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
convertedTotal = this.convertUnits(displayTotal, unit);
|
convertedTotal = this.convertUnits(displayTotal, unit);
|
||||||
totalDisplayText = this.formatNumber(convertedTotal, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
totalDisplayText = this.formatNumber(convertedTotal, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dailyFoodValue.textContent = totalDisplayText;
|
dailyFoodValue.textContent = totalDisplayText;
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dog-calculator-container {
|
.dog-calculator-container {
|
||||||
max-width: 600px;
|
max-width: 640px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -203,6 +203,7 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
gap: 10px; /* Add gap between label and value */
|
||||||
}
|
}
|
||||||
|
|
||||||
.dog-calculator-result-item:last-child {
|
.dog-calculator-result-item:last-child {
|
||||||
@ -222,6 +223,7 @@
|
|||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
background: rgba(241, 154, 95, 0.15);
|
background: rgba(241, 154, 95, 0.15);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
white-space: nowrap; /* Prevent text from wrapping to multiple lines */
|
||||||
}
|
}
|
||||||
|
|
||||||
.dog-calculator-collapsible {
|
.dog-calculator-collapsible {
|
||||||
@ -533,6 +535,24 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stack result items vertically on small screens */
|
||||||
|
.dog-calculator-result-item {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-result-label {
|
||||||
|
margin-right: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-result-value {
|
||||||
|
font-size: 1rem;
|
||||||
|
align-self: stretch;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark theme - manual override */
|
/* Dark theme - manual override */
|
||||||
|
|||||||
@ -6,6 +6,8 @@
|
|||||||
class DogCalorieCalculator {
|
class DogCalorieCalculator {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.currentMER = 0;
|
this.currentMER = 0;
|
||||||
|
this.currentMERMin = 0; // For range calculations
|
||||||
|
this.currentMERMax = 0; // For range calculations
|
||||||
this.isImperial = false;
|
this.isImperial = false;
|
||||||
this.theme = this.getThemeFromURL() || CALCULATOR_CONFIG.defaultTheme;
|
this.theme = this.getThemeFromURL() || CALCULATOR_CONFIG.defaultTheme;
|
||||||
this.scale = this.getScaleFromURL() || CALCULATOR_CONFIG.defaultScale;
|
this.scale = this.getScaleFromURL() || CALCULATOR_CONFIG.defaultScale;
|
||||||
@ -1053,6 +1055,25 @@
|
|||||||
return rer * factor;
|
return rer * factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the range multipliers for each life stage
|
||||||
|
getLifeStageRange(factor) {
|
||||||
|
// Define ranges based on the reference image
|
||||||
|
const ranges = {
|
||||||
|
'3.0': { min: 3.0, max: 3.0 }, // Puppy 0-4 months (no range)
|
||||||
|
'2.0': { min: 2.0, max: 2.0 }, // Puppy 4m-adult OR Working light (no range for puppies)
|
||||||
|
'1.2': { min: 1.2, max: 1.4 }, // Adult inactive/obese
|
||||||
|
'1.6': { min: 1.4, max: 1.6 }, // Adult neutered/spayed
|
||||||
|
'1.8': { min: 1.6, max: 1.8 }, // Adult intact
|
||||||
|
'1.0': { min: 1.0, max: 1.0 }, // Weight loss (fixed)
|
||||||
|
'1.7': { min: 1.2, max: 1.8 }, // Weight gain (wide range)
|
||||||
|
'5.0': { min: 5.0, max: 5.0 }, // Working heavy (upper bound)
|
||||||
|
'1.1': { min: 1.1, max: 1.1 } // Senior (no range)
|
||||||
|
};
|
||||||
|
|
||||||
|
const key = factor.toFixed(1);
|
||||||
|
return ranges[key] || { min: factor, max: factor };
|
||||||
|
}
|
||||||
|
|
||||||
validateInput(value, min = 0, isInteger = false) {
|
validateInput(value, min = 0, isInteger = false) {
|
||||||
const num = parseFloat(value);
|
const num = parseFloat(value);
|
||||||
if (isNaN(num) || num < min) return false;
|
if (isNaN(num) || num < min) return false;
|
||||||
@ -1184,10 +1205,21 @@
|
|||||||
const rer = this.calculateRER(weightKg);
|
const rer = this.calculateRER(weightKg);
|
||||||
const mer = this.calculateMER(rer, factor);
|
const mer = this.calculateMER(rer, factor);
|
||||||
|
|
||||||
this.currentMER = mer;
|
// Calculate range for MER
|
||||||
|
const range = this.getLifeStageRange(factor);
|
||||||
|
this.currentMERMin = this.calculateMER(rer, range.min);
|
||||||
|
this.currentMERMax = this.calculateMER(rer, range.max);
|
||||||
|
this.currentMER = mer; // Keep middle/selected value for compatibility
|
||||||
|
|
||||||
rerValue.textContent = this.formatNumber(rer, 0) + ' cal/day';
|
rerValue.textContent = this.formatNumber(rer, 0) + ' cal/day';
|
||||||
|
|
||||||
|
// Show MER as range if applicable
|
||||||
|
if (range.min !== range.max) {
|
||||||
|
merValue.textContent = this.formatNumber(this.currentMERMin, 0) + '-' +
|
||||||
|
this.formatNumber(this.currentMERMax, 0) + ' cal/day';
|
||||||
|
} else {
|
||||||
merValue.textContent = this.formatNumber(mer, 0) + ' cal/day';
|
merValue.textContent = this.formatNumber(mer, 0) + ' cal/day';
|
||||||
|
}
|
||||||
calorieResults.style.display = 'block';
|
calorieResults.style.display = 'block';
|
||||||
|
|
||||||
this.updateFoodCalculations();
|
this.updateFoodCalculations();
|
||||||
@ -1222,6 +1254,9 @@
|
|||||||
updateFoodCalculations() {
|
updateFoodCalculations() {
|
||||||
if (this.currentMER === 0) return;
|
if (this.currentMER === 0) return;
|
||||||
|
|
||||||
|
// Check if we have a range
|
||||||
|
const hasRange = this.currentMERMin !== this.currentMERMax;
|
||||||
|
|
||||||
const daysInput = document.getElementById('days');
|
const daysInput = document.getElementById('days');
|
||||||
const unitSelect = document.getElementById('unit');
|
const unitSelect = document.getElementById('unit');
|
||||||
const dailyFoodResults = document.getElementById('dailyFoodResults');
|
const dailyFoodResults = document.getElementById('dailyFoodResults');
|
||||||
@ -1294,43 +1329,70 @@
|
|||||||
|
|
||||||
if (energyPer100g && energyPer100g > 0.1 && fs.percentage > 0) {
|
if (energyPer100g && energyPer100g > 0.1 && fs.percentage > 0) {
|
||||||
const dailyCaloriesForThisFood = (this.currentMER * fs.percentage) / 100;
|
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 dailyGramsForThisFood;
|
||||||
|
let dailyGramsMin, dailyGramsMax;
|
||||||
let dailyCupsForThisFood = null;
|
let dailyCupsForThisFood = null;
|
||||||
|
let dailyCupsMin, dailyCupsMax;
|
||||||
|
|
||||||
// For kcal/cup, calculate cups directly from calories
|
// For kcal/cup, calculate cups directly from calories
|
||||||
if (fs.energyUnit === 'kcalcup' && fs.energy) {
|
if (fs.energyUnit === 'kcalcup' && fs.energy) {
|
||||||
const caloriesPerCup = parseFloat(fs.energy);
|
const caloriesPerCup = parseFloat(fs.energy);
|
||||||
dailyCupsForThisFood = dailyCaloriesForThisFood / caloriesPerCup;
|
dailyCupsForThisFood = dailyCaloriesForThisFood / caloriesPerCup;
|
||||||
|
dailyCupsMin = dailyCaloriesMin / caloriesPerCup;
|
||||||
|
dailyCupsMax = dailyCaloriesMax / caloriesPerCup;
|
||||||
// We still need grams for total calculation, use approximation
|
// We still need grams for total calculation, use approximation
|
||||||
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
||||||
console.log('Cups calculation:', {
|
dailyGramsMin = (dailyCaloriesMin / energyPer100g) * 100;
|
||||||
caloriesPerCup,
|
dailyGramsMax = (dailyCaloriesMax / energyPer100g) * 100;
|
||||||
dailyCaloriesForThisFood,
|
|
||||||
dailyCupsForThisFood,
|
|
||||||
dailyGramsForThisFood
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// For other units, calculate grams normally
|
// For other units, calculate grams normally
|
||||||
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
||||||
|
dailyGramsMin = (dailyCaloriesMin / energyPer100g) * 100;
|
||||||
|
dailyGramsMax = (dailyCaloriesMax / energyPer100g) * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate per-meal amounts if needed
|
// Calculate per-meal amounts if needed
|
||||||
const displayGrams = this.showPerMeal ? dailyGramsForThisFood / this.mealsPerDay : dailyGramsForThisFood;
|
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 ?
|
const displayCups = dailyCupsForThisFood !== null ?
|
||||||
(this.showPerMeal ? dailyCupsForThisFood / this.mealsPerDay : 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 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({
|
foodBreakdowns.push({
|
||||||
name: fs.name,
|
name: fs.name,
|
||||||
percentage: fs.percentage,
|
percentage: fs.percentage,
|
||||||
dailyGrams: dailyGramsForThisFood,
|
dailyGrams: dailyGramsForThisFood,
|
||||||
|
dailyGramsMin: dailyGramsMin,
|
||||||
|
dailyGramsMax: dailyGramsMax,
|
||||||
displayGrams: displayGrams,
|
displayGrams: displayGrams,
|
||||||
|
displayGramsMin: displayGramsMin,
|
||||||
|
displayGramsMax: displayGramsMax,
|
||||||
dailyCups: dailyCupsForThisFood,
|
dailyCups: dailyCupsForThisFood,
|
||||||
|
dailyCupsMin: dailyCupsMin,
|
||||||
|
dailyCupsMax: dailyCupsMax,
|
||||||
displayCups: displayCups,
|
displayCups: displayCups,
|
||||||
|
displayCupsMin: displayCupsMin,
|
||||||
|
displayCupsMax: displayCupsMax,
|
||||||
calories: dailyCaloriesForThisFood,
|
calories: dailyCaloriesForThisFood,
|
||||||
displayCalories: displayCalories,
|
displayCalories: displayCalories,
|
||||||
|
displayCaloriesMin: displayCaloriesMin,
|
||||||
|
displayCaloriesMax: displayCaloriesMax,
|
||||||
isLocked: fs.isLocked,
|
isLocked: fs.isLocked,
|
||||||
hasEnergyContent: true,
|
hasEnergyContent: true,
|
||||||
|
hasRange: hasRange,
|
||||||
foodSource: fs // Store reference for cups conversion
|
foodSource: fs // Store reference for cups conversion
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1435,13 +1497,24 @@
|
|||||||
if (unit === 'cups') {
|
if (unit === 'cups') {
|
||||||
// For cups, use the pre-calculated cups value if available
|
// For cups, use the pre-calculated cups value if available
|
||||||
if (breakdown.displayCups !== null) {
|
if (breakdown.displayCups !== null) {
|
||||||
|
if (breakdown.hasRange && breakdown.displayCupsMin !== breakdown.displayCupsMax) {
|
||||||
|
valueContent = `${this.formatNumber(breakdown.displayCupsMin, decimals)}-${this.formatNumber(breakdown.displayCupsMax, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
|
} else {
|
||||||
valueContent = `${this.formatNumber(breakdown.displayCups, decimals)} ${unitLabel}${frequencySuffix}`;
|
valueContent = `${this.formatNumber(breakdown.displayCups, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
valueContent = `<span class="dog-calculator-warning" title="Cups only available for foods with kcal/cup measurement">N/A</span>`;
|
valueContent = `<span class="dog-calculator-warning" title="Cups only available for foods with kcal/cup measurement">N/A</span>`;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// For other units (g, kg, oz, lb)
|
||||||
|
if (breakdown.hasRange && breakdown.displayGramsMin !== breakdown.displayGramsMax) {
|
||||||
|
const minConverted = this.convertUnits(breakdown.displayGramsMin, unit);
|
||||||
|
const maxConverted = this.convertUnits(breakdown.displayGramsMax, unit);
|
||||||
|
valueContent = `${this.formatNumber(minConverted, decimals)}-${this.formatNumber(maxConverted, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
} else {
|
} else {
|
||||||
valueContent = `${this.formatNumber(this.convertUnits(breakdown.displayGrams, unit), decimals)} ${unitLabel}${frequencySuffix}`;
|
valueContent = `${this.formatNumber(this.convertUnits(breakdown.displayGrams, unit), decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
valueContent = `<span class="dog-calculator-warning" title="Enter energy content to calculate amount">⚠️</span>`;
|
valueContent = `<span class="dog-calculator-warning" title="Enter energy content to calculate amount">⚠️</span>`;
|
||||||
}
|
}
|
||||||
@ -1478,24 +1551,54 @@
|
|||||||
if (validForCups) {
|
if (validForCups) {
|
||||||
// Calculate total cups using pre-calculated values
|
// Calculate total cups using pre-calculated values
|
||||||
let totalCups = 0;
|
let totalCups = 0;
|
||||||
|
let totalCupsMin = 0;
|
||||||
|
let totalCupsMax = 0;
|
||||||
foodBreakdowns.forEach(breakdown => {
|
foodBreakdowns.forEach(breakdown => {
|
||||||
if (breakdown.percentage > 0 && breakdown.displayCups !== null) {
|
if (breakdown.percentage > 0 && breakdown.displayCups !== null) {
|
||||||
totalCups += breakdown.displayCups;
|
totalCups += breakdown.displayCups;
|
||||||
|
if (breakdown.hasRange) {
|
||||||
|
totalCupsMin += breakdown.displayCupsMin || breakdown.displayCups;
|
||||||
|
totalCupsMax += breakdown.displayCupsMax || breakdown.displayCups;
|
||||||
|
} else {
|
||||||
|
totalCupsMin += breakdown.displayCups;
|
||||||
|
totalCupsMax += breakdown.displayCups;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log('Total cups display:', {
|
|
||||||
totalCups,
|
if (hasRange && totalCupsMin !== totalCupsMax) {
|
||||||
displayTotal,
|
totalDisplayText = `${this.formatNumber(totalCupsMin, decimals)}-${this.formatNumber(totalCupsMax, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
foodBreakdowns: foodBreakdowns.map(b => ({ name: b.name, displayCups: b.displayCups }))
|
} else {
|
||||||
});
|
|
||||||
totalDisplayText = this.formatNumber(totalCups, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
totalDisplayText = this.formatNumber(totalCups, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
totalDisplayText = 'Mixed units - see breakdown';
|
totalDisplayText = 'Mixed units - see breakdown';
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Calculate totals for ranges
|
||||||
|
if (hasRange) {
|
||||||
|
let totalGramsMin = 0;
|
||||||
|
let totalGramsMax = 0;
|
||||||
|
foodBreakdowns.forEach(breakdown => {
|
||||||
|
if (breakdown.percentage > 0 && breakdown.hasEnergyContent) {
|
||||||
|
totalGramsMin += breakdown.displayGramsMin || breakdown.displayGrams;
|
||||||
|
totalGramsMax += breakdown.displayGramsMax || breakdown.displayGrams;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const convertedMin = this.convertUnits(totalGramsMin, unit);
|
||||||
|
const convertedMax = this.convertUnits(totalGramsMax, unit);
|
||||||
|
|
||||||
|
if (totalGramsMin !== totalGramsMax) {
|
||||||
|
totalDisplayText = `${this.formatNumber(convertedMin, decimals)}-${this.formatNumber(convertedMax, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
|
} else {
|
||||||
|
totalDisplayText = this.formatNumber(convertedMin, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
convertedTotal = this.convertUnits(displayTotal, unit);
|
convertedTotal = this.convertUnits(displayTotal, unit);
|
||||||
totalDisplayText = this.formatNumber(convertedTotal, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
totalDisplayText = this.formatNumber(convertedTotal, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dailyFoodValue.textContent = totalDisplayText;
|
dailyFoodValue.textContent = totalDisplayText;
|
||||||
|
|
||||||
|
|||||||
@ -47,7 +47,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dog-calculator-container {
|
.dog-calculator-container {
|
||||||
max-width: 600px;
|
max-width: 640px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -228,6 +228,7 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
gap: 10px; /* Add gap between label and value */
|
||||||
}
|
}
|
||||||
|
|
||||||
.dog-calculator-result-item:last-child {
|
.dog-calculator-result-item:last-child {
|
||||||
@ -247,6 +248,7 @@
|
|||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
background: rgba(241, 154, 95, 0.15);
|
background: rgba(241, 154, 95, 0.15);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
white-space: nowrap; /* Prevent text from wrapping to multiple lines */
|
||||||
}
|
}
|
||||||
|
|
||||||
.dog-calculator-collapsible {
|
.dog-calculator-collapsible {
|
||||||
@ -558,6 +560,24 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stack result items vertically on small screens */
|
||||||
|
.dog-calculator-result-item {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-result-label {
|
||||||
|
margin-right: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-result-value {
|
||||||
|
font-size: 1rem;
|
||||||
|
align-self: stretch;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark theme - manual override */
|
/* Dark theme - manual override */
|
||||||
@ -2105,6 +2125,8 @@ const CALCULATOR_CONFIG = {
|
|||||||
this.theme = this.options.theme;
|
this.theme = this.options.theme;
|
||||||
this.scale = this.options.scale;
|
this.scale = this.options.scale;
|
||||||
this.currentMER = 0;
|
this.currentMER = 0;
|
||||||
|
this.currentMERMin = 0; // For range calculations
|
||||||
|
this.currentMERMax = 0; // For range calculations
|
||||||
this.isImperial = false;
|
this.isImperial = false;
|
||||||
|
|
||||||
|
|
||||||
@ -3372,6 +3394,25 @@ const CALCULATOR_CONFIG = {
|
|||||||
return rer * factor;
|
return rer * factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the range multipliers for each life stage
|
||||||
|
getLifeStageRange(factor) {
|
||||||
|
// Define ranges based on the reference image
|
||||||
|
const ranges = {
|
||||||
|
'3.0': { min: 3.0, max: 3.0 }, // Puppy 0-4 months (no range)
|
||||||
|
'2.0': { min: 2.0, max: 2.0 }, // Puppy 4m-adult OR Working light (no range for puppies)
|
||||||
|
'1.2': { min: 1.2, max: 1.4 }, // Adult inactive/obese
|
||||||
|
'1.6': { min: 1.4, max: 1.6 }, // Adult neutered/spayed
|
||||||
|
'1.8': { min: 1.6, max: 1.8 }, // Adult intact
|
||||||
|
'1.0': { min: 1.0, max: 1.0 }, // Weight loss (fixed)
|
||||||
|
'1.7': { min: 1.2, max: 1.8 }, // Weight gain (wide range)
|
||||||
|
'5.0': { min: 5.0, max: 5.0 }, // Working heavy (upper bound)
|
||||||
|
'1.1': { min: 1.1, max: 1.1 } // Senior (no range)
|
||||||
|
};
|
||||||
|
|
||||||
|
const key = factor.toFixed(1);
|
||||||
|
return ranges[key] || { min: factor, max: factor };
|
||||||
|
}
|
||||||
|
|
||||||
validateInput(value, min = 0, isInteger = false) {
|
validateInput(value, min = 0, isInteger = false) {
|
||||||
const num = parseFloat(value);
|
const num = parseFloat(value);
|
||||||
if (isNaN(num) || num < min) return false;
|
if (isNaN(num) || num < min) return false;
|
||||||
@ -3503,10 +3544,21 @@ const CALCULATOR_CONFIG = {
|
|||||||
const rer = this.calculateRER(weightKg);
|
const rer = this.calculateRER(weightKg);
|
||||||
const mer = this.calculateMER(rer, factor);
|
const mer = this.calculateMER(rer, factor);
|
||||||
|
|
||||||
this.currentMER = mer;
|
// Calculate range for MER
|
||||||
|
const range = this.getLifeStageRange(factor);
|
||||||
|
this.currentMERMin = this.calculateMER(rer, range.min);
|
||||||
|
this.currentMERMax = this.calculateMER(rer, range.max);
|
||||||
|
this.currentMER = mer; // Keep middle/selected value for compatibility
|
||||||
|
|
||||||
rerValue.textContent = this.formatNumber(rer, 0) + ' cal/day';
|
rerValue.textContent = this.formatNumber(rer, 0) + ' cal/day';
|
||||||
|
|
||||||
|
// Show MER as range if applicable
|
||||||
|
if (range.min !== range.max) {
|
||||||
|
merValue.textContent = this.formatNumber(this.currentMERMin, 0) + '-' +
|
||||||
|
this.formatNumber(this.currentMERMax, 0) + ' cal/day';
|
||||||
|
} else {
|
||||||
merValue.textContent = this.formatNumber(mer, 0) + ' cal/day';
|
merValue.textContent = this.formatNumber(mer, 0) + ' cal/day';
|
||||||
|
}
|
||||||
calorieResults.style.display = 'block';
|
calorieResults.style.display = 'block';
|
||||||
|
|
||||||
this.updateFoodCalculations();
|
this.updateFoodCalculations();
|
||||||
@ -3541,6 +3593,9 @@ const CALCULATOR_CONFIG = {
|
|||||||
updateFoodCalculations() {
|
updateFoodCalculations() {
|
||||||
if (this.currentMER === 0) return;
|
if (this.currentMER === 0) return;
|
||||||
|
|
||||||
|
// Check if we have a range
|
||||||
|
const hasRange = this.currentMERMin !== this.currentMERMax;
|
||||||
|
|
||||||
const daysInput = this.container.querySelector('#days');
|
const daysInput = this.container.querySelector('#days');
|
||||||
const unitSelect = this.container.querySelector('#unit');
|
const unitSelect = this.container.querySelector('#unit');
|
||||||
const dailyFoodResults = this.container.querySelector('#dailyFoodResults');
|
const dailyFoodResults = this.container.querySelector('#dailyFoodResults');
|
||||||
@ -3613,43 +3668,70 @@ const CALCULATOR_CONFIG = {
|
|||||||
|
|
||||||
if (energyPer100g && energyPer100g > 0.1 && fs.percentage > 0) {
|
if (energyPer100g && energyPer100g > 0.1 && fs.percentage > 0) {
|
||||||
const dailyCaloriesForThisFood = (this.currentMER * fs.percentage) / 100;
|
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 dailyGramsForThisFood;
|
||||||
|
let dailyGramsMin, dailyGramsMax;
|
||||||
let dailyCupsForThisFood = null;
|
let dailyCupsForThisFood = null;
|
||||||
|
let dailyCupsMin, dailyCupsMax;
|
||||||
|
|
||||||
// For kcal/cup, calculate cups directly from calories
|
// For kcal/cup, calculate cups directly from calories
|
||||||
if (fs.energyUnit === 'kcalcup' && fs.energy) {
|
if (fs.energyUnit === 'kcalcup' && fs.energy) {
|
||||||
const caloriesPerCup = parseFloat(fs.energy);
|
const caloriesPerCup = parseFloat(fs.energy);
|
||||||
dailyCupsForThisFood = dailyCaloriesForThisFood / caloriesPerCup;
|
dailyCupsForThisFood = dailyCaloriesForThisFood / caloriesPerCup;
|
||||||
|
dailyCupsMin = dailyCaloriesMin / caloriesPerCup;
|
||||||
|
dailyCupsMax = dailyCaloriesMax / caloriesPerCup;
|
||||||
// We still need grams for total calculation, use approximation
|
// We still need grams for total calculation, use approximation
|
||||||
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
||||||
console.log('Cups calculation:', {
|
dailyGramsMin = (dailyCaloriesMin / energyPer100g) * 100;
|
||||||
caloriesPerCup,
|
dailyGramsMax = (dailyCaloriesMax / energyPer100g) * 100;
|
||||||
dailyCaloriesForThisFood,
|
|
||||||
dailyCupsForThisFood,
|
|
||||||
dailyGramsForThisFood
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// For other units, calculate grams normally
|
// For other units, calculate grams normally
|
||||||
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
dailyGramsForThisFood = (dailyCaloriesForThisFood / energyPer100g) * 100;
|
||||||
|
dailyGramsMin = (dailyCaloriesMin / energyPer100g) * 100;
|
||||||
|
dailyGramsMax = (dailyCaloriesMax / energyPer100g) * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate per-meal amounts if needed
|
// Calculate per-meal amounts if needed
|
||||||
const displayGrams = this.showPerMeal ? dailyGramsForThisFood / this.mealsPerDay : dailyGramsForThisFood;
|
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 ?
|
const displayCups = dailyCupsForThisFood !== null ?
|
||||||
(this.showPerMeal ? dailyCupsForThisFood / this.mealsPerDay : 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 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({
|
foodBreakdowns.push({
|
||||||
name: fs.name,
|
name: fs.name,
|
||||||
percentage: fs.percentage,
|
percentage: fs.percentage,
|
||||||
dailyGrams: dailyGramsForThisFood,
|
dailyGrams: dailyGramsForThisFood,
|
||||||
|
dailyGramsMin: dailyGramsMin,
|
||||||
|
dailyGramsMax: dailyGramsMax,
|
||||||
displayGrams: displayGrams,
|
displayGrams: displayGrams,
|
||||||
|
displayGramsMin: displayGramsMin,
|
||||||
|
displayGramsMax: displayGramsMax,
|
||||||
dailyCups: dailyCupsForThisFood,
|
dailyCups: dailyCupsForThisFood,
|
||||||
|
dailyCupsMin: dailyCupsMin,
|
||||||
|
dailyCupsMax: dailyCupsMax,
|
||||||
displayCups: displayCups,
|
displayCups: displayCups,
|
||||||
|
displayCupsMin: displayCupsMin,
|
||||||
|
displayCupsMax: displayCupsMax,
|
||||||
calories: dailyCaloriesForThisFood,
|
calories: dailyCaloriesForThisFood,
|
||||||
displayCalories: displayCalories,
|
displayCalories: displayCalories,
|
||||||
|
displayCaloriesMin: displayCaloriesMin,
|
||||||
|
displayCaloriesMax: displayCaloriesMax,
|
||||||
isLocked: fs.isLocked,
|
isLocked: fs.isLocked,
|
||||||
hasEnergyContent: true,
|
hasEnergyContent: true,
|
||||||
|
hasRange: hasRange,
|
||||||
foodSource: fs // Store reference for cups conversion
|
foodSource: fs // Store reference for cups conversion
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -3754,13 +3836,24 @@ const CALCULATOR_CONFIG = {
|
|||||||
if (unit === 'cups') {
|
if (unit === 'cups') {
|
||||||
// For cups, use the pre-calculated cups value if available
|
// For cups, use the pre-calculated cups value if available
|
||||||
if (breakdown.displayCups !== null) {
|
if (breakdown.displayCups !== null) {
|
||||||
|
if (breakdown.hasRange && breakdown.displayCupsMin !== breakdown.displayCupsMax) {
|
||||||
|
valueContent = `${this.formatNumber(breakdown.displayCupsMin, decimals)}-${this.formatNumber(breakdown.displayCupsMax, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
|
} else {
|
||||||
valueContent = `${this.formatNumber(breakdown.displayCups, decimals)} ${unitLabel}${frequencySuffix}`;
|
valueContent = `${this.formatNumber(breakdown.displayCups, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
valueContent = `<span class="dog-calculator-warning" title="Cups only available for foods with kcal/cup measurement">N/A</span>`;
|
valueContent = `<span class="dog-calculator-warning" title="Cups only available for foods with kcal/cup measurement">N/A</span>`;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// For other units (g, kg, oz, lb)
|
||||||
|
if (breakdown.hasRange && breakdown.displayGramsMin !== breakdown.displayGramsMax) {
|
||||||
|
const minConverted = this.convertUnits(breakdown.displayGramsMin, unit);
|
||||||
|
const maxConverted = this.convertUnits(breakdown.displayGramsMax, unit);
|
||||||
|
valueContent = `${this.formatNumber(minConverted, decimals)}-${this.formatNumber(maxConverted, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
} else {
|
} else {
|
||||||
valueContent = `${this.formatNumber(this.convertUnits(breakdown.displayGrams, unit), decimals)} ${unitLabel}${frequencySuffix}`;
|
valueContent = `${this.formatNumber(this.convertUnits(breakdown.displayGrams, unit), decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
valueContent = `<span class="dog-calculator-warning" title="Enter energy content to calculate amount">⚠️</span>`;
|
valueContent = `<span class="dog-calculator-warning" title="Enter energy content to calculate amount">⚠️</span>`;
|
||||||
}
|
}
|
||||||
@ -3797,24 +3890,54 @@ const CALCULATOR_CONFIG = {
|
|||||||
if (validForCups) {
|
if (validForCups) {
|
||||||
// Calculate total cups using pre-calculated values
|
// Calculate total cups using pre-calculated values
|
||||||
let totalCups = 0;
|
let totalCups = 0;
|
||||||
|
let totalCupsMin = 0;
|
||||||
|
let totalCupsMax = 0;
|
||||||
foodBreakdowns.forEach(breakdown => {
|
foodBreakdowns.forEach(breakdown => {
|
||||||
if (breakdown.percentage > 0 && breakdown.displayCups !== null) {
|
if (breakdown.percentage > 0 && breakdown.displayCups !== null) {
|
||||||
totalCups += breakdown.displayCups;
|
totalCups += breakdown.displayCups;
|
||||||
|
if (breakdown.hasRange) {
|
||||||
|
totalCupsMin += breakdown.displayCupsMin || breakdown.displayCups;
|
||||||
|
totalCupsMax += breakdown.displayCupsMax || breakdown.displayCups;
|
||||||
|
} else {
|
||||||
|
totalCupsMin += breakdown.displayCups;
|
||||||
|
totalCupsMax += breakdown.displayCups;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log('Total cups display:', {
|
|
||||||
totalCups,
|
if (hasRange && totalCupsMin !== totalCupsMax) {
|
||||||
displayTotal,
|
totalDisplayText = `${this.formatNumber(totalCupsMin, decimals)}-${this.formatNumber(totalCupsMax, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
foodBreakdowns: foodBreakdowns.map(b => ({ name: b.name, displayCups: b.displayCups }))
|
} else {
|
||||||
});
|
|
||||||
totalDisplayText = this.formatNumber(totalCups, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
totalDisplayText = this.formatNumber(totalCups, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
totalDisplayText = 'Mixed units - see breakdown';
|
totalDisplayText = 'Mixed units - see breakdown';
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Calculate totals for ranges
|
||||||
|
if (hasRange) {
|
||||||
|
let totalGramsMin = 0;
|
||||||
|
let totalGramsMax = 0;
|
||||||
|
foodBreakdowns.forEach(breakdown => {
|
||||||
|
if (breakdown.percentage > 0 && breakdown.hasEnergyContent) {
|
||||||
|
totalGramsMin += breakdown.displayGramsMin || breakdown.displayGrams;
|
||||||
|
totalGramsMax += breakdown.displayGramsMax || breakdown.displayGrams;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const convertedMin = this.convertUnits(totalGramsMin, unit);
|
||||||
|
const convertedMax = this.convertUnits(totalGramsMax, unit);
|
||||||
|
|
||||||
|
if (totalGramsMin !== totalGramsMax) {
|
||||||
|
totalDisplayText = `${this.formatNumber(convertedMin, decimals)}-${this.formatNumber(convertedMax, decimals)} ${unitLabel}${frequencySuffix}`;
|
||||||
|
} else {
|
||||||
|
totalDisplayText = this.formatNumber(convertedMin, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
convertedTotal = this.convertUnits(displayTotal, unit);
|
convertedTotal = this.convertUnits(displayTotal, unit);
|
||||||
totalDisplayText = this.formatNumber(convertedTotal, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
totalDisplayText = this.formatNumber(convertedTotal, decimals) + ` ${unitLabel}${frequencySuffix}`;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dailyFoodValue.textContent = totalDisplayText;
|
dailyFoodValue.textContent = totalDisplayText;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user