Compare commits
No commits in common. "clients/kaya" and "dev" have entirely different histories.
clients/ka
...
dev
27
.gitignore
vendored
27
.gitignore
vendored
@ -1,27 +0,0 @@
|
|||||||
# Dependencies
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Temporary files
|
|
||||||
*.tmp
|
|
||||||
*.temp
|
|
||||||
|
|
||||||
# Personal files
|
|
||||||
theme.scss
|
|
||||||
_variables.scss
|
|
||||||
reference.png
|
|
||||||
better.png
|
|
||||||
START_PROMPT.md
|
|
||||||
CLAUDE.md
|
|
||||||
CLAUDE_V2.md
|
|
||||||
math.md
|
|
||||||
vetcalculators/
|
|
||||||
*.js.backup*
|
|
||||||
backup/
|
|
||||||
292
README.md
292
README.md
@ -1,292 +0,0 @@
|
|||||||
# 🐕 Sundog Dog Food Calorie Calculator
|
|
||||||
|
|
||||||
A professional veterinary nutrition tool for calculating dogs' daily calorie requirements and food amounts. Features advanced multi-food source management, percentage locking, and detailed food amount breakdowns. Distributed as a standalone page; third‑party embedding is no longer supported.
|
|
||||||
|
|
||||||
**By [Canine Nutrition and Wellness](https://caninenutritionandwellness.com)**
|
|
||||||
|
|
||||||
## ✨ Features
|
|
||||||
|
|
||||||
### Core Calculation Features
|
|
||||||
- **Accurate Calculations**: Uses veterinary-standard RER formula: `70 × (weight in kg)^0.75`
|
|
||||||
- **Multiple Activity Levels**: 11 different dog types and activity factors
|
|
||||||
- **Multi-Region Support**: kcal/100g (EU/UK), kcal/kg, kcal/cup, kcal/can (US/Canada)
|
|
||||||
- **Unit Conversion**: Automatic conversion to grams/kg/oz/lb for food amounts
|
|
||||||
- **Smart Unit Selection**: Auto-selects grams for metric and ounces for imperial systems
|
|
||||||
- **Unit Selection Buttons**: Intuitive button interface for choosing display units (g/kg/oz/lb)
|
|
||||||
|
|
||||||
### Multi-Food Source Management
|
|
||||||
- **Multiple Food Sources**: Add up to 5 different food sources per diet plan
|
|
||||||
- **Percentage System**: Distribute diet percentages across multiple foods with real-time validation
|
|
||||||
- **Percentage Locking**: Lock specific food source percentages to maintain fixed ratios
|
|
||||||
- **Smart Redistribution**: Automatic percentage rebalancing when sources are added/removed
|
|
||||||
- **Editable Food Names**: Click-to-edit food source names (e.g., "Morning Kibble", "Evening Wet Food")
|
|
||||||
|
|
||||||
### Food Amount Breakdown
|
|
||||||
- **Individual Food Amounts**: See exact amounts needed for each food source
|
|
||||||
- **Per-Food Calculations**: Calculate specific quantities for different food types
|
|
||||||
- **Total Summary**: Combined totals with clear breakdown by food source
|
|
||||||
- **Lock Indicators**: Visual indicators showing which percentages are locked
|
|
||||||
|
|
||||||
### User Experience
|
|
||||||
- **Scalable UI**: Resize from 50% to 200% via query params
|
|
||||||
- **Theme Support**: Light, dark, and system themes
|
|
||||||
- **Responsive Design**: Mobile-first, optimized layouts for all devices
|
|
||||||
- **Accessibility**: Full keyboard navigation and screen reader support
|
|
||||||
|
|
||||||
### Brand & Integration
|
|
||||||
- **Brand Integration**: Uses Canine Nutrition and Wellness color scheme
|
|
||||||
- **Professional Design**: Clean, veterinary-grade interface
|
|
||||||
- **Brand Protection**: Complete iframe isolation option
|
|
||||||
|
|
||||||
## 🚀 Usage
|
|
||||||
|
|
||||||
- Open `iframe.html` locally, or host it as a standalone page on your site.
|
|
||||||
- Embedding is allowed only on these domains: `caninenutritionandwellness.com`, `www.caninenutritionandwellness.com`.
|
|
||||||
- Use an iframe to embed on your site, for example:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<iframe
|
|
||||||
src="https://embed.caninenutritionandwellness.com/dog-calorie-calculator/iframe.html?theme=light&scale=0.8"
|
|
||||||
width="100%" height="640" frameborder="0" title="Dog Calorie Calculator">
|
|
||||||
</iframe>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🍽️ Multi-Food Source Usage
|
|
||||||
|
|
||||||
The calculator supports complex feeding plans with multiple food sources, perfect for mixed diets combining dry food, wet food, treats, and supplements.
|
|
||||||
|
|
||||||
### Basic Multi-Food Workflow
|
|
||||||
|
|
||||||
1. **Start with one food source** - Calculator begins with a single "Food Source 1"
|
|
||||||
2. **Add more sources** - Click "Add another food source" (up to 5 total)
|
|
||||||
3. **Customize names** - Click any food source name to edit (e.g., "Morning Kibble", "Evening Wet Food")
|
|
||||||
4. **Set energy content** - Enter kcal values and select appropriate units for each food
|
|
||||||
5. **Adjust percentages** - Use sliders or input fields to distribute diet percentages
|
|
||||||
6. **Lock percentages** - Click 🔒 to lock specific food source percentages
|
|
||||||
7. **Get individual amounts** - See exact quantities needed for each food source
|
|
||||||
|
|
||||||
### Percentage System Features
|
|
||||||
|
|
||||||
- **Real-time Validation**: Percentages always total exactly 100%
|
|
||||||
- **Smart Redistribution**: When you change one percentage, others adjust automatically
|
|
||||||
- **Percentage Locking**: Lock specific sources to maintain fixed ratios
|
|
||||||
- **Visual Feedback**: Lock indicators show which percentages are fixed
|
|
||||||
- **Bulletproof Logic**: Prevents impossible states (negative percentages, >100% totals)
|
|
||||||
|
|
||||||
### Example Usage Scenarios
|
|
||||||
|
|
||||||
**Mixed Diet Example:**
|
|
||||||
```
|
|
||||||
Royal Canin Dry Food → 70% (locked)
|
|
||||||
Blue Buffalo Wet Food → 25%
|
|
||||||
Training Treats → 5%
|
|
||||||
```
|
|
||||||
|
|
||||||
**Meal-Based Planning:**
|
|
||||||
```
|
|
||||||
Morning Kibble → 50%
|
|
||||||
Evening Wet Food → 30%
|
|
||||||
Midday Snacks → 20%
|
|
||||||
```
|
|
||||||
|
|
||||||
**Transition Diet:**
|
|
||||||
```
|
|
||||||
Old Food (reducing) → 25%
|
|
||||||
New Food (increasing) → 75% (locked)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Food Amount Breakdown
|
|
||||||
|
|
||||||
The calculator provides detailed breakdowns showing:
|
|
||||||
- **Individual amounts** for each food source
|
|
||||||
- **Percentage distribution** with visual indicators
|
|
||||||
- **Lock status** for each food source
|
|
||||||
- **Total combined amount** for the specified number of days
|
|
||||||
- **Unit conversion** (grams, kg, oz, lb) for all amounts
|
|
||||||
|
|
||||||
## ⚙️ Configuration Options
|
|
||||||
|
|
||||||
### Theme Options
|
|
||||||
Choose from three themes:
|
|
||||||
- `light` - Light theme
|
|
||||||
- `dark` - Dark theme
|
|
||||||
- `system` - Follows user's OS preference (default)
|
|
||||||
|
|
||||||
### Scale Options
|
|
||||||
Resize the interface from 50% to 200%:
|
|
||||||
- Range: `0.5` to `2.0`
|
|
||||||
- Default: `1.0` (100% size)
|
|
||||||
- Examples: `0.8` (80%), `1.2` (120%), `1.5` (150%)
|
|
||||||
|
|
||||||
### Food Energy Units
|
|
||||||
Support for regional differences:
|
|
||||||
- `kcal/100g` - European/UK standard (default)
|
|
||||||
- `kcal/kg` - North American standard
|
|
||||||
- `kcal/cup` - US/Canada dry food
|
|
||||||
- `kcal/can` - US/Canada wet food
|
|
||||||
|
|
||||||
<!-- Embedding and external widget initialization are no longer supported. -->
|
|
||||||
|
|
||||||
## 🛠️ Development
|
|
||||||
|
|
||||||
### Build System
|
|
||||||
This project uses an organized source layout compiled into a single page:
|
|
||||||
|
|
||||||
- **Sources**: `src/` (HTML, CSS, JS modules)
|
|
||||||
- **Build Script**: `build.js` - Generates `iframe.html` from `src/`
|
|
||||||
- **Output**: `iframe.html` - Standalone calculator page
|
|
||||||
|
|
||||||
### Development Workflow
|
|
||||||
1. **Make changes in `src/`** - Edit calculations, design, layout, or functionality
|
|
||||||
2. **Run the build script**: `node build.js`
|
|
||||||
3. **Done!** - `iframe.html` is regenerated
|
|
||||||
|
|
||||||
### Why This Approach?
|
|
||||||
- ✅ **Single Source of Truth** - No need to maintain two separate files
|
|
||||||
- ✅ **Identical Functionality** - Widget matches iframe exactly
|
|
||||||
- ✅ **Easy Maintenance** - Edit once, deploy everywhere
|
|
||||||
- ✅ **No Sync Issues** - Build script ensures consistency
|
|
||||||
|
|
||||||
### Build Script Features
|
|
||||||
- Compiles CSS, HTML, and JavaScript from `src/`
|
|
||||||
- Preserves all functionality including unit switching and calculations
|
|
||||||
- Maintains theme and scale support via URL query parameters
|
|
||||||
|
|
||||||
## 📁 Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
├── src/ # ✏️ Source (HTML/CSS/JS)
|
|
||||||
├── build.js # 🔧 Build script - Run after changes
|
|
||||||
├── iframe.html # 📦 Generated standalone page
|
|
||||||
└── README.md # 📖 This file
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎨 Brand Integration
|
|
||||||
|
|
||||||
The calculator uses your brand's color system:
|
|
||||||
- **Primary**: `#f19a5f` (Coral)
|
|
||||||
- **Secondary**: `#9f5999` (Purple)
|
|
||||||
- **Text**: `#6f3f6d` (Deep Purple)
|
|
||||||
- **Backgrounds**: Light purple tints
|
|
||||||
- **Font**: Montserrat
|
|
||||||
|
|
||||||
Colors automatically adapt to light/dark themes via CSS custom properties.
|
|
||||||
|
|
||||||
## 📊 Dog Activity Factors
|
|
||||||
|
|
||||||
| Dog Type | Factor | Use Case |
|
|
||||||
|----------|--------|----------|
|
|
||||||
| Puppy (0-4 months) | 3.0 | Rapid growth phase |
|
|
||||||
| Puppy (4 months - adult) | 2.0 | Continued growth |
|
|
||||||
| Adult - inactive/obese | 1.2 | Weight management |
|
|
||||||
| Adult (neutered/spayed) | 1.6 | Typical house pet |
|
|
||||||
| Adult (intact) | 1.8 | Unaltered adult |
|
|
||||||
| Adult - weight loss | 1.0 | Calorie restriction |
|
|
||||||
| Adult - weight gain | 1.7 | Weight building |
|
|
||||||
| Working dog - light work | 2.0 | Light activity |
|
|
||||||
| Working dog - moderate work | 3.0 | Regular work |
|
|
||||||
| Working dog - heavy work | 5.0 | Intensive work |
|
|
||||||
| Senior dog | 1.1 | Reduced activity |
|
|
||||||
|
|
||||||
## 🔧 Technical Notes
|
|
||||||
- Standalone page with theme and scale controls via URL params.
|
|
||||||
- Embedding is allowlisted at runtime to your domains and should be enforced with server headers.
|
|
||||||
|
|
||||||
### Server Headers (required for robust enforcement)
|
|
||||||
Configure your server or CDN to send this header on `iframe.html`:
|
|
||||||
|
|
||||||
```
|
|
||||||
Content-Security-Policy: frame-ancestors https://caninenutritionandwellness.com https://www.caninenutritionandwellness.com;
|
|
||||||
```
|
|
||||||
|
|
||||||
Optional legacy header (deprecated but harmless as a supplement):
|
|
||||||
|
|
||||||
```
|
|
||||||
X-Frame-Options: SAMEORIGIN
|
|
||||||
```
|
|
||||||
|
|
||||||
If you serve the calculator from a subdomain (e.g., `embed.caninenutritionandwellness.com`) and embed it on the root domain, prefer the CSP `frame-ancestors` directive above.
|
|
||||||
|
|
||||||
## 🚀 Deployment Guide
|
|
||||||
|
|
||||||
### Deployment
|
|
||||||
Host `iframe.html` (e.g., on `embed.caninenutritionandwellness.com`) and embed via iframe on your approved domains.
|
|
||||||
|
|
||||||
## 🔒 Brand Protection
|
|
||||||
Embedding is disabled to protect branding and ensure consistent presentation.
|
|
||||||
|
|
||||||
## 📱 Mobile Optimization
|
|
||||||
|
|
||||||
- **Responsive breakpoints**: 576px (mobile), 850px (tablet)
|
|
||||||
- **Touch-friendly**: Larger tap targets on mobile
|
|
||||||
- **Input optimization**: Numeric keyboards for number inputs
|
|
||||||
- **Collapsible sections**: Better mobile space utilization
|
|
||||||
|
|
||||||
## 🧪 Testing
|
|
||||||
|
|
||||||
### Manual Testing Checklist
|
|
||||||
|
|
||||||
#### Core Functionality
|
|
||||||
- [ ] All dog type selections work
|
|
||||||
- [ ] Weight validation (minimum 0.1kg)
|
|
||||||
- [ ] RER/MER calculations accurate
|
|
||||||
- [ ] Unit conversions (g/kg/oz/lb) correct
|
|
||||||
- [ ] Theme switching (light/dark/system)
|
|
||||||
- [ ] Scale options (0.5x to 2.0x) work properly
|
|
||||||
|
|
||||||
#### Multi-Food Source Features
|
|
||||||
- [ ] Add food sources (up to 5 maximum)
|
|
||||||
- [ ] Remove food sources (minimum 1 maintained)
|
|
||||||
- [ ] Edit food source names (click-to-edit functionality)
|
|
||||||
- [ ] Food energy content validation per source
|
|
||||||
- [ ] Food energy unit selector per source (kcal/100g, kcal/kg, kcal/cup, kcal/can)
|
|
||||||
- [ ] Percentage slider adjustments work correctly
|
|
||||||
- [ ] Percentage input field validation
|
|
||||||
- [ ] Percentage locking/unlocking (🔒 icon)
|
|
||||||
- [ ] Smart percentage redistribution when sources change
|
|
||||||
- [ ] Total percentages always equal 100%
|
|
||||||
- [ ] Individual food amount calculations
|
|
||||||
- [ ] Food amount breakdown display
|
|
||||||
- [ ] Add button states ("Add another food source" vs "Maximum 5 sources reached")
|
|
||||||
|
|
||||||
#### User Interface
|
|
||||||
- [ ] Mobile responsive layout
|
|
||||||
- [ ] Collapsible section toggles
|
|
||||||
- [ ] Visual lock indicators display correctly
|
|
||||||
- [ ] Percentage badges and styling
|
|
||||||
- [ ] Branded footer link works
|
|
||||||
- [ ] Box shadows consistent across all sections
|
|
||||||
- [ ] Food source name alignment on mobile
|
|
||||||
- [ ] Proper input field sizing on mobile
|
|
||||||
|
|
||||||
### Browser Compatibility
|
|
||||||
- ✅ Chrome 90+
|
|
||||||
- ✅ Firefox 88+
|
|
||||||
- ✅ Safari 14+
|
|
||||||
- ✅ Edge 90+
|
|
||||||
- ✅ Mobile Safari (iOS 14+)
|
|
||||||
- ✅ Chrome Mobile (Android 10+)
|
|
||||||
|
|
||||||
## 🤝 Contributing
|
|
||||||
|
|
||||||
This tool is maintained by Canine Nutrition and Wellness. For suggestions or issues:
|
|
||||||
|
|
||||||
1. Test the issue on the demo page
|
|
||||||
2. Provide specific browser/device information
|
|
||||||
3. Include steps to reproduce
|
|
||||||
4. Suggest improvements based on veterinary nutrition standards
|
|
||||||
|
|
||||||
## 📄 License
|
|
||||||
|
|
||||||
© 2024 Canine Nutrition and Wellness. All rights reserved.
|
|
||||||
|
|
||||||
This calculator is provided for educational and professional use. The formulas are based on established veterinary nutrition standards. Always consult with a veterinary nutritionist for specific dietary recommendations.
|
|
||||||
|
|
||||||
## 🔗 Links
|
|
||||||
|
|
||||||
- **Website**: [caninenutritionandwellness.com](https://caninenutritionandwellness.com)
|
|
||||||
- **Standalone**: Open `iframe.html` in your browser
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Built with ❤️ for canine nutrition professionals**
|
|
||||||
336
build.js
336
build.js
@ -1,336 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dog Calculator Build System - ORGANIZED VERSION
|
|
||||||
*
|
|
||||||
* This build script generates iframe.html from organized source files
|
|
||||||
* in the src/ directory.
|
|
||||||
*
|
|
||||||
* Source structure:
|
|
||||||
* - src/index.html - HTML template
|
|
||||||
* - src/css/main.css - Main styles
|
|
||||||
* - src/css/themes.css - Theme-specific styles
|
|
||||||
* - src/js/config.js - Configuration constants
|
|
||||||
* - src/js/calculator.js - JavaScript functionality
|
|
||||||
*
|
|
||||||
* Output files:
|
|
||||||
* - iframe.html - Standalone calculator page
|
|
||||||
*
|
|
||||||
* Usage: node build.js
|
|
||||||
*/
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
console.log('🎯 Dog Calculator Build System - ORGANIZED VERSION');
|
|
||||||
console.log('');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read organized components from src directory
|
|
||||||
*/
|
|
||||||
function readSourceComponents() {
|
|
||||||
const srcDir = 'src';
|
|
||||||
|
|
||||||
// Check if src directory exists
|
|
||||||
if (!fs.existsSync(srcDir)) {
|
|
||||||
throw new Error('src directory not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read CSS files
|
|
||||||
const mainCssPath = path.join(srcDir, 'css', 'main.css');
|
|
||||||
const themesCssPath = path.join(srcDir, 'css', 'themes.css');
|
|
||||||
if (!fs.existsSync(mainCssPath)) {
|
|
||||||
throw new Error('src/css/main.css not found');
|
|
||||||
}
|
|
||||||
if (!fs.existsSync(themesCssPath)) {
|
|
||||||
throw new Error('src/css/themes.css not found');
|
|
||||||
}
|
|
||||||
const mainCss = fs.readFileSync(mainCssPath, 'utf8').trim();
|
|
||||||
const themesCss = fs.readFileSync(themesCssPath, 'utf8').trim();
|
|
||||||
const css = mainCss + '\n\n' + themesCss;
|
|
||||||
|
|
||||||
// Read HTML template
|
|
||||||
const htmlPath = path.join(srcDir, 'index.html');
|
|
||||||
if (!fs.existsSync(htmlPath)) {
|
|
||||||
throw new Error('src/index.html not found');
|
|
||||||
}
|
|
||||||
const html = fs.readFileSync(htmlPath, 'utf8').trim();
|
|
||||||
|
|
||||||
// Read JavaScript files
|
|
||||||
const configPath = path.join(srcDir, 'js', 'config.js');
|
|
||||||
const calculatorPath = path.join(srcDir, 'js', 'calculator.js');
|
|
||||||
if (!fs.existsSync(configPath)) {
|
|
||||||
throw new Error('src/js/config.js not found');
|
|
||||||
}
|
|
||||||
if (!fs.existsSync(calculatorPath)) {
|
|
||||||
throw new Error('src/js/calculator.js not found');
|
|
||||||
}
|
|
||||||
const config = fs.readFileSync(configPath, 'utf8').trim();
|
|
||||||
const calculator = fs.readFileSync(calculatorPath, 'utf8').trim();
|
|
||||||
const js = config + '\n\n' + calculator;
|
|
||||||
|
|
||||||
return { css, html, js };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Backup existing files
|
|
||||||
*/
|
|
||||||
function backupFiles() {
|
|
||||||
const backupDir = 'backup';
|
|
||||||
|
|
||||||
// Create backup directory if it doesn't exist
|
|
||||||
if (!fs.existsSync(backupDir)) {
|
|
||||||
fs.mkdirSync(backupDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
||||||
const filesToBackup = ['iframe.html'];
|
|
||||||
|
|
||||||
filesToBackup.forEach(file => {
|
|
||||||
if (fs.existsSync(file)) {
|
|
||||||
const backupName = path.join(backupDir, `${file}.backup-${timestamp}`);
|
|
||||||
fs.copyFileSync(file, backupName);
|
|
||||||
console.log(` 📦 Backed up ${file}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate iframe.html from modular components
|
|
||||||
*/
|
|
||||||
function generateIframe(css, html, js) {
|
|
||||||
const content = `<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Dog Calorie Calculator - Canine Nutrition and Wellness</title>
|
|
||||||
<style>
|
|
||||||
/* Sundog Dog Food Calorie Calculator Styles */
|
|
||||||
|
|
||||||
/* CSS Variables for theming */
|
|
||||||
${css}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
${html}
|
|
||||||
<script>
|
|
||||||
${js}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create production-ready widget JavaScript
|
|
||||||
*/
|
|
||||||
function createWidgetJS(css, html, js) {
|
|
||||||
// Transform the JavaScript from calculator.js to work as a widget
|
|
||||||
|
|
||||||
// Replace the iframe's DOMContentLoaded listener with widget initialization
|
|
||||||
let transformedJS = js
|
|
||||||
// Replace the iframe class name with widget class name
|
|
||||||
.replace(/class DogCalorieCalculator/g, 'class DogCalorieCalculatorWidget')
|
|
||||||
// Replace document.getElementById with scoped selectors within the widget
|
|
||||||
.replace(/document\.getElementById\('([^']+)'\)/g, 'this.container.querySelector(\'#$1\')')
|
|
||||||
// Replace direct document queries in the class with container-scoped queries
|
|
||||||
.replace(/document\.querySelector\(/g, 'this.container.querySelector(')
|
|
||||||
.replace(/document\.querySelectorAll\(/g, 'this.container.querySelectorAll(')
|
|
||||||
// Remove the DOMContentLoaded listener and class instantiation - we'll handle this in the widget wrapper
|
|
||||||
.replace(/document\.addEventListener\('DOMContentLoaded'.*?\n.*?new DogCalorieCalculator.*?\n.*?\}\);/s, '')
|
|
||||||
// Remove theme/scale assignments that would override widget options
|
|
||||||
.replace(/this\.theme = this\.getThemeFromURL\(\) \|\| CALCULATOR_CONFIG\.defaultTheme;/g, '')
|
|
||||||
.replace(/this\.scale = this\.getScaleFromURL\(\) \|\| CALCULATOR_CONFIG\.defaultScale;/g, '')
|
|
||||||
// Add widget initialization methods
|
|
||||||
.replace(/constructor\(\) \{/, `constructor(container, options = {}) {
|
|
||||||
this.container = container;
|
|
||||||
this.options = {
|
|
||||||
theme: options.theme || this.getThemeFromURL() || 'system',
|
|
||||||
scale: options.scale || this.getScaleFromURL() || 1.0,
|
|
||||||
...options
|
|
||||||
};
|
|
||||||
this.theme = this.options.theme;
|
|
||||||
this.scale = this.options.scale;`)
|
|
||||||
// Replace the init() method to inject HTML and apply widget settings
|
|
||||||
.replace(/init\(\) \{/, `init() {
|
|
||||||
// Inject the calculator HTML into the container
|
|
||||||
this.container.innerHTML = \`${html}\`;
|
|
||||||
|
|
||||||
// Apply widget-specific settings
|
|
||||||
this.applyTheme();
|
|
||||||
this.applyScale();
|
|
||||||
|
|
||||||
// Continue with original init logic`)
|
|
||||||
// Remove duplicate applyTheme/applyScale calls
|
|
||||||
.replace(/this\.applyTheme\(\);\s*\n\s*this\.applyScale\(\);\s*\n\s*this\.bindEvents/g, 'this.bindEvents');
|
|
||||||
|
|
||||||
// Add widget-specific methods before the class closing brace
|
|
||||||
transformedJS = transformedJS.replace(/(\s+)(\}\s*$)/, `$1
|
|
||||||
getThemeFromURL() {
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
const theme = urlParams.get('theme');
|
|
||||||
return ['light', 'dark', 'system'].includes(theme) ? theme : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getScaleFromURL() {
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
const scale = parseFloat(urlParams.get('scale'));
|
|
||||||
return (!isNaN(scale) && scale >= 0.5 && scale <= 2.0) ? scale : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
applyTheme() {
|
|
||||||
const calculatorContainer = this.container.querySelector('#dogCalculator');
|
|
||||||
if (calculatorContainer) {
|
|
||||||
// Remove existing theme classes
|
|
||||||
calculatorContainer.classList.remove('theme-light', 'theme-dark', 'theme-system');
|
|
||||||
// Add the selected theme class
|
|
||||||
if (['light', 'dark', 'system'].includes(this.theme)) {
|
|
||||||
calculatorContainer.classList.add('theme-' + this.theme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
applyScale() {
|
|
||||||
const scale = Math.max(0.5, Math.min(2.0, this.scale));
|
|
||||||
if (scale !== 1.0) {
|
|
||||||
this.container.style.transform = \`scale(\${scale})\`;
|
|
||||||
this.container.style.transformOrigin = 'top left';
|
|
||||||
}
|
|
||||||
}$2`);
|
|
||||||
|
|
||||||
// Add CSS variables to widget CSS
|
|
||||||
const widgetCSS = `/* Sundog Dog Food Calorie Calculator Styles */
|
|
||||||
|
|
||||||
/* CSS Variables for theming */
|
|
||||||
${css}`;
|
|
||||||
|
|
||||||
const widgetCode = `/**
|
|
||||||
* Dog Calorie Calculator Widget
|
|
||||||
* Embeddable JavaScript widget for websites
|
|
||||||
*
|
|
||||||
* THIS CODE IS AUTO-GENERATED FROM src/ FILES - DO NOT EDIT MANUALLY
|
|
||||||
* Edit files in src/ directory and run 'node build.js' to update
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* <script src="sundog-dog-food-calculator.js"></script>
|
|
||||||
* <div id="dog-calorie-calculator"></div>
|
|
||||||
*
|
|
||||||
* Or with options:
|
|
||||||
* <div id="dog-calorie-calculator" data-theme="dark" data-scale="1.2"></div>
|
|
||||||
*
|
|
||||||
* By Canine Nutrition and Wellness
|
|
||||||
* https://caninenutritionandwellness.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// Inject widget styles
|
|
||||||
const CSS_STYLES = \`${widgetCSS}\`;
|
|
||||||
|
|
||||||
function injectStyles() {
|
|
||||||
if (document.getElementById('dog-calculator-styles')) return;
|
|
||||||
|
|
||||||
const style = document.createElement('style');
|
|
||||||
style.id = 'dog-calculator-styles';
|
|
||||||
style.textContent = CSS_STYLES;
|
|
||||||
document.head.appendChild(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
// JavaScript from src/calculator.js (transformed for widget use)
|
|
||||||
${transformedJS}
|
|
||||||
|
|
||||||
// Auto-initialize widgets on page load
|
|
||||||
function initializeWidget() {
|
|
||||||
injectStyles();
|
|
||||||
|
|
||||||
const containers = document.querySelectorAll('#dog-calorie-calculator, .dog-calorie-calculator');
|
|
||||||
containers.forEach(container => {
|
|
||||||
if (container.dataset.initialized) return;
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
theme: container.dataset.theme || 'system',
|
|
||||||
scale: parseFloat(container.dataset.scale) || 1.0
|
|
||||||
};
|
|
||||||
|
|
||||||
new DogCalorieCalculatorWidget(container, options);
|
|
||||||
container.dataset.initialized = 'true';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize when DOM is ready
|
|
||||||
if (document.readyState === 'loading') {
|
|
||||||
document.addEventListener('DOMContentLoaded', initializeWidget);
|
|
||||||
} else {
|
|
||||||
initializeWidget();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export for manual initialization
|
|
||||||
window.DogCalorieCalculatorWidget = DogCalorieCalculatorWidget;
|
|
||||||
|
|
||||||
})();`;
|
|
||||||
|
|
||||||
return widgetCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main build function
|
|
||||||
*/
|
|
||||||
function build() {
|
|
||||||
try {
|
|
||||||
console.log('📖 Reading organized components from src/ directory...');
|
|
||||||
const { css, html, js } = readSourceComponents();
|
|
||||||
console.log(' ✅ src/index.html (' + html.split('\n').length + ' lines)');
|
|
||||||
console.log(' ✅ src/css/main.css + themes.css (' + css.split('\n').length + ' lines)');
|
|
||||||
console.log(' ✅ src/js/config.js + calculator.js (' + js.split('\n').length + ' lines)');
|
|
||||||
|
|
||||||
console.log('');
|
|
||||||
console.log('📦 Backing up existing files...');
|
|
||||||
backupFiles();
|
|
||||||
|
|
||||||
console.log('');
|
|
||||||
console.log('🏗️ Building output files...');
|
|
||||||
|
|
||||||
// Generate iframe.html
|
|
||||||
const iframeContent = generateIframe(css, html, js);
|
|
||||||
fs.writeFileSync('iframe.html', iframeContent);
|
|
||||||
console.log(' ✅ Generated iframe.html');
|
|
||||||
|
|
||||||
// Embeddable widget generation removed (embedding no longer supported)
|
|
||||||
|
|
||||||
console.log('');
|
|
||||||
console.log('🎉 Build completed successfully!');
|
|
||||||
console.log('');
|
|
||||||
console.log('📋 Summary:');
|
|
||||||
console.log(' Source structure:');
|
|
||||||
console.log(' • src/index.html - HTML structure');
|
|
||||||
console.log(' • src/css/main.css - Core styles');
|
|
||||||
console.log(' • src/css/themes.css - Theme variations');
|
|
||||||
console.log(' • src/js/config.js - Configuration');
|
|
||||||
console.log(' • src/js/calculator.js - Main logic');
|
|
||||||
console.log('');
|
|
||||||
console.log(' Generated files:');
|
|
||||||
console.log(' • iframe.html - Standalone calculator');
|
|
||||||
console.log('');
|
|
||||||
console.log('🔄 Your workflow:');
|
|
||||||
console.log(' 1. Edit organized files in src/');
|
|
||||||
console.log(' 2. Run: node build.js');
|
|
||||||
console.log(' 3. Output file is regenerated!');
|
|
||||||
console.log('');
|
|
||||||
console.log('💡 Clean, organized structure - easy to maintain!');
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Build failed:', error.message);
|
|
||||||
console.error(error.stack);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run build if called directly
|
|
||||||
if (require.main === module) {
|
|
||||||
build();
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { build };
|
|
||||||
495
calculator.js
Normal file
495
calculator.js
Normal file
@ -0,0 +1,495 @@
|
|||||||
|
/**
|
||||||
|
* Dog Calorie Calculator
|
||||||
|
* Calculates dog's daily calorie requirements and food amounts
|
||||||
|
* by Canine Nutrition and Wellness
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DogCalorieCalculator {
|
||||||
|
constructor() {
|
||||||
|
this.currentMER = 0;
|
||||||
|
this.isImperial = false;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.bindEvents();
|
||||||
|
this.updateUnitLabels();
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEvents() {
|
||||||
|
const weightInput = document.getElementById('weight');
|
||||||
|
const dogTypeSelect = document.getElementById('dogType');
|
||||||
|
const foodEnergyInput = document.getElementById('foodEnergy');
|
||||||
|
const daysInput = document.getElementById('days');
|
||||||
|
const unitSelect = document.getElementById('unit');
|
||||||
|
const unitToggle = document.getElementById('unitToggle');
|
||||||
|
|
||||||
|
if (weightInput) weightInput.addEventListener('input', () => this.updateCalorieCalculations());
|
||||||
|
if (weightInput) weightInput.addEventListener('blur', () => this.validateWeight());
|
||||||
|
|
||||||
|
if (dogTypeSelect) dogTypeSelect.addEventListener('change', () => this.updateCalorieCalculations());
|
||||||
|
|
||||||
|
if (foodEnergyInput) foodEnergyInput.addEventListener('input', () => this.updateFoodCalculations());
|
||||||
|
if (foodEnergyInput) foodEnergyInput.addEventListener('blur', () => this.validateFoodEnergy());
|
||||||
|
|
||||||
|
if (daysInput) daysInput.addEventListener('input', () => this.updateFoodCalculations());
|
||||||
|
if (daysInput) daysInput.addEventListener('blur', () => this.validateDays());
|
||||||
|
|
||||||
|
if (unitSelect) unitSelect.addEventListener('change', () => this.updateFoodCalculations());
|
||||||
|
|
||||||
|
if (unitToggle) unitToggle.addEventListener('change', () => this.toggleUnits());
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateRER(weightKg) {
|
||||||
|
return 70 * Math.pow(weightKg, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateMER(rer, factor) {
|
||||||
|
return rer * factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
showError(elementId, show = true) {
|
||||||
|
const errorElement = document.getElementById(elementId);
|
||||||
|
if (errorElement) {
|
||||||
|
if (show) {
|
||||||
|
errorElement.classList.remove('dog-calculator-hidden');
|
||||||
|
} else {
|
||||||
|
errorElement.classList.add('dog-calculator-hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
convertUnits(grams, unit) {
|
||||||
|
switch (unit) {
|
||||||
|
case 'kg':
|
||||||
|
return grams / 1000;
|
||||||
|
case 'oz':
|
||||||
|
return grams / 28.3495;
|
||||||
|
case 'lb':
|
||||||
|
return grams / 453.592;
|
||||||
|
default:
|
||||||
|
return grams;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formatNumber(num, decimals = 0) {
|
||||||
|
return num.toFixed(decimals).replace(/\.?0+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleUnits() {
|
||||||
|
const toggle = document.getElementById('unitToggle');
|
||||||
|
this.isImperial = toggle.checked;
|
||||||
|
|
||||||
|
this.updateUnitLabels();
|
||||||
|
this.convertExistingValues();
|
||||||
|
this.updateCalorieCalculations();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUnitLabels() {
|
||||||
|
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) {
|
||||||
|
metricLabel.classList.toggle('active', !this.isImperial);
|
||||||
|
imperialLabel.classList.toggle('active', this.isImperial);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isImperial) {
|
||||||
|
if (weightLabel) weightLabel.textContent = "Dog's Weight (lbs):";
|
||||||
|
if (weightInput) {
|
||||||
|
weightInput.placeholder = "Enter weight in lbs";
|
||||||
|
weightInput.min = "0.2"; // ~0.1kg in lbs
|
||||||
|
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"; // ~1 kcal/100g in kcal/oz
|
||||||
|
foodEnergyInput.step = "0.01";
|
||||||
|
}
|
||||||
|
if (unitSelect) {
|
||||||
|
unitSelect.innerHTML = `
|
||||||
|
<option value="oz">ounces (oz)</option>
|
||||||
|
<option value="lb">pounds (lb)</option>
|
||||||
|
<option value="g">grams (g)</option>
|
||||||
|
<option value="kg">kilograms (kg)</option>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (weightLabel) weightLabel.textContent = "Dog's Weight (kg):";
|
||||||
|
if (weightInput) {
|
||||||
|
weightInput.placeholder = "Enter weight in kg";
|
||||||
|
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>
|
||||||
|
<option value="oz">ounces (oz)</option>
|
||||||
|
<option value="lb">pounds (lb)</option>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
convertExistingValues() {
|
||||||
|
const weightInput = document.getElementById('weight');
|
||||||
|
const foodEnergyInput = document.getElementById('foodEnergy');
|
||||||
|
|
||||||
|
if (weightInput && weightInput.value) {
|
||||||
|
const currentWeight = parseFloat(weightInput.value);
|
||||||
|
if (!isNaN(currentWeight)) {
|
||||||
|
if (this.isImperial) {
|
||||||
|
// Convert kg to lbs
|
||||||
|
weightInput.value = this.formatNumber(currentWeight * 2.20462, 1);
|
||||||
|
} else {
|
||||||
|
// Convert lbs to kg
|
||||||
|
weightInput.value = this.formatNumber(currentWeight / 2.20462, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foodEnergyInput && foodEnergyInput.value) {
|
||||||
|
const currentEnergy = parseFloat(foodEnergyInput.value);
|
||||||
|
if (!isNaN(currentEnergy)) {
|
||||||
|
if (this.isImperial) {
|
||||||
|
// Convert kcal/100g to kcal/oz
|
||||||
|
foodEnergyInput.value = this.formatNumber(currentEnergy / 3.52739, 2);
|
||||||
|
} else {
|
||||||
|
// Convert kcal/oz to kcal/100g
|
||||||
|
foodEnergyInput.value = this.formatNumber(currentEnergy * 3.52739, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getWeightInKg() {
|
||||||
|
const weightInput = document.getElementById('weight');
|
||||||
|
if (!weightInput || !weightInput.value) return null;
|
||||||
|
|
||||||
|
const weight = parseFloat(weightInput.value);
|
||||||
|
if (isNaN(weight)) return null;
|
||||||
|
|
||||||
|
return this.isImperial ? weight / 2.20462 : weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFoodEnergyPer100g() {
|
||||||
|
const foodEnergyInput = document.getElementById('foodEnergy');
|
||||||
|
if (!foodEnergyInput || !foodEnergyInput.value) return null;
|
||||||
|
|
||||||
|
const energy = parseFloat(foodEnergyInput.value);
|
||||||
|
if (isNaN(energy)) return null;
|
||||||
|
|
||||||
|
return this.isImperial ? energy * 3.52739 : energy;
|
||||||
|
}
|
||||||
|
|
||||||
|
validateWeight() {
|
||||||
|
const weight = document.getElementById('weight')?.value;
|
||||||
|
if (weight && !this.validateInput(weight, 0.1)) {
|
||||||
|
this.showError('weightError', true);
|
||||||
|
} else {
|
||||||
|
this.showError('weightError', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateFoodEnergy() {
|
||||||
|
const energy = document.getElementById('foodEnergy')?.value;
|
||||||
|
if (energy && !this.validateInput(energy, 1)) {
|
||||||
|
this.showError('foodEnergyError', true);
|
||||||
|
} else {
|
||||||
|
this.showError('foodEnergyError', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateDays() {
|
||||||
|
const days = document.getElementById('days')?.value;
|
||||||
|
if (days && !this.validateInput(days, 1, true)) {
|
||||||
|
this.showError('daysError', true);
|
||||||
|
} else {
|
||||||
|
this.showError('daysError', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCalorieCalculations() {
|
||||||
|
const dogTypeSelect = document.getElementById('dogType');
|
||||||
|
const calorieResults = document.getElementById('calorieResults');
|
||||||
|
const rerValue = document.getElementById('rerValue');
|
||||||
|
const merValue = document.getElementById('merValue');
|
||||||
|
|
||||||
|
if (!dogTypeSelect || !calorieResults || !rerValue || !merValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const weightKg = this.getWeightInKg();
|
||||||
|
const dogTypeFactor = dogTypeSelect.value;
|
||||||
|
|
||||||
|
this.showError('weightError', false);
|
||||||
|
|
||||||
|
if (!weightKg || weightKg < 0.1) {
|
||||||
|
if (weightKg !== null) this.showError('weightError', true);
|
||||||
|
calorieResults.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dogTypeFactor) {
|
||||||
|
calorieResults.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const factor = parseFloat(dogTypeFactor);
|
||||||
|
|
||||||
|
const rer = this.calculateRER(weightKg);
|
||||||
|
const mer = this.calculateMER(rer, factor);
|
||||||
|
|
||||||
|
this.currentMER = mer;
|
||||||
|
|
||||||
|
rerValue.textContent = this.formatNumber(rer, 0) + ' cal/day';
|
||||||
|
merValue.textContent = this.formatNumber(mer, 0) + ' cal/day';
|
||||||
|
calorieResults.style.display = 'block';
|
||||||
|
|
||||||
|
this.updateFoodCalculations();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFoodCalculations() {
|
||||||
|
if (this.currentMER === 0) return;
|
||||||
|
|
||||||
|
const daysInput = document.getElementById('days');
|
||||||
|
const unitSelect = document.getElementById('unit');
|
||||||
|
const dailyFoodResults = document.getElementById('dailyFoodResults');
|
||||||
|
const dailyFoodValue = document.getElementById('dailyFoodValue');
|
||||||
|
const totalFoodDisplay = document.getElementById('totalFoodDisplay');
|
||||||
|
|
||||||
|
if (!daysInput || !unitSelect || !dailyFoodResults || !dailyFoodValue || !totalFoodDisplay) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const energyPer100g = this.getFoodEnergyPer100g();
|
||||||
|
const days = daysInput.value;
|
||||||
|
const unit = unitSelect.value;
|
||||||
|
|
||||||
|
this.showError('foodEnergyError', false);
|
||||||
|
this.showError('daysError', false);
|
||||||
|
|
||||||
|
const minEnergy = this.isImperial ? 0.03 : 1;
|
||||||
|
if (!energyPer100g || energyPer100g < minEnergy) {
|
||||||
|
if (energyPer100g !== null) this.showError('foodEnergyError', true);
|
||||||
|
dailyFoodResults.style.display = 'none';
|
||||||
|
totalFoodDisplay.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!days || !this.validateInput(days, 1, true)) {
|
||||||
|
if (days) this.showError('daysError', true);
|
||||||
|
totalFoodDisplay.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const numDays = parseInt(days);
|
||||||
|
|
||||||
|
const dailyFoodGrams = (this.currentMER / energyPer100g) * 100;
|
||||||
|
const totalFoodGrams = dailyFoodGrams * numDays;
|
||||||
|
|
||||||
|
dailyFoodValue.textContent = this.formatNumber(dailyFoodGrams, 1) + ' g/day';
|
||||||
|
dailyFoodResults.style.display = 'block';
|
||||||
|
|
||||||
|
const convertedAmount = this.convertUnits(totalFoodGrams, unit);
|
||||||
|
const unitLabel = unit === 'g' ? 'g' : unit === 'kg' ? 'kg' : unit === 'oz' ? 'oz' : 'lb';
|
||||||
|
const decimals = unit === 'g' ? 0 : unit === 'kg' ? 2 : 1;
|
||||||
|
|
||||||
|
totalFoodDisplay.value = this.formatNumber(convertedAmount, decimals) + ' ' + unitLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global function for collapsible functionality (no longer used)
|
||||||
|
function toggleCollapsible(id) {
|
||||||
|
// Food section is now always expanded
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize calculator when DOM is ready
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
new DogCalorieCalculator();
|
||||||
|
|
||||||
|
// Initialize share URL
|
||||||
|
const shareUrlInput = document.getElementById('shareUrl');
|
||||||
|
if (shareUrlInput) {
|
||||||
|
shareUrlInput.value = window.location.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update embed codes with current domain
|
||||||
|
updateEmbedCodes();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Share and Embed Functions
|
||||||
|
function showShareOptions() {
|
||||||
|
const modal = document.getElementById('shareModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.style.display = 'block';
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeShareModal() {
|
||||||
|
const modal = document.getElementById('shareModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showEmbedModal() {
|
||||||
|
const modal = document.getElementById('embedModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.style.display = 'block';
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeEmbedModal() {
|
||||||
|
const modal = document.getElementById('embedModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Share functions
|
||||||
|
function shareToFacebook() {
|
||||||
|
const url = encodeURIComponent(window.location.href);
|
||||||
|
const text = encodeURIComponent('Check out this Dog Calorie Calculator!');
|
||||||
|
window.open(`https://www.facebook.com/sharer/sharer.php?u=${url}"e=${text}`, '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
|
function shareToTwitter() {
|
||||||
|
const url = encodeURIComponent(window.location.href);
|
||||||
|
const text = encodeURIComponent('Calculate your dog\'s daily calorie needs with this helpful tool!');
|
||||||
|
window.open(`https://twitter.com/intent/tweet?url=${url}&text=${text}`, '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
|
function shareToLinkedIn() {
|
||||||
|
const url = encodeURIComponent(window.location.href);
|
||||||
|
window.open(`https://www.linkedin.com/sharing/share-offsite/?url=${url}`, '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
|
function shareViaEmail() {
|
||||||
|
const subject = encodeURIComponent('Dog Calorie Calculator');
|
||||||
|
const body = encodeURIComponent(`Check out this helpful Dog Calorie Calculator:\n\n${window.location.href}\n\nIt helps calculate your dog's daily calorie needs based on their weight and activity level.`);
|
||||||
|
window.location.href = `mailto:?subject=${subject}&body=${body}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyShareLink() {
|
||||||
|
const shareUrlInput = document.getElementById('shareUrl');
|
||||||
|
if (shareUrlInput) {
|
||||||
|
shareUrlInput.select();
|
||||||
|
shareUrlInput.setSelectionRange(0, 99999); // For mobile devices
|
||||||
|
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
|
||||||
|
// Update button to show success
|
||||||
|
const copyBtn = document.querySelector('.dog-calculator-share-copy');
|
||||||
|
if (copyBtn) {
|
||||||
|
const originalHTML = copyBtn.innerHTML;
|
||||||
|
copyBtn.innerHTML = '<i class="fas fa-check"></i> Copied!';
|
||||||
|
copyBtn.style.background = '#7fa464';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
copyBtn.innerHTML = originalHTML;
|
||||||
|
copyBtn.style.background = '';
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to copy text: ', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Embed functions
|
||||||
|
function updateEmbedCodes() {
|
||||||
|
const currentDomain = window.location.origin;
|
||||||
|
const currentPath = window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/'));
|
||||||
|
const baseUrl = currentDomain + currentPath;
|
||||||
|
|
||||||
|
const widgetCode = document.getElementById('widgetCode');
|
||||||
|
const iframeCode = document.getElementById('iframeCode');
|
||||||
|
|
||||||
|
if (widgetCode) {
|
||||||
|
widgetCode.textContent = `<script src="${baseUrl}/dog-calculator-widget.js"></script>\n<div id="dog-calorie-calculator"></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iframeCode) {
|
||||||
|
iframeCode.textContent = `<iframe src="${baseUrl}/iframe.html" \n width="100%" height="600" \n frameborder="0" \n title="Dog Calorie Calculator">\n</iframe>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyEmbedCode(type) {
|
||||||
|
const codeElement = type === 'widget' ? document.getElementById('widgetCode') : document.getElementById('iframeCode');
|
||||||
|
const button = event.target.closest('.dog-calculator-copy-btn');
|
||||||
|
|
||||||
|
if (codeElement && button) {
|
||||||
|
const textArea = document.createElement('textarea');
|
||||||
|
textArea.value = codeElement.textContent;
|
||||||
|
textArea.style.position = 'fixed';
|
||||||
|
textArea.style.left = '-999999px';
|
||||||
|
textArea.style.top = '-999999px';
|
||||||
|
document.body.appendChild(textArea);
|
||||||
|
textArea.focus();
|
||||||
|
textArea.select();
|
||||||
|
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
textArea.remove();
|
||||||
|
|
||||||
|
// Update button to show success
|
||||||
|
const originalHTML = button.innerHTML;
|
||||||
|
button.innerHTML = '<i class="fas fa-check"></i> Copied';
|
||||||
|
button.classList.add('copied');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
button.innerHTML = originalHTML;
|
||||||
|
button.classList.remove('copied');
|
||||||
|
}, 2000);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to copy text: ', err);
|
||||||
|
textArea.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close modals when clicking outside
|
||||||
|
window.addEventListener('click', function(event) {
|
||||||
|
const shareModal = document.getElementById('shareModal');
|
||||||
|
const embedModal = document.getElementById('embedModal');
|
||||||
|
|
||||||
|
if (event.target === shareModal) {
|
||||||
|
closeShareModal();
|
||||||
|
} else if (event.target === embedModal) {
|
||||||
|
closeEmbedModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close modals with Escape key
|
||||||
|
window.addEventListener('keydown', function(event) {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
closeShareModal();
|
||||||
|
closeEmbedModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -1,190 +0,0 @@
|
|||||||
# Feeding Transition Calculator — Implementation Guide (30 kg adult)
|
|
||||||
|
|
||||||
## 1) Purpose & principle
|
|
||||||
|
|
||||||
- The calculator **keeps energy intake continuous** while transitioning from kibble (current) to gently cooked (GC, target).
|
|
||||||
|
|
||||||
- Internally it uses the **kibble chart’s month-level precision**; externally it **communicates in GC phases** (<5 mo, 5–6 mo, 7–12 mo).
|
|
||||||
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
## 2) Fixed scope
|
|
||||||
|
|
||||||
- Dog profile: **30 kg adult** only.
|
|
||||||
|
|
||||||
- Supported ages: **2.0 to 12.0 months** inclusive.
|
|
||||||
If the user enters a value outside this range, **clamp** to the nearest bound and show a subtle note.
|
|
||||||
|
|
||||||
- Required configuration at runtime: **kibble energy density (kcal per 100 g)**.
|
|
||||||
If missing, show an error and do not compute.
|
|
||||||
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
## 3) Data you must hard-code
|
|
||||||
|
|
||||||
1. **Kibble reference points (grams/day, 30 kg column):**
|
|
||||||
(2→250), (3→330), (4→365), (6→400), (8→410), (10→410), (12→405).
|
|
||||||
|
|
||||||
2. **Interpolated monthly kibble (round to whole grams):**
|
|
||||||
|
|
||||||
|
|
||||||
- 2 mo: 250
|
|
||||||
|
|
||||||
- 3 mo: 330
|
|
||||||
|
|
||||||
- 4 mo: 365
|
|
||||||
|
|
||||||
- 5 mo: 382
|
|
||||||
|
|
||||||
- 6 mo: 400
|
|
||||||
|
|
||||||
- 7 mo: 405
|
|
||||||
|
|
||||||
- 8 mo: 410
|
|
||||||
|
|
||||||
- 9 mo: 410
|
|
||||||
|
|
||||||
- 10 mo: 410
|
|
||||||
|
|
||||||
- 11 mo: 408
|
|
||||||
|
|
||||||
- 12 mo: 405
|
|
||||||
|
|
||||||
|
|
||||||
3. **GC communication buckets and ranges (g/day):**
|
|
||||||
|
|
||||||
- **< 5 months** (covers 2.0–4.999… mo): **950–1350**
|
|
||||||
|
|
||||||
- **5–6 months** (covers 5.0–6.999… mo): **1250–1550**
|
|
||||||
|
|
||||||
- **7–12 months** (covers 7.0–12.0 mo): **1300–1500**
|
|
||||||
|
|
||||||
|
|
||||||
**Boundary rule:** exact 5.0 and 7.0 belong to the **later** bucket (5.0 → “5–6”, 7.0 → “7–12”).
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
## 4) How to calculate results (conceptual, no code)
|
|
||||||
|
|
||||||
### A) Kibble grams/day at any age (2.0–12.0)
|
|
||||||
|
|
||||||
- Use **linear interpolation** between the nearest kibble reference points listed above.
|
|
||||||
|
|
||||||
- Round the resulting kibble grams/day to **whole grams** (or to the nearest **5 g** if the user enables a rounding toggle).
|
|
||||||
|
|
||||||
|
|
||||||
### B) GC bucket assignment
|
|
||||||
|
|
||||||
- Based on the **age**, assign the corresponding GC bucket and attach that bucket’s **low/high range** (g/day).
|
|
||||||
|
|
||||||
### C) Energy-matched GC grams/day (the backbone)
|
|
||||||
|
|
||||||
- Convert kibble grams/day to **kcal/day** using the **user-provided** kibble energy density (kcal/100 g).
|
|
||||||
|
|
||||||
- Convert kcal/day to **GC grams/day** using GC’s energy density **115 kcal per 100 g**.
|
|
||||||
(Equivalently, GC provides **1.15 kcal per gram**.)
|
|
||||||
|
|
||||||
- Round to **whole grams** (or to **nearest 5 g** if the user toggled it).
|
|
||||||
|
|
||||||
|
|
||||||
### D) Range status
|
|
||||||
|
|
||||||
- Compare the **energy-matched GC grams/day** to the GC bucket’s **low/high**:
|
|
||||||
|
|
||||||
- “within” if inside \[low, high\]
|
|
||||||
|
|
||||||
- “below” if under the low
|
|
||||||
|
|
||||||
- “above” if over the high
|
|
||||||
(Do **not** alter the energy-matched amount; just flag it.)
|
|
||||||
|
|
||||||
|
|
||||||
### E) Transition schedule (blended days)
|
|
||||||
|
|
||||||
- Default to **7 days** (configurable).
|
|
||||||
|
|
||||||
- Linearly ramp daily fractions from **100% kibble / 0% GC** on Day 1 to **0% kibble / 100% GC** on the last day, in equal steps.
|
|
||||||
|
|
||||||
- Each day’s grams = (kibble grams/day × kibble fraction) + (GC grams/day × GC fraction).
|
|
||||||
Round after multiplying (whole grams or nearest 5 g per the user setting).
|
|
||||||
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
## 5) What to display (UX rules)
|
|
||||||
|
|
||||||
1. **Primary number:** “Gently cooked (energy-matched): **X g/day**”.
|
|
||||||
Directly below, show the GC bucket label and its range (e.g., “7–12 months: 1300–1500 g/day”) plus a small **status chip** (within/below/above).
|
|
||||||
|
|
||||||
2. **Context line:** “Based on your kibble energy density: **Y kcal / 100 g**” with an edit control.
|
|
||||||
|
|
||||||
3. **Age input:** accept decimals (e.g., 5.5 months).
|
|
||||||
Add tick marks at 2, 3, 4, 6, 8, 10, 12 (the original kibble points).
|
|
||||||
|
|
||||||
4. **Transition widget:** a simple 5–7 day table or bar chart that shows **kibble g** and **GC g** per day, plus the day total.
|
|
||||||
(Totals will typically increase during the transition because GC is less energy-dense; this is expected.)
|
|
||||||
|
|
||||||
5. **Rounding toggle:** whole grams vs nearest 5 g.
|
|
||||||
|
|
||||||
6. **Download/export:** CSV with columns:
|
|
||||||
age_months, kibble_g_per_day, kibble_kcal_per_100g, kcal_per_day, gc_energy_matched_g_per_day, gc_bucket_name, gc_low_g, gc_high_g, range_status, and per-day transition grams.
|
|
||||||
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
## 6) Validation & edge cases
|
|
||||||
|
|
||||||
- **Missing kibble kcal density:** block calculation and display a clear prompt to enter kcal/100 g.
|
|
||||||
|
|
||||||
- **Age outside 2–12 months:** clamp to the nearest bound; show a subtle informational note.
|
|
||||||
|
|
||||||
- **Energy-matched GC outside bucket range:** keep the energy-matched number; display the status chip and a short educational tooltip (growth varies; this tool prioritizes energy continuity).
|
|
||||||
|
|
||||||
- **Rounding:** perform all math in floating point; **round only for display** (and for the per-day plan after multiplying by fractions).
|
|
||||||
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
## 7) Acceptance checks (use these to verify)
|
|
||||||
|
|
||||||
- At **5.5 months** with **380 kcal/100 g** kibble:
|
|
||||||
|
|
||||||
- Interpolated kibble ≈ **391 g/day**.
|
|
||||||
|
|
||||||
- Kcal/day ≈ **1,486 kcal**.
|
|
||||||
|
|
||||||
- Energy-matched GC ≈ **1,292 g/day**.
|
|
||||||
|
|
||||||
- GC bucket “5–6 months” (1,250–1,550) → **within**.
|
|
||||||
|
|
||||||
- At **8.0 months** with **380 kcal/100 g** kibble:
|
|
||||||
|
|
||||||
- Kibble ≈ **410 g/day** → ≈ **1,558 kcal/day** → GC ≈ **1,355 g/day**.
|
|
||||||
|
|
||||||
- GC bucket “7–12 months” (1,300–1,500) → **within**.
|
|
||||||
|
|
||||||
- At **2.0 months** with **380 kcal/100 g** kibble:
|
|
||||||
|
|
||||||
- Kibble **250 g/day** → **950 kcal/day** → GC **≈ 826 g/day**.
|
|
||||||
|
|
||||||
- GC bucket “<5 months” (950–1,350) → **below** (expected for some formulas).
|
|
||||||
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
## 8) Deliverables checklist
|
|
||||||
|
|
||||||
- Precise monthly interpolation (ready values above).
|
|
||||||
|
|
||||||
- Age-aware GC bucket labelling and ranges.
|
|
||||||
|
|
||||||
- Energy-matched GC grams/day with status flag.
|
|
||||||
|
|
||||||
- Configurable transition length; per-day blend table.
|
|
||||||
|
|
||||||
- Rounding control.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
437
dog-calorie-calculator.html
Normal file
437
dog-calorie-calculator.html
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Dog Calorie Calculator</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
background: #f8f9fa;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #2c3e50;
|
||||||
|
font-size: 1.4em;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #495057;
|
||||||
|
}
|
||||||
|
|
||||||
|
select,
|
||||||
|
input[type="number"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: white;
|
||||||
|
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
select:focus,
|
||||||
|
input[type="number"]:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #80bdff;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.results {
|
||||||
|
background: #e8f5e8;
|
||||||
|
border: 1px solid #c3e6c3;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-item:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #2d5a2d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1e3a1e;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-header {
|
||||||
|
background: #f1f3f4;
|
||||||
|
padding: 15px 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-header:hover {
|
||||||
|
background: #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.2em;
|
||||||
|
color: #2c3e50;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-arrow {
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
font-size: 1.2em;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible.active .collapsible-arrow {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-content {
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: max-height 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible.active .collapsible-content {
|
||||||
|
max-height: 1000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-inner {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group .form-group {
|
||||||
|
flex: 1;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit-select {
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #dc3545;
|
||||||
|
font-size: 0.875em;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.calculator-container {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group .form-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-item {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="calculator-container">
|
||||||
|
<div class="section">
|
||||||
|
<h2>Dog's Characteristics</h2>
|
||||||
|
|
||||||
|
<div class="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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="weight">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="error hidden">Please enter a valid weight (minimum 0.1 kg)</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="results" id="calorieResults" style="display: none;">
|
||||||
|
<div class="result-item">
|
||||||
|
<span class="result-label">Resting Energy Requirement (RER):</span>
|
||||||
|
<span class="result-value" id="rerValue">- cal/day</span>
|
||||||
|
</div>
|
||||||
|
<div class="result-item">
|
||||||
|
<span class="result-label">Maintenance Energy Requirement (MER):</span>
|
||||||
|
<span class="result-value" id="merValue">- cal/day</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="collapsible" id="foodCalculator">
|
||||||
|
<div class="collapsible-header" onclick="toggleCollapsible('foodCalculator')">
|
||||||
|
<h3>How much food is that?</h3>
|
||||||
|
<span class="collapsible-arrow">▼</span>
|
||||||
|
</div>
|
||||||
|
<div class="collapsible-content">
|
||||||
|
<div class="collapsible-inner">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="foodEnergy">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="error hidden">Please enter a valid energy content (minimum 1 kcal/100g)</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="results" id="dailyFoodResults" style="display: none;">
|
||||||
|
<div class="result-item">
|
||||||
|
<span class="result-label">Daily Food Amount:</span>
|
||||||
|
<span class="result-value" id="dailyFoodValue">- g/day</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="margin-top: 20px;">
|
||||||
|
<label for="days">Number of Days:</label>
|
||||||
|
<input type="number" id="days" min="1" step="1" value="1" placeholder="Enter number of days" aria-describedby="daysHelp">
|
||||||
|
<div id="daysError" class="error hidden">Please enter a valid number of days (minimum 1)</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="totalFoodDisplay">Total Food Amount:</label>
|
||||||
|
<input type="text" id="totalFoodDisplay" readonly style="background-color: #f8f9fa; cursor: not-allowed;">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="unit">Unit:</label>
|
||||||
|
<select id="unit" class="unit-select" aria-describedby="unitHelp">
|
||||||
|
<option value="g">grams (g)</option>
|
||||||
|
<option value="kg">kilograms (kg)</option>
|
||||||
|
<option value="oz">ounces (oz)</option>
|
||||||
|
<option value="lb">pounds (lb)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let currentMER = 0;
|
||||||
|
|
||||||
|
function toggleCollapsible(id) {
|
||||||
|
const element = document.getElementById(id);
|
||||||
|
element.classList.toggle('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateRER(weightKg) {
|
||||||
|
return 70 * Math.pow(weightKg, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateMER(rer, factor) {
|
||||||
|
return rer * factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
function 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showError(elementId, show = true) {
|
||||||
|
const errorElement = document.getElementById(elementId);
|
||||||
|
if (show) {
|
||||||
|
errorElement.classList.remove('hidden');
|
||||||
|
} else {
|
||||||
|
errorElement.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertUnits(grams, unit) {
|
||||||
|
switch (unit) {
|
||||||
|
case 'kg':
|
||||||
|
return grams / 1000;
|
||||||
|
case 'oz':
|
||||||
|
return grams / 28.3495;
|
||||||
|
case 'lb':
|
||||||
|
return grams / 453.592;
|
||||||
|
default:
|
||||||
|
return grams;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(num, decimals = 0) {
|
||||||
|
return num.toFixed(decimals).replace(/\.?0+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCalorieCalculations() {
|
||||||
|
const weight = document.getElementById('weight').value;
|
||||||
|
const dogTypeFactor = document.getElementById('dogType').value;
|
||||||
|
|
||||||
|
showError('weightError', false);
|
||||||
|
|
||||||
|
if (!weight || !validateInput(weight, 0.1)) {
|
||||||
|
showError('weightError', true);
|
||||||
|
document.getElementById('calorieResults').style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dogTypeFactor) {
|
||||||
|
document.getElementById('calorieResults').style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const weightKg = parseFloat(weight);
|
||||||
|
const factor = parseFloat(dogTypeFactor);
|
||||||
|
|
||||||
|
const rer = calculateRER(weightKg);
|
||||||
|
const mer = calculateMER(rer, factor);
|
||||||
|
|
||||||
|
currentMER = mer;
|
||||||
|
|
||||||
|
document.getElementById('rerValue').textContent = formatNumber(rer, 0) + ' cal/day';
|
||||||
|
document.getElementById('merValue').textContent = formatNumber(mer, 0) + ' cal/day';
|
||||||
|
document.getElementById('calorieResults').style.display = 'block';
|
||||||
|
|
||||||
|
updateFoodCalculations();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFoodCalculations() {
|
||||||
|
if (currentMER === 0) return;
|
||||||
|
|
||||||
|
const foodEnergy = document.getElementById('foodEnergy').value;
|
||||||
|
const days = document.getElementById('days').value;
|
||||||
|
const unit = document.getElementById('unit').value;
|
||||||
|
|
||||||
|
showError('foodEnergyError', false);
|
||||||
|
showError('daysError', false);
|
||||||
|
|
||||||
|
if (!foodEnergy || !validateInput(foodEnergy, 1)) {
|
||||||
|
if (foodEnergy) showError('foodEnergyError', true);
|
||||||
|
document.getElementById('dailyFoodResults').style.display = 'none';
|
||||||
|
document.getElementById('totalFoodDisplay').value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!days || !validateInput(days, 1, true)) {
|
||||||
|
if (days) showError('daysError', true);
|
||||||
|
document.getElementById('totalFoodDisplay').value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const energyPer100g = parseFloat(foodEnergy);
|
||||||
|
const numDays = parseInt(days);
|
||||||
|
|
||||||
|
const dailyFoodGrams = (currentMER / energyPer100g) * 100;
|
||||||
|
const totalFoodGrams = dailyFoodGrams * numDays;
|
||||||
|
|
||||||
|
document.getElementById('dailyFoodValue').textContent = formatNumber(dailyFoodGrams, 1) + ' g/day';
|
||||||
|
document.getElementById('dailyFoodResults').style.display = 'block';
|
||||||
|
|
||||||
|
const convertedAmount = convertUnits(totalFoodGrams, unit);
|
||||||
|
const unitLabel = unit === 'g' ? 'g' : unit === 'kg' ? 'kg' : unit === 'oz' ? 'oz' : 'lb';
|
||||||
|
const decimals = unit === 'g' ? 0 : unit === 'kg' ? 2 : 1;
|
||||||
|
|
||||||
|
document.getElementById('totalFoodDisplay').value = formatNumber(convertedAmount, decimals) + ' ' + unitLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('weight').addEventListener('input', updateCalorieCalculations);
|
||||||
|
document.getElementById('dogType').addEventListener('change', updateCalorieCalculations);
|
||||||
|
document.getElementById('foodEnergy').addEventListener('input', updateFoodCalculations);
|
||||||
|
document.getElementById('days').addEventListener('input', updateFoodCalculations);
|
||||||
|
document.getElementById('unit').addEventListener('change', updateFoodCalculations);
|
||||||
|
|
||||||
|
document.getElementById('weight').addEventListener('blur', function() {
|
||||||
|
const weight = this.value;
|
||||||
|
if (weight && !validateInput(weight, 0.1)) {
|
||||||
|
showError('weightError', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('foodEnergy').addEventListener('blur', function() {
|
||||||
|
const energy = this.value;
|
||||||
|
if (energy && !validateInput(energy, 1)) {
|
||||||
|
showError('foodEnergyError', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('days').addEventListener('blur', function() {
|
||||||
|
const days = this.value;
|
||||||
|
if (days && !validateInput(days, 1, true)) {
|
||||||
|
showError('daysError', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1729
dog-food-calculator-widget.js
Normal file
1729
dog-food-calculator-widget.js
Normal file
File diff suppressed because it is too large
Load Diff
3581
iframe.html
3581
iframe.html
File diff suppressed because it is too large
Load Diff
188
index.html
Normal file
188
index.html
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Dog Calorie Calculator</title>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<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" 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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dog-calculator-collapsible active" id="foodCalculator">
|
||||||
|
<div class="dog-calculator-collapsible-header">
|
||||||
|
<h3>How much should I feed?</h3>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<div class="dog-calculator-results" id="dailyFoodResults" style="display: none;">
|
||||||
|
<div class="dog-calculator-result-item">
|
||||||
|
<span class="dog-calculator-result-label">Daily Food Amount:</span>
|
||||||
|
<span class="dog-calculator-result-value" id="dailyFoodValue">- g/day</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dog-calculator-form-group" style="margin-top: 20px;">
|
||||||
|
<label for="days">Number of Days:</label>
|
||||||
|
<input type="number" id="days" min="1" step="1" value="1" placeholder="Enter number of days" aria-describedby="daysHelp">
|
||||||
|
<div id="daysError" class="dog-calculator-error dog-calculator-hidden">Please enter a valid number of days (minimum 1)</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dog-calculator-input-group">
|
||||||
|
<div class="dog-calculator-form-group">
|
||||||
|
<label for="totalFoodDisplay">Total Food Amount:</label>
|
||||||
|
<input type="text" id="totalFoodDisplay" readonly>
|
||||||
|
</div>
|
||||||
|
<div class="dog-calculator-form-group">
|
||||||
|
<label for="unit">Unit:</label>
|
||||||
|
<select id="unit" class="dog-calculator-unit-select" aria-describedby="unitHelp">
|
||||||
|
<option value="g">grams (g)</option>
|
||||||
|
<option value="kg">kilograms (kg)</option>
|
||||||
|
<option value="oz">ounces (oz)</option>
|
||||||
|
<option value="lb">pounds (lb)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dog-calculator-action-buttons">
|
||||||
|
<button class="dog-calculator-btn dog-calculator-btn-share" onclick="showShareOptions()">
|
||||||
|
<i class="fas fa-share-alt"></i> Share
|
||||||
|
</button>
|
||||||
|
<button class="dog-calculator-btn dog-calculator-btn-embed" onclick="showEmbedModal()">
|
||||||
|
<i class="fas fa-code"></i> Embed
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dog-calculator-footer">
|
||||||
|
<a href="https://caninenutritionandwellness.com" target="_blank" rel="noopener noreferrer">
|
||||||
|
by caninenutritionandwellness.com
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Share Modal -->
|
||||||
|
<div id="shareModal" class="dog-calculator-modal">
|
||||||
|
<div class="dog-calculator-modal-content">
|
||||||
|
<span class="dog-calculator-modal-close" onclick="closeShareModal()">×</span>
|
||||||
|
<h3><i class="fas fa-share-alt"></i> Share Calculator</h3>
|
||||||
|
<div class="dog-calculator-share-buttons">
|
||||||
|
<button class="dog-calculator-share-btn dog-calculator-share-facebook" onclick="shareToFacebook()">
|
||||||
|
<i class="fab fa-facebook-f"></i> Facebook
|
||||||
|
</button>
|
||||||
|
<button class="dog-calculator-share-btn dog-calculator-share-twitter" onclick="shareToTwitter()">
|
||||||
|
<i class="fab fa-twitter"></i> Twitter
|
||||||
|
</button>
|
||||||
|
<button class="dog-calculator-share-btn dog-calculator-share-linkedin" onclick="shareToLinkedIn()">
|
||||||
|
<i class="fab fa-linkedin-in"></i> LinkedIn
|
||||||
|
</button>
|
||||||
|
<button class="dog-calculator-share-btn dog-calculator-share-email" onclick="shareViaEmail()">
|
||||||
|
<i class="fas fa-envelope"></i> Email
|
||||||
|
</button>
|
||||||
|
<button class="dog-calculator-share-btn dog-calculator-share-copy" onclick="copyShareLink()">
|
||||||
|
<i class="fas fa-link"></i> Copy Link
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="dog-calculator-share-url">
|
||||||
|
<input type="text" id="shareUrl" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Embed Modal -->
|
||||||
|
<div id="embedModal" class="dog-calculator-modal">
|
||||||
|
<div class="dog-calculator-modal-content dog-calculator-modal-embed">
|
||||||
|
<span class="dog-calculator-modal-close" onclick="closeEmbedModal()">×</span>
|
||||||
|
<h3><i class="fas fa-code"></i> Embed Calculator</h3>
|
||||||
|
|
||||||
|
<div class="dog-calculator-embed-options">
|
||||||
|
<div class="dog-calculator-embed-option">
|
||||||
|
<h4><i class="fas fa-bolt"></i> JavaScript Widget (Recommended)</h4>
|
||||||
|
<p>SEO-friendly, responsive, integrates with your site's design</p>
|
||||||
|
<div class="dog-calculator-code-container">
|
||||||
|
<pre><code id="widgetCode"><script src="https://yourdomain.com/dog-calculator-widget.js"></script>
|
||||||
|
<div id="dog-calorie-calculator"></div></code></pre>
|
||||||
|
<button class="dog-calculator-copy-btn" onclick="copyEmbedCode('widget')">
|
||||||
|
<i class="fas fa-copy"></i> Copy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dog-calculator-embed-option">
|
||||||
|
<h4><i class="fas fa-shield-alt"></i> iframe Embed (Brand Protected)</h4>
|
||||||
|
<p>Complete style isolation, your branding stays intact</p>
|
||||||
|
<div class="dog-calculator-code-container">
|
||||||
|
<pre><code id="iframeCode"><iframe src="https://yourdomain.com/iframe.html"
|
||||||
|
width="100%" height="600"
|
||||||
|
frameborder="0"
|
||||||
|
title="Dog Calorie Calculator">
|
||||||
|
</iframe></code></pre>
|
||||||
|
<button class="dog-calculator-copy-btn" onclick="copyEmbedCode('iframe')">
|
||||||
|
<i class="fas fa-copy"></i> Copy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="calculator.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
563
src/css/main.css
563
src/css/main.css
@ -1,563 +0,0 @@
|
|||||||
:root {
|
|
||||||
--bg-primary: #fdfcfe;
|
|
||||||
--bg-secondary: #ffffff;
|
|
||||||
--border-color: #e8e3ed;
|
|
||||||
--text-primary: #6f3f6d;
|
|
||||||
--text-secondary: #8f7a8e;
|
|
||||||
--accent-color: #f19a5f;
|
|
||||||
--text-label: #635870; /* For form labels, secondary UI text */
|
|
||||||
--success-color: #7fa464; /* Green for success states */
|
|
||||||
--bg-tertiary: #f8f5fa; /* Light background variant */
|
|
||||||
--error-color: #e87159; /* Error states and messages */
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
background: transparent;
|
|
||||||
overflow: hidden; /* hide internal scrollbars; parent resizes iframe */
|
|
||||||
font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
||||||
line-height: 1.5;
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-container {
|
|
||||||
max-width: 640px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 24px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-container.loaded {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-container *,
|
|
||||||
.dog-calculator-container *::before,
|
|
||||||
.dog-calculator-container *::after {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-section {
|
|
||||||
background: var(--bg-primary);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 8px 8px 0 0;
|
|
||||||
padding: 24px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-section-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-section h2 {
|
|
||||||
margin: 0;
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unit Switch */
|
|
||||||
.dog-calculator-unit-switch {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-unit-label {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--text-label);
|
|
||||||
transition: color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-unit-label.active {
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-switch {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
width: 48px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-switch input {
|
|
||||||
opacity: 0;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-slider {
|
|
||||||
position: absolute;
|
|
||||||
cursor: pointer;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: var(--border-color);
|
|
||||||
transition: 0.3s;
|
|
||||||
border-radius: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-slider:before {
|
|
||||||
position: absolute;
|
|
||||||
content: "";
|
|
||||||
height: 18px;
|
|
||||||
width: 18px;
|
|
||||||
left: 3px;
|
|
||||||
bottom: 3px;
|
|
||||||
background-color: white;
|
|
||||||
transition: 0.3s;
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-switch input:checked + .dog-calculator-slider {
|
|
||||||
background-color: #f19a5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-switch input:checked + .dog-calculator-slider:before {
|
|
||||||
transform: translateX(24px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-form-group {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-form-group label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-form-group select,
|
|
||||||
.dog-calculator-form-group input[type="number"],
|
|
||||||
.dog-calculator-form-group input[type="text"] {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px 16px;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-family: inherit;
|
|
||||||
background-color: var(--bg-secondary);
|
|
||||||
color: var(--text-primary);
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
-moz-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236f3f6d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: right 12px center;
|
|
||||||
background-size: 20px;
|
|
||||||
padding-right: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-form-group select option {
|
|
||||||
background-color: var(--bg-secondary);
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-form-group input[type="number"],
|
|
||||||
.dog-calculator-form-group input[type="text"] {
|
|
||||||
background-image: none;
|
|
||||||
padding-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-form-group select:focus,
|
|
||||||
.dog-calculator-form-group input[type="number"]:focus,
|
|
||||||
.dog-calculator-form-group input[type="text"]:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: #f19a5f;
|
|
||||||
background-color: var(--bg-secondary);
|
|
||||||
box-shadow: 0 0 0 3px rgba(241, 154, 95, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-form-group input[readonly] {
|
|
||||||
background-color: var(--bg-tertiary);
|
|
||||||
cursor: not-allowed;
|
|
||||||
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);
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 20px;
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-result-item {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
gap: 10px; /* Add gap between label and value */
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-result-item:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-result-label {
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-size: 0.95rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-result-value {
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-size: 1.1rem;
|
|
||||||
padding: 4px 12px;
|
|
||||||
background: rgba(241, 154, 95, 0.15);
|
|
||||||
border-radius: 4px;
|
|
||||||
white-space: nowrap; /* Prevent text from wrapping to multiple lines */
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-collapsible {
|
|
||||||
background: var(--bg-primary);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-top: none;
|
|
||||||
margin-bottom: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-collapsible-header {
|
|
||||||
background: var(--bg-tertiary);
|
|
||||||
padding: 20px 24px;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-collapsible-header h3 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-collapsible-content {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-collapsible-inner {
|
|
||||||
padding: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-input-group {
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-input-group .dog-calculator-form-group {
|
|
||||||
flex: 1;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-unit-select {
|
|
||||||
min-width: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-error {
|
|
||||||
color: var(--error-color);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
margin-top: 6px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Action Buttons */
|
|
||||||
.dog-calculator-action-buttons {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 16px;
|
|
||||||
padding: 20px;
|
|
||||||
background: var(--bg-tertiary);
|
|
||||||
border-left: 1px solid var(--border-color);
|
|
||||||
border-right: 1px solid var(--border-color);
|
|
||||||
margin-top: -1px;
|
|
||||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-btn {
|
|
||||||
padding: 8px 16px;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 500;
|
|
||||||
font-family: inherit;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
background: white;
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-btn:hover {
|
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-btn-share:hover {
|
|
||||||
border-color: #9f5999;
|
|
||||||
color: #9f5999;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Embed button removed */
|
|
||||||
|
|
||||||
.dog-calculator-footer {
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
background: var(--bg-primary);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 0 0 8px 8px;
|
|
||||||
border-top: none;
|
|
||||||
margin-top: -1px;
|
|
||||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-footer a {
|
|
||||||
color: #9f5999;
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-footer a:hover {
|
|
||||||
color: #f19a5f;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile Responsive Design */
|
|
||||||
@media (max-width: 576px) {
|
|
||||||
.dog-calculator-container {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-section,
|
|
||||||
.dog-calculator-collapsible-inner {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-section h2,
|
|
||||||
.dog-calculator-collapsible-header h3 {
|
|
||||||
font-size: 1.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-section-header {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-section h2 {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-unit-switch {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-action-buttons {
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-btn {
|
|
||||||
width: 100%;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-input-group {
|
|
||||||
flex-direction: row;
|
|
||||||
gap: 12px;
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-input-group .dog-calculator-form-group {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* First form group takes 55%, second takes 40% with some flex */
|
|
||||||
.dog-calculator-input-group .dog-calculator-form-group:first-child {
|
|
||||||
flex: 0 0 55%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-input-group .dog-calculator-form-group:last-child {
|
|
||||||
flex: 1 1 40%;
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure number inputs don't get too wide */
|
|
||||||
.dog-calculator-input-group input[type="number"] {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure dropdowns don't overflow their containers */
|
|
||||||
.dog-calculator-input-group select {
|
|
||||||
max-width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.dog-calculator-result-item {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-result-value {
|
|
||||||
align-self: stretch;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-collapsible-header {
|
|
||||||
padding: 16px 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Feeding Configuration Styles */
|
|
||||||
.dog-calculator-container .dog-calculator-feeding-config {
|
|
||||||
margin-top: 20px;
|
|
||||||
padding: 16px;
|
|
||||||
background: var(--bg-tertiary);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-container .dog-calculator-frequency-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 16px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-container .dog-calculator-frequency-row > label {
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--text-label);
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-container .dog-calculator-radio-group {
|
|
||||||
display: flex;
|
|
||||||
gap: 20px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-container .dog-calculator-radio-group label {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-size: 0.95rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-container .dog-calculator-radio-group input[type="radio"] {
|
|
||||||
cursor: pointer;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-container .dog-calculator-radio-group input[type="radio"]:checked + span {
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--accent-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-container .dog-calculator-meal-input {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-container .dog-calculator-meal-input span {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 0.95rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-container .dog-calculator-meal-input input[type="number"] {
|
|
||||||
width: 50px;
|
|
||||||
padding: 4px 8px;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
color: var(--text-primary);
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-container .dog-calculator-meal-input input[type="number"]:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--accent-color);
|
|
||||||
box-shadow: 0 0 0 2px rgba(241, 154, 95, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update meal note styling */
|
|
||||||
.dog-calculator-container #mealNote {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: normal;
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile responsive adjustments for feeding config */
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.dog-calculator-meal-input {
|
|
||||||
margin-left: 0;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dog-calculator-frequency-row {
|
|
||||||
flex-direction: column;
|
|
||||||
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 */
|
|
||||||
1394
src/css/themes.css
1394
src/css/themes.css
File diff suppressed because it is too large
Load Diff
112
src/index.html
112
src/index.html
@ -1,112 +0,0 @@
|
|||||||
<div class="dog-calculator-container" id="dogCalculator">
|
|
||||||
<div class="dog-calculator-section">
|
|
||||||
<div class="dog-calculator-section-header">
|
|
||||||
<h2>Kaya’s Transition</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dog-calculator-form-group">
|
|
||||||
<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-form-group">
|
|
||||||
<label for="kayaEndWeight">Kaya’s end‑weight:</label>
|
|
||||||
<input type="text" id="kayaEndWeight" value="30 kg" readonly>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dog-calculator-collapsible active" id="foodCalculator">
|
|
||||||
<div class="dog-calculator-collapsible-header">
|
|
||||||
<h3>How much should I feed?</h3>
|
|
||||||
</div>
|
|
||||||
<div class="dog-calculator-collapsible-content">
|
|
||||||
<div class="dog-calculator-collapsible-inner">
|
|
||||||
<!-- Food Sources Container -->
|
|
||||||
<div class="dog-calculator-food-sources" id="foodSources">
|
|
||||||
<!-- Initial food source will be added by JavaScript -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Food Source Button -->
|
|
||||||
<button class="dog-calculator-add-food-btn" id="addFoodBtn" type="button">
|
|
||||||
<span>+</span>
|
|
||||||
<span>Add another food source</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Feeding Configuration -->
|
|
||||||
<div class="dog-calculator-feeding-config" id="feedingConfig" style="display: none;">
|
|
||||||
<div class="dog-calculator-frequency-row">
|
|
||||||
<label>Show amounts:</label>
|
|
||||||
<div class="dog-calculator-radio-group">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="showAs" value="daily" id="showDaily" checked>
|
|
||||||
<span>Per day</span>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="showAs" value="meal" id="showPerMeal">
|
|
||||||
<span>Per meal</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="dog-calculator-meal-input" id="mealInputGroup" style="display: none;">
|
|
||||||
<span>×</span>
|
|
||||||
<input type="number" id="mealsPerDay" value="2" min="1" max="10">
|
|
||||||
<span>meals/day</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Per-Food Results -->
|
|
||||||
<div class="dog-calculator-food-results" id="foodBreakdownResults" style="display: none;">
|
|
||||||
<div id="foodBreakdownList">
|
|
||||||
<!-- Individual food breakdowns will be populated by JavaScript -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Unit Selection Buttons -->
|
|
||||||
<div class="dog-calculator-unit-buttons" id="unitButtons" style="display: none;">
|
|
||||||
<button type="button" class="dog-calculator-unit-btn active" data-unit="g">g</button>
|
|
||||||
<button type="button" class="dog-calculator-unit-btn" data-unit="kg">kg</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Daily Total Results -->
|
|
||||||
<div class="dog-calculator-results" id="dailyFoodResults" style="display: none;">
|
|
||||||
<div class="dog-calculator-result-item">
|
|
||||||
<span class="dog-calculator-result-label">Total Daily Amount:</span>
|
|
||||||
<span class="dog-calculator-result-value" id="dailyFoodValue">- g/day</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Hidden select for compatibility -->
|
|
||||||
<select id="unit" class="dog-calculator-unit-select-hidden" aria-describedby="unitHelp">
|
|
||||||
<option value="g">grams (g)</option>
|
|
||||||
<option value="kg">kilograms (kg)</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<div class="dog-calculator-food-amounts-section" id="foodAmountsSection" style="display: none;">
|
|
||||||
<h4 class="dog-calculator-section-title">
|
|
||||||
Calculate amounts for
|
|
||||||
<input type="number" id="days" min="1" step="1" value="1" placeholder="1" aria-describedby="daysHelp" class="dog-calculator-inline-days">
|
|
||||||
<span id="dayLabel">day</span><span id="mealNote" style="display: none;"></span>:
|
|
||||||
</h4>
|
|
||||||
<div id="daysError" class="dog-calculator-error dog-calculator-hidden">Please enter a valid number of days (minimum 1)</div>
|
|
||||||
<div id="foodAmountsList" class="dog-calculator-food-amounts-list">
|
|
||||||
<!-- Individual food amounts will be populated here -->
|
|
||||||
</div>
|
|
||||||
<div class="dog-calculator-total-row" id="totalAmountRow">
|
|
||||||
<span class="dog-calculator-total-label">Total Amount:</span>
|
|
||||||
<span class="dog-calculator-total-value" id="totalAmountDisplay"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dog-calculator-footer">
|
|
||||||
<a href="https://caninenutritionandwellness.com" target="_blank" rel="noopener noreferrer">
|
|
||||||
by caninenutritionandwellness.com
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
1956
src/js/calculator.js
1956
src/js/calculator.js
File diff suppressed because it is too large
Load Diff
@ -1,11 +0,0 @@
|
|||||||
/**
|
|
||||||
* Configuration constants for Dog Calorie Calculator
|
|
||||||
*/
|
|
||||||
|
|
||||||
const CALCULATOR_CONFIG = {
|
|
||||||
defaultTheme: 'system',
|
|
||||||
defaultScale: 1.0,
|
|
||||||
maxFoodSources: 5,
|
|
||||||
minScale: 0.5,
|
|
||||||
maxScale: 2.0
|
|
||||||
};
|
|
||||||
787
styles.css
Normal file
787
styles.css
Normal file
@ -0,0 +1,787 @@
|
|||||||
|
/* Dog Calorie Calculator Styles */
|
||||||
|
/* Using Canine Nutrition and Wellness brand colors and design system */
|
||||||
|
|
||||||
|
.dog-calculator-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 24px;
|
||||||
|
font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #6f3f6d;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-container *,
|
||||||
|
.dog-calculator-container *::before,
|
||||||
|
.dog-calculator-container *::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-section {
|
||||||
|
background: #fdfcfe;
|
||||||
|
border: 1px solid #e8e3ed;
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
padding: 24px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-section-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-section h2 {
|
||||||
|
margin: 0;
|
||||||
|
color: #6f3f6d;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unit Switch */
|
||||||
|
.dog-calculator-unit-switch {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-unit-label {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #635870;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-unit-label.active {
|
||||||
|
color: #6f3f6d;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 48px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #e8e3ed;
|
||||||
|
transition: 0.3s;
|
||||||
|
border-radius: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
left: 3px;
|
||||||
|
bottom: 3px;
|
||||||
|
background-color: white;
|
||||||
|
transition: 0.3s;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-switch input:checked + .dog-calculator-slider {
|
||||||
|
background-color: #f19a5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-switch input:checked + .dog-calculator-slider:before {
|
||||||
|
transform: translateX(24px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #6f3f6d;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-form-group select,
|
||||||
|
.dog-calculator-form-group input[type="number"],
|
||||||
|
.dog-calculator-form-group input[type="text"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: 1px solid #e8e3ed;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-family: inherit;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #6f3f6d;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236f3f6d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 12px center;
|
||||||
|
background-size: 20px;
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-form-group select option {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #6f3f6d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-form-group input[type="number"],
|
||||||
|
.dog-calculator-form-group input[type="text"] {
|
||||||
|
background-image: none;
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-form-group select:focus,
|
||||||
|
.dog-calculator-form-group input[type="number"]:focus,
|
||||||
|
.dog-calculator-form-group input[type="text"]:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #f19a5f;
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-shadow: 0 0 0 3px rgba(241, 154, 95, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-form-group input[readonly] {
|
||||||
|
background-color: #f8f5fa;
|
||||||
|
cursor: not-allowed;
|
||||||
|
color: #635870;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-result-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-result-item:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-result-label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #6f3f6d;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-result-value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6f3f6d;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
padding: 4px 12px;
|
||||||
|
background: rgba(241, 154, 95, 0.15);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-collapsible {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e8e3ed;
|
||||||
|
border-top: none;
|
||||||
|
margin-bottom: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-collapsible-header {
|
||||||
|
background: #f8f5fa;
|
||||||
|
padding: 20px 24px;
|
||||||
|
border-bottom: 1px solid #e8e3ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-collapsible-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: #6f3f6d;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-collapsible-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-collapsible-inner {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-input-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-input-group .dog-calculator-form-group {
|
||||||
|
flex: 1;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-unit-select {
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-error {
|
||||||
|
color: #e87159;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
margin-top: 6px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-footer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
background: #fdfcfe;
|
||||||
|
border: 1px solid #e8e3ed;
|
||||||
|
border-radius: 0 0 8px 8px;
|
||||||
|
border-top: none;
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-footer a {
|
||||||
|
color: #9f5999;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-footer a:hover {
|
||||||
|
color: #f19a5f;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action Buttons */
|
||||||
|
.dog-calculator-action-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #f8f5fa;
|
||||||
|
border-left: 1px solid #e8e3ed;
|
||||||
|
border-right: 1px solid #e8e3ed;
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-btn {
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: 1px solid #e8e3ed;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
background: white;
|
||||||
|
color: #6f3f6d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-btn:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-btn-share:hover {
|
||||||
|
border-color: #9f5999;
|
||||||
|
color: #9f5999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-btn-embed:hover {
|
||||||
|
border-color: #7fa464;
|
||||||
|
color: #7fa464;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal Styles */
|
||||||
|
.dog-calculator-modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 10000;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
animation: fadeIn 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-modal-content {
|
||||||
|
position: relative;
|
||||||
|
background-color: #ffffff;
|
||||||
|
margin: 5% auto;
|
||||||
|
padding: 30px;
|
||||||
|
border: 1px solid #e8e3ed;
|
||||||
|
border-radius: 12px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 500px;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
||||||
|
animation: slideIn 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-modal-embed {
|
||||||
|
max-width: 700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-modal-close {
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
top: 20px;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #6f3f6d;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-modal-close:hover {
|
||||||
|
color: #f19a5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-modal h3 {
|
||||||
|
margin: 0 0 24px 0;
|
||||||
|
color: #6f3f6d;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Share Modal */
|
||||||
|
.dog-calculator-share-buttons {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-btn {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-facebook {
|
||||||
|
background: #1877f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-facebook:hover {
|
||||||
|
background: #1664d1;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-twitter {
|
||||||
|
background: #1da1f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-twitter:hover {
|
||||||
|
background: #1991da;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-linkedin {
|
||||||
|
background: #0a66c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-linkedin:hover {
|
||||||
|
background: #084d95;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-email {
|
||||||
|
background: #6f3f6d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-email:hover {
|
||||||
|
background: #5a3357;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-copy {
|
||||||
|
background: #f19a5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-copy:hover {
|
||||||
|
background: #e87741;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-url {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-url input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border: 1px solid #e8e3ed;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-family: monospace;
|
||||||
|
background: #f8f5fa;
|
||||||
|
color: #6f3f6d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embed Modal */
|
||||||
|
.dog-calculator-embed-options {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-embed-option {
|
||||||
|
border: 1px solid #e8e3ed;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #fcfafd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-embed-option h4 {
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
color: #6f3f6d;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-embed-option p {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
color: #635870;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-code-container {
|
||||||
|
position: relative;
|
||||||
|
background: #312b3b;
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-code-container pre {
|
||||||
|
margin: 0;
|
||||||
|
padding: 16px;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-code-container code {
|
||||||
|
color: #f5f3f7;
|
||||||
|
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-copy-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
background: #f19a5f;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-copy-btn:hover {
|
||||||
|
background: #e87741;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-copy-btn.copied {
|
||||||
|
background: #7fa464;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-copy-btn.copied:hover {
|
||||||
|
background: #7fa464;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Responsive Design */
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.dog-calculator-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-section,
|
||||||
|
.dog-calculator-collapsible-inner {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-section h2,
|
||||||
|
.dog-calculator-collapsible-header h3 {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-section-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-section h2 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-unit-switch {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-action-buttons {
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-btn {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-input-group {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-input-group .dog-calculator-form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-result-item {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-result-value {
|
||||||
|
align-self: stretch;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-collapsible-header {
|
||||||
|
padding: 16px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-modal-content {
|
||||||
|
margin: 10% auto;
|
||||||
|
padding: 20px;
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-buttons {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-embed-option {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-code-container pre {
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme support */
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.dog-calculator-container {
|
||||||
|
color: #f5f3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-section,
|
||||||
|
.dog-calculator-collapsible {
|
||||||
|
background: #24202d;
|
||||||
|
border-color: #433c4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-collapsible-header {
|
||||||
|
background: #312b3b;
|
||||||
|
border-color: #433c4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-collapsible-header:hover {
|
||||||
|
background: #3a3446;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-section h2,
|
||||||
|
.dog-calculator-collapsible-header h3,
|
||||||
|
.dog-calculator-form-group label,
|
||||||
|
.dog-calculator-result-label {
|
||||||
|
color: #f5f3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-unit-label {
|
||||||
|
color: #b8b0c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-unit-label.active {
|
||||||
|
color: #f5f3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-slider {
|
||||||
|
background-color: #433c4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-form-group select,
|
||||||
|
.dog-calculator-form-group input[type="number"],
|
||||||
|
.dog-calculator-form-group input[type="text"] {
|
||||||
|
background-color: #312b3b;
|
||||||
|
border-color: #433c4f;
|
||||||
|
color: #f5f3f7;
|
||||||
|
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f5f3f7' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-form-group select option {
|
||||||
|
background-color: #312b3b;
|
||||||
|
color: #f5f3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-form-group select:focus,
|
||||||
|
.dog-calculator-form-group input[type="number"]:focus,
|
||||||
|
.dog-calculator-form-group input[type="text"]:focus {
|
||||||
|
background-color: #312b3b;
|
||||||
|
border-color: #f19a5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-form-group input[readonly] {
|
||||||
|
background-color: #433c4f;
|
||||||
|
color: #b8b0c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-results {
|
||||||
|
background: linear-gradient(135deg, rgba(241, 154, 95, 0.15) 0%, rgba(241, 154, 95, 0.08) 100%);
|
||||||
|
border-color: rgba(241, 154, 95, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-result-value {
|
||||||
|
color: #f5f3f7;
|
||||||
|
background: rgba(241, 154, 95, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-footer {
|
||||||
|
background: #24202d;
|
||||||
|
border-color: #433c4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-action-buttons {
|
||||||
|
background: #312b3b;
|
||||||
|
border-color: #433c4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-btn {
|
||||||
|
background: #433c4f;
|
||||||
|
border-color: #433c4f;
|
||||||
|
color: #f5f3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-btn:hover {
|
||||||
|
background: #524a5f;
|
||||||
|
border-color: #524a5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-btn-share:hover {
|
||||||
|
border-color: #9f5999;
|
||||||
|
color: #f19a5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-btn-embed:hover {
|
||||||
|
border-color: #7fa464;
|
||||||
|
color: #7fa464;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-modal-content {
|
||||||
|
background-color: #24202d;
|
||||||
|
border-color: #433c4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-modal h3 {
|
||||||
|
color: #f5f3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-modal-close {
|
||||||
|
color: #f5f3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-modal-close:hover {
|
||||||
|
color: #f19a5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-share-url input {
|
||||||
|
background: #312b3b;
|
||||||
|
border-color: #433c4f;
|
||||||
|
color: #f5f3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-embed-option {
|
||||||
|
background: #312b3b;
|
||||||
|
border-color: #433c4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-embed-option h4 {
|
||||||
|
color: #f5f3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dog-calculator-embed-option p {
|
||||||
|
color: #b8b0c2;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user