Kaya transition v1
This commit is contained in:
parent
90657f9aa4
commit
7fd139b321
251
iframe.html
251
iframe.html
@ -200,6 +200,12 @@
|
||||
color: var(--text-label);
|
||||
}
|
||||
|
||||
/* Kaya end-weight readonly field: compact, non-editable */
|
||||
#kayaEndWeight {
|
||||
width: 120px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dog-calculator-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);
|
||||
@ -1964,50 +1970,18 @@
|
||||
<div class="dog-calculator-container" id="dogCalculator">
|
||||
<div class="dog-calculator-section">
|
||||
<div class="dog-calculator-section-header">
|
||||
<h2>Dog's Characteristics</h2>
|
||||
<div class="dog-calculator-unit-switch">
|
||||
<span class="dog-calculator-unit-label active" id="metricLabel">Metric</span>
|
||||
<label class="dog-calculator-switch">
|
||||
<input type="checkbox" id="unitToggle">
|
||||
<span class="dog-calculator-slider"></span>
|
||||
</label>
|
||||
<span class="dog-calculator-unit-label" id="imperialLabel">Imperial</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dog-calculator-form-group">
|
||||
<label for="dogType">Dog Type / Activity Level:</label>
|
||||
<select id="dogType" aria-describedby="dogTypeHelp">
|
||||
<option value="">Select dog type...</option>
|
||||
<option value="3.0">Puppy (0-4 months)</option>
|
||||
<option value="2.0">Puppy (4 months - adult)</option>
|
||||
<option value="1.2">Adult - inactive/obese</option>
|
||||
<option value="1.6">Adult (neutered/spayed) - average activity</option>
|
||||
<option value="1.8">Adult (intact) - average activity</option>
|
||||
<option value="1.0">Adult - weight loss</option>
|
||||
<option value="1.7">Adult - weight gain</option>
|
||||
<option value="2.0">Working dog - light work</option>
|
||||
<option value="3.0">Working dog - moderate work</option>
|
||||
<option value="5.0">Working dog - heavy work</option>
|
||||
<option value="1.1">Senior dog</option>
|
||||
</select>
|
||||
<h2>Kaya’s Transition</h2>
|
||||
</div>
|
||||
|
||||
<div class="dog-calculator-form-group">
|
||||
<label for="weight" id="weightLabel">Dog's Weight (kg):</label>
|
||||
<input type="number" id="weight" min="0.1" step="0.1" placeholder="Enter weight in kg" aria-describedby="weightHelp">
|
||||
<div id="weightError" class="dog-calculator-error dog-calculator-hidden">Please enter a valid weight (minimum 0.1 kg)</div>
|
||||
<label for="ageMonths">Kaya’s age (months):</label>
|
||||
<input type="number" id="ageMonths" min="2" max="12" step="0.1" placeholder="Enter age in months" aria-describedby="ageHelp">
|
||||
<div id="ageClampNote" class="dog-calculator-error dog-calculator-hidden">Age adjusted to the supported 2–12 month range.</div>
|
||||
</div>
|
||||
|
||||
<div class="dog-calculator-results" id="calorieResults" style="display: none;">
|
||||
<div class="dog-calculator-result-item">
|
||||
<span class="dog-calculator-result-label">Resting Energy Requirement (RER):</span>
|
||||
<span class="dog-calculator-result-value" id="rerValue">- cal/day</span>
|
||||
</div>
|
||||
<div class="dog-calculator-result-item">
|
||||
<span class="dog-calculator-result-label">Maintenance Energy Requirement (MER):</span>
|
||||
<span class="dog-calculator-result-value" id="merValue">- cal/day</span>
|
||||
</div>
|
||||
<div class="dog-calculator-form-group">
|
||||
<label for="kayaEndWeight">Kaya’s end‑weight:</label>
|
||||
<input type="text" id="kayaEndWeight" value="30 kg" readonly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2102,46 +2076,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dog-calculator-action-buttons">
|
||||
<button class="dog-calculator-btn dog-calculator-btn-share" id="shareBtn">
|
||||
Share
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dog-calculator-footer">
|
||||
<a href="https://caninenutritionandwellness.com" target="_blank" rel="noopener noreferrer">
|
||||
by caninenutritionandwellness.com
|
||||
</a>
|
||||
</div>
|
||||
<!-- Share Modal -->
|
||||
<div id="shareModal" class="dog-calculator-modal" style="display: none;">
|
||||
<div class="dog-calculator-modal-content">
|
||||
<span class="dog-calculator-modal-close" id="shareModalClose">×</span>
|
||||
<h3>Share Calculator</h3>
|
||||
<div class="dog-calculator-share-buttons">
|
||||
<button class="dog-calculator-share-btn dog-calculator-share-facebook plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-facebook" id="shareFacebook">
|
||||
Facebook
|
||||
</button>
|
||||
<button class="dog-calculator-share-btn dog-calculator-share-twitter plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-twitter" id="shareTwitter">
|
||||
Twitter
|
||||
</button>
|
||||
<button class="dog-calculator-share-btn dog-calculator-share-linkedin plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-linkedin" id="shareLinkedIn">
|
||||
LinkedIn
|
||||
</button>
|
||||
<button class="dog-calculator-share-btn dog-calculator-share-email plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-email" id="shareEmail">
|
||||
Email
|
||||
</button>
|
||||
<button class="dog-calculator-share-btn dog-calculator-share-copy plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-copy-link" id="shareCopy">
|
||||
Copy Link
|
||||
</button>
|
||||
</div>
|
||||
<div class="dog-calculator-share-url">
|
||||
<input type="text" id="shareUrl" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<script>
|
||||
/**
|
||||
@ -2173,6 +2113,9 @@ const CALCULATOR_CONFIG = {
|
||||
this.maxFoodSources = CALCULATOR_CONFIG.maxFoodSources;
|
||||
this.mealsPerDay = 2;
|
||||
this.showPerMeal = false;
|
||||
// Kayafied reference source tracking
|
||||
this.kibbleRefId = null;
|
||||
this.gcRefId = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
@ -2227,8 +2170,45 @@ const CALCULATOR_CONFIG = {
|
||||
|
||||
// Food Source Management Methods
|
||||
initializeFoodSources() {
|
||||
this.addFoodSource();
|
||||
// Seed three sources for Kaya's transition
|
||||
const gc = {
|
||||
id: this.generateFoodSourceId(),
|
||||
name: 'Fred & Felia, gently cooked',
|
||||
energy: '115',
|
||||
energyUnit: 'kcal100g',
|
||||
percentage: 5,
|
||||
isLocked: false
|
||||
};
|
||||
this.foodSources.push(gc);
|
||||
this.renderFoodSource(gc);
|
||||
this.gcRefId = gc.id;
|
||||
|
||||
const kibble = {
|
||||
id: this.generateFoodSourceId(),
|
||||
name: 'Eukanuba, kibble',
|
||||
energy: '372',
|
||||
energyUnit: 'kcal100g',
|
||||
percentage: 95,
|
||||
isLocked: false
|
||||
};
|
||||
this.foodSources.push(kibble);
|
||||
this.renderFoodSource(kibble);
|
||||
this.kibbleRefId = kibble.id;
|
||||
|
||||
const treats = {
|
||||
id: this.generateFoodSourceId(),
|
||||
name: 'Treats',
|
||||
energy: '',
|
||||
energyUnit: 'kcal100g',
|
||||
percentage: 0,
|
||||
isLocked: false
|
||||
};
|
||||
this.foodSources.push(treats);
|
||||
this.renderFoodSource(treats);
|
||||
|
||||
this.updateAddButton();
|
||||
this.updateRemoveButtons();
|
||||
this.refreshAllPercentageUI();
|
||||
}
|
||||
|
||||
addFoodSource() {
|
||||
@ -2263,6 +2243,9 @@ const CALCULATOR_CONFIG = {
|
||||
if (index === -1) return;
|
||||
|
||||
this.foodSources.splice(index, 1);
|
||||
// If reference IDs were removed, clear them
|
||||
if (this.kibbleRefId === id) this.kibbleRefId = null;
|
||||
if (this.gcRefId === id) this.gcRefId = null;
|
||||
|
||||
// Remove the DOM element
|
||||
const element = document.getElementById(`foodSource-${id}`);
|
||||
@ -2276,6 +2259,7 @@ const CALCULATOR_CONFIG = {
|
||||
this.updateAddButton();
|
||||
this.updateRemoveButtons();
|
||||
this.refreshAllPercentageUI();
|
||||
this.updateCalorieCalculations();
|
||||
}
|
||||
|
||||
generateFoodSourceId() {
|
||||
@ -2747,6 +2731,10 @@ const CALCULATOR_CONFIG = {
|
||||
if (energyInput) {
|
||||
energyInput.addEventListener('input', () => {
|
||||
this.updateFoodSourceData(id, 'energy', energyInput.value);
|
||||
// If kibble reference changed, recompute daily target
|
||||
if (id === this.kibbleRefId) {
|
||||
this.updateCalorieCalculations();
|
||||
}
|
||||
|
||||
// Auto-select cups when entering energy for kcal/cup
|
||||
const foodSource = this.foodSources.find(fs => fs.id === id);
|
||||
@ -2784,6 +2772,9 @@ const CALCULATOR_CONFIG = {
|
||||
if (energyUnitSelect) {
|
||||
energyUnitSelect.addEventListener('change', () => {
|
||||
this.updateFoodSourceData(id, 'energyUnit', energyUnitSelect.value);
|
||||
if (id === this.kibbleRefId) {
|
||||
this.updateCalorieCalculations();
|
||||
}
|
||||
|
||||
// Auto-select the most appropriate unit based on energy unit
|
||||
const unitSelect = document.getElementById('unit');
|
||||
@ -2961,6 +2952,7 @@ const CALCULATOR_CONFIG = {
|
||||
bindEvents() {
|
||||
const weightInput = document.getElementById('weight');
|
||||
const dogTypeSelect = document.getElementById('dogType');
|
||||
const ageInput = document.getElementById('ageMonths');
|
||||
const daysInput = document.getElementById('days');
|
||||
const unitSelect = document.getElementById('unit');
|
||||
const unitToggle = document.getElementById('unitToggle');
|
||||
@ -2972,6 +2964,12 @@ const CALCULATOR_CONFIG = {
|
||||
}
|
||||
|
||||
if (dogTypeSelect) dogTypeSelect.addEventListener('change', () => this.updateCalorieCalculations());
|
||||
|
||||
// Kayafied: age input drives energy target
|
||||
if (ageInput) {
|
||||
ageInput.addEventListener('input', () => this.updateCalorieCalculations());
|
||||
ageInput.addEventListener('blur', () => this.updateCalorieCalculations());
|
||||
}
|
||||
|
||||
if (daysInput) {
|
||||
daysInput.addEventListener('input', () => {
|
||||
@ -3316,57 +3314,86 @@ const CALCULATOR_CONFIG = {
|
||||
}
|
||||
|
||||
updateCalorieCalculations() {
|
||||
const dogTypeSelect = document.getElementById('dogType');
|
||||
const calorieResults = document.getElementById('calorieResults');
|
||||
const rerValue = document.getElementById('rerValue');
|
||||
const merValue = document.getElementById('merValue');
|
||||
// Kaya-specific: compute daily kcal target from age + kibble energy
|
||||
const ageInput = document.getElementById('ageMonths');
|
||||
const ageClampNote = document.getElementById('ageClampNote');
|
||||
|
||||
if (!dogTypeSelect || !calorieResults || !rerValue || !merValue) {
|
||||
// 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;
|
||||
return;
|
||||
}
|
||||
|
||||
const weightKg = this.getWeightInKg();
|
||||
const dogTypeFactor = dogTypeSelect.value;
|
||||
|
||||
this.showError('weightError', false);
|
||||
|
||||
if (!weightKg || weightKg < 0.1) {
|
||||
const weightInput = document.getElementById('weight');
|
||||
if (weightInput && weightInput.value) this.showError('weightError', true);
|
||||
calorieResults.style.display = 'none';
|
||||
let age = parseFloat(ageInput.value);
|
||||
if (isNaN(age)) {
|
||||
this.currentMER = 0;
|
||||
this.currentMERMin = 0;
|
||||
this.currentMERMax = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dogTypeFactor) {
|
||||
calorieResults.style.display = 'none';
|
||||
// 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'); }
|
||||
|
||||
// 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 factor = parseFloat(dogTypeFactor);
|
||||
|
||||
const rer = this.calculateRER(weightKg);
|
||||
const mer = this.calculateMER(rer, factor);
|
||||
|
||||
// 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';
|
||||
|
||||
// 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';
|
||||
}
|
||||
calorieResults.style.display = 'block';
|
||||
|
||||
const kcalPerGram = kibbleEnergyPer100g / 100.0;
|
||||
const dailyKcal = kibbleGrams * kcalPerGram;
|
||||
this.currentMER = dailyKcal;
|
||||
this.currentMERMin = dailyKcal;
|
||||
this.currentMERMax = dailyKcal;
|
||||
|
||||
this.updateFoodCalculations();
|
||||
}
|
||||
|
||||
// Kaya: monthly table with interpolation between months
|
||||
getKayaKibbleGramsForAge(ageMonths) {
|
||||
// Precomputed monthly values for 30 kg (g/day)
|
||||
const table = {
|
||||
2: 250,
|
||||
3: 330,
|
||||
4: 365,
|
||||
5: 382,
|
||||
6: 400,
|
||||
7: 405,
|
||||
8: 410,
|
||||
9: 410,
|
||||
10: 410,
|
||||
11: 408,
|
||||
12: 405
|
||||
};
|
||||
// Exact integer month
|
||||
const lower = Math.floor(ageMonths);
|
||||
const upper = Math.ceil(ageMonths);
|
||||
if (lower === upper) return table[lower] || 0;
|
||||
// Linear interpolation between bounds
|
||||
const lowerVal = table[lower] || 0;
|
||||
const upperVal = table[upper] || 0;
|
||||
const t = (ageMonths - lower) / (upper - lower);
|
||||
return lowerVal + (upperVal - lowerVal) * t;
|
||||
}
|
||||
|
||||
updateCupsButtonState() {
|
||||
const cupsButton = document.getElementById('cupsButton');
|
||||
if (!cupsButton) return;
|
||||
|
||||
@ -190,6 +190,12 @@
|
||||
color: var(--text-label);
|
||||
}
|
||||
|
||||
/* Kaya end-weight readonly field: compact, non-editable */
|
||||
#kayaEndWeight {
|
||||
width: 120px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dog-calculator-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);
|
||||
|
||||
@ -1,50 +1,18 @@
|
||||
<div class="dog-calculator-container" id="dogCalculator">
|
||||
<div class="dog-calculator-section">
|
||||
<div class="dog-calculator-section-header">
|
||||
<h2>Dog's Characteristics</h2>
|
||||
<div class="dog-calculator-unit-switch">
|
||||
<span class="dog-calculator-unit-label active" id="metricLabel">Metric</span>
|
||||
<label class="dog-calculator-switch">
|
||||
<input type="checkbox" id="unitToggle">
|
||||
<span class="dog-calculator-slider"></span>
|
||||
</label>
|
||||
<span class="dog-calculator-unit-label" id="imperialLabel">Imperial</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dog-calculator-form-group">
|
||||
<label for="dogType">Dog Type / Activity Level:</label>
|
||||
<select id="dogType" aria-describedby="dogTypeHelp">
|
||||
<option value="">Select dog type...</option>
|
||||
<option value="3.0">Puppy (0-4 months)</option>
|
||||
<option value="2.0">Puppy (4 months - adult)</option>
|
||||
<option value="1.2">Adult - inactive/obese</option>
|
||||
<option value="1.6">Adult (neutered/spayed) - average activity</option>
|
||||
<option value="1.8">Adult (intact) - average activity</option>
|
||||
<option value="1.0">Adult - weight loss</option>
|
||||
<option value="1.7">Adult - weight gain</option>
|
||||
<option value="2.0">Working dog - light work</option>
|
||||
<option value="3.0">Working dog - moderate work</option>
|
||||
<option value="5.0">Working dog - heavy work</option>
|
||||
<option value="1.1">Senior dog</option>
|
||||
</select>
|
||||
<h2>Kaya’s Transition</h2>
|
||||
</div>
|
||||
|
||||
<div class="dog-calculator-form-group">
|
||||
<label for="weight" id="weightLabel">Dog's Weight (kg):</label>
|
||||
<input type="number" id="weight" min="0.1" step="0.1" placeholder="Enter weight in kg" aria-describedby="weightHelp">
|
||||
<div id="weightError" class="dog-calculator-error dog-calculator-hidden">Please enter a valid weight (minimum 0.1 kg)</div>
|
||||
<label for="ageMonths">Kaya’s age (months):</label>
|
||||
<input type="number" id="ageMonths" min="2" max="12" step="0.1" placeholder="Enter age in months" aria-describedby="ageHelp">
|
||||
<div id="ageClampNote" class="dog-calculator-error dog-calculator-hidden">Age adjusted to the supported 2–12 month range.</div>
|
||||
</div>
|
||||
|
||||
<div class="dog-calculator-results" id="calorieResults" style="display: none;">
|
||||
<div class="dog-calculator-result-item">
|
||||
<span class="dog-calculator-result-label">Resting Energy Requirement (RER):</span>
|
||||
<span class="dog-calculator-result-value" id="rerValue">- cal/day</span>
|
||||
</div>
|
||||
<div class="dog-calculator-result-item">
|
||||
<span class="dog-calculator-result-label">Maintenance Energy Requirement (MER):</span>
|
||||
<span class="dog-calculator-result-value" id="merValue">- cal/day</span>
|
||||
</div>
|
||||
<div class="dog-calculator-form-group">
|
||||
<label for="kayaEndWeight">Kaya’s end‑weight:</label>
|
||||
<input type="text" id="kayaEndWeight" value="30 kg" readonly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -139,44 +107,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dog-calculator-action-buttons">
|
||||
<button class="dog-calculator-btn dog-calculator-btn-share" id="shareBtn">
|
||||
Share
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dog-calculator-footer">
|
||||
<a href="https://caninenutritionandwellness.com" target="_blank" rel="noopener noreferrer">
|
||||
by caninenutritionandwellness.com
|
||||
</a>
|
||||
</div>
|
||||
<!-- Share Modal -->
|
||||
<div id="shareModal" class="dog-calculator-modal" style="display: none;">
|
||||
<div class="dog-calculator-modal-content">
|
||||
<span class="dog-calculator-modal-close" id="shareModalClose">×</span>
|
||||
<h3>Share Calculator</h3>
|
||||
<div class="dog-calculator-share-buttons">
|
||||
<button class="dog-calculator-share-btn dog-calculator-share-facebook plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-facebook" id="shareFacebook">
|
||||
Facebook
|
||||
</button>
|
||||
<button class="dog-calculator-share-btn dog-calculator-share-twitter plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-twitter" id="shareTwitter">
|
||||
Twitter
|
||||
</button>
|
||||
<button class="dog-calculator-share-btn dog-calculator-share-linkedin plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-linkedin" id="shareLinkedIn">
|
||||
LinkedIn
|
||||
</button>
|
||||
<button class="dog-calculator-share-btn dog-calculator-share-email plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-email" id="shareEmail">
|
||||
Email
|
||||
</button>
|
||||
<button class="dog-calculator-share-btn dog-calculator-share-copy plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-copy-link" id="shareCopy">
|
||||
Copy Link
|
||||
</button>
|
||||
</div>
|
||||
<div class="dog-calculator-share-url">
|
||||
<input type="text" id="shareUrl" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@ -15,6 +15,9 @@
|
||||
this.maxFoodSources = CALCULATOR_CONFIG.maxFoodSources;
|
||||
this.mealsPerDay = 2;
|
||||
this.showPerMeal = false;
|
||||
// Kayafied reference source tracking
|
||||
this.kibbleRefId = null;
|
||||
this.gcRefId = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
@ -69,8 +72,45 @@
|
||||
|
||||
// Food Source Management Methods
|
||||
initializeFoodSources() {
|
||||
this.addFoodSource();
|
||||
// Seed three sources for Kaya's transition
|
||||
const gc = {
|
||||
id: this.generateFoodSourceId(),
|
||||
name: 'Fred & Felia, gently cooked',
|
||||
energy: '115',
|
||||
energyUnit: 'kcal100g',
|
||||
percentage: 5,
|
||||
isLocked: false
|
||||
};
|
||||
this.foodSources.push(gc);
|
||||
this.renderFoodSource(gc);
|
||||
this.gcRefId = gc.id;
|
||||
|
||||
const kibble = {
|
||||
id: this.generateFoodSourceId(),
|
||||
name: 'Eukanuba, kibble',
|
||||
energy: '372',
|
||||
energyUnit: 'kcal100g',
|
||||
percentage: 95,
|
||||
isLocked: false
|
||||
};
|
||||
this.foodSources.push(kibble);
|
||||
this.renderFoodSource(kibble);
|
||||
this.kibbleRefId = kibble.id;
|
||||
|
||||
const treats = {
|
||||
id: this.generateFoodSourceId(),
|
||||
name: 'Treats',
|
||||
energy: '',
|
||||
energyUnit: 'kcal100g',
|
||||
percentage: 0,
|
||||
isLocked: false
|
||||
};
|
||||
this.foodSources.push(treats);
|
||||
this.renderFoodSource(treats);
|
||||
|
||||
this.updateAddButton();
|
||||
this.updateRemoveButtons();
|
||||
this.refreshAllPercentageUI();
|
||||
}
|
||||
|
||||
addFoodSource() {
|
||||
@ -105,6 +145,9 @@
|
||||
if (index === -1) return;
|
||||
|
||||
this.foodSources.splice(index, 1);
|
||||
// If reference IDs were removed, clear them
|
||||
if (this.kibbleRefId === id) this.kibbleRefId = null;
|
||||
if (this.gcRefId === id) this.gcRefId = null;
|
||||
|
||||
// Remove the DOM element
|
||||
const element = document.getElementById(`foodSource-${id}`);
|
||||
@ -118,6 +161,7 @@
|
||||
this.updateAddButton();
|
||||
this.updateRemoveButtons();
|
||||
this.refreshAllPercentageUI();
|
||||
this.updateCalorieCalculations();
|
||||
}
|
||||
|
||||
generateFoodSourceId() {
|
||||
@ -589,6 +633,10 @@
|
||||
if (energyInput) {
|
||||
energyInput.addEventListener('input', () => {
|
||||
this.updateFoodSourceData(id, 'energy', energyInput.value);
|
||||
// If kibble reference changed, recompute daily target
|
||||
if (id === this.kibbleRefId) {
|
||||
this.updateCalorieCalculations();
|
||||
}
|
||||
|
||||
// Auto-select cups when entering energy for kcal/cup
|
||||
const foodSource = this.foodSources.find(fs => fs.id === id);
|
||||
@ -626,6 +674,9 @@
|
||||
if (energyUnitSelect) {
|
||||
energyUnitSelect.addEventListener('change', () => {
|
||||
this.updateFoodSourceData(id, 'energyUnit', energyUnitSelect.value);
|
||||
if (id === this.kibbleRefId) {
|
||||
this.updateCalorieCalculations();
|
||||
}
|
||||
|
||||
// Auto-select the most appropriate unit based on energy unit
|
||||
const unitSelect = document.getElementById('unit');
|
||||
@ -803,6 +854,7 @@
|
||||
bindEvents() {
|
||||
const weightInput = document.getElementById('weight');
|
||||
const dogTypeSelect = document.getElementById('dogType');
|
||||
const ageInput = document.getElementById('ageMonths');
|
||||
const daysInput = document.getElementById('days');
|
||||
const unitSelect = document.getElementById('unit');
|
||||
const unitToggle = document.getElementById('unitToggle');
|
||||
@ -814,6 +866,12 @@
|
||||
}
|
||||
|
||||
if (dogTypeSelect) dogTypeSelect.addEventListener('change', () => this.updateCalorieCalculations());
|
||||
|
||||
// Kayafied: age input drives energy target
|
||||
if (ageInput) {
|
||||
ageInput.addEventListener('input', () => this.updateCalorieCalculations());
|
||||
ageInput.addEventListener('blur', () => this.updateCalorieCalculations());
|
||||
}
|
||||
|
||||
if (daysInput) {
|
||||
daysInput.addEventListener('input', () => {
|
||||
@ -1158,57 +1216,86 @@
|
||||
}
|
||||
|
||||
updateCalorieCalculations() {
|
||||
const dogTypeSelect = document.getElementById('dogType');
|
||||
const calorieResults = document.getElementById('calorieResults');
|
||||
const rerValue = document.getElementById('rerValue');
|
||||
const merValue = document.getElementById('merValue');
|
||||
// Kaya-specific: compute daily kcal target from age + kibble energy
|
||||
const ageInput = document.getElementById('ageMonths');
|
||||
const ageClampNote = document.getElementById('ageClampNote');
|
||||
|
||||
if (!dogTypeSelect || !calorieResults || !rerValue || !merValue) {
|
||||
// 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;
|
||||
return;
|
||||
}
|
||||
|
||||
const weightKg = this.getWeightInKg();
|
||||
const dogTypeFactor = dogTypeSelect.value;
|
||||
|
||||
this.showError('weightError', false);
|
||||
|
||||
if (!weightKg || weightKg < 0.1) {
|
||||
const weightInput = document.getElementById('weight');
|
||||
if (weightInput && weightInput.value) this.showError('weightError', true);
|
||||
calorieResults.style.display = 'none';
|
||||
let age = parseFloat(ageInput.value);
|
||||
if (isNaN(age)) {
|
||||
this.currentMER = 0;
|
||||
this.currentMERMin = 0;
|
||||
this.currentMERMax = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dogTypeFactor) {
|
||||
calorieResults.style.display = 'none';
|
||||
// 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'); }
|
||||
|
||||
// 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 factor = parseFloat(dogTypeFactor);
|
||||
|
||||
const rer = this.calculateRER(weightKg);
|
||||
const mer = this.calculateMER(rer, factor);
|
||||
|
||||
// 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';
|
||||
|
||||
// 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';
|
||||
}
|
||||
calorieResults.style.display = 'block';
|
||||
|
||||
const kcalPerGram = kibbleEnergyPer100g / 100.0;
|
||||
const dailyKcal = kibbleGrams * kcalPerGram;
|
||||
this.currentMER = dailyKcal;
|
||||
this.currentMERMin = dailyKcal;
|
||||
this.currentMERMax = dailyKcal;
|
||||
|
||||
this.updateFoodCalculations();
|
||||
}
|
||||
|
||||
// Kaya: monthly table with interpolation between months
|
||||
getKayaKibbleGramsForAge(ageMonths) {
|
||||
// Precomputed monthly values for 30 kg (g/day)
|
||||
const table = {
|
||||
2: 250,
|
||||
3: 330,
|
||||
4: 365,
|
||||
5: 382,
|
||||
6: 400,
|
||||
7: 405,
|
||||
8: 410,
|
||||
9: 410,
|
||||
10: 410,
|
||||
11: 408,
|
||||
12: 405
|
||||
};
|
||||
// Exact integer month
|
||||
const lower = Math.floor(ageMonths);
|
||||
const upper = Math.ceil(ageMonths);
|
||||
if (lower === upper) return table[lower] || 0;
|
||||
// Linear interpolation between bounds
|
||||
const lowerVal = table[lower] || 0;
|
||||
const upperVal = table[upper] || 0;
|
||||
const t = (ageMonths - lower) / (upper - lower);
|
||||
return lowerVal + (upperVal - lowerVal) * t;
|
||||
}
|
||||
|
||||
updateCupsButtonState() {
|
||||
const cupsButton = document.getElementById('cupsButton');
|
||||
if (!cupsButton) return;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user