/** * Dog Calorie Calculator Widget * Embeddable JavaScript widget for calculating dog's daily calorie requirements * by Canine Nutrition and Wellness - https://caninenutritionandwellness.com * * Basic Usage: * *
* * Advanced Usage with theme and scale options: * * * * Or with JavaScript: * new DogCalorieCalculatorWidget(container, { theme: 'light', scale: 1.2 }); // theme: 'light', 'dark', 'system' | scale: 0.5-2.0 */ (function() { 'use strict'; // Widget styles - Using Shadow DOM for better isolation where supported const widgetStyles = ` .dog-calc-widget { 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-calc-widget *, .dog-calc-widget *::before, .dog-calc-widget *::after { box-sizing: border-box; } .dog-calc-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-calc-section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; flex-wrap: wrap; gap: 16px; } .dog-calc-section h2 { margin: 0; color: #6f3f6d; font-size: 1.5rem; font-weight: 600; } /* Unit Switch */ .dog-calc-unit-switch { display: flex; align-items: center; gap: 12px; } .dog-calc-unit-label { font-size: 0.9rem; font-weight: 500; color: #635870; transition: color 0.2s ease; } .dog-calc-unit-label.active { color: #6f3f6d; font-weight: 600; } .dog-calc-switch { position: relative; display: inline-block; width: 48px; height: 24px; } .dog-calc-switch input { opacity: 0; width: 0; height: 0; } .dog-calc-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #e8e3ed; transition: 0.3s; border-radius: 24px; } .dog-calc-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-calc-switch input:checked + .dog-calc-slider { background-color: #f19a5f; } .dog-calc-switch input:checked + .dog-calc-slider:before { transform: translateX(24px); } .dog-calc-form-group { margin-bottom: 20px; } .dog-calc-form-group label { display: block; margin-bottom: 8px; font-weight: 500; color: #6f3f6d; font-size: 1rem; } .dog-calc-form-group select, .dog-calc-form-group input[type="number"], .dog-calc-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-calc-form-group select option { background-color: #ffffff; color: #6f3f6d; } .dog-calc-form-group input[type="number"], .dog-calc-form-group input[type="text"] { background-image: none; padding-right: 16px; } .dog-calc-form-group select:focus, .dog-calc-form-group input[type="number"]:focus, .dog-calc-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-calc-form-group input[readonly] { background-color: #f8f5fa; cursor: not-allowed; color: #635870; } .dog-calc-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-calc-result-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; } .dog-calc-result-item:last-child { margin-bottom: 0; } .dog-calc-result-label { font-weight: 500; color: #6f3f6d; font-size: 0.95rem; } .dog-calc-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-calc-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-calc-collapsible-header { background: #f8f5fa; padding: 20px 24px; border-bottom: 1px solid #e8e3ed; } .dog-calc-collapsible-header h3 { margin: 0; font-size: 1.25rem; color: #6f3f6d; font-weight: 600; } .dog-calc-collapsible-content { display: block; } .dog-calc-collapsible-inner { padding: 24px; } /* Action Buttons */ .dog-calc-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; box-shadow: 0 3px 6px rgba(0, 0, 0, 0.08); } .dog-calc-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-calc-btn:hover { transform: translateY(-1px); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .dog-calc-btn-share:hover { border-color: #9f5999; color: #9f5999; } .dog-calc-btn-embed:hover { border-color: #7fa464; color: #7fa464; } .dog-calc-input-group { display: flex; gap: 16px; align-items: flex-end; } .dog-calc-input-group .dog-calc-form-group { flex: 1; margin-bottom: 0; } .dog-calc-unit-select { min-width: 120px; } .dog-calc-error { color: #e87159; font-size: 0.875rem; margin-top: 6px; font-weight: 500; } .dog-calc-hidden { display: none !important; } .dog-calc-footer { text-align: center; padding: 20px; background: #fdfcfe; border: 1px solid #e8e3ed; border-radius: 0 0 8px 8px; border-top: none; margin-top: -1px; box-shadow: 0 3px 6px rgba(0, 0, 0, 0.08); } .dog-calc-footer a { color: #9f5999; text-decoration: none; font-size: 0.9rem; font-weight: 500; transition: color 0.2s ease; } .dog-calc-footer a:hover { color: #f19a5f; text-decoration: underline; } @media (max-width: 576px) { .dog-calc-widget { padding: 16px; } .dog-calc-section, .dog-calc-collapsible-inner { padding: 20px; } .dog-calc-section h2, .dog-calc-collapsible-header h3 { font-size: 1.3rem; } .dog-calc-input-group { flex-direction: column; gap: 20px; } .dog-calc-input-group .dog-calc-form-group { margin-bottom: 20px; } .dog-calc-result-item { flex-direction: column; align-items: flex-start; gap: 8px; } .dog-calc-result-value { align-self: stretch; text-align: center; } .dog-calc-collapsible-header { padding: 16px 20px; } .dog-calc-section-header { flex-direction: column; align-items: stretch; gap: 12px; } .dog-calc-section h2 { text-align: center; } .dog-calc-unit-switch { justify-content: center; } .dog-calc-action-buttons { flex-direction: column; padding: 16px; } .dog-calc-btn { width: 100%; justify-content: center; } } /* Dark theme - manual override */ .dog-calc-widget.dog-calc-theme-dark { color: #f5f3f7; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-section, .dog-calc-widget.dog-calc-theme-dark .dog-calc-collapsible { background: #24202d; border-color: #433c4f; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-collapsible-header { background: #312b3b; border-color: #433c4f; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-collapsible-header:hover { background: #3a3446; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-section h2, .dog-calc-widget.dog-calc-theme-dark .dog-calc-collapsible-header h3, .dog-calc-widget.dog-calc-theme-dark .dog-calc-form-group label, .dog-calc-widget.dog-calc-theme-dark .dog-calc-result-label { color: #f5f3f7; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-form-group select, .dog-calc-widget.dog-calc-theme-dark .dog-calc-form-group input[type="number"], .dog-calc-widget.dog-calc-theme-dark .dog-calc-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-calc-widget.dog-calc-theme-dark .dog-calc-form-group select option { background-color: #312b3b; color: #f5f3f7; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-form-group select:focus, .dog-calc-widget.dog-calc-theme-dark .dog-calc-form-group input[type="number"]:focus, .dog-calc-widget.dog-calc-theme-dark .dog-calc-form-group input[type="text"]:focus { background-color: #312b3b; border-color: #f19a5f; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-form-group input[readonly] { background-color: #433c4f; color: #b8b0c2; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-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-calc-widget.dog-calc-theme-dark .dog-calc-result-value { color: #f5f3f7; background: rgba(241, 154, 95, 0.2); } .dog-calc-widget.dog-calc-theme-dark .dog-calc-footer { background: #24202d; border-color: #433c4f; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-unit-label { color: #b8b0c2; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-unit-label.active { color: #f5f3f7; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-slider { background-color: #433c4f; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-action-buttons { background: #312b3b; border-color: #433c4f; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-btn { background: #433c4f; border-color: #433c4f; color: #f5f3f7; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-btn:hover { background: #524a5f; border-color: #524a5f; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-btn-share:hover { border-color: #9f5999; color: #f19a5f; } .dog-calc-widget.dog-calc-theme-dark .dog-calc-btn-embed:hover { border-color: #7fa464; color: #7fa464; } /* System theme - follows user's OS preference */ @media (prefers-color-scheme: dark) { .dog-calc-widget.dog-calc-theme-system { color: #f5f3f7; } .dog-calc-widget.dog-calc-theme-system .dog-calc-section, .dog-calc-widget.dog-calc-theme-system .dog-calc-collapsible { background: #24202d; border-color: #433c4f; } .dog-calc-widget.dog-calc-theme-system .dog-calc-collapsible-header { background: #312b3b; border-color: #433c4f; } .dog-calc-widget.dog-calc-theme-system .dog-calc-collapsible-header:hover { background: #3a3446; } .dog-calc-widget.dog-calc-theme-system .dog-calc-section h2, .dog-calc-widget.dog-calc-theme-system .dog-calc-collapsible-header h3, .dog-calc-widget.dog-calc-theme-system .dog-calc-form-group label, .dog-calc-widget.dog-calc-theme-system .dog-calc-result-label { color: #f5f3f7; } .dog-calc-widget.dog-calc-theme-system .dog-calc-unit-label { color: #b8b0c2; } .dog-calc-widget.dog-calc-theme-system .dog-calc-unit-label.active { color: #f5f3f7; } .dog-calc-widget.dog-calc-theme-system .dog-calc-slider { background-color: #433c4f; } .dog-calc-widget.dog-calc-theme-system .dog-calc-form-group select, .dog-calc-widget.dog-calc-theme-system .dog-calc-form-group input[type="number"], .dog-calc-widget.dog-calc-theme-system .dog-calc-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-calc-widget.dog-calc-theme-system .dog-calc-form-group select option { background-color: #312b3b; color: #f5f3f7; } .dog-calc-widget.dog-calc-theme-system .dog-calc-form-group select:focus, .dog-calc-widget.dog-calc-theme-system .dog-calc-form-group input[type="number"]:focus, .dog-calc-widget.dog-calc-theme-system .dog-calc-form-group input[type="text"]:focus { background-color: #312b3b; border-color: #f19a5f; } .dog-calc-widget.dog-calc-theme-system .dog-calc-form-group input[readonly] { background-color: #433c4f; color: #b8b0c2; } .dog-calc-widget.dog-calc-theme-system .dog-calc-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-calc-widget.dog-calc-theme-system .dog-calc-result-value { color: #f5f3f7; background: rgba(241, 154, 95, 0.2); } .dog-calc-widget.dog-calc-theme-system .dog-calc-footer { background: #24202d; border-color: #433c4f; } .dog-calc-widget.dog-calc-theme-system .dog-calc-action-buttons { background: #312b3b; border-color: #433c4f; } .dog-calc-widget.dog-calc-theme-system .dog-calc-btn { background: #433c4f; border-color: #433c4f; color: #f5f3f7; } .dog-calc-widget.dog-calc-theme-system .dog-calc-btn:hover { background: #524a5f; border-color: #524a5f; } .dog-calc-widget.dog-calc-theme-system .dog-calc-btn-share:hover { border-color: #9f5999; color: #f19a5f; } .dog-calc-widget.dog-calc-theme-system .dog-calc-btn-embed:hover { border-color: #7fa464; color: #7fa464; } } /* Modal Styles */ .dog-calc-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-calc-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-calc-modal-embed { max-width: 700px; } @keyframes slideIn { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } .dog-calc-modal-close { position: absolute; right: 20px; top: 20px; font-size: 28px; font-weight: 300; color: #6f3f6d; cursor: pointer; transition: color 0.2s ease; } .dog-calc-modal-close:hover { color: #f19a5f; } .dog-calc-modal h3 { margin: 0 0 24px 0; color: #6f3f6d; font-size: 1.5rem; } /* Share Modal */ .dog-calc-share-buttons { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; margin-bottom: 20px; } .dog-calc-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-calc-share-facebook { background: #1877f2; } .dog-calc-share-facebook:hover { background: #1664d1; transform: translateY(-1px); } .dog-calc-share-twitter { background: #1da1f2; } .dog-calc-share-twitter:hover { background: #1991da; transform: translateY(-1px); } .dog-calc-share-linkedin { background: #0a66c2; } .dog-calc-share-linkedin:hover { background: #084d95; transform: translateY(-1px); } .dog-calc-share-email { background: #6f3f6d; } .dog-calc-share-email:hover { background: #5a3357; transform: translateY(-1px); } .dog-calc-share-copy { background: #f19a5f; } .dog-calc-share-copy:hover { background: #e87741; transform: translateY(-1px); } .dog-calc-share-url { display: flex; width: 100%; } .dog-calc-share-url input { flex: 1; width: 100%; padding: 10px 16px; border: 1px solid #e8e3ed; border-radius: 6px; font-size: 0.9rem; font-family: monospace; background: #f8f5fa; color: #6f3f6d; } /* Embed Modal */ .dog-calc-embed-options { display: flex; flex-direction: column; gap: 24px; } .dog-calc-embed-option h4 { margin: 0 0 8px 0; color: #6f3f6d; font-size: 1.1rem; } .dog-calc-embed-option p { margin: 0 0 16px 0; color: #635870; font-size: 0.9rem; } /* Default (light theme) code containers */ .dog-calc-code-container { position: relative; background: #ffffff; border: 1px solid #e8e3ed; border-radius: 6px; overflow: hidden; } .dog-calc-code-container pre { margin: 0; padding: 16px 60px 16px 16px; overflow-x: auto; } .dog-calc-code-container code { color: #312b3b; font-family: 'Consolas', 'Monaco', 'Courier New', monospace; font-size: 0.85rem; line-height: 1.4; } .dog-calc-copy-btn { position: absolute; top: 8px; right: 8px; padding: 6px 10px; background: #f19a5f; color: white; border: none; border-radius: 4px; font-size: 0.8rem; font-weight: 500; cursor: pointer; transition: all 0.2s ease; font-family: inherit; z-index: 1; } /* Better container backgrounds for contrast */ .dog-calc-embed-option { border: 1px solid #e8e3ed; border-radius: 8px; padding: 20px; background: #fcfafd; } .dog-calc-copy-btn:hover { background: #e87741; } .dog-calc-copy-btn.copied { background: #7fa464; } .dog-calc-copy-btn.copied:hover { background: #7fa464; } /* Dark theme modal styles */ .dog-calc-theme-dark .dog-calc-modal-content, .dog-calc-widget.dog-calc-theme-dark .dog-calc-modal-content { background-color: #24202d; border-color: #433c4f; } .dog-calc-theme-dark .dog-calc-modal h3, .dog-calc-widget.dog-calc-theme-dark .dog-calc-modal h3 { color: #f5f3f7; } .dog-calc-theme-dark .dog-calc-modal-close, .dog-calc-widget.dog-calc-theme-dark .dog-calc-modal-close { color: #f5f3f7; } .dog-calc-theme-dark .dog-calc-modal-close:hover, .dog-calc-widget.dog-calc-theme-dark .dog-calc-modal-close:hover { color: #f19a5f; } .dog-calc-theme-dark .dog-calc-share-url input, .dog-calc-widget.dog-calc-theme-dark .dog-calc-share-url input { background: #312b3b; border-color: #433c4f; color: #f5f3f7; } .dog-calc-theme-dark .dog-calc-embed-option, .dog-calc-widget.dog-calc-theme-dark .dog-calc-embed-option { background: #312b3b; border-color: #433c4f; } .dog-calc-theme-dark .dog-calc-embed-option h4, .dog-calc-widget.dog-calc-theme-dark .dog-calc-embed-option h4 { color: #f5f3f7; } .dog-calc-theme-dark .dog-calc-embed-option p, .dog-calc-widget.dog-calc-theme-dark .dog-calc-embed-option p { color: #b8b0c2; } /* Dark theme code containers - different from embed option background */ .dog-calc-theme-dark .dog-calc-code-container, .dog-calc-widget.dog-calc-theme-dark .dog-calc-code-container { background: #1a1621; border-color: #2a2330; } .dog-calc-theme-dark .dog-calc-code-container code, .dog-calc-widget.dog-calc-theme-dark .dog-calc-code-container code { color: #f5f3f7; } /* System theme modal styles */ @media (prefers-color-scheme: dark) { .dog-calc-theme-system .dog-calc-modal-content, .dog-calc-widget.dog-calc-theme-system .dog-calc-modal-content { background-color: #24202d; border-color: #433c4f; } .dog-calc-theme-system .dog-calc-modal h3, .dog-calc-widget.dog-calc-theme-system .dog-calc-modal h3 { color: #f5f3f7; } .dog-calc-theme-system .dog-calc-modal-close, .dog-calc-widget.dog-calc-theme-system .dog-calc-modal-close { color: #f5f3f7; } .dog-calc-theme-system .dog-calc-modal-close:hover, .dog-calc-widget.dog-calc-theme-system .dog-calc-modal-close:hover { color: #f19a5f; } .dog-calc-theme-system .dog-calc-share-url input, .dog-calc-widget.dog-calc-theme-system .dog-calc-share-url input { background: #312b3b; border-color: #433c4f; color: #f5f3f7; } .dog-calc-theme-system .dog-calc-embed-option, .dog-calc-widget.dog-calc-theme-system .dog-calc-embed-option { background: #312b3b; border-color: #433c4f; } .dog-calc-theme-system .dog-calc-embed-option h4, .dog-calc-widget.dog-calc-theme-system .dog-calc-embed-option h4 { color: #f5f3f7; } .dog-calc-theme-system .dog-calc-embed-option p, .dog-calc-widget.dog-calc-theme-system .dog-calc-embed-option p { color: #b8b0c2; } /* System theme code containers - different from embed option background */ .dog-calc-theme-system .dog-calc-code-container, .dog-calc-widget.dog-calc-theme-system .dog-calc-code-container { background: #1a1621; border-color: #2a2330; } .dog-calc-theme-system .dog-calc-code-container code, .dog-calc-widget.dog-calc-theme-system .dog-calc-code-container code { color: #f5f3f7; } } `; // Widget HTML template const widgetTemplate = ` `; // Calculator class class DogCalorieCalculatorWidget { constructor(container, options = {}) { this.container = container; this.currentMER = 0; this.isImperial = false; this.theme = options.theme || 'system'; // 'light', 'dark', 'system' this.scale = options.scale || 1.0; // 0.5 to 2.0 this.init(); } init() { this.injectStyles(); this.injectHTML(); this.applyTheme(); this.applyScale(); this.bindEvents(); this.updateUnitLabels(); } injectStyles() { if (!document.getElementById('dog-calc-widget-styles')) { const style = document.createElement('style'); style.id = 'dog-calc-widget-styles'; style.textContent = widgetStyles; document.head.appendChild(style); } } injectHTML() { this.container.innerHTML = widgetTemplate; // Move modals to document body for proper positioning and theme inheritance this.moveModalsToBody(); } moveModalsToBody() { const shareModal = this.container.querySelector('#shareModal'); const embedModal = this.container.querySelector('#embedModal'); if (shareModal) { shareModal.remove(); shareModal.classList.add(`dog-calc-theme-${this.theme}`); document.body.appendChild(shareModal); } if (embedModal) { embedModal.remove(); embedModal.classList.add(`dog-calc-theme-${this.theme}`); document.body.appendChild(embedModal); } // Bind events for modal elements now that they're in the document this.bindModalEvents(); } applyTheme() { const widget = this.container.querySelector('.dog-calc-widget'); if (!widget) return; // Remove existing theme classes widget.classList.remove('dog-calc-theme-light', 'dog-calc-theme-dark', 'dog-calc-theme-system'); // Apply new theme class widget.classList.add(`dog-calc-theme-${this.theme}`); // Also apply theme to modals if they exist const shareModal = document.querySelector('#shareModal'); const embedModal = document.querySelector('#embedModal'); if (shareModal) { shareModal.classList.remove('dog-calc-theme-light', 'dog-calc-theme-dark', 'dog-calc-theme-system'); shareModal.classList.add(`dog-calc-theme-${this.theme}`); } if (embedModal) { embedModal.classList.remove('dog-calc-theme-light', 'dog-calc-theme-dark', 'dog-calc-theme-system'); embedModal.classList.add(`dog-calc-theme-${this.theme}`); } } applyScale() { const widget = this.container.querySelector('.dog-calc-widget'); if (!widget) return; // Clamp scale between 0.5 and 2.0 for usability const clampedScale = Math.max(0.5, Math.min(2.0, this.scale)); if (clampedScale !== 1.0) { widget.style.transform = `scale(${clampedScale})`; widget.style.transformOrigin = 'top center'; // Adjust container to account for scaling const actualHeight = widget.offsetHeight * clampedScale; widget.style.marginBottom = `${(clampedScale - 1) * widget.offsetHeight}px`; } } bindEvents() { const weightInput = this.container.querySelector('#weight'); const dogTypeSelect = this.container.querySelector('#dogType'); const foodEnergyInput = this.container.querySelector('#foodEnergy'); const daysInput = this.container.querySelector('#days'); const unitSelect = this.container.querySelector('#unit'); const unitToggle = this.container.querySelector('#unitToggle'); if (weightInput) { weightInput.addEventListener('input', () => this.updateCalorieCalculations()); weightInput.addEventListener('blur', () => this.validateWeight()); } if (dogTypeSelect) dogTypeSelect.addEventListener('change', () => this.updateCalorieCalculations()); if (foodEnergyInput) { foodEnergyInput.addEventListener('input', () => this.updateFoodCalculations()); foodEnergyInput.addEventListener('blur', () => this.validateFoodEnergy()); } const energyUnitSelect = this.container.querySelector('#energyUnit'); if (energyUnitSelect) energyUnitSelect.addEventListener('change', () => this.updateFoodCalculations()); if (daysInput) { daysInput.addEventListener('input', () => this.updateFoodCalculations()); daysInput.addEventListener('blur', () => this.validateDays()); } if (unitSelect) unitSelect.addEventListener('change', () => this.updateFoodCalculations()); if (unitToggle) unitToggle.addEventListener('change', () => this.toggleUnits()); // Modal event listeners - buttons are in widget, modals are in document const shareBtn = this.container.querySelector('#shareBtn'); const embedBtn = this.container.querySelector('#embedBtn'); if (shareBtn) shareBtn.addEventListener('click', () => this.showShareModal()); if (embedBtn) embedBtn.addEventListener('click', () => this.showEmbedModal()); } bindModalEvents() { // Modal elements are in document body, so we need to set up their events after moving them const shareModalClose = document.querySelector('#shareModalClose'); const embedModalClose = document.querySelector('#embedModalClose'); if (shareModalClose) shareModalClose.addEventListener('click', () => this.hideShareModal()); if (embedModalClose) embedModalClose.addEventListener('click', () => this.hideEmbedModal()); // Share buttons const shareFacebook = document.querySelector('#shareFacebook'); const shareTwitter = document.querySelector('#shareTwitter'); const shareLinkedIn = document.querySelector('#shareLinkedIn'); const shareEmail = document.querySelector('#shareEmail'); const shareCopy = document.querySelector('#shareCopy'); if (shareFacebook) shareFacebook.addEventListener('click', () => this.shareToFacebook()); if (shareTwitter) shareTwitter.addEventListener('click', () => this.shareToTwitter()); if (shareLinkedIn) shareLinkedIn.addEventListener('click', () => this.shareToLinkedIn()); if (shareEmail) shareEmail.addEventListener('click', () => this.shareViaEmail()); if (shareCopy) shareCopy.addEventListener('click', () => this.copyShareLink()); // Copy buttons const copyWidget = document.querySelector('#copyWidget'); const copyIframe = document.querySelector('#copyIframe'); if (copyWidget) copyWidget.addEventListener('click', () => this.copyEmbedCode('widget')); if (copyIframe) copyIframe.addEventListener('click', () => this.copyEmbedCode('iframe')); // Close modals on outside click const shareModal = document.querySelector('#shareModal'); const embedModal = document.querySelector('#embedModal'); if (shareModal) { shareModal.addEventListener('click', (e) => { if (e.target === shareModal) this.hideShareModal(); }); } if (embedModal) { embedModal.addEventListener('click', (e) => { if (e.target === embedModal) this.hideEmbedModal(); }); } } toggleUnits() { const toggle = this.container.querySelector('#unitToggle'); this.isImperial = toggle.checked; this.updateUnitLabels(); this.convertExistingValues(); this.updateCalorieCalculations(); } updateUnitLabels() { const metricLabel = this.container.querySelector('#metricLabel'); const imperialLabel = this.container.querySelector('#imperialLabel'); const weightLabel = this.container.querySelector('#weightLabel'); const weightInput = this.container.querySelector('#weight'); const unitSelect = this.container.querySelector('#unit'); const energyUnitSelect = this.container.querySelector('#energyUnit'); 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"; weightInput.step = "0.1"; } if (unitSelect) { unitSelect.innerHTML = ` `; } // Set energy unit to kcal/cup for imperial if (energyUnitSelect && energyUnitSelect.value === 'kcal100g') { energyUnitSelect.value = 'kcalcup'; } } 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 (unitSelect) { unitSelect.innerHTML = ` `; } // Set energy unit to kcal/100g for metric if (energyUnitSelect && energyUnitSelect.value === 'kcalcup') { energyUnitSelect.value = 'kcal100g'; } } } convertExistingValues() { const weightInput = this.container.querySelector('#weight'); if (weightInput && weightInput.value) { const currentWeight = parseFloat(weightInput.value); if (!isNaN(currentWeight)) { if (this.isImperial) { weightInput.value = this.formatNumber(currentWeight * 2.20462, 1); } else { weightInput.value = this.formatNumber(currentWeight / 2.20462, 1); } } } } getWeightInKg() { const weightInput = this.container.querySelector('#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 = this.container.querySelector('#foodEnergy'); const energyUnitSelect = this.container.querySelector('#energyUnit'); if (!foodEnergyInput || !foodEnergyInput.value || !energyUnitSelect) return null; const energy = parseFloat(foodEnergyInput.value); if (isNaN(energy)) return null; const unit = energyUnitSelect.value; // Convert all units to kcal/100g for internal calculations switch (unit) { case 'kcal100g': return energy; case 'kcalkg': return energy / 10; // 1 kg = 10 × 100g case 'kcalcup': return energy / 1.2; // Assume 1 cup ≈ 120g for dry dog food case 'kcalcan': return energy / 4.5; // Assume 1 can ≈ 450g for wet dog food default: return energy; } } calculateRER(weightKg) { 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 = this.container.querySelector('#' + elementId); if (errorElement) { if (show) { errorElement.classList.remove('dog-calc-hidden'); } else { errorElement.classList.add('dog-calc-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) { if (decimals === 0) { return Math.round(num).toString(); } return num.toFixed(decimals).replace(/\.?0+$/, ''); } validateWeight() { const weight = this.container.querySelector('#weight')?.value; if (weight && !this.validateInput(weight, 0.1)) { this.showError('weightError', true); } else { this.showError('weightError', false); } } validateFoodEnergy() { const energyInput = this.container.querySelector('#foodEnergy'); const energyUnitSelect = this.container.querySelector('#energyUnit'); if (!energyInput || !energyInput.value) { this.showError('foodEnergyError', false); return; } const energy = parseFloat(energyInput.value); const unit = energyUnitSelect?.value || 'kcal100g'; let minValue = 1; switch (unit) { case 'kcal100g': minValue = 1; break; case 'kcalkg': minValue = 10; break; case 'kcalcup': minValue = 50; break; case 'kcalcan': minValue = 100; break; } if (!this.validateInput(energy, minValue)) { this.showError('foodEnergyError', true); } else { this.showError('foodEnergyError', false); } } validateDays() { const days = this.container.querySelector('#days')?.value; if (days && !this.validateInput(days, 1, true)) { this.showError('daysError', true); } else { this.showError('daysError', false); } } updateCalorieCalculations() { const dogTypeSelect = this.container.querySelector('#dogType'); const calorieResults = this.container.querySelector('#calorieResults'); const rerValue = this.container.querySelector('#rerValue'); const merValue = this.container.querySelector('#merValue'); if (!dogTypeSelect || !calorieResults || !rerValue || !merValue) { return; } const weightKg = this.getWeightInKg(); const dogTypeFactor = dogTypeSelect.value; this.showError('weightError', false); if (!weightKg || weightKg < 0.1) { const weightInput = this.container.querySelector('#weight'); if (weightInput && weightInput.value) 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 = this.container.querySelector('#days'); const unitSelect = this.container.querySelector('#unit'); const dailyFoodResults = this.container.querySelector('#dailyFoodResults'); const dailyFoodValue = this.container.querySelector('#dailyFoodValue'); const totalFoodDisplay = this.container.querySelector('#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); if (!energyPer100g || energyPer100g < 0.1) { const foodEnergyInput = this.container.querySelector('#foodEnergy'); if (foodEnergyInput && foodEnergyInput.value) 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; } // Modal functionality showShareModal() { const modal = document.querySelector('#shareModal'); const shareUrl = document.querySelector('#shareUrl'); if (modal && shareUrl) { shareUrl.value = window.location.href; modal.style.display = 'block'; } } hideShareModal() { const modal = document.querySelector('#shareModal'); if (modal) modal.style.display = 'none'; } showEmbedModal() { const modal = document.querySelector('#embedModal'); const widgetCode = document.querySelector('#widgetCode'); const iframeCode = document.querySelector('#iframeCode'); if (modal && widgetCode && iframeCode) { // Extract domain from current URL to build embed subdomain const hostname = window.location.hostname; const protocol = window.location.protocol; const embedUrl = `${protocol}//embed.${hostname}`; const theme = this.theme !== 'system' ? ` data-theme="${this.theme}"` : ''; widgetCode.textContent = ` `; iframeCode.textContent = ``; modal.style.display = 'block'; } } hideEmbedModal() { const modal = document.querySelector('#embedModal'); if (modal) modal.style.display = 'none'; } shareToFacebook() { const url = encodeURIComponent(window.location.href); window.open(`https://www.facebook.com/sharer/sharer.php?u=${url}`, '_blank', 'width=600,height=400'); } shareToTwitter() { const url = encodeURIComponent(window.location.href); const text = encodeURIComponent('Check out this useful dog calorie calculator!'); window.open(`https://twitter.com/intent/tweet?url=${url}&text=${text}`, '_blank', 'width=600,height=400'); } shareToLinkedIn() { const url = encodeURIComponent(window.location.href); window.open(`https://www.linkedin.com/sharing/share-offsite/?url=${url}`, '_blank', 'width=600,height=400'); } shareViaEmail() { const subject = encodeURIComponent('Dog Calorie Calculator'); const body = encodeURIComponent(`Check out this useful dog calorie calculator: ${window.location.href}`); window.location.href = `mailto:?subject=${subject}&body=${body}`; } async copyShareLink() { const shareUrl = document.querySelector('#shareUrl'); const copyBtn = document.querySelector('#shareCopy'); if (shareUrl && copyBtn) { try { await navigator.clipboard.writeText(shareUrl.value); const originalText = copyBtn.textContent; copyBtn.textContent = 'Copied!'; copyBtn.classList.add('copied'); setTimeout(() => { copyBtn.textContent = originalText; copyBtn.classList.remove('copied'); }, 2000); } catch (err) { // Fallback for older browsers shareUrl.select(); document.execCommand('copy'); } } } async copyEmbedCode(type) { const codeElement = document.querySelector(type === 'widget' ? '#widgetCode' : '#iframeCode'); const copyBtn = document.querySelector(type === 'widget' ? '#copyWidget' : '#copyIframe'); if (codeElement && copyBtn) { try { await navigator.clipboard.writeText(codeElement.textContent); const originalText = copyBtn.textContent; copyBtn.textContent = 'Copied!'; copyBtn.classList.add('copied'); setTimeout(() => { copyBtn.textContent = originalText; copyBtn.classList.remove('copied'); }, 2000); } catch (err) { // Fallback for older browsers console.log('Copy fallback needed'); } } } } // Auto-initialize widget function initDogCalorieCalculator() { const containers = document.querySelectorAll('#dog-calorie-calculator, .dog-calorie-calculator'); containers.forEach(container => { if (!container.dataset.initialized) { const theme = container.dataset.theme || 'system'; const scale = parseFloat(container.dataset.scale) || 1.0; new DogCalorieCalculatorWidget(container, { theme, scale }); container.dataset.initialized = 'true'; } }); } // Initialize when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initDogCalorieCalculator); } else { initDogCalorieCalculator(); } // Expose for manual initialization window.DogCalorieCalculatorWidget = DogCalorieCalculatorWidget; })();