271 lines
8.8 KiB
JavaScript
271 lines
8.8 KiB
JavaScript
|
|
/**
|
|||
|
|
* Dog Calorie Calculator - Shared Core Logic
|
|||
|
|
* This file contains all the business logic, calculations, and configurations
|
|||
|
|
* that are shared between the iframe and widget versions.
|
|||
|
|
*
|
|||
|
|
* By Canine Nutrition and Wellness
|
|||
|
|
* https://caninenutritionandwellness.com
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// Core configuration and constants
|
|||
|
|
const DOG_CALCULATOR_CONFIG = {
|
|||
|
|
// Dog type factors and labels
|
|||
|
|
dogTypes: [
|
|||
|
|
{ value: "3.0", label: "Puppy (0-4 months)" },
|
|||
|
|
{ value: "2.0", label: "Puppy (4 months - adult)" },
|
|||
|
|
{ value: "1.2", label: "Adult - inactive/obese" },
|
|||
|
|
{ value: "1.6", label: "Adult (neutered/spayed) - average activity" },
|
|||
|
|
{ value: "1.8", label: "Adult (intact) - average activity" },
|
|||
|
|
{ value: "1.0", label: "Adult - weight loss" },
|
|||
|
|
{ value: "1.7", label: "Adult - weight gain" },
|
|||
|
|
{ value: "2.0", label: "Working dog - light work" },
|
|||
|
|
{ value: "3.0", label: "Working dog - moderate work" },
|
|||
|
|
{ value: "5.0", label: "Working dog - heavy work" },
|
|||
|
|
{ value: "1.1", label: "Senior dog" }
|
|||
|
|
],
|
|||
|
|
|
|||
|
|
// Energy unit configurations
|
|||
|
|
energyUnits: [
|
|||
|
|
{ value: "kcal100g", label: "kcal/100g" },
|
|||
|
|
{ value: "kcalkg", label: "kcal/kg" },
|
|||
|
|
{ value: "kcalcup", label: "kcal/cup" },
|
|||
|
|
{ value: "kcalcan", label: "kcal/can" }
|
|||
|
|
],
|
|||
|
|
|
|||
|
|
// Unit options for different measurement systems
|
|||
|
|
units: {
|
|||
|
|
metric: [
|
|||
|
|
{ value: "g", label: "grams (g)" },
|
|||
|
|
{ value: "kg", label: "kilograms (kg)" },
|
|||
|
|
{ value: "oz", label: "ounces (oz)" },
|
|||
|
|
{ value: "lb", label: "pounds (lb)" }
|
|||
|
|
],
|
|||
|
|
imperial: [
|
|||
|
|
{ value: "oz", label: "ounces (oz)" },
|
|||
|
|
{ value: "lb", label: "pounds (lb)" },
|
|||
|
|
{ value: "g", label: "grams (g)" },
|
|||
|
|
{ value: "kg", label: "kilograms (kg)" }
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// Conversion factors
|
|||
|
|
conversions: {
|
|||
|
|
lbToKg: 2.20462,
|
|||
|
|
gToOz: 28.3495,
|
|||
|
|
gToLb: 453.592,
|
|||
|
|
gToKg: 1000
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// Energy unit conversion factors to kcal/100g
|
|||
|
|
energyConversions: {
|
|||
|
|
kcal100g: 1,
|
|||
|
|
kcalkg: 0.1, // 1 kg = 10 × 100g
|
|||
|
|
kcalcup: 0.833, // Assume 1 cup ≈ 120g for dry dog food
|
|||
|
|
kcalcan: 0.222 // Assume 1 can ≈ 450g for wet dog food
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// Validation limits for energy units
|
|||
|
|
energyLimits: {
|
|||
|
|
kcal100g: { min: 1, max: 1000 },
|
|||
|
|
kcalkg: { min: 10, max: 10000 },
|
|||
|
|
kcalcup: { min: 50, max: 1000 },
|
|||
|
|
kcalcan: { min: 100, max: 2000 }
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Core calculator functions
|
|||
|
|
const DogCalculatorCore = {
|
|||
|
|
/**
|
|||
|
|
* Calculate Resting Energy Requirement (RER)
|
|||
|
|
* Formula: 70 × (weight in kg)^0.75
|
|||
|
|
*/
|
|||
|
|
calculateRER(weightKg) {
|
|||
|
|
if (!weightKg || weightKg <= 0) return 0;
|
|||
|
|
return 70 * Math.pow(weightKg, 0.75);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Calculate Maintenance Energy Requirement (MER)
|
|||
|
|
* Formula: RER × activity factor
|
|||
|
|
*/
|
|||
|
|
calculateMER(rer, factor) {
|
|||
|
|
if (!rer || !factor || factor <= 0) return 0;
|
|||
|
|
return rer * factor;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Convert weight to kg regardless of input unit
|
|||
|
|
*/
|
|||
|
|
convertWeightToKg(weight, isImperial) {
|
|||
|
|
if (!weight || weight <= 0) return null;
|
|||
|
|
return isImperial ? weight / DOG_CALCULATOR_CONFIG.conversions.lbToKg : weight;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Convert weight from kg to display unit
|
|||
|
|
*/
|
|||
|
|
convertWeightFromKg(weightKg, isImperial) {
|
|||
|
|
if (!weightKg || weightKg <= 0) return null;
|
|||
|
|
return isImperial ? weightKg * DOG_CALCULATOR_CONFIG.conversions.lbToKg : weightKg;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Convert food energy to kcal/100g for calculations
|
|||
|
|
*/
|
|||
|
|
convertFoodEnergyTo100g(energy, unit) {
|
|||
|
|
if (!energy || energy <= 0) return null;
|
|||
|
|
const factor = DOG_CALCULATOR_CONFIG.energyConversions[unit];
|
|||
|
|
return factor ? energy * factor : energy;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Convert food amounts between units
|
|||
|
|
*/
|
|||
|
|
convertUnits(grams, targetUnit) {
|
|||
|
|
if (!grams || grams <= 0) return 0;
|
|||
|
|
|
|||
|
|
switch (targetUnit) {
|
|||
|
|
case 'kg':
|
|||
|
|
return grams / DOG_CALCULATOR_CONFIG.conversions.gToKg;
|
|||
|
|
case 'oz':
|
|||
|
|
return grams / DOG_CALCULATOR_CONFIG.conversions.gToOz;
|
|||
|
|
case 'lb':
|
|||
|
|
return grams / DOG_CALCULATOR_CONFIG.conversions.gToLb;
|
|||
|
|
default:
|
|||
|
|
return grams;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Calculate daily food amount in grams
|
|||
|
|
*/
|
|||
|
|
calculateDailyFoodGrams(merCalories, energyPer100g) {
|
|||
|
|
if (!merCalories || !energyPer100g || energyPer100g <= 0) return 0;
|
|||
|
|
return (merCalories / energyPer100g) * 100;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Validate input values
|
|||
|
|
*/
|
|||
|
|
validateInput(value, min = 0, isInteger = false) {
|
|||
|
|
const num = parseFloat(value);
|
|||
|
|
if (isNaN(num) || num < min) return false;
|
|||
|
|
if (isInteger && !Number.isInteger(num)) return false;
|
|||
|
|
return true;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Validate energy input based on unit
|
|||
|
|
*/
|
|||
|
|
validateEnergyInput(energy, unit) {
|
|||
|
|
if (!energy || !unit) return false;
|
|||
|
|
const limits = DOG_CALCULATOR_CONFIG.energyLimits[unit];
|
|||
|
|
if (!limits) return false;
|
|||
|
|
|
|||
|
|
const num = parseFloat(energy);
|
|||
|
|
return !isNaN(num) && num >= limits.min && num <= limits.max;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Format number for display
|
|||
|
|
*/
|
|||
|
|
formatNumber(num, decimals = 0) {
|
|||
|
|
if (typeof num !== 'number' || isNaN(num)) return '0';
|
|||
|
|
|
|||
|
|
if (decimals === 0) {
|
|||
|
|
return Math.round(num).toString();
|
|||
|
|
}
|
|||
|
|
return num.toFixed(decimals).replace(/\.?0+$/, '');
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get theme from URL parameters
|
|||
|
|
*/
|
|||
|
|
getThemeFromURL() {
|
|||
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|||
|
|
const theme = urlParams.get('theme');
|
|||
|
|
return ['light', 'dark', 'system'].includes(theme) ? theme : 'system';
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get scale from URL parameters
|
|||
|
|
*/
|
|||
|
|
getScaleFromURL() {
|
|||
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|||
|
|
const scale = parseFloat(urlParams.get('scale'));
|
|||
|
|
return (!isNaN(scale) && scale >= 0.5 && scale <= 2.0) ? scale : 1.0;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Generate embed codes
|
|||
|
|
*/
|
|||
|
|
generateEmbedCodes(baseUrl) {
|
|||
|
|
const scriptTag = `<script src="${baseUrl}/dog-food-calculator-widget.js"></script>`;
|
|||
|
|
const divTag = `<div id="dog-calorie-calculator"></div>`;
|
|||
|
|
const widgetCode = scriptTag + '\n' + divTag;
|
|||
|
|
|
|||
|
|
const iframeCode = `<iframe src="${baseUrl}/iframe.html" width="100%" height="600" frameborder="0" title="Dog Calorie Calculator"></iframe>`;
|
|||
|
|
|
|||
|
|
return { widgetCode, iframeCode };
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Share URLs for different platforms
|
|||
|
|
*/
|
|||
|
|
generateShareUrls(currentUrl) {
|
|||
|
|
const encodedUrl = encodeURIComponent(currentUrl);
|
|||
|
|
const text = encodeURIComponent('Check out this useful dog calorie calculator!');
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,
|
|||
|
|
twitter: `https://twitter.com/intent/tweet?url=${encodedUrl}&text=${text}`,
|
|||
|
|
linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
|
|||
|
|
email: `mailto:?subject=${encodeURIComponent('Dog Calorie Calculator')}&body=${encodeURIComponent('Check out this useful dog calorie calculator: ' + currentUrl)}`
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Copy text to clipboard with fallback
|
|||
|
|
*/
|
|||
|
|
async copyToClipboard(text, button) {
|
|||
|
|
if (!text || !button) return false;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
await navigator.clipboard.writeText(text);
|
|||
|
|
const originalText = button.textContent;
|
|||
|
|
button.textContent = 'Copied!';
|
|||
|
|
button.classList.add('copied');
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
button.textContent = originalText;
|
|||
|
|
button.classList.remove('copied');
|
|||
|
|
}, 2000);
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
} catch (err) {
|
|||
|
|
// Fallback for older browsers
|
|||
|
|
try {
|
|||
|
|
const textArea = document.createElement('textarea');
|
|||
|
|
textArea.value = text;
|
|||
|
|
textArea.style.position = 'fixed';
|
|||
|
|
textArea.style.opacity = '0';
|
|||
|
|
document.body.appendChild(textArea);
|
|||
|
|
textArea.select();
|
|||
|
|
document.execCommand('copy');
|
|||
|
|
document.body.removeChild(textArea);
|
|||
|
|
return true;
|
|||
|
|
} catch (fallbackErr) {
|
|||
|
|
console.error('Copy to clipboard failed:', fallbackErr);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Export for different module systems
|
|||
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|||
|
|
module.exports = { DOG_CALCULATOR_CONFIG, DogCalculatorCore };
|
|||
|
|
} else if (typeof window !== 'undefined') {
|
|||
|
|
window.DOG_CALCULATOR_CONFIG = DOG_CALCULATOR_CONFIG;
|
|||
|
|
window.DogCalculatorCore = DogCalculatorCore;
|
|||
|
|
}
|