Add North American calorie unit support (kcal/kg, kcal/cup, kcal/can)

This commit is contained in:
Dayowe 2025-06-09 09:53:16 +02:00
parent 46b3560a68
commit bb04a4d63f
2 changed files with 128 additions and 70 deletions

View File

@ -978,10 +978,21 @@
</div>
<div class="dog-calc-collapsible-content">
<div class="dog-calc-collapsible-inner">
<div class="dog-calc-form-group">
<label for="foodEnergy" id="foodEnergyLabel">Food Energy Content (kcal/100g):</label>
<input type="number" id="foodEnergy" min="1" step="1" placeholder="Enter kcal per 100g" aria-describedby="foodEnergyHelp">
<div id="foodEnergyError" class="dog-calc-error dog-calc-hidden">Please enter a valid energy content (minimum 1 kcal/100g)</div>
<div class="dog-calc-input-group">
<div class="dog-calc-form-group">
<label for="foodEnergy" id="foodEnergyLabel">Food Energy Content:</label>
<input type="number" id="foodEnergy" min="1" step="1" placeholder="Enter energy content" aria-describedby="foodEnergyHelp">
<div id="foodEnergyError" class="dog-calc-error dog-calc-hidden">Please enter a valid energy content</div>
</div>
<div class="dog-calc-form-group">
<label for="energyUnit">Unit:</label>
<select id="energyUnit" class="dog-calc-unit-select" aria-describedby="energyUnitHelp">
<option value="kcal100g">kcal/100g</option>
<option value="kcalkg">kcal/kg</option>
<option value="kcalcup">kcal/cup</option>
<option value="kcalcan">kcal/can</option>
</select>
</div>
</div>
<div class="dog-calc-results" id="dailyFoodResults" style="display: none;">
@ -1188,6 +1199,9 @@
foodEnergyInput.addEventListener('input', () => this.updateFoodCalculations());
foodEnergyInput.addEventListener('blur', () => this.validateFoodEnergy());
}
const energyUnitSelect = this.container.querySelector('#energyUnit');
if (energyUnitSelect) energyUnitSelect.addEventListener('change', () => this.updateFoodCalculations());
if (daysInput) {
daysInput.addEventListener('input', () => this.updateFoodCalculations());
@ -1264,9 +1278,7 @@
const metricLabel = this.container.querySelector('#metricLabel');
const imperialLabel = this.container.querySelector('#imperialLabel');
const weightLabel = this.container.querySelector('#weightLabel');
const foodEnergyLabel = this.container.querySelector('#foodEnergyLabel');
const weightInput = this.container.querySelector('#weight');
const foodEnergyInput = this.container.querySelector('#foodEnergy');
const unitSelect = this.container.querySelector('#unit');
if (metricLabel && imperialLabel) {
@ -1281,12 +1293,6 @@
weightInput.min = "0.2";
weightInput.step = "0.1";
}
if (foodEnergyLabel) foodEnergyLabel.textContent = "Food Energy Content (kcal/oz):";
if (foodEnergyInput) {
foodEnergyInput.placeholder = "Enter kcal per oz";
foodEnergyInput.min = "0.03";
foodEnergyInput.step = "0.01";
}
if (unitSelect) {
unitSelect.innerHTML = `
<option value="oz">ounces (oz)</option>
@ -1302,12 +1308,6 @@
weightInput.min = "0.1";
weightInput.step = "0.1";
}
if (foodEnergyLabel) foodEnergyLabel.textContent = "Food Energy Content (kcal/100g):";
if (foodEnergyInput) {
foodEnergyInput.placeholder = "Enter kcal per 100g";
foodEnergyInput.min = "1";
foodEnergyInput.step = "1";
}
if (unitSelect) {
unitSelect.innerHTML = `
<option value="g">grams (g)</option>
@ -1321,7 +1321,6 @@
convertExistingValues() {
const weightInput = this.container.querySelector('#weight');
const foodEnergyInput = this.container.querySelector('#foodEnergy');
if (weightInput && weightInput.value) {
const currentWeight = parseFloat(weightInput.value);
@ -1333,17 +1332,6 @@
}
}
}
if (foodEnergyInput && foodEnergyInput.value) {
const currentEnergy = parseFloat(foodEnergyInput.value);
if (!isNaN(currentEnergy)) {
if (this.isImperial) {
foodEnergyInput.value = this.formatNumber(currentEnergy / 3.52739, 2);
} else {
foodEnergyInput.value = this.formatNumber(currentEnergy * 3.52739, 0);
}
}
}
}
getWeightInKg() {
@ -1358,12 +1346,27 @@
getFoodEnergyPer100g() {
const foodEnergyInput = this.container.querySelector('#foodEnergy');
if (!foodEnergyInput || !foodEnergyInput.value) return null;
const energyUnitSelect = this.container.querySelector('#energyUnit');
if (!foodEnergyInput || !foodEnergyInput.value || !energyUnitSelect) return null;
const energy = parseFloat(foodEnergyInput.value);
if (isNaN(energy)) return null;
return this.isImperial ? energy * 3.52739 : energy;
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) {
@ -1418,8 +1421,34 @@
}
validateFoodEnergy() {
const energy = this.container.querySelector('#foodEnergy')?.value;
if (energy && !this.validateInput(energy, 1)) {
const energyInput = this.container.querySelector('#foodEnergy');
const energyUnitSelect = this.container.querySelector('#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);
@ -1496,7 +1525,7 @@
this.showError('foodEnergyError', false);
this.showError('daysError', false);
if (!energyPer100g || energyPer100g < 1) {
if (!energyPer100g || energyPer100g < 0.1) {
const foodEnergyInput = this.container.querySelector('#foodEnergy');
if (foodEnergyInput && foodEnergyInput.value) this.showError('foodEnergyError', true);
dailyFoodResults.style.display = 'none';

View File

@ -958,10 +958,21 @@
</div>
<div class="dog-calculator-collapsible-content">
<div class="dog-calculator-collapsible-inner">
<div class="dog-calculator-form-group">
<label for="foodEnergy" id="foodEnergyLabel">Food Energy Content (kcal/100g):</label>
<input type="number" id="foodEnergy" min="1" step="1" placeholder="Enter kcal per 100g" aria-describedby="foodEnergyHelp">
<div id="foodEnergyError" class="dog-calculator-error dog-calculator-hidden">Please enter a valid energy content (minimum 1 kcal/100g)</div>
<div class="dog-calculator-input-group">
<div class="dog-calculator-form-group">
<label for="foodEnergy" id="foodEnergyLabel">Food Energy Content:</label>
<input type="number" id="foodEnergy" min="1" step="1" placeholder="Enter energy content" aria-describedby="foodEnergyHelp">
<div id="foodEnergyError" class="dog-calculator-error dog-calculator-hidden">Please enter a valid energy content</div>
</div>
<div class="dog-calculator-form-group">
<label for="energyUnit">Unit:</label>
<select id="energyUnit" class="dog-calculator-unit-select" aria-describedby="energyUnitHelp">
<option value="kcal100g">kcal/100g</option>
<option value="kcalkg">kcal/kg</option>
<option value="kcalcup">kcal/cup</option>
<option value="kcalcan">kcal/can</option>
</select>
</div>
</div>
<div class="dog-calculator-results" id="dailyFoodResults" style="display: none;">
@ -1126,6 +1137,9 @@
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());
@ -1197,9 +1211,7 @@
const metricLabel = document.getElementById('metricLabel');
const imperialLabel = document.getElementById('imperialLabel');
const weightLabel = document.getElementById('weightLabel');
const foodEnergyLabel = document.getElementById('foodEnergyLabel');
const weightInput = document.getElementById('weight');
const foodEnergyInput = document.getElementById('foodEnergy');
const unitSelect = document.getElementById('unit');
if (metricLabel && imperialLabel) {
@ -1214,12 +1226,6 @@
weightInput.min = "0.2";
weightInput.step = "0.1";
}
if (foodEnergyLabel) foodEnergyLabel.textContent = "Food Energy Content (kcal/oz):";
if (foodEnergyInput) {
foodEnergyInput.placeholder = "Enter kcal per oz";
foodEnergyInput.min = "0.03";
foodEnergyInput.step = "0.01";
}
if (unitSelect) {
unitSelect.innerHTML = '<option value="oz">ounces (oz)</option>' +
'<option value="lb">pounds (lb)</option>' +
@ -1233,12 +1239,6 @@
weightInput.min = "0.1";
weightInput.step = "0.1";
}
if (foodEnergyLabel) foodEnergyLabel.textContent = "Food Energy Content (kcal/100g):";
if (foodEnergyInput) {
foodEnergyInput.placeholder = "Enter kcal per 100g";
foodEnergyInput.min = "1";
foodEnergyInput.step = "1";
}
if (unitSelect) {
unitSelect.innerHTML = '<option value="g">grams (g)</option>' +
'<option value="kg">kilograms (kg)</option>' +
@ -1250,7 +1250,6 @@
convertExistingValues() {
const weightInput = document.getElementById('weight');
const foodEnergyInput = document.getElementById('foodEnergy');
if (weightInput && weightInput.value) {
const currentWeight = parseFloat(weightInput.value);
@ -1262,17 +1261,6 @@
}
}
}
if (foodEnergyInput && foodEnergyInput.value) {
const currentEnergy = parseFloat(foodEnergyInput.value);
if (!isNaN(currentEnergy)) {
if (this.isImperial) {
foodEnergyInput.value = this.formatNumber(currentEnergy / 3.52739, 2);
} else {
foodEnergyInput.value = this.formatNumber(currentEnergy * 3.52739, 0);
}
}
}
}
getWeightInKg() {
@ -1287,12 +1275,27 @@
getFoodEnergyPer100g() {
const foodEnergyInput = document.getElementById('foodEnergy');
if (!foodEnergyInput || !foodEnergyInput.value) return null;
const energyUnitSelect = document.getElementById('energyUnit');
if (!foodEnergyInput || !foodEnergyInput.value || !energyUnitSelect) return null;
const energy = parseFloat(foodEnergyInput.value);
if (isNaN(energy)) return null;
return this.isImperial ? energy * 3.52739 : energy;
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) {
@ -1351,8 +1354,34 @@
}
validateFoodEnergy() {
const energyPer100g = this.getFoodEnergyPer100g();
if (energyPer100g !== null && energyPer100g < 1) {
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);
@ -1430,7 +1459,7 @@
this.showError('foodEnergyError', false);
this.showError('daysError', false);
if (!energyPer100g || energyPer100g < 1) {
if (!energyPer100g || energyPer100g < 0.1) {
const foodEnergyInput = document.getElementById('foodEnergy');
if (foodEnergyInput && foodEnergyInput.value) this.showError('foodEnergyError', true);
dailyFoodResults.style.display = 'none';