2025-06-15 21:57:27 +02:00
/ * *
* Dog Calorie Calculator Widget
* Embeddable JavaScript widget for websites
*
2025-08-17 21:20:05 +02:00
* THIS CODE IS AUTO - GENERATED FROM src / FILES - DO NOT EDIT MANUALLY
* Edit files in src / directory and run 'node build.js' to update
2025-06-19 10:50:09 +02:00
*
2025-06-15 21:57:27 +02:00
* Usage :
* < script src = "sundog-dog-food-calculator.js" > < / s c r i p t >
* < div id = "dog-calorie-calculator" > < / d i v >
*
* Or with options :
* < div id = "dog-calorie-calculator" data - theme = "dark" data - scale = "1.2" > < / d i v >
*
* By Canine Nutrition and Wellness
* https : //caninenutritionandwellness.com
* /
( function ( ) {
'use strict' ;
2025-06-19 10:50:09 +02:00
// Inject widget styles
2025-06-15 21:57:27 +02:00
const CSS _STYLES = ` /* Sundog Dog Food Calorie Calculator Styles */
2025-06-26 17:24:32 +02:00
/* CSS Variables for theming */
: root {
-- bg - primary : # fdfcfe ;
-- bg - secondary : # ffffff ;
-- border - color : # e8e3ed ;
-- text - primary : # 6 f3f6d ;
-- text - secondary : # 8 f7a8e ;
-- accent - color : # f19a5f ;
-- text - label : # 635870 ; /* For form labels, secondary UI text */
-- success - color : # 7 fa464 ; /* Green for success states */
-- bg - tertiary : # f8f5fa ; /* Light background variant */
-- error - color : # e87159 ; /* Error states and messages */
}
2025-06-15 21:57:27 +02:00
body {
margin : 0 ;
padding : 0 ;
background : transparent ;
overflow - x : hidden ;
font - family : 'Montserrat' , - apple - system , BlinkMacSystemFont , 'Segoe UI' , Roboto , Oxygen , Ubuntu , Cantarell , sans - serif ;
line - height : 1.5 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container {
2025-06-15 21:57:27 +02:00
max - width : 600 px ;
margin : 0 auto ;
padding : 24 px ;
box - sizing : border - box ;
opacity : 0 ;
transition : opacity 0.3 s ease ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . loaded {
2025-06-15 21:57:27 +02:00
opacity : 1 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container * ,
. dog - calculator - container * : : before ,
. dog - calculator - container * : : after {
2025-06-15 21:57:27 +02:00
box - sizing : border - box ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - section {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - primary ) ;
border : 1 px solid var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
border - radius : 8 px 8 px 0 0 ;
padding : 24 px ;
margin - bottom : 0 ;
box - shadow : 0 3 px 6 px rgba ( 0 , 0 , 0 , 0.08 ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - section - header {
2025-06-15 21:57:27 +02:00
display : flex ;
justify - content : space - between ;
align - items : center ;
margin - bottom : 24 px ;
flex - wrap : wrap ;
gap : 16 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - section h2 {
2025-06-15 21:57:27 +02:00
margin : 0 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
font - size : 1.5 rem ;
font - weight : 600 ;
}
/* Unit Switch */
2025-06-15 22:59:45 +02:00
. dog - calculator - unit - switch {
2025-06-15 21:57:27 +02:00
display : flex ;
align - items : center ;
gap : 12 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - unit - label {
2025-06-15 21:57:27 +02:00
font - size : 0.9 rem ;
font - weight : 500 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - label ) ;
2025-06-15 21:57:27 +02:00
transition : color 0.2 s ease ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - unit - label . active {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
font - weight : 600 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - switch {
2025-06-15 21:57:27 +02:00
position : relative ;
display : inline - block ;
width : 48 px ;
height : 24 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - switch input {
2025-06-15 21:57:27 +02:00
opacity : 0 ;
width : 0 ;
height : 0 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - slider {
2025-06-15 21:57:27 +02:00
position : absolute ;
cursor : pointer ;
top : 0 ;
left : 0 ;
right : 0 ;
bottom : 0 ;
2025-06-26 17:24:32 +02:00
background - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
transition : 0.3 s ;
border - radius : 24 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - slider : before {
2025-06-15 21:57:27 +02:00
position : absolute ;
content : "" ;
height : 18 px ;
width : 18 px ;
left : 3 px ;
bottom : 3 px ;
background - color : white ;
transition : 0.3 s ;
border - radius : 50 % ;
box - shadow : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.2 ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - switch input : checked + . dog - calculator - slider {
2025-06-15 21:57:27 +02:00
background - color : # f19a5f ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - switch input : checked + . dog - calculator - slider : before {
2025-06-15 21:57:27 +02:00
transform : translateX ( 24 px ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - form - group {
2025-06-15 21:57:27 +02:00
margin - bottom : 20 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - form - group label {
2025-06-15 21:57:27 +02:00
display : block ;
margin - bottom : 8 px ;
font - weight : 500 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
font - size : 1 rem ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - form - group select ,
. dog - calculator - form - group input [ type = "number" ] ,
. dog - calculator - form - group input [ type = "text" ] {
2025-06-15 21:57:27 +02:00
width : 100 % ;
padding : 12 px 16 px ;
2025-06-26 17:24:32 +02:00
border : 1 px solid var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
border - radius : 6 px ;
font - size : 1 rem ;
font - family : inherit ;
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - secondary ) ;
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
transition : all 0.2 s 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 12 px center ;
background - size : 20 px ;
padding - right : 40 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - form - group select option {
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - secondary ) ;
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - form - group input [ type = "number" ] ,
. dog - calculator - form - group input [ type = "text" ] {
2025-06-15 21:57:27 +02:00
background - image : none ;
padding - right : 16 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - form - group select : focus ,
. dog - calculator - form - group input [ type = "number" ] : focus ,
. dog - calculator - form - group input [ type = "text" ] : focus {
2025-06-15 21:57:27 +02:00
outline : none ;
border - color : # f19a5f ;
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - secondary ) ;
2025-06-15 21:57:27 +02:00
box - shadow : 0 0 0 3 px rgba ( 241 , 154 , 95 , 0.1 ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - form - group input [ readonly ] {
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - tertiary ) ;
2025-06-15 21:57:27 +02:00
cursor : not - allowed ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - label ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - results {
2025-06-15 21:57:27 +02:00
background : linear - gradient ( 135 deg , rgba ( 241 , 154 , 95 , 0.08 ) 0 % , rgba ( 241 , 154 , 95 , 0.04 ) 100 % ) ;
border : 1 px solid rgba ( 241 , 154 , 95 , 0.2 ) ;
border - radius : 6 px ;
padding : 20 px ;
margin - top : 24 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - result - item {
2025-06-15 21:57:27 +02:00
display : flex ;
justify - content : space - between ;
align - items : center ;
margin - bottom : 12 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - result - item : last - child {
2025-06-15 21:57:27 +02:00
margin - bottom : 0 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - result - label {
2025-06-15 21:57:27 +02:00
font - weight : 500 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
font - size : 0.95 rem ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - result - value {
2025-06-15 21:57:27 +02:00
font - weight : 600 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
font - size : 1.1 rem ;
padding : 4 px 12 px ;
background : rgba ( 241 , 154 , 95 , 0.15 ) ;
border - radius : 4 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - collapsible {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - primary ) ;
border : 1 px solid var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
border - top : none ;
margin - bottom : 0 ;
overflow : hidden ;
box - shadow : 0 3 px 6 px rgba ( 0 , 0 , 0 , 0.08 ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - collapsible - header {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - tertiary ) ;
2025-06-15 21:57:27 +02:00
padding : 20 px 24 px ;
2025-06-26 17:24:32 +02:00
border - bottom : 1 px solid var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - collapsible - header h3 {
2025-06-15 21:57:27 +02:00
margin : 0 ;
font - size : 1.25 rem ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
font - weight : 600 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - collapsible - content {
2025-06-15 21:57:27 +02:00
display : block ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - collapsible - inner {
2025-06-15 21:57:27 +02:00
padding : 24 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - input - group {
2025-06-15 21:57:27 +02:00
display : flex ;
gap : 16 px ;
align - items : flex - end ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - input - group . dog - calculator - form - group {
2025-06-15 21:57:27 +02:00
flex : 1 ;
margin - bottom : 0 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - unit - select {
2025-06-15 21:57:27 +02:00
min - width : 120 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - error {
2025-06-26 17:24:32 +02:00
color : var ( -- error - color ) ;
2025-06-15 21:57:27 +02:00
font - size : 0.875 rem ;
margin - top : 6 px ;
font - weight : 500 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - hidden {
2025-06-15 21:57:27 +02:00
display : none ;
}
/* Action Buttons */
2025-06-15 22:59:45 +02:00
. dog - calculator - action - buttons {
2025-06-15 21:57:27 +02:00
display : flex ;
justify - content : center ;
gap : 16 px ;
padding : 20 px ;
2025-06-26 17:24:32 +02:00
background : var ( -- bg - tertiary ) ;
border - left : 1 px solid var ( -- border - color ) ;
border - right : 1 px solid var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
margin - top : - 1 px ;
box - shadow : 0 3 px 6 px rgba ( 0 , 0 , 0 , 0.08 ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - btn {
2025-06-15 21:57:27 +02:00
padding : 8 px 16 px ;
2025-06-26 17:24:32 +02:00
border : 1 px solid var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
border - radius : 6 px ;
font - size : 0.9 rem ;
font - weight : 500 ;
font - family : inherit ;
cursor : pointer ;
transition : all 0.2 s ease ;
display : inline - flex ;
align - items : center ;
gap : 6 px ;
background : white ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - btn : hover {
2025-06-15 21:57:27 +02:00
transform : translateY ( - 1 px ) ;
box - shadow : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.1 ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - btn - share : hover {
2025-06-15 21:57:27 +02:00
border - color : # 9 f5999 ;
color : # 9 f5999 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - btn - embed : hover {
2025-06-26 17:24:32 +02:00
border - color : var ( -- success - color ) ;
color : var ( -- success - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - footer {
2025-06-15 21:57:27 +02:00
text - align : center ;
padding : 20 px ;
2025-06-26 17:24:32 +02:00
background : var ( -- bg - primary ) ;
border : 1 px solid var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
border - radius : 0 0 8 px 8 px ;
border - top : none ;
margin - top : - 1 px ;
box - shadow : 0 3 px 6 px rgba ( 0 , 0 , 0 , 0.08 ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - footer a {
2025-06-15 21:57:27 +02:00
color : # 9 f5999 ;
text - decoration : none ;
font - size : 0.9 rem ;
font - weight : 500 ;
transition : color 0.2 s ease ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - footer a : hover {
2025-06-15 21:57:27 +02:00
color : # f19a5f ;
text - decoration : underline ;
}
/* Mobile Responsive Design */
@ media ( max - width : 576 px ) {
2025-06-15 22:59:45 +02:00
. dog - calculator - container {
2025-06-15 21:57:27 +02:00
padding : 16 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - section ,
. dog - calculator - collapsible - inner {
2025-06-15 21:57:27 +02:00
padding : 20 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - section h2 ,
. dog - calculator - collapsible - header h3 {
2025-06-15 21:57:27 +02:00
font - size : 1.3 rem ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - section - header {
2025-06-15 21:57:27 +02:00
flex - direction : column ;
align - items : stretch ;
gap : 12 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - section h2 {
2025-06-15 21:57:27 +02:00
text - align : center ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - unit - switch {
2025-06-15 21:57:27 +02:00
justify - content : center ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - action - buttons {
2025-06-15 21:57:27 +02:00
flex - direction : column ;
padding : 16 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - btn {
2025-06-15 21:57:27 +02:00
width : 100 % ;
justify - content : center ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - input - group {
2025-06-26 13:10:32 +02:00
flex - direction : row ;
gap : 12 px ;
align - items : flex - end ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - input - group . dog - calculator - form - group {
2025-06-26 13:10:32 +02:00
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 : 100 px ;
}
/* Make sure number inputs don't get too wide */
. dog - calculator - input - group input [ type = "number" ] {
max - width : 100 % ;
2025-06-15 21:57:27 +02:00
}
2025-06-26 13:10:32 +02:00
/* Ensure dropdowns don't overflow their containers */
. dog - calculator - input - group select {
max - width : 100 % ;
overflow : hidden ;
text - overflow : ellipsis ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - result - item {
2025-06-15 21:57:27 +02:00
flex - direction : column ;
align - items : flex - start ;
gap : 8 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - result - value {
2025-06-15 21:57:27 +02:00
align - self : stretch ;
text - align : center ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - collapsible - header {
2025-06-15 21:57:27 +02:00
padding : 16 px 20 px ;
}
}
2025-08-18 12:45:44 +02:00
/* Feeding Configuration Styles */
. dog - calculator - container . dog - calculator - feeding - config {
margin - top : 20 px ;
padding : 16 px ;
background : var ( -- bg - tertiary ) ;
border : 1 px solid var ( -- border - color ) ;
border - radius : 8 px ;
}
. dog - calculator - container . dog - calculator - frequency - row {
display : flex ;
align - items : center ;
gap : 16 px ;
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 : 20 px ;
align - items : center ;
}
. dog - calculator - container . dog - calculator - radio - group label {
display : flex ;
align - items : center ;
gap : 6 px ;
cursor : pointer ;
color : var ( -- text - primary ) ;
font - size : 0.95 rem ;
}
. 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 : 6 px ;
2025-08-18 14:36:25 +02:00
margin : 0 auto ;
2025-08-18 12:45:44 +02:00
}
. dog - calculator - container . dog - calculator - meal - input span {
color : var ( -- text - secondary ) ;
font - size : 0.95 rem ;
}
. dog - calculator - container . dog - calculator - meal - input input [ type = "number" ] {
width : 50 px ;
padding : 4 px 8 px ;
border : 1 px solid var ( -- border - color ) ;
border - radius : 4 px ;
font - size : 0.95 rem ;
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 2 px rgba ( 241 , 154 , 95 , 0.1 ) ;
}
/* Update meal note styling */
. dog - calculator - container # mealNote {
color : var ( -- text - secondary ) ;
font - size : 0.9 rem ;
font - weight : normal ;
margin - left : 4 px ;
}
/* Mobile responsive adjustments for feeding config */
@ media ( max - width : 480 px ) {
. dog - calculator - meal - input {
margin - left : 0 ;
width : 100 % ;
margin - top : 8 px ;
}
. dog - calculator - frequency - row {
flex - direction : column ;
align - items : flex - start ;
}
}
2025-06-15 21:57:27 +02:00
/* Dark theme - manual override */
2025-08-18 09:05:00 +02:00
. dog - calculator - container . theme - dark {
2025-06-26 17:24:32 +02:00
-- bg - primary : # 24202 d ;
-- bg - secondary : # 312 b3b ;
2025-08-18 12:45:44 +02:00
-- bg - tertiary : # 1 f1b26 ;
2025-06-26 17:24:32 +02:00
-- border - color : # 433 c4f ;
-- text - primary : # f5f3f7 ;
-- text - secondary : # b8b0c2 ;
2025-08-18 12:45:44 +02:00
-- text - label : # 9 f94ae ;
-- success - color : # 7 fa464 ;
-- error - color : # e87159 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - section ,
. dog - calculator - container . theme - dark . dog - calculator - collapsible {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - primary ) ;
border - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - collapsible - header {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - collapsible - header : hover {
2025-06-15 21:57:27 +02:00
background : # 3 a3446 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - section h2 ,
. dog - calculator - container . theme - dark . dog - calculator - collapsible - header h3 ,
. dog - calculator - container . theme - dark . dog - calculator - form - group label ,
. dog - calculator - container . theme - dark . dog - calculator - result - label {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - unit - label {
2025-06-26 17:24:32 +02:00
color : var ( -- text - secondary )
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - unit - label . active {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - slider {
2025-06-26 17:24:32 +02:00
background - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - form - group select ,
. dog - calculator - container . theme - dark . dog - calculator - form - group input [ type = "number" ] ,
. dog - calculator - container . theme - dark . dog - calculator - form - group input [ type = "text" ] {
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
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" ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - form - group select option {
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - secondary ) ;
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - form - group select : focus ,
. dog - calculator - container . theme - dark . dog - calculator - form - group input [ type = "number" ] : focus ,
. dog - calculator - container . theme - dark . dog - calculator - form - group input [ type = "text" ] : focus {
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - secondary ) ;
2025-06-15 21:57:27 +02:00
border - color : # f19a5f ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - form - group input [ readonly ] {
2025-06-26 17:24:32 +02:00
background - color : var ( -- border - color ) ;
color : var ( -- text - secondary )
}
. dog - calculator - container . theme - dark . dog - calculator - inline - unit {
background - color : var ( -- bg - secondary ) ;
border - color : rgba ( 241 , 154 , 95 , 0.5 ) ;
color : var ( -- text - primary ) ;
box - shadow : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.3 ) ;
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 - container . theme - dark . dog - calculator - inline - unit : hover {
border - color : # f19a5f ;
box - shadow : 0 2 px 6 px rgba ( 241 , 154 , 95 , 0.3 ) ;
}
. dog - calculator - container . theme - dark . dog - calculator - inline - unit : focus {
border - color : # f19a5f ;
box - shadow : 0 0 0 3 px rgba ( 241 , 154 , 95 , 0.15 ) ;
}
. dog - calculator - container . theme - dark . dog - calculator - inline - unit option {
background - color : var ( -- bg - secondary ) ;
color : var ( -- text - primary ) ;
}
. dog - calculator - container . theme - dark . dog - calculator - unit - btn {
background - color : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
color : var ( -- text - primary ) ;
}
. dog - calculator - container . theme - dark . dog - calculator - unit - btn : hover {
border - color : # f19a5f ;
background : rgba ( 241 , 154 , 95 , 0.2 ) ;
}
. dog - calculator - container . theme - dark . dog - calculator - unit - btn . active {
border - color : # f19a5f ;
background : # f19a5f ;
color : white ;
2025-06-15 21:57:27 +02:00
}
2025-08-18 14:36:25 +02:00
. dog - calculator - container . theme - dark . dog - calculator - unit - btn : disabled {
opacity : 0.5 ;
cursor : not - allowed ;
border - color : var ( -- border - color ) ;
background : var ( -- bg - tertiary ) ;
color : var ( -- text - secondary ) ;
}
. dog - calculator - container . theme - dark . dog - calculator - unit - btn : disabled : hover {
border - color : var ( -- border - color ) ;
background : var ( -- bg - tertiary ) ;
}
2025-06-15 21:57:27 +02:00
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - results {
2025-06-15 21:57:27 +02:00
background : linear - gradient ( 135 deg , rgba ( 241 , 154 , 95 , 0.15 ) 0 % , rgba ( 241 , 154 , 95 , 0.08 ) 100 % ) ;
border - color : rgba ( 241 , 154 , 95 , 0.3 ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - result - value {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
background : rgba ( 241 , 154 , 95 , 0.2 ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - footer {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - primary ) ;
border - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - action - buttons {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - btn {
2025-06-26 17:24:32 +02:00
background : var ( -- border - color ) ;
border - color : var ( -- border - color ) ;
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - btn : hover {
2025-06-15 21:57:27 +02:00
background : # 524 a5f ;
border - color : # 524 a5f ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - btn - share : hover {
2025-06-15 21:57:27 +02:00
border - color : # 9 f5999 ;
color : # f19a5f ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - btn - embed : hover {
2025-06-26 17:24:32 +02:00
border - color : var ( -- success - color ) ;
color : var ( -- success - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-08-18 12:45:44 +02:00
/* Dark theme feeding configuration styles */
. dog - calculator - container . theme - dark . dog - calculator - feeding - config {
background : var ( -- bg - tertiary ) ;
border - color : var ( -- border - color ) ;
}
. dog - calculator - container . theme - dark . dog - calculator - frequency - row > label {
color : var ( -- text - label ) ;
}
. dog - calculator - container . theme - dark . dog - calculator - radio - group label {
color : var ( -- text - primary ) ;
}
. dog - calculator - container . theme - dark . dog - calculator - radio - group input [ type = "radio" ] : checked + span {
color : # f19a5f ;
}
. dog - calculator - container . theme - dark . dog - calculator - meal - input span {
color : var ( -- text - secondary ) ;
}
. dog - calculator - container . theme - dark . dog - calculator - meal - input input [ type = "number" ] {
background : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
color : var ( -- text - primary ) ;
}
. dog - calculator - container . theme - dark . dog - calculator - meal - input input [ type = "number" ] : focus {
border - color : # f19a5f ;
box - shadow : 0 0 0 2 px rgba ( 241 , 154 , 95 , 0.15 ) ;
}
. dog - calculator - container . theme - dark # mealNote {
color : var ( -- text - secondary ) ;
}
2025-06-15 21:57:27 +02:00
/* System theme - follows user's OS preference */
@ media ( prefers - color - scheme : dark ) {
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system {
2025-06-26 17:24:32 +02:00
-- bg - primary : # 24202 d ;
-- bg - secondary : # 312 b3b ;
2025-08-18 12:45:44 +02:00
-- bg - tertiary : # 1 f1b26 ;
2025-06-26 17:24:32 +02:00
-- border - color : # 433 c4f ;
-- text - primary : # f5f3f7 ;
-- text - secondary : # b8b0c2 ;
2025-08-18 12:45:44 +02:00
-- text - label : # 9 f94ae ;
-- success - color : # 7 fa464 ;
-- error - color : # e87159 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - section ,
. dog - calculator - container . theme - system . dog - calculator - collapsible {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - primary ) ;
border - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - collapsible - header {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - collapsible - header : hover {
2025-06-15 21:57:27 +02:00
background : # 3 a3446 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - section h2 ,
. dog - calculator - container . theme - system . dog - calculator - collapsible - header h3 ,
. dog - calculator - container . theme - system . dog - calculator - form - group label ,
. dog - calculator - container . theme - system . dog - calculator - result - label {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - unit - label {
2025-06-26 17:24:32 +02:00
color : var ( -- text - secondary )
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - unit - label . active {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - slider {
2025-06-26 17:24:32 +02:00
background - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - form - group select ,
. dog - calculator - container . theme - system . dog - calculator - form - group input [ type = "number" ] ,
. dog - calculator - container . theme - system . dog - calculator - form - group input [ type = "text" ] {
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
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" ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - form - group select option {
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - secondary ) ;
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - form - group select : focus ,
. dog - calculator - container . theme - system . dog - calculator - form - group input [ type = "number" ] : focus ,
. dog - calculator - container . theme - system . dog - calculator - form - group input [ type = "text" ] : focus {
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - secondary ) ;
2025-06-15 21:57:27 +02:00
border - color : # f19a5f ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - form - group input [ readonly ] {
2025-06-26 17:24:32 +02:00
background - color : var ( -- border - color ) ;
color : var ( -- text - secondary )
}
. dog - calculator - container . theme - system . dog - calculator - inline - unit {
background - color : var ( -- bg - secondary ) ;
border - color : rgba ( 241 , 154 , 95 , 0.5 ) ;
color : var ( -- text - primary ) ;
box - shadow : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.3 ) ;
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 - container . theme - system . dog - calculator - inline - unit : hover {
border - color : # f19a5f ;
box - shadow : 0 2 px 6 px rgba ( 241 , 154 , 95 , 0.3 ) ;
}
. dog - calculator - container . theme - system . dog - calculator - inline - unit : focus {
border - color : # f19a5f ;
box - shadow : 0 0 0 3 px rgba ( 241 , 154 , 95 , 0.15 ) ;
}
. dog - calculator - container . theme - system . dog - calculator - inline - unit option {
background - color : var ( -- bg - secondary ) ;
color : var ( -- text - primary ) ;
}
. dog - calculator - container . theme - system . dog - calculator - unit - btn {
background - color : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
color : var ( -- text - primary ) ;
}
. dog - calculator - container . theme - system . dog - calculator - unit - btn : hover {
border - color : # f19a5f ;
background : rgba ( 241 , 154 , 95 , 0.2 ) ;
}
. dog - calculator - container . theme - system . dog - calculator - unit - btn . active {
border - color : # f19a5f ;
background : # f19a5f ;
color : white ;
2025-06-15 21:57:27 +02:00
}
2025-08-18 14:36:25 +02:00
. dog - calculator - container . theme - system . dog - calculator - unit - btn : disabled {
opacity : 0.5 ;
cursor : not - allowed ;
border - color : var ( -- border - color ) ;
background : var ( -- bg - tertiary ) ;
color : var ( -- text - secondary ) ;
}
. dog - calculator - container . theme - system . dog - calculator - unit - btn : disabled : hover {
border - color : var ( -- border - color ) ;
background : var ( -- bg - tertiary ) ;
}
2025-06-15 21:57:27 +02:00
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - results {
2025-06-15 21:57:27 +02:00
background : linear - gradient ( 135 deg , rgba ( 241 , 154 , 95 , 0.15 ) 0 % , rgba ( 241 , 154 , 95 , 0.08 ) 100 % ) ;
border - color : rgba ( 241 , 154 , 95 , 0.3 ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - result - value {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
background : rgba ( 241 , 154 , 95 , 0.2 ) ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - footer {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - primary ) ;
border - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - action - buttons {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - btn {
2025-06-26 17:24:32 +02:00
background : var ( -- border - color ) ;
border - color : var ( -- border - color ) ;
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - btn : hover {
2025-06-15 21:57:27 +02:00
background : # 524 a5f ;
border - color : # 524 a5f ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - btn - share : hover {
2025-06-15 21:57:27 +02:00
border - color : # 9 f5999 ;
color : # f19a5f ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - btn - embed : hover {
2025-06-26 17:24:32 +02:00
border - color : var ( -- success - color ) ;
color : var ( -- success - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-08-18 12:45:44 +02:00
/* System theme feeding configuration styles in dark mode */
. dog - calculator - container . theme - system . dog - calculator - feeding - config {
background : var ( -- bg - tertiary ) ;
border - color : var ( -- border - color ) ;
}
. dog - calculator - container . theme - system . dog - calculator - frequency - row > label {
color : var ( -- text - label ) ;
}
. dog - calculator - container . theme - system . dog - calculator - radio - group label {
color : var ( -- text - primary ) ;
}
. dog - calculator - container . theme - system . dog - calculator - radio - group input [ type = "radio" ] : checked + span {
color : # f19a5f ;
}
. dog - calculator - container . theme - system . dog - calculator - meal - input span {
color : var ( -- text - secondary ) ;
}
. dog - calculator - container . theme - system . dog - calculator - meal - input input [ type = "number" ] {
background : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
color : var ( -- text - primary ) ;
}
. dog - calculator - container . theme - system . dog - calculator - meal - input input [ type = "number" ] : focus {
border - color : # f19a5f ;
box - shadow : 0 0 0 2 px rgba ( 241 , 154 , 95 , 0.15 ) ;
}
. dog - calculator - container . theme - system # mealNote {
color : var ( -- text - secondary ) ;
}
2025-06-15 21:57:27 +02:00
}
/* Modal Styles */
2025-06-15 22:59:45 +02:00
. dog - calculator - modal {
2025-06-15 21:57:27 +02:00
display : none ;
position : fixed ;
z - index : 10000 ;
left : 0 ;
top : 0 ;
width : 100 % ;
height : 100 % ;
animation : fadeIn 0.3 s ease ;
}
@ keyframes fadeIn {
from { opacity : 0 ; }
to { opacity : 1 ; }
}
2025-06-15 22:59:45 +02:00
. dog - calculator - modal - content {
2025-06-15 21:57:27 +02:00
position : relative ;
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - secondary ) ;
2025-06-15 21:57:27 +02:00
margin : 5 % auto ;
padding : 30 px ;
2025-06-26 17:24:32 +02:00
border : 1 px solid var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
border - radius : 12 px ;
width : 90 % ;
max - width : 500 px ;
box - shadow : 0 8 px 24 px rgba ( 0 , 0 , 0 , 0.15 ) ;
animation : slideIn 0.3 s ease ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - modal - embed {
2025-06-15 21:57:27 +02:00
max - width : 700 px ;
}
@ keyframes slideIn {
from {
opacity : 0 ;
transform : translateY ( - 20 px ) ;
}
to {
opacity : 1 ;
transform : translateY ( 0 ) ;
}
}
2025-06-15 22:59:45 +02:00
. dog - calculator - modal - close {
2025-06-15 21:57:27 +02:00
position : absolute ;
right : 20 px ;
top : 20 px ;
font - size : 28 px ;
font - weight : 300 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
cursor : pointer ;
transition : color 0.2 s ease ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - modal - close : hover {
2025-06-15 21:57:27 +02:00
color : # f19a5f ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - modal h3 {
2025-06-15 21:57:27 +02:00
margin : 0 0 24 px 0 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
font - size : 1.5 rem ;
}
/* Share Modal */
2025-06-15 22:59:45 +02:00
. dog - calculator - share - buttons {
2025-06-15 21:57:27 +02:00
display : grid ;
grid - template - columns : repeat ( auto - fit , minmax ( 120 px , 1 fr ) ) ;
gap : 12 px ;
margin - bottom : 20 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - share - btn {
2025-06-15 21:57:27 +02:00
padding : 12 px 16 px ;
border : none ;
border - radius : 6 px ;
font - size : 0.9 rem ;
font - weight : 500 ;
color : white ;
cursor : pointer ;
transition : all 0.2 s ease ;
display : flex ;
align - items : center ;
justify - content : center ;
gap : 8 px ;
font - family : inherit ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - share - facebook { background : # 1877 f2 ; }
. dog - calculator - share - facebook : hover { background : # 1664 d1 ; transform : translateY ( - 1 px ) ; }
. dog - calculator - share - twitter { background : # 1 da1f2 ; }
. dog - calculator - share - twitter : hover { background : # 1991 da ; transform : translateY ( - 1 px ) ; }
. dog - calculator - share - linkedin { background : # 0 a66c2 ; }
. dog - calculator - share - linkedin : hover { background : # 084 d95 ; transform : translateY ( - 1 px ) ; }
2025-06-26 18:03:39 +02:00
. dog - calculator - share - email { background : # 6 f3f6d ; }
2025-06-15 22:59:45 +02:00
. dog - calculator - share - email : hover { background : # 5 a3357 ; transform : translateY ( - 1 px ) ; }
. dog - calculator - share - copy { background : # f19a5f ; }
. dog - calculator - share - copy : hover { background : # e87741 ; transform : translateY ( - 1 px ) ; }
2025-06-15 21:57:27 +02:00
2025-06-15 22:59:45 +02:00
. dog - calculator - share - url {
2025-06-15 21:57:27 +02:00
display : flex ;
width : 100 % ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - share - url input {
2025-06-15 21:57:27 +02:00
flex : 1 ;
width : 100 % ;
padding : 10 px 16 px ;
2025-06-26 17:24:32 +02:00
border : 1 px solid var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
border - radius : 6 px ;
font - size : 0.9 rem ;
font - family : monospace ;
2025-06-26 17:24:32 +02:00
background : var ( -- bg - tertiary ) ;
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
/* Embed Modal */
2025-06-15 22:59:45 +02:00
. dog - calculator - embed - options {
2025-06-15 21:57:27 +02:00
display : flex ;
flex - direction : column ;
gap : 24 px ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - embed - option {
2025-06-26 17:24:32 +02:00
border : 1 px solid var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
border - radius : 8 px ;
padding : 20 px ;
background : # fcfafd ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - embed - option h4 {
2025-06-15 21:57:27 +02:00
margin : 0 0 8 px 0 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
font - size : 1.1 rem ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - embed - option p {
2025-06-15 21:57:27 +02:00
margin : 0 0 16 px 0 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - label ) ;
2025-06-15 21:57:27 +02:00
font - size : 0.9 rem ;
}
/* Default (light theme) code containers */
2025-06-15 22:59:45 +02:00
. dog - calculator - code - container {
2025-06-15 21:57:27 +02:00
position : relative ;
2025-06-26 17:24:32 +02:00
background : var ( -- bg - secondary ) ;
border : 1 px solid var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
border - radius : 6 px ;
overflow : hidden ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - code - container pre {
2025-06-15 21:57:27 +02:00
margin : 0 ;
padding : 16 px 60 px 16 px 16 px ;
overflow - x : auto ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - code - container code {
2025-06-26 18:03:39 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
font - family : 'Consolas' , 'Monaco' , 'Courier New' , monospace ;
font - size : 0.85 rem ;
line - height : 1.4 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - copy - btn {
2025-06-15 21:57:27 +02:00
position : absolute ;
top : 8 px ;
right : 8 px ;
padding : 6 px 10 px ;
background : # f19a5f ;
color : white ;
border : none ;
border - radius : 4 px ;
font - size : 0.8 rem ;
font - weight : 500 ;
cursor : pointer ;
transition : all 0.2 s ease ;
font - family : inherit ;
z - index : 1 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - copy - btn : hover { background : # e87741 ; }
2025-06-26 17:24:32 +02:00
. dog - calculator - copy - btn . copied { background : var ( -- success - color ) ; }
. dog - calculator - copy - btn . copied : hover { background : var ( -- success - color ) ; }
2025-06-15 21:57:27 +02:00
/* Dark theme modal styles */
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - modal - content {
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - primary ) ;
border - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - modal h3 {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - modal - close {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - modal - close : hover {
2025-06-15 21:57:27 +02:00
color : # f19a5f ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - share - url input {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - embed - option {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - embed - option h4 {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - embed - option p {
2025-06-26 17:24:32 +02:00
color : var ( -- text - secondary )
2025-06-15 21:57:27 +02:00
}
/* Dark theme code containers - different from embed option background */
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - code - container {
2025-06-15 21:57:27 +02:00
background : # 1 a1621 ;
border - color : # 2 a2330 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - dark . dog - calculator - code - container code {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
/* System theme modal styles */
@ media ( prefers - color - scheme : dark ) {
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - modal - content {
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - primary ) ;
border - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - modal h3 {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - modal - close {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - modal - close : hover {
2025-06-15 21:57:27 +02:00
color : # f19a5f ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - share - url input {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - embed - option {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - embed - option h4 {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - embed - option p {
2025-06-26 17:24:32 +02:00
color : var ( -- text - secondary )
2025-06-15 21:57:27 +02:00
}
/* System theme code containers - different from embed option background */
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - code - container {
2025-06-15 21:57:27 +02:00
background : # 1 a1621 ;
border - color : # 2 a2330 ;
}
2025-06-15 22:59:45 +02:00
. dog - calculator - container . theme - system . dog - calculator - code - container code {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-26 13:10:32 +02:00
}
2025-06-19 10:50:09 +02:00
2025-06-26 13:10:32 +02:00
/* Multi-Food Source Styles */
. dog - calculator - food - sources {
display : flex ;
flex - direction : column ;
gap : 16 px ;
}
2025-06-19 10:50:09 +02:00
2025-06-26 13:10:32 +02:00
. dog - calculator - food - source - card {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - secondary ) ;
border : 1 px solid var ( -- border - color ) ;
2025-06-26 13:10:32 +02:00
border - radius : 8 px ;
padding : 20 px ;
position : relative ;
transition : all 0.2 s ease ;
}
2025-06-15 21:57:27 +02:00
2025-06-26 13:10:32 +02:00
. dog - calculator - food - source - card : hover {
box - shadow : 0 2 px 8 px rgba ( 0 , 0 , 0 , 0.1 ) ;
}
2025-06-15 21:57:27 +02:00
2025-06-26 13:10:32 +02:00
. dog - calculator - food - source - header {
display : flex ;
justify - content : space - between ;
align - items : center ;
margin - bottom : 16 px ;
}
2025-06-15 21:57:27 +02:00
2025-06-26 13:10:32 +02:00
. dog - calculator - food - source - title {
font - weight : 600 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
font - size : 1.1 rem ;
margin : 0 ;
}
2025-06-15 21:57:27 +02:00
2025-06-26 13:10:32 +02:00
. dog - calculator - remove - food - btn {
2025-06-26 17:24:32 +02:00
background : var ( -- error - color ) ;
2025-06-26 13:10:32 +02:00
color : white ;
border : none ;
border - radius : 50 % ;
width : 28 px ;
height : 28 px ;
display : flex ;
align - items : center ;
justify - content : center ;
cursor : pointer ;
font - size : 16 px ;
font - weight : 600 ;
transition : all 0.2 s ease ;
line - height : 1 ;
}
2025-06-15 21:57:27 +02:00
2025-06-26 13:10:32 +02:00
. dog - calculator - remove - food - btn : hover {
background : # d65a47 ;
transform : scale ( 1.1 ) ;
}
2025-06-15 21:57:27 +02:00
2025-06-26 13:10:32 +02:00
. dog - calculator - percentage - group {
margin - top : 16 px ;
padding - top : 16 px ;
2025-06-26 17:24:32 +02:00
border - top : 1 px solid var ( -- border - color ) ;
2025-06-26 13:10:32 +02:00
}
2025-06-15 21:57:27 +02:00
2025-06-26 13:10:32 +02:00
. dog - calculator - percentage - label {
display : block ;
margin - bottom : 8 px ;
font - weight : 500 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
font - size : 1 rem ;
}
2025-06-15 21:57:27 +02:00
2025-06-26 13:10:32 +02:00
. dog - calculator - percentage - input - group {
display : flex ;
align - items : center ;
gap : 12 px ;
}
2025-06-15 21:57:27 +02:00
2025-06-26 13:10:32 +02:00
. dog - calculator - percentage - slider {
flex : 1 ;
height : 6 px ;
border - radius : 3 px ;
2025-06-26 17:24:32 +02:00
background : var ( -- border - color ) ;
2025-06-26 13:10:32 +02:00
outline : none ;
transition : all 0.2 s ease ;
- webkit - appearance : none ;
appearance : none ;
}
2025-06-15 21:57:27 +02:00
2025-06-26 13:10:32 +02:00
. dog - calculator - percentage - slider : : - webkit - slider - thumb {
- webkit - appearance : none ;
appearance : none ;
width : 20 px ;
height : 20 px ;
border - radius : 50 % ;
background : # f19a5f ;
cursor : pointer ;
border : 2 px solid white ;
box - shadow : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.2 ) ;
transition : all 0.2 s ease ;
}
. dog - calculator - percentage - slider : : - webkit - slider - thumb : hover {
background : # e87741 ;
transform : scale ( 1.1 ) ;
}
. dog - calculator - percentage - slider : : - moz - range - thumb {
width : 20 px ;
height : 20 px ;
border - radius : 50 % ;
background : # f19a5f ;
cursor : pointer ;
border : 2 px solid white ;
box - shadow : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.2 ) ;
transition : all 0.2 s ease ;
}
. dog - calculator - percentage - input {
width : 70 px ;
padding : 8 px 12 px ;
2025-06-26 17:24:32 +02:00
border : 1 px solid var ( -- border - color ) ;
2025-06-26 13:10:32 +02:00
border - radius : 6 px ;
font - size : 0.9 rem ;
text - align : center ;
2025-06-26 17:24:32 +02:00
background - color : var ( -- bg - secondary ) ;
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - percentage - input : focus {
outline : none ;
border - color : # f19a5f ;
box - shadow : 0 0 0 3 px rgba ( 241 , 154 , 95 , 0.1 ) ;
}
. dog - calculator - add - food - btn {
display : flex ;
align - items : center ;
justify - content : center ;
gap : 8 px ;
width : 100 % ;
padding : 16 px ;
2025-06-26 17:24:32 +02:00
border : 2 px dashed var ( -- border - color ) ;
2025-06-26 13:10:32 +02:00
border - radius : 8 px ;
background : transparent ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - label ) ;
2025-06-26 13:10:32 +02:00
font - size : 1 rem ;
font - weight : 500 ;
cursor : pointer ;
transition : all 0.2 s ease ;
font - family : inherit ;
margin - top : 16 px ;
}
. dog - calculator - add - food - btn : hover {
border - color : # f19a5f ;
color : # f19a5f ;
background : rgba ( 241 , 154 , 95 , 0.05 ) ;
}
. dog - calculator - add - food - btn : disabled {
opacity : 0.5 ;
cursor : not - allowed ;
2025-06-26 17:24:32 +02:00
border - color : var ( -- border - color ) ;
color : var ( -- text - label ) ;
2025-06-26 13:10:32 +02:00
background : transparent ;
}
. dog - calculator - add - food - btn : disabled : hover {
2025-06-26 17:24:32 +02:00
border - color : var ( -- border - color ) ;
color : var ( -- text - label ) ;
2025-06-26 13:10:32 +02:00
background : transparent ;
}
. dog - calculator - food - results {
background : linear - gradient ( 135 deg , rgba ( 241 , 154 , 95 , 0.08 ) 0 % , rgba ( 241 , 154 , 95 , 0.04 ) 100 % ) ;
border : 1 px solid rgba ( 241 , 154 , 95 , 0.2 ) ;
border - radius : 6 px ;
padding : 16 px ;
margin - top : 20 px ;
}
. dog - calculator - food - result - item {
display : flex ;
justify - content : space - between ;
align - items : center ;
margin - bottom : 8 px ;
font - size : 0.9 rem ;
}
. dog - calculator - food - result - item : last - child {
margin - bottom : 0 ;
}
. dog - calculator - food - result - label {
font - weight : 500 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - food - result - value {
font - weight : 600 ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
padding : 2 px 8 px ;
background : rgba ( 241 , 154 , 95 , 0.15 ) ;
border - radius : 3 px ;
font - size : 0.85 rem ;
}
/* Dark theme support for food sources */
. dog - calculator - container . theme - dark . dog - calculator - food - source - card {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - dark . dog - calculator - food - source - title {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - dark . dog - calculator - percentage - label {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - dark . dog - calculator - percentage - slider {
2025-06-26 17:24:32 +02:00
background : var ( -- border - color ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - dark . dog - calculator - percentage - input {
2025-06-26 17:24:32 +02:00
background : var ( -- border - color ) ;
2025-06-26 13:10:32 +02:00
border - color : # 524 a5f ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - dark . dog - calculator - percentage - group {
2025-06-26 17:24:32 +02:00
border - color : var ( -- border - color ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - dark . dog - calculator - add - food - btn {
2025-06-26 17:24:32 +02:00
border - color : var ( -- border - color ) ;
color : var ( -- text - secondary )
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - dark . dog - calculator - add - food - btn : hover {
border - color : # f19a5f ;
color : # f19a5f ;
background : rgba ( 241 , 154 , 95 , 0.1 ) ;
}
. dog - calculator - container . theme - dark . dog - calculator - food - results {
background : linear - gradient ( 135 deg , rgba ( 241 , 154 , 95 , 0.15 ) 0 % , rgba ( 241 , 154 , 95 , 0.08 ) 100 % ) ;
border - color : rgba ( 241 , 154 , 95 , 0.3 ) ;
}
. dog - calculator - container . theme - dark . dog - calculator - food - result - label {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - dark . dog - calculator - food - result - value {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
background : rgba ( 241 , 154 , 95 , 0.2 ) ;
}
/* System theme support for food sources */
@ media ( prefers - color - scheme : dark ) {
. dog - calculator - container . theme - system . dog - calculator - food - source - card {
2025-06-26 17:24:32 +02:00
background : var ( -- bg - secondary ) ;
border - color : var ( -- border - color ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - system . dog - calculator - food - source - title {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - system . dog - calculator - percentage - label {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - system . dog - calculator - percentage - slider {
2025-06-26 17:24:32 +02:00
background : var ( -- border - color ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - system . dog - calculator - percentage - input {
2025-06-26 17:24:32 +02:00
background : var ( -- border - color ) ;
2025-06-26 13:10:32 +02:00
border - color : # 524 a5f ;
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - system . dog - calculator - percentage - group {
2025-06-26 17:24:32 +02:00
border - color : var ( -- border - color ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - system . dog - calculator - add - food - btn {
2025-06-26 17:24:32 +02:00
border - color : var ( -- border - color ) ;
color : var ( -- text - secondary )
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - system . dog - calculator - add - food - btn : hover {
border - color : # f19a5f ;
color : # f19a5f ;
background : rgba ( 241 , 154 , 95 , 0.1 ) ;
}
. dog - calculator - container . theme - system . dog - calculator - food - results {
background : linear - gradient ( 135 deg , rgba ( 241 , 154 , 95 , 0.15 ) 0 % , rgba ( 241 , 154 , 95 , 0.08 ) 100 % ) ;
border - color : rgba ( 241 , 154 , 95 , 0.3 ) ;
}
. dog - calculator - container . theme - system . dog - calculator - food - result - label {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - system . dog - calculator - food - result - value {
2025-06-26 17:24:32 +02:00
color : var ( -- text - primary ) ;
2025-06-26 13:10:32 +02:00
background : rgba ( 241 , 154 , 95 , 0.2 ) ;
}
}
/* Mobile responsive design for food sources */
@ media ( max - width : 576 px ) {
. dog - calculator - food - source - card {
padding : 16 px ;
}
. dog - calculator - food - source - header {
flex - direction : column ;
align - items : flex - start ;
gap : 12 px ;
}
. dog - calculator - remove - food - btn {
align - self : flex - end ;
margin - top : - 8 px ;
}
. dog - calculator - percentage - input - group {
flex - direction : column ;
gap : 8 px ;
align - items : stretch ;
}
. dog - calculator - percentage - input {
width : 100 % ;
}
. dog - calculator - add - food - btn {
padding : 12 px ;
font - size : 0.9 rem ;
}
. dog - calculator - food - result - item {
flex - direction : column ;
align - items : flex - start ;
gap : 4 px ;
}
. dog - calculator - food - result - value {
align - self : stretch ;
text - align : center ;
}
}
/* Lock Icon Styles */
. dog - calculator - lock - icon {
display : inline - block ;
width : 16 px ;
height : 16 px ;
margin - left : 8 px ;
cursor : pointer ;
font - size : 14 px ;
line - height : 1 ;
vertical - align : middle ;
transition : all 0.2 s ease ;
user - select : none ;
opacity : 0.6 ;
}
. dog - calculator - lock - icon : hover {
opacity : 1 ;
transform : scale ( 1.1 ) ;
}
. dog - calculator - lock - icon . locked {
color : # f19a5f ;
opacity : 1 ;
font - weight : bold ;
}
. dog - calculator - lock - icon . unlocked {
2025-06-26 17:24:32 +02:00
color : var ( -- text - label ) ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - lock - icon . disabled {
opacity : 0.3 ;
cursor : not - allowed ;
}
. dog - calculator - lock - icon . disabled : hover {
opacity : 0.3 ;
transform : none ;
}
/* Dark theme support for lock icons */
. dog - calculator - container . theme - dark . dog - calculator - lock - icon . unlocked {
2025-06-26 17:24:32 +02:00
color : var ( -- text - secondary )
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - dark . dog - calculator - lock - icon . locked {
color : # f19a5f ;
}
/* System theme support for lock icons */
@ media ( prefers - color - scheme : dark ) {
. dog - calculator - container . theme - system . dog - calculator - lock - icon . unlocked {
2025-06-26 17:24:32 +02:00
color : var ( -- text - secondary )
2025-06-26 13:10:32 +02:00
}
. dog - calculator - container . theme - system . dog - calculator - lock - icon . locked {
color : # f19a5f ;
}
}
/* Disabled slider and input styles */
. dog - calculator - percentage - slider : disabled {
opacity : 0.5 ;
cursor : not - allowed ;
background : # f0f0f0 ;
pointer - events : none ;
}
. dog - calculator - percentage - slider : disabled : : - webkit - slider - thumb {
background : # ccc ;
cursor : not - allowed ;
}
. dog - calculator - percentage - slider : disabled : : - webkit - slider - thumb : hover {
background : # ccc ;
transform : none ;
}
. dog - calculator - percentage - slider : disabled : : - moz - range - thumb {
background : # ccc ;
cursor : not - allowed ;
}
. dog - calculator - percentage - input : disabled {
opacity : 0.5 ;
cursor : not - allowed ;
background - color : # f8f8f8 ;
border - color : # ddd ;
pointer - events : none ;
}
/* Dark theme disabled styles */
. dog - calculator - container . theme - dark . dog - calculator - percentage - slider : disabled {
background : # 2 a2530 ;
}
. dog - calculator - container . theme - dark . dog - calculator - percentage - input : disabled {
background - color : # 2 a2530 ;
border - color : # 3 a3442 ;
color : # 8 a8a8a ;
}
/* System theme disabled styles */
@ media ( prefers - color - scheme : dark ) {
. dog - calculator - container . theme - system . dog - calculator - percentage - slider : disabled {
background : # 2 a2530 ;
}
. dog - calculator - container . theme - system . dog - calculator - percentage - input : disabled {
background - color : # 2 a2530 ;
border - color : # 3 a3442 ;
color : # 8 a8a8a ;
}
}
/* Food Amount Breakdown Styling */
. dog - calculator - food - amounts - section {
margin - top : 1.5 rem ;
padding : 1 rem ;
background : var ( -- bg - secondary ) ;
border - radius : 8 px ;
border : 1 px solid var ( -- border - color ) ;
}
. dog - calculator - section - title {
margin : 0 0 1 rem 0 ;
font - size : 1 rem ;
font - weight : 600 ;
color : var ( -- text - primary ) ;
}
. dog - calculator - food - amounts - list {
display : flex ;
flex - direction : column ;
gap : 0.75 rem ;
margin - bottom : 1 rem ;
}
. dog - calculator - food - amount - item {
display : flex ;
justify - content : space - between ;
align - items : center ;
padding : 0.75 rem ;
background : var ( -- bg - primary ) ;
border - radius : 6 px ;
border : 1 px solid var ( -- border - color ) ;
}
. dog - calculator - food - amount - label {
font - weight : 500 ;
color : var ( -- text - primary ) ;
display : flex ;
align - items : center ;
gap : 0.5 rem ;
}
. dog - calculator - food - percentage {
background : var ( -- primary - color ) ;
color : white ;
padding : 0.2 rem 0.5 rem ;
border - radius : 12 px ;
font - size : 0.8 rem ;
font - weight : 500 ;
}
. dog - calculator - lock - indicator {
font - size : 0.8 rem ;
opacity : 0.7 ;
}
. dog - calculator - food - amount - value {
font - weight : 600 ;
color : var ( -- text - primary ) ;
font - size : 1 rem ;
}
2025-06-26 17:24:32 +02:00
/* Warning styles for missing energy content */
. dog - calculator - warning {
color : # e11d48 ;
font - weight : 500 ;
font - size : 1.2 rem ;
text - align : left ;
cursor : help ;
}
/* Inline unit selector in results */
. dog - calculator - inline - unit {
margin - left : 12 px ;
min - width : 110 px ;
padding : 4 px 8 px ;
background : var ( -- bg - primary ) ;
border : 1 px solid rgba ( 241 , 154 , 95 , 0.4 ) ;
border - radius : 6 px ;
color : var ( -- text - primary ) ;
font - size : 0.9 rem ;
font - weight : 500 ;
box - shadow : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.1 ) ;
transition : all 0.2 s 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 8 px center ;
background - size : 16 px ;
padding - right : 32 px ;
}
. dog - calculator - inline - unit : hover {
border - color : # f19a5f ;
box - shadow : 0 2 px 6 px rgba ( 241 , 154 , 95 , 0.2 ) ;
}
. dog - calculator - inline - unit : focus {
outline : none ;
border - color : # f19a5f ;
box - shadow : 0 0 0 3 px rgba ( 241 , 154 , 95 , 0.1 ) ;
}
/* Inline days input in breakdown header */
. dog - calculator - inline - days {
width : 60 px ;
padding : 2 px 6 px ;
border : 1 px solid var ( -- border - color ) ;
border - radius : 4 px ;
text - align : center ;
font - size : inherit ;
font - family : inherit ;
margin : 0 4 px ;
}
/* Unit selection buttons */
. dog - calculator - unit - buttons {
display : flex ;
justify - content : center ;
gap : 16 px ;
margin : 24 px auto ;
flex - wrap : wrap ;
width : fit - content ;
}
. dog - calculator - unit - btn {
padding : 8 px 14 px ;
border : 2 px solid var ( -- border - color ) ;
border - radius : 6 px ;
background : var ( -- bg - primary ) ;
color : var ( -- text - primary ) ;
font - size : 0.9 rem ;
font - weight : 500 ;
cursor : pointer ;
transition : all 0.2 s ease ;
min - width : 50 px ;
text - align : center ;
}
. dog - calculator - unit - btn : hover {
border - color : # f19a5f ;
background : rgba ( 241 , 154 , 95 , 0.1 ) ;
}
. dog - calculator - unit - btn . active {
border - color : # f19a5f ;
background : # f19a5f ;
color : white ;
}
2025-08-18 14:36:25 +02:00
. dog - calculator - unit - btn : disabled {
opacity : 0.5 ;
cursor : not - allowed ;
border - color : var ( -- border - color ) ;
background : var ( -- bg - tertiary ) ;
color : var ( -- text - secondary ) ;
}
. dog - calculator - unit - btn : disabled : hover {
border - color : var ( -- border - color ) ;
background : var ( -- bg - tertiary ) ;
transform : none ;
}
2025-06-26 17:29:39 +02:00
/* Hidden unit select for compatibility */
. dog - calculator - unit - select - hidden {
display : none ;
}
2025-06-26 17:24:32 +02:00
/* Mobile responsive adjustments for inline unit selector */
@ media ( max - width : 576 px ) {
. dog - calculator - result - item {
display : flex ;
flex - direction : column ;
align - items : center ;
gap : 8 px ;
}
. dog - calculator - result - label {
width : 100 % ;
text - align : center ;
margin - bottom : 4 px ;
}
. dog - calculator - result - value {
display : inline - block ;
}
. dog - calculator - inline - unit {
display : inline - block ;
margin - left : 8 px ;
min - width : 90 px ;
vertical - align : middle ;
}
/* Center the breakdown header on mobile */
. dog - calculator - section - title {
text - align : center ;
}
/* Ensure food breakdown items stay on one line */
. dog - calculator - food - amount - item {
display : flex ;
justify - content : space - between ;
align - items : center ;
flex - wrap : nowrap ;
text - align : left ;
}
. dog - calculator - food - amount - label {
flex : 1 ;
min - width : 0 ;
white - space : nowrap ;
overflow : hidden ;
text - overflow : ellipsis ;
text - align : left ;
}
. dog - calculator - food - amount - value {
flex - shrink : 0 ;
margin - left : 8 px ;
text - align : right ;
}
}
2025-06-26 13:10:32 +02:00
. dog - calculator - total - row {
display : flex ;
justify - content : space - between ;
align - items : center ;
padding : 1 rem ;
background : var ( -- primary - color ) ;
color : white ;
border - radius : 6 px ;
font - weight : 600 ;
margin - top : 0.5 rem ;
}
. dog - calculator - total - label {
font - size : 1 rem ;
}
. dog - calculator - total - value {
font - size : 1.1 rem ;
font - weight : 700 ;
}
. dog - calculator - full - width {
flex : 1 ;
}
/* Editable Food Source Name Styling */
. dog - calculator - food - source - name - input {
background : transparent ;
border : 2 px solid transparent ;
color : var ( -- text - primary ) ;
font - size : 1.1 rem ;
font - weight : 600 ;
font - family : inherit ;
padding : 0.5 rem 0 ;
border - radius : 4 px ;
width : 100 % ;
outline : none ;
transition : all 0.2 s ease ;
cursor : text ;
}
. dog - calculator - food - source - name - input : hover {
border - color : var ( -- border - color ) ;
background : var ( -- bg - secondary ) ;
padding : 0.5 rem ;
}
. dog - calculator - food - source - name - input : focus {
border - color : var ( -- primary - color ) ;
background : var ( -- bg - primary ) ;
box - shadow : 0 0 0 3 px rgba ( 241 , 154 , 95 , 0.1 ) ;
padding : 0.5 rem ;
}
. dog - calculator - food - source - name - input : : placeholder {
color : var ( -- text - secondary ) ;
opacity : 0.7 ;
}
/* Dark theme adjustments */
. dog - calculator - container . theme - dark . dog - calculator - food - source - name - input : hover {
background : # 2 a2530 ;
}
. dog - calculator - container . theme - dark . dog - calculator - food - source - name - input : focus {
background : # 1e1 a24 ;
}
/* System theme adjustments */
@ media ( prefers - color - scheme : dark ) {
. dog - calculator - container . theme - system . dog - calculator - food - source - name - input : hover {
background : # 2 a2530 ;
}
. dog - calculator - container . theme - system . dog - calculator - food - source - name - input : focus {
background : # 1e1 a24 ;
}
}
/* Responsive adjustments */
@ media ( max - width : 576 px ) {
. dog - calculator - food - amount - item {
2025-06-26 17:24:32 +02:00
flex - direction : row ! important ;
2025-06-26 13:10:32 +02:00
gap : 0.5 rem ;
2025-06-26 17:24:32 +02:00
text - align : left ! important ;
justify - content : space - between ! important ;
align - items : center ! important ;
flex - wrap : nowrap ! important ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - food - amount - label {
2025-06-26 17:24:32 +02:00
justify - content : flex - start ! important ;
text - align : left ! important ;
flex : 1 ;
min - width : 0 ;
white - space : nowrap ;
overflow : hidden ;
text - overflow : ellipsis ;
}
. dog - calculator - food - amount - value {
flex - shrink : 0 ;
margin - left : 8 px ;
text - align : right ! important ;
2025-06-26 13:10:32 +02:00
}
. dog - calculator - food - source - name - input {
font - size : 1 rem ;
}
} ` ;
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 ) ;
}
2025-08-17 21:20:05 +02:00
// JavaScript from src/calculator.js (transformed for widget use)
2025-06-26 13:10:32 +02:00
/ * *
2025-08-18 09:05:00 +02:00
* Configuration constants for Dog Calorie Calculator
* /
const CALCULATOR _CONFIG = {
defaultTheme : 'system' ,
defaultScale : 1.0 ,
maxFoodSources : 5 ,
minScale : 0.5 ,
maxScale : 2.0
} ;
/ * *
2025-06-26 13:10:32 +02:00
* Dog Calorie Calculator - iframe version
* by Canine Nutrition and Wellness
* /
class DogCalorieCalculatorWidget {
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 ;
this . currentMER = 0 ;
this . isImperial = false ;
2025-08-18 14:54:25 +02:00
2025-06-26 13:10:32 +02:00
this . foodSources = [ ] ;
2025-08-18 09:05:00 +02:00
this . maxFoodSources = CALCULATOR _CONFIG . maxFoodSources ;
2025-08-18 12:45:44 +02:00
this . mealsPerDay = 2 ;
this . showPerMeal = false ;
2025-06-26 13:10:32 +02:00
this . init ( ) ;
}
init ( ) {
// Inject the calculator HTML into the container
this . container . innerHTML = ` <div class="dog-calculator-container" id="dogCalculator">
< div class = "dog-calculator-section" >
< div class = "dog-calculator-section-header" >
< h2 > Dog ' s Characteristics < / h 2 >
< div class = "dog-calculator-unit-switch" >
< span class = "dog-calculator-unit-label active" id = "metricLabel" > Metric < / s p a n >
< label class = "dog-calculator-switch" >
< input type = "checkbox" id = "unitToggle" >
< span class = "dog-calculator-slider" > < / s p a n >
< / l a b e l >
< span class = "dog-calculator-unit-label" id = "imperialLabel" > Imperial < / s p a n >
< / d i v >
< / d i v >
< div class = "dog-calculator-form-group" >
< label for = "dogType" > Dog Type / Activity Level : < / l a b e l >
< select id = "dogType" aria - describedby = "dogTypeHelp" >
< option value = "" > Select dog type ... < / o p t i o n >
< option value = "3.0" > Puppy ( 0 - 4 months ) < / o p t i o n >
< option value = "2.0" > Puppy ( 4 months - adult ) < / o p t i o n >
< option value = "1.2" > Adult - inactive / obese < / o p t i o n >
< option value = "1.6" > Adult ( neutered / spayed ) - average activity < / o p t i o n >
< option value = "1.8" > Adult ( intact ) - average activity < / o p t i o n >
< option value = "1.0" > Adult - weight loss < / o p t i o n >
< option value = "1.7" > Adult - weight gain < / o p t i o n >
< option value = "2.0" > Working dog - light work < / o p t i o n >
< option value = "3.0" > Working dog - moderate work < / o p t i o n >
< option value = "5.0" > Working dog - heavy work < / o p t i o n >
< option value = "1.1" > Senior dog < / o p t i o n >
< / s e l e c t >
< / d i v >
< div class = "dog-calculator-form-group" >
< label for = "weight" id = "weightLabel" > Dog ' s Weight ( kg ) : < / l a b e l >
< 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 ) < / d i v >
< / d i v >
< 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 ) : < / s p a n >
< span class = "dog-calculator-result-value" id = "rerValue" > - cal / day < / s p a n >
< / d i v >
< div class = "dog-calculator-result-item" >
< span class = "dog-calculator-result-label" > Maintenance Energy Requirement ( MER ) : < / s p a n >
< span class = "dog-calculator-result-value" id = "merValue" > - cal / day < / s p a n >
< / d i v >
< / d i v >
< / d i v >
< div class = "dog-calculator-collapsible active" id = "foodCalculator" >
< div class = "dog-calculator-collapsible-header" >
< h3 > How much should I feed ? < / h 3 >
< / d i v >
< 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 -- >
< / d i v >
<!-- Add Food Source Button -- >
< button class = "dog-calculator-add-food-btn" id = "addFoodBtn" type = "button" >
< span > + < / s p a n >
< span > Add another food source < / s p a n >
< / b u t t o n >
2025-08-18 12:45:44 +02:00
<!-- Feeding Configuration -- >
< div class = "dog-calculator-feeding-config" id = "feedingConfig" style = "display: none;" >
< div class = "dog-calculator-frequency-row" >
< label > Show amounts : < / l a b e l >
< div class = "dog-calculator-radio-group" >
< label >
< input type = "radio" name = "showAs" value = "daily" id = "showDaily" checked >
< span > Per day < / s p a n >
< / l a b e l >
< label >
< input type = "radio" name = "showAs" value = "meal" id = "showPerMeal" >
< span > Per meal < / s p a n >
< / l a b e l >
< / d i v >
< div class = "dog-calculator-meal-input" id = "mealInputGroup" style = "display: none;" >
< span > × < / s p a n >
< input type = "number" id = "mealsPerDay" value = "2" min = "1" max = "10" >
< span > meals / day < / s p a n >
< / d i v >
< / d i v >
< / d i v >
2025-06-26 13:10:32 +02:00
<!-- 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 -- >
< / d i v >
< / d i v >
2025-06-26 17:24:32 +02:00
<!-- 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 < / b u t t o n >
< button type = "button" class = "dog-calculator-unit-btn" data - unit = "kg" > kg < / b u t t o n >
< button type = "button" class = "dog-calculator-unit-btn" data - unit = "oz" > oz < / b u t t o n >
< button type = "button" class = "dog-calculator-unit-btn" data - unit = "lb" > lb < / b u t t o n >
2025-08-18 14:36:25 +02:00
< button type = "button" class = "dog-calculator-unit-btn" data - unit = "cups" id = "cupsButton" disabled title = "Available when using kcal/cup measurement" > cups < / b u t t o n >
2025-06-26 17:24:32 +02:00
< / d i v >
2025-06-26 13:10:32 +02:00
<!-- 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 : < / s p a n >
< span class = "dog-calculator-result-value" id = "dailyFoodValue" > - g / day < / s p a n >
< / d i v >
< / d i v >
2025-06-26 17:24:32 +02:00
<!-- Hidden select for compatibility -- >
2025-06-26 17:29:39 +02:00
< select id = "unit" class = "dog-calculator-unit-select-hidden" aria - describedby = "unitHelp" >
2025-06-26 17:24:32 +02:00
< option value = "g" > grams ( g ) < / o p t i o n >
< option value = "kg" > kilograms ( kg ) < / o p t i o n >
< option value = "oz" > ounces ( oz ) < / o p t i o n >
< option value = "lb" > pounds ( lb ) < / o p t i o n >
2025-08-18 14:36:25 +02:00
< option value = "cups" > cups < / o p t i o n >
2025-06-26 17:24:32 +02:00
< / s e l e c t >
2025-06-26 13:10:32 +02:00
< div class = "dog-calculator-food-amounts-section" id = "foodAmountsSection" style = "display: none;" >
2025-06-26 17:24:32 +02:00
< 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" >
2025-08-18 12:45:44 +02:00
< span id = "dayLabel" > day < / s p a n > < s p a n i d = " m e a l N o t e " s t y l e = " d i s p l a y : n o n e ; " > < / s p a n > :
2025-06-26 17:24:32 +02:00
< / h 4 >
< div id = "daysError" class = "dog-calculator-error dog-calculator-hidden" > Please enter a valid number of days ( minimum 1 ) < / d i v >
2025-06-26 13:10:32 +02:00
< div id = "foodAmountsList" class = "dog-calculator-food-amounts-list" >
<!-- Individual food amounts will be populated here -- >
< / d i v >
< div class = "dog-calculator-total-row" id = "totalAmountRow" >
< span class = "dog-calculator-total-label" > Total Amount : < / s p a n >
< span class = "dog-calculator-total-value" id = "totalAmountDisplay" > < / s p a n >
< / d i v >
< / d i v >
< / d i v >
< / d i v >
< / d i v >
< div class = "dog-calculator-action-buttons" >
< button class = "dog-calculator-btn dog-calculator-btn-share" id = "shareBtn" >
Share
< / b u t t o n >
< button class = "dog-calculator-btn dog-calculator-btn-embed" id = "embedBtn" >
Embed
< / b u t t o n >
< / d i v >
< div class = "dog-calculator-footer" >
< a href = "https://caninenutritionandwellness.com" target = "_blank" rel = "noopener noreferrer" >
by caninenutritionandwellness . com
< / a >
< / d i v >
2025-06-26 18:03:39 +02:00
<!-- Share Modal -- >
< div id = "shareModal" class = "dog-calculator-modal" style = "display: none;" >
< div class = "dog-calculator-modal-content" >
< span class = "dog-calculator-modal-close" id = "shareModalClose" > & times ; < / s p a n >
< h3 > Share Calculator < / h 3 >
< div class = "dog-calculator-share-buttons" >
< button class = "dog-calculator-share-btn dog-calculator-share-facebook plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-facebook" id = "shareFacebook" >
Facebook
< / b u t t o n >
< button class = "dog-calculator-share-btn dog-calculator-share-twitter plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-twitter" id = "shareTwitter" >
Twitter
< / b u t t o n >
< button class = "dog-calculator-share-btn dog-calculator-share-linkedin plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-linkedin" id = "shareLinkedIn" >
LinkedIn
< / b u t t o n >
< button class = "dog-calculator-share-btn dog-calculator-share-email plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-email" id = "shareEmail" >
Email
< / b u t t o n >
< button class = "dog-calculator-share-btn dog-calculator-share-copy plausible-event-name=Calculator+Usage plausible-event-action=calculator-share-copy-link" id = "shareCopy" >
Copy Link
< / b u t t o n >
< / d i v >
< div class = "dog-calculator-share-url" >
< input type = "text" id = "shareUrl" readonly >
< / d i v >
2025-06-26 13:10:32 +02:00
< / d i v >
< / d i v >
2025-06-26 18:03:39 +02:00
<!-- Embed Modal -- >
< div id = "embedModal" class = "dog-calculator-modal" style = "display: none;" >
< div class = "dog-calculator-modal-content dog-calculator-modal-embed" >
< span class = "dog-calculator-modal-close" id = "embedModalClose" > & times ; < / s p a n >
< h3 > ⚡ Embed the Calculator < / h 3 >
2025-06-26 13:10:32 +02:00
2025-06-26 18:03:39 +02:00
< div class = "dog-calculator-embed-options" >
< div class = "dog-calculator-embed-option" >
< h4 > ⚡ JavaScript Widget < / h 4 >
< div class = "dog-calculator-code-container" >
< pre > < code id = "widgetCode" > < / c o d e > < / p r e >
< button class = "dog-calculator-copy-btn plausible-event-name=Calculator+Usage plausible-event-action=calculator-embed-js" id = "copyWidget" >
Copy
< / b u t t o n >
< / d i v >
< / d i v >
< div class = "dog-calculator-embed-option" >
< h4 > 🛡 ️ iframe Embed < / h 4 >
< div class = "dog-calculator-code-container" >
< pre > < code id = "iframeCode" > < / c o d e > < / p r e >
< button class = "dog-calculator-copy-btn plausible-event-name=Calculator+Usage plausible-event-action=calculator-embed-iframe" id = "copyIframe" >
Copy
< / b u t t o n >
< / d i v >
2025-06-26 13:10:32 +02:00
< / d i v >
< / d i v >
< / d i v >
< / d i v >
< / d i v > ` ;
// Apply widget-specific settings
this . applyTheme ( ) ;
this . applyScale ( ) ;
// Continue with original init logic
this . applyTheme ( ) ;
this . applyScale ( ) ;
this . initializeFoodSources ( ) ;
this . bindEvents ( ) ;
this . updateUnitLabels ( ) ;
this . setupIframeResize ( ) ;
// Show the calculator with fade-in
const container = this . container . querySelector ( '#dogCalculator' ) ;
container . classList . add ( 'loaded' ) ;
}
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' ) ) ;
2025-08-18 09:05:00 +02:00
return ( ! isNaN ( scale ) && scale >= CALCULATOR _CONFIG . minScale && scale <= CALCULATOR _CONFIG . maxScale ) ? scale : null ;
2025-06-26 13:10:32 +02:00
}
applyTheme ( ) {
const container = this . container . querySelector ( '#dogCalculator' ) ;
container . classList . remove ( 'theme-light' , 'theme-dark' , 'theme-system' ) ;
container . classList . add ( 'theme-' + this . theme ) ;
}
applyScale ( ) {
const container = this . container . querySelector ( '#dogCalculator' ) ;
if ( ! container ) return ;
2025-08-18 09:05:00 +02:00
// Clamp scale between min and max for usability
const clampedScale = Math . max ( CALCULATOR _CONFIG . minScale , Math . min ( CALCULATOR _CONFIG . maxScale , this . scale ) ) ;
2025-06-26 13:10:32 +02:00
if ( clampedScale !== 1.0 ) {
container . style . transform = ` scale( ${ clampedScale } ) ` ;
container . style . transformOrigin = 'top center' ;
// Adjust container to account for scaling
setTimeout ( ( ) => {
const actualHeight = container . offsetHeight * clampedScale ;
container . style . marginBottom = ` ${ ( clampedScale - 1 ) * container . offsetHeight } px ` ;
this . sendHeightToParent ( ) ;
} , 100 ) ;
}
}
// Food Source Management Methods
initializeFoodSources ( ) {
this . addFoodSource ( ) ;
this . updateAddButton ( ) ;
}
addFoodSource ( ) {
if ( this . foodSources . length >= this . maxFoodSources ) {
return ;
}
const id = this . generateFoodSourceId ( ) ;
const foodSource = {
id : id ,
name : ` Food Source ${ this . foodSources . length + 1 } ` ,
energy : '' ,
energyUnit : this . isImperial ? 'kcalcup' : 'kcal100g' ,
percentage : this . foodSources . length === 0 ? 100 : 0 ,
isLocked : false
} ;
this . foodSources . push ( foodSource ) ;
this . redistributePercentages ( ) ;
this . renderFoodSource ( foodSource ) ;
this . updateAddButton ( ) ;
2025-06-26 17:24:32 +02:00
this . updateRemoveButtons ( ) ;
2025-06-26 13:10:32 +02:00
this . refreshAllPercentageUI ( ) ;
}
removeFoodSource ( id ) {
if ( this . foodSources . length <= 1 ) {
return ; // Cannot remove the last food source
}
const index = this . foodSources . findIndex ( fs => fs . id === id ) ;
if ( index === - 1 ) return ;
this . foodSources . splice ( index , 1 ) ;
// Remove the DOM element
const element = document . getElementById ( ` foodSource- ${ id } ` ) ;
if ( element ) {
element . remove ( ) ;
}
// Redistribute percentages among remaining sources
this . redistributePercentages ( ) ;
this . updateFoodSourceNames ( ) ;
this . updateAddButton ( ) ;
2025-06-26 17:24:32 +02:00
this . updateRemoveButtons ( ) ;
2025-06-26 13:10:32 +02:00
this . refreshAllPercentageUI ( ) ;
}
generateFoodSourceId ( ) {
return 'fs_' + Date . now ( ) + '_' + Math . random ( ) . toString ( 36 ) . substr ( 2 , 5 ) ;
}
redistributePercentages ( ) {
const count = this . foodSources . length ;
if ( count === 0 ) return ;
// Only redistribute among unlocked sources
const unlockedSources = this . foodSources . filter ( fs => ! fs . isLocked ) ;
const lockedSources = this . foodSources . filter ( fs => fs . isLocked ) ;
// Calculate total locked percentage
const totalLockedPercentage = lockedSources . reduce ( ( sum , fs ) => sum + fs . percentage , 0 ) ;
// Available percentage for unlocked sources
const availablePercentage = 100 - totalLockedPercentage ;
if ( unlockedSources . length > 0 ) {
const equalPercentage = Math . floor ( availablePercentage / unlockedSources . length ) ;
const remainder = availablePercentage - ( equalPercentage * unlockedSources . length ) ;
unlockedSources . forEach ( ( fs , index ) => {
fs . percentage = equalPercentage + ( index < remainder ? 1 : 0 ) ;
} ) ;
}
// Update the UI sliders and inputs
this . refreshAllPercentageUI ( ) ;
}
// OBSOLETE METHODS - Replaced by new validation system
// Keeping for reference but these are no longer used
/ *
updatePercentageInputs ( ) {
this . foodSources . forEach ( fs => {
const slider = document . getElementById ( ` percentage-slider- ${ fs . id } ` ) ;
const input = document . getElementById ( ` percentage-input- ${ fs . id } ` ) ;
const display = document . getElementById ( ` percentage-display- ${ fs . id } ` ) ;
if ( slider ) slider . value = fs . percentage ;
if ( input ) input . value = fs . percentage ;
if ( display ) display . textContent = ` ${ fs . percentage } % ` ;
} ) ;
// Update constraints after values are set
this . updatePercentageConstraints ( ) ;
}
updatePercentageConstraints ( ) {
this . foodSources . forEach ( fs => {
const slider = document . getElementById ( ` percentage-slider- ${ fs . id } ` ) ;
const input = document . getElementById ( ` percentage-input- ${ fs . id } ` ) ;
if ( ! slider || ! input ) return ;
// Always keep full 0-100 scale for all sliders
slider . max = 100 ;
input . max = 100 ;
if ( fs . isLocked ) {
// Locked sources can't be changed
slider . disabled = true ;
input . disabled = true ;
} else {
// Calculate the maximum this source can have
const lockedSources = this . foodSources . filter ( other => other . id !== fs . id && other . isLocked ) ;
const totalLockedPercentage = lockedSources . reduce ( ( sum , other ) => sum + other . percentage , 0 ) ;
const maxAllowed = 100 - totalLockedPercentage ;
// Re-enable
slider . disabled = false ;
input . disabled = false ;
// Store max allowed for validation (we'll check this in event handlers)
slider . dataset . maxAllowed = maxAllowed ;
input . dataset . maxAllowed = maxAllowed ;
// If current value exceeds max, adjust it
if ( fs . percentage > maxAllowed ) {
fs . percentage = maxAllowed ;
slider . value = maxAllowed ;
input . value = maxAllowed ;
document . getElementById ( ` percentage-display- ${ fs . id } ` ) . textContent = ` ${ maxAllowed } % ` ;
}
}
} ) ;
}
adjustPercentages ( changedId , newPercentage ) {
const changedIndex = this . foodSources . findIndex ( fs => fs . id === changedId ) ;
if ( changedIndex === - 1 ) return ;
const oldPercentage = this . foodSources [ changedIndex ] . percentage ;
const difference = newPercentage - oldPercentage ;
this . foodSources [ changedIndex ] . percentage = newPercentage ;
// Only redistribute among unlocked sources (excluding the changed one)
const otherUnlockedSources = this . foodSources . filter ( ( fs , index ) =>
index !== changedIndex && ! fs . isLocked
) ;
// If this is the only unlocked source, force it to fill remaining percentage
if ( otherUnlockedSources . length === 0 ) {
const lockedSources = this . foodSources . filter ( ( fs , index ) =>
index !== changedIndex && fs . isLocked
) ;
const totalLockedPercentage = lockedSources . reduce ( ( sum , fs ) => sum + fs . percentage , 0 ) ;
const requiredPercentage = 100 - totalLockedPercentage ;
// Force the changed source to the required percentage
this . foodSources [ changedIndex ] . percentage = requiredPercentage ;
this . updatePercentageInputs ( ) ;
this . updateFoodCalculations ( ) ;
return ;
}
// Calculate total locked percentage (excluding the changed source)
const lockedSources = this . foodSources . filter ( ( fs , index ) =>
index !== changedIndex && fs . isLocked
) ;
const totalLockedPercentage = lockedSources . reduce ( ( sum , fs ) => sum + fs . percentage , 0 ) ;
// Available percentage for unlocked sources
const availablePercentage = 100 - newPercentage - totalLockedPercentage ;
const totalUnlockedPercentage = otherUnlockedSources . reduce ( ( sum , fs ) => sum + fs . percentage , 0 ) ;
if ( totalUnlockedPercentage === 0 ) {
// If all other unlocked sources are 0, distribute equally
const equalShare = Math . floor ( availablePercentage / otherUnlockedSources . length ) ;
const remainder = availablePercentage - ( equalShare * otherUnlockedSources . length ) ;
otherUnlockedSources . forEach ( ( fs , index ) => {
fs . percentage = equalShare + ( index < remainder ? 1 : 0 ) ;
} ) ;
} else {
// Distribute proportionally among unlocked sources
const scale = availablePercentage / totalUnlockedPercentage ;
let distributedTotal = 0 ;
otherUnlockedSources . forEach ( ( fs , index ) => {
if ( index === otherUnlockedSources . length - 1 ) {
// Last item gets the remainder to ensure exact 100%
fs . percentage = availablePercentage - distributedTotal ;
} else {
fs . percentage = Math . round ( fs . percentage * scale ) ;
distributedTotal += fs . percentage ;
}
} ) ;
}
this . updatePercentageInputs ( ) ;
this . updateFoodCalculations ( ) ;
}
* /
// New validation system methods
validatePercentageChange ( sourceId , requestedValue ) {
// Find the source being changed
const changedSource = this . foodSources . find ( fs => fs . id === sourceId ) ;
if ( ! changedSource ) {
return { isValid : false , reason : 'Source not found' } ;
}
// If the source is locked, no change allowed
if ( changedSource . isLocked ) {
return { isValid : false , reason : 'Source is locked' } ;
}
// Ensure requested value is within bounds
const clampedValue = Math . max ( 0 , Math . min ( 100 , requestedValue ) ) ;
// Calculate locked and other unlocked totals
const lockedSources = this . foodSources . filter ( fs => fs . id !== sourceId && fs . isLocked ) ;
const otherUnlockedSources = this . foodSources . filter ( fs => fs . id !== sourceId && ! fs . isLocked ) ;
const totalLocked = lockedSources . reduce ( ( sum , fs ) => sum + fs . percentage , 0 ) ;
// Check if the only unlocked source
if ( otherUnlockedSources . length === 0 ) {
// This is the only unlocked source, must fill remaining percentage
const requiredPercentage = 100 - totalLocked ;
return {
isValid : true ,
actualValue : requiredPercentage ,
affectedSources : [ { id : sourceId , newPercentage : requiredPercentage } ] ,
reason : 'Only unlocked source, forced to fill remainder'
} ;
}
// Calculate available percentage for redistribution
const availableForOthers = 100 - clampedValue - totalLocked ;
// Check if redistribution is possible
if ( availableForOthers < 0 ) {
// Cannot accommodate this value
const maxAllowed = 100 - totalLocked ;
return {
isValid : true ,
actualValue : maxAllowed ,
affectedSources : this . calculateRedistribution ( sourceId , maxAllowed , otherUnlockedSources ) ,
reason : 'Value clamped to maximum allowed'
} ;
}
// Calculate redistribution
const affectedSources = this . calculateRedistribution ( sourceId , clampedValue , otherUnlockedSources ) ;
return {
isValid : true ,
actualValue : clampedValue ,
affectedSources : affectedSources ,
reason : 'Valid change'
} ;
2025-06-19 10:50:09 +02:00
}
2025-06-26 13:10:32 +02:00
calculateRedistribution ( sourceId , newValue , otherUnlockedSources ) {
const result = [ { id : sourceId , newPercentage : newValue } ] ;
if ( otherUnlockedSources . length === 0 ) {
return result ;
}
// Calculate total locked percentage
const lockedSources = this . foodSources . filter ( fs => fs . id !== sourceId && fs . isLocked ) ;
const totalLocked = lockedSources . reduce ( ( sum , fs ) => sum + fs . percentage , 0 ) ;
// Available percentage for other unlocked sources
const availableForOthers = 100 - newValue - totalLocked ;
// Current total of other unlocked sources
const currentOtherTotal = otherUnlockedSources . reduce ( ( sum , fs ) => sum + fs . percentage , 0 ) ;
if ( currentOtherTotal === 0 || availableForOthers === 0 ) {
// Distribute equally among other unlocked sources
const equalShare = Math . floor ( availableForOthers / otherUnlockedSources . length ) ;
const remainder = availableForOthers - ( equalShare * otherUnlockedSources . length ) ;
otherUnlockedSources . forEach ( ( fs , index ) => {
const newPercentage = equalShare + ( index < remainder ? 1 : 0 ) ;
result . push ( { id : fs . id , newPercentage } ) ;
} ) ;
} else {
// Distribute proportionally
const scale = availableForOthers / currentOtherTotal ;
let distributedTotal = 0 ;
otherUnlockedSources . forEach ( ( fs , index ) => {
let newPercentage ;
if ( index === otherUnlockedSources . length - 1 ) {
// Last item gets remainder to ensure exact total
newPercentage = availableForOthers - distributedTotal ;
} else {
newPercentage = Math . round ( fs . percentage * scale ) ;
distributedTotal += newPercentage ;
}
result . push ( { id : fs . id , newPercentage } ) ;
} ) ;
}
return result ;
2025-06-19 10:50:09 +02:00
}
2025-06-26 13:10:32 +02:00
applyValidatedChanges ( validationResult ) {
if ( ! validationResult . isValid ) {
return false ;
}
// Apply all percentage changes
validationResult . affectedSources . forEach ( change => {
const source = this . foodSources . find ( fs => fs . id === change . id ) ;
if ( source ) {
source . percentage = change . newPercentage ;
}
} ) ;
return true ;
2025-06-19 10:50:09 +02:00
}
2025-06-26 13:10:32 +02:00
refreshAllPercentageUI ( ) {
this . foodSources . forEach ( fs => {
// Update all UI elements from single source of truth
const slider = document . getElementById ( ` percentage-slider- ${ fs . id } ` ) ;
const input = document . getElementById ( ` percentage-input- ${ fs . id } ` ) ;
const display = document . getElementById ( ` percentage-display- ${ fs . id } ` ) ;
if ( slider ) slider . value = fs . percentage ;
if ( input ) input . value = fs . percentage ;
if ( display ) display . textContent = ` ${ fs . percentage } % ` ;
// Update constraints and disabled states
this . updateSliderConstraints ( fs ) ;
} ) ;
// Update food calculations
this . updateFoodCalculations ( ) ;
2025-06-19 10:50:09 +02:00
}
2025-06-26 13:10:32 +02:00
updateSliderConstraints ( foodSource ) {
const slider = document . getElementById ( ` percentage-slider- ${ foodSource . id } ` ) ;
const input = document . getElementById ( ` percentage-input- ${ foodSource . id } ` ) ;
if ( ! slider || ! input ) return ;
// Always keep 0-100 scale
slider . max = 100 ;
input . max = 100 ;
if ( foodSource . isLocked ) {
slider . disabled = true ;
input . disabled = true ;
} else {
// Calculate maximum allowed and store for validation
const maxAllowed = this . calculateMaxAllowed ( foodSource . id ) ;
slider . disabled = ( maxAllowed <= 0 ) ;
input . disabled = ( maxAllowed <= 0 ) ;
slider . dataset . maxAllowed = maxAllowed ;
input . dataset . maxAllowed = maxAllowed ;
}
}
calculateMaxAllowed ( sourceId ) {
const lockedSources = this . foodSources . filter ( fs => fs . id !== sourceId && fs . isLocked ) ;
const otherUnlockedSources = this . foodSources . filter ( fs => fs . id !== sourceId && ! fs . isLocked ) ;
const totalLocked = lockedSources . reduce ( ( sum , fs ) => sum + fs . percentage , 0 ) ;
// If this is the only unlocked source, it must take up the remainder
if ( otherUnlockedSources . length === 0 ) {
return 100 - totalLocked ;
}
// Otherwise, maximum is 100 minus locked percentages
return Math . max ( 0 , 100 - totalLocked ) ;
}
updateFoodSourceNames ( ) {
this . foodSources . forEach ( ( fs , index ) => {
// Only update if the name is still the default pattern
if ( fs . name . match ( /^Food Source \d+$/ ) ) {
fs . name = ` Food Source ${ index + 1 } ` ;
const titleElement = document . getElementById ( ` food-title- ${ fs . id } ` ) ;
if ( titleElement ) {
titleElement . value = fs . name ;
}
}
} ) ;
}
updateAddButton ( ) {
const addBtn = this . container . querySelector ( '#addFoodBtn' ) ;
if ( addBtn ) {
const remaining = this . maxFoodSources - this . foodSources . length ;
const buttonText = addBtn . querySelector ( 'span:last-child' ) ;
if ( remaining <= 0 ) {
// Disable button and show max reached message
addBtn . disabled = true ;
if ( buttonText ) {
buttonText . textContent = ` Maximum ${ this . maxFoodSources } sources reached ` ;
}
} else {
// Enable button with normal text
addBtn . disabled = false ;
if ( buttonText ) {
buttonText . textContent = 'Add another food source' ;
}
}
}
}
2025-06-26 17:24:32 +02:00
updateRemoveButtons ( ) {
// Show/hide remove buttons based on whether we have more than one source
const hasMultipleSources = this . foodSources . length > 1 ;
this . foodSources . forEach ( fs => {
const removeBtn = document . getElementById ( ` remove- ${ fs . id } ` ) ;
if ( removeBtn ) {
removeBtn . style . display = hasMultipleSources ? 'block' : 'none' ;
}
} ) ;
}
2025-06-26 13:10:32 +02:00
renderFoodSource ( foodSource ) {
const container = this . container . querySelector ( '#foodSources' ) ;
2025-06-19 10:50:09 +02:00
if ( ! container ) return ;
2025-06-26 13:10:32 +02:00
const cardHTML = `
< div class = "dog-calculator-food-source-card" id = "foodSource-${foodSource.id}" >
< div class = "dog-calculator-food-source-header" >
< input type = "text" class = "dog-calculator-food-source-name-input" id = "food-title-${foodSource.id}" value = "${foodSource.name}" placeholder = "Enter food name" maxlength = "50" title = "Click to edit food source name" >
2025-06-26 17:24:32 +02:00
< button class = "dog-calculator-remove-food-btn" id = "remove-${foodSource.id}" type = "button" title = "Remove this food source" style = "display: ${this.foodSources.length > 1 ? 'block' : 'none'}" > × < / b u t t o n >
2025-06-26 13:10:32 +02:00
< / d i v >
< div class = "dog-calculator-input-group" >
< div class = "dog-calculator-form-group" >
< label for = "energy-${foodSource.id}" > Energy Content : < / l a b e l >
< input type = "number" id = "energy-${foodSource.id}" min = "1" step = "1" placeholder = "Enter energy content" value = "${foodSource.energy}" >
< / d i v >
< div class = "dog-calculator-form-group" >
< label for = "energy-unit-${foodSource.id}" > Unit : < / l a b e l >
< select id = "energy-unit-${foodSource.id}" class = "dog-calculator-unit-select" >
< option value = "kcal100g" $ { foodSource . energyUnit === 'kcal100g' ? 'selected' : '' } > kcal / 100 g < / o p t i o n >
< option value = "kcalkg" $ { foodSource . energyUnit === 'kcalkg' ? 'selected' : '' } > kcal / kg < / o p t i o n >
< option value = "kcalcup" $ { foodSource . energyUnit === 'kcalcup' ? 'selected' : '' } > kcal / cup < / o p t i o n >
< option value = "kcalcan" $ { foodSource . energyUnit === 'kcalcan' ? 'selected' : '' } > kcal / can < / o p t i o n >
< / s e l e c t >
< / d i v >
< / d i v >
2025-06-26 17:24:32 +02:00
< div id = "energy-error-${foodSource.id}" class = "dog-calculator-error dog-calculator-hidden" > Please enter a valid energy content < / d i v >
2025-06-26 13:10:32 +02:00
< div class = "dog-calculator-percentage-group" >
< label class = "dog-calculator-percentage-label" for = "percentage-slider-${foodSource.id}" >
Percentage of Diet : < span id = "percentage-display-${foodSource.id}" > $ { foodSource . percentage } % < / s p a n >
< span class = "dog-calculator-lock-icon unlocked" id = "lock-${foodSource.id}" title = "Lock this percentage" > 🔒 < / s p a n >
< / l a b e l >
< div class = "dog-calculator-percentage-input-group" >
< input type = "range" id = "percentage-slider-${foodSource.id}" class = "dog-calculator-percentage-slider"
min = "0" max = "100" value = "${foodSource.percentage}" >
< input type = "number" id = "percentage-input-${foodSource.id}" class = "dog-calculator-percentage-input"
min = "0" max = "100" value = "${foodSource.percentage}" >
< / d i v >
< / d i v >
< / d i v >
` ;
container . insertAdjacentHTML ( 'beforeend' , cardHTML ) ;
2025-06-19 10:50:09 +02:00
2025-06-26 13:10:32 +02:00
// Bind events for the new food source
this . bindFoodSourceEvents ( foodSource . id ) ;
}
bindFoodSourceEvents ( id ) {
// Name input events
const nameInput = document . getElementById ( ` food-title- ${ id } ` ) ;
// Energy input events
const energyInput = document . getElementById ( ` energy- ${ id } ` ) ;
const energyUnitSelect = document . getElementById ( ` energy-unit- ${ id } ` ) ;
const percentageSlider = document . getElementById ( ` percentage-slider- ${ id } ` ) ;
const percentageInput = document . getElementById ( ` percentage-input- ${ id } ` ) ;
const removeBtn = document . getElementById ( ` remove- ${ id } ` ) ;
const lockBtn = document . getElementById ( ` lock- ${ id } ` ) ;
if ( nameInput ) {
nameInput . addEventListener ( 'input' , ( ) => {
const newName = nameInput . value . trim ( ) || ` Food Source ${ this . foodSources . findIndex ( fs => fs . id === id ) + 1 } ` ;
this . updateFoodSourceData ( id , 'name' , newName ) ;
this . updateFoodCalculations ( ) ; // This will refresh the food amount breakdown with new names
} ) ;
2025-06-19 10:50:09 +02:00
2025-06-26 13:10:32 +02:00
nameInput . addEventListener ( 'blur' , ( ) => {
// If field is empty, restore default name
if ( ! nameInput . value . trim ( ) ) {
const defaultName = ` Food Source ${ this . foodSources . findIndex ( fs => fs . id === id ) + 1 } ` ;
nameInput . value = defaultName ;
this . updateFoodSourceData ( id , 'name' , defaultName ) ;
this . updateFoodCalculations ( ) ;
}
} ) ;
}
if ( energyInput ) {
energyInput . addEventListener ( 'input' , ( ) => {
this . updateFoodSourceData ( id , 'energy' , energyInput . value ) ;
2025-08-18 14:36:25 +02:00
// Auto-select cups when entering energy for kcal/cup
const foodSource = this . foodSources . find ( fs => fs . id === id ) ;
if ( foodSource && foodSource . energyUnit === 'kcalcup' && parseFloat ( energyInput . value ) > 0 ) {
const unitSelect = this . container . querySelector ( '#unit' ) ;
const cupsButton = this . container . querySelector ( '#cupsButton' ) ;
// First check if cups button will be enabled after update
const willEnableCups = this . foodSources . some ( fs =>
fs . energyUnit === 'kcalcup' && fs . energy && parseFloat ( fs . energy ) > 0
) ;
if ( willEnableCups && unitSelect ) {
// Set cups BEFORE updating calculations
unitSelect . value = 'cups' ;
unitSelect . setAttribute ( 'value' , 'cups' ) ;
this . setActiveUnitButton ( 'cups' ) ;
// Enable the cups button manually since we know it will be valid
if ( cupsButton ) {
cupsButton . disabled = false ;
cupsButton . title = 'Show amounts in cups' ;
}
}
// Now update calculations with cups already selected
this . updateFoodCalculations ( ) ;
} else {
this . updateFoodCalculations ( ) ;
}
2025-06-26 13:10:32 +02:00
} ) ;
energyInput . addEventListener ( 'blur' , ( ) => this . validateFoodSourceEnergy ( id ) ) ;
}
if ( energyUnitSelect ) {
energyUnitSelect . addEventListener ( 'change' , ( ) => {
this . updateFoodSourceData ( id , 'energyUnit' , energyUnitSelect . value ) ;
2025-08-18 14:36:25 +02:00
// Auto-select the most appropriate unit based on energy unit
const unitSelect = this . container . querySelector ( '#unit' ) ;
const energyInput = document . getElementById ( ` energy- ${ id } ` ) ;
if ( unitSelect ) {
switch ( energyUnitSelect . value ) {
case 'kcalcup' :
// Check if we have energy value to enable cups
const foodSource = this . foodSources . find ( fs => fs . id === id ) ;
if ( foodSource && foodSource . energy && parseFloat ( foodSource . energy ) > 0 ) {
// Set cups BEFORE updating calculations
unitSelect . value = 'cups' ;
unitSelect . setAttribute ( 'value' , 'cups' ) ;
this . setActiveUnitButton ( 'cups' ) ;
// Enable the cups button manually
const cupsButton = this . container . querySelector ( '#cupsButton' ) ;
if ( cupsButton ) {
cupsButton . disabled = false ;
cupsButton . title = 'Show amounts in cups' ;
}
}
this . updateFoodCalculations ( ) ;
break ;
case 'kcal100g' :
// For kcal/100g, select grams
unitSelect . value = 'g' ;
this . setActiveUnitButton ( 'g' ) ;
this . updateFoodCalculations ( ) ;
break ;
case 'kcalkg' :
// For kcal/kg, also select grams (or could be kg)
unitSelect . value = 'g' ;
this . setActiveUnitButton ( 'g' ) ;
this . updateFoodCalculations ( ) ;
break ;
case 'kcalcan' :
// For kcal/can, use grams as default (or ounces in imperial)
unitSelect . value = this . isImperial ? 'oz' : 'g' ;
this . setActiveUnitButton ( unitSelect . value ) ;
this . updateFoodCalculations ( ) ;
break ;
}
} else {
// No unit select, just update calculations
this . updateFoodCalculations ( ) ;
}
2025-06-26 13:10:32 +02:00
} ) ;
}
if ( percentageSlider ) {
percentageSlider . addEventListener ( 'input' , ( ) => {
const requestedValue = parseInt ( percentageSlider . value ) ;
const result = this . validatePercentageChange ( id , requestedValue ) ;
if ( result . isValid ) {
this . applyValidatedChanges ( result ) ;
}
// Always refresh to ensure valid state
this . refreshAllPercentageUI ( ) ;
} ) ;
}
if ( percentageInput ) {
percentageInput . addEventListener ( 'change' , ( ) => {
const requestedValue = parseInt ( percentageInput . value ) || 0 ;
const result = this . validatePercentageChange ( id , requestedValue ) ;
if ( result . isValid ) {
this . applyValidatedChanges ( result ) ;
}
this . refreshAllPercentageUI ( ) ;
} ) ;
}
if ( removeBtn ) {
removeBtn . addEventListener ( 'click' , ( ) => this . removeFoodSource ( id ) ) ;
}
if ( lockBtn ) {
lockBtn . addEventListener ( 'click' , ( ) => this . toggleLock ( id ) ) ;
}
}
toggleLock ( id ) {
const foodSource = this . foodSources . find ( fs => fs . id === id ) ;
if ( ! foodSource ) return ;
// Check if we're trying to lock the last unlocked source
const unlockedSources = this . foodSources . filter ( fs => ! fs . isLocked ) ;
if ( unlockedSources . length === 1 && unlockedSources [ 0 ] . id === id ) {
// Cannot lock the last unlocked source
alert ( 'At least one food source must remain flexible for percentage adjustments.' ) ;
return ;
}
// Toggle lock state
foodSource . isLocked = ! foodSource . isLocked ;
this . updateLockIcon ( id ) ;
this . updateLockStates ( ) ;
this . refreshAllPercentageUI ( ) ;
}
updateLockIcon ( id ) {
const foodSource = this . foodSources . find ( fs => fs . id === id ) ;
const lockIcon = document . getElementById ( ` lock- ${ id } ` ) ;
if ( ! lockIcon || ! foodSource ) return ;
if ( foodSource . isLocked ) {
lockIcon . classList . remove ( 'unlocked' ) ;
lockIcon . classList . add ( 'locked' ) ;
lockIcon . title = 'Unlock this percentage' ;
} else {
lockIcon . classList . remove ( 'locked' ) ;
lockIcon . classList . add ( 'unlocked' ) ;
lockIcon . title = 'Lock this percentage' ;
}
}
updateLockStates ( ) {
const unlockedSources = this . foodSources . filter ( fs => ! fs . isLocked ) ;
// Update lock icon states - disable lock for last unlocked source
this . foodSources . forEach ( fs => {
const lockIcon = document . getElementById ( ` lock- ${ fs . id } ` ) ;
if ( lockIcon ) {
if ( ! fs . isLocked && unlockedSources . length === 1 ) {
lockIcon . classList . add ( 'disabled' ) ;
lockIcon . title = 'Cannot lock - at least one source must remain flexible' ;
} else {
lockIcon . classList . remove ( 'disabled' ) ;
lockIcon . title = fs . isLocked ? 'Unlock this percentage' : 'Lock this percentage' ;
}
}
} ) ;
// Update percentage constraints based on lock states
this . refreshAllPercentageUI ( ) ;
}
updateFoodSourceData ( id , field , value ) {
const foodSource = this . foodSources . find ( fs => fs . id === id ) ;
if ( foodSource ) {
foodSource [ field ] = value ;
}
}
validateFoodSourceEnergy ( id ) {
const energyInput = document . getElementById ( ` energy- ${ id } ` ) ;
const energyUnitSelect = document . getElementById ( ` energy-unit- ${ id } ` ) ;
const errorElement = document . getElementById ( ` energy-error- ${ id } ` ) ;
if ( ! energyInput || ! energyUnitSelect || ! errorElement ) return ;
const energy = parseFloat ( energyInput . value ) ;
const unit = energyUnitSelect . value ;
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 ) ) {
errorElement . classList . remove ( 'dog-calculator-hidden' ) ;
} else {
errorElement . classList . add ( 'dog-calculator-hidden' ) ;
2025-06-19 10:50:09 +02:00
}
}
bindEvents ( ) {
const weightInput = this . container . querySelector ( '#weight' ) ;
const dogTypeSelect = this . container . querySelector ( '#dogType' ) ;
const daysInput = this . container . querySelector ( '#days' ) ;
const unitSelect = this . container . querySelector ( '#unit' ) ;
const unitToggle = this . container . querySelector ( '#unitToggle' ) ;
2025-06-26 13:10:32 +02:00
const addFoodBtn = this . container . querySelector ( '#addFoodBtn' ) ;
2025-06-19 10:50:09 +02:00
if ( weightInput ) {
weightInput . addEventListener ( 'input' , ( ) => this . updateCalorieCalculations ( ) ) ;
weightInput . addEventListener ( 'blur' , ( ) => this . validateWeight ( ) ) ;
}
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
if ( dogTypeSelect ) dogTypeSelect . addEventListener ( 'change' , ( ) => this . updateCalorieCalculations ( ) ) ;
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
if ( daysInput ) {
2025-06-26 17:24:32 +02:00
daysInput . addEventListener ( 'input' , ( ) => {
this . updateDayLabel ( ) ;
this . updateFoodCalculations ( ) ;
} ) ;
2025-06-19 10:50:09 +02:00
daysInput . addEventListener ( 'blur' , ( ) => this . validateDays ( ) ) ;
}
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
if ( unitSelect ) unitSelect . addEventListener ( 'change' , ( ) => this . updateFoodCalculations ( ) ) ;
2025-06-26 17:24:32 +02:00
// Unit button event listeners
const unitButtons = this . container . querySelectorAll ( '.dog-calculator-unit-btn' ) ;
unitButtons . forEach ( button => {
button . addEventListener ( 'click' , ( e ) => {
const selectedUnit = e . target . dataset . unit ;
this . setActiveUnitButton ( selectedUnit ) ;
// Update hidden select to trigger existing logic
if ( unitSelect ) {
unitSelect . value = selectedUnit ;
this . updateFoodCalculations ( ) ;
}
} ) ;
} ) ;
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
if ( unitToggle ) unitToggle . addEventListener ( 'change' , ( ) => this . toggleUnits ( ) ) ;
2025-06-26 13:10:32 +02:00
if ( addFoodBtn ) addFoodBtn . addEventListener ( 'click' , ( ) => this . addFoodSource ( ) ) ;
2025-08-18 12:45:44 +02:00
// Feeding configuration event listeners
const showDaily = this . container . querySelector ( '#showDaily' ) ;
const showPerMeal = this . container . querySelector ( '#showPerMeal' ) ;
const mealsPerDayInput = this . container . querySelector ( '#mealsPerDay' ) ;
const mealInputGroup = this . container . querySelector ( '#mealInputGroup' ) ;
if ( showDaily ) {
showDaily . addEventListener ( 'change' , ( ) => {
if ( showDaily . checked ) {
this . showPerMeal = false ;
if ( mealInputGroup ) mealInputGroup . style . display = 'none' ;
this . updateDayLabel ( ) ;
this . updateFoodCalculations ( ) ;
}
} ) ;
}
if ( showPerMeal ) {
showPerMeal . addEventListener ( 'change' , ( ) => {
if ( showPerMeal . checked ) {
this . showPerMeal = true ;
if ( mealInputGroup ) mealInputGroup . style . display = 'inline-flex' ;
this . updateDayLabel ( ) ;
this . updateFoodCalculations ( ) ;
}
} ) ;
}
if ( mealsPerDayInput ) {
mealsPerDayInput . addEventListener ( 'input' , ( ) => {
const meals = parseInt ( mealsPerDayInput . value ) ;
if ( meals && meals >= 1 && meals <= 10 ) {
this . mealsPerDay = meals ;
if ( this . showPerMeal ) {
this . updateDayLabel ( ) ;
this . updateFoodCalculations ( ) ;
}
}
} ) ;
mealsPerDayInput . addEventListener ( 'blur' , ( ) => {
if ( ! mealsPerDayInput . value || parseInt ( mealsPerDayInput . value ) < 1 ) {
mealsPerDayInput . value = 2 ;
this . mealsPerDay = 2 ;
if ( this . showPerMeal ) {
this . updateDayLabel ( ) ;
this . updateFoodCalculations ( ) ;
}
}
} ) ;
}
2025-06-19 10:50:09 +02:00
// Modal event listeners
const shareBtn = this . container . querySelector ( '#shareBtn' ) ;
const embedBtn = this . container . querySelector ( '#embedBtn' ) ;
const shareModalClose = this . container . querySelector ( '#shareModalClose' ) ;
const embedModalClose = this . container . querySelector ( '#embedModalClose' ) ;
if ( shareBtn ) shareBtn . addEventListener ( 'click' , ( ) => this . showShareModal ( ) ) ;
if ( embedBtn ) embedBtn . addEventListener ( 'click' , ( ) => this . showEmbedModal ( ) ) ;
if ( shareModalClose ) shareModalClose . addEventListener ( 'click' , ( ) => this . hideShareModal ( ) ) ;
if ( embedModalClose ) embedModalClose . addEventListener ( 'click' , ( ) => this . hideEmbedModal ( ) ) ;
// Share buttons
const shareFacebook = this . container . querySelector ( '#shareFacebook' ) ;
const shareTwitter = this . container . querySelector ( '#shareTwitter' ) ;
const shareLinkedIn = this . container . querySelector ( '#shareLinkedIn' ) ;
const shareEmail = this . container . querySelector ( '#shareEmail' ) ;
const shareCopy = this . container . 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 = this . container . querySelector ( '#copyWidget' ) ;
const copyIframe = this . container . 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 = this . container . querySelector ( '#shareModal' ) ;
const embedModal = this . container . 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 ;
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
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' ) ;
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" ;
2025-06-15 21:57:27 +02:00
}
2025-06-19 10:50:09 +02:00
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>' ;
2025-06-26 17:24:32 +02:00
unitSelect . value = 'oz' ; // Auto-select ounces for imperial
this . setActiveUnitButton ( 'oz' ) ; // Sync unit buttons
2025-06-19 10:50:09 +02:00
}
2025-06-26 13:10:32 +02:00
// Update energy units for all food sources to kcal/cup for imperial
this . foodSources . forEach ( fs => {
if ( fs . energyUnit === 'kcal100g' ) {
fs . energyUnit = 'kcalcup' ;
const energyUnitSelect = document . getElementById ( ` energy-unit- ${ fs . id } ` ) ;
if ( energyUnitSelect ) {
energyUnitSelect . value = 'kcalcup' ;
}
}
} ) ;
2025-06-19 10:50:09 +02:00
} 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 = '<option value="g">grams (g)</option>' +
'<option value="kg">kilograms (kg)</option>' +
'<option value="oz">ounces (oz)</option>' +
'<option value="lb">pounds (lb)</option>' ;
2025-06-26 17:24:32 +02:00
unitSelect . value = 'g' ; // Auto-select grams for metric
this . setActiveUnitButton ( 'g' ) ; // Sync unit buttons
2025-06-19 10:50:09 +02:00
}
2025-06-26 13:10:32 +02:00
// Update energy units for all food sources to kcal/100g for metric
this . foodSources . forEach ( fs => {
if ( fs . energyUnit === 'kcalcup' ) {
fs . energyUnit = 'kcal100g' ;
const energyUnitSelect = document . getElementById ( ` energy-unit- ${ fs . id } ` ) ;
if ( energyUnitSelect ) {
energyUnitSelect . value = 'kcal100g' ;
}
}
} ) ;
2025-06-19 10:50:09 +02:00
}
}
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 ) ;
2025-06-15 21:57:27 +02:00
} else {
2025-06-19 10:50:09 +02:00
weightInput . value = this . formatNumber ( currentWeight / 2.20462 , 1 ) ;
2025-06-15 21:57:27 +02:00
}
}
2025-06-19 10:50:09 +02:00
}
}
getWeightInKg ( ) {
const weightInput = this . container . querySelector ( '#weight' ) ;
if ( ! weightInput || ! weightInput . value ) return null ;
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
const weight = parseFloat ( weightInput . value ) ;
if ( isNaN ( weight ) ) return null ;
return this . isImperial ? weight / 2.20462 : weight ;
}
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
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 ;
}
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
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' ) ;
2025-06-15 21:57:27 +02:00
}
2025-06-19 10:50:09 +02:00
}
}
2025-06-15 21:57:27 +02:00
2025-08-18 14:36:25 +02:00
convertUnits ( grams , unit , foodSource = null ) {
2025-06-19 10:50:09 +02:00
switch ( unit ) {
case 'kg' :
return grams / 1000 ;
case 'oz' :
return grams / 28.3495 ;
case 'lb' :
return grams / 453.592 ;
2025-08-18 14:36:25 +02:00
case 'cups' :
// For cups, we need to convert from grams worth of calories to cups
if ( foodSource && foodSource . energyUnit === 'kcalcup' && foodSource . energy ) {
// Get calories per 100g for this food
const caloriesPerGram = this . getFoodSourceEnergyPer100g ( foodSource ) / 100 ;
// Calculate total calories represented by these grams
const totalCalories = grams * caloriesPerGram ;
// Divide by calories per cup to get number of cups
const caloriesPerCup = parseFloat ( foodSource . energy ) ;
return totalCalories / caloriesPerCup ;
}
return null ; // Cannot convert to cups without kcal/cup
2025-06-19 10:50:09 +02:00
default :
return grams ;
}
}
formatNumber ( num , decimals = 0 ) {
if ( decimals === 0 ) {
return Math . round ( num ) . toString ( ) ;
}
return num . toFixed ( decimals ) . replace ( /\.?0+$/ , '' ) ;
}
validateWeight ( ) {
const weightKg = this . getWeightInKg ( ) ;
if ( weightKg !== null && weightKg < 0.1 ) {
this . showError ( 'weightError' , true ) ;
} else {
this . showError ( 'weightError' , false ) ;
}
}
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
validateDays ( ) {
const days = this . container . querySelector ( '#days' ) ? . value ;
if ( days && ! this . validateInput ( days , 1 , true ) ) {
this . showError ( 'daysError' , true ) ;
} else {
this . showError ( 'daysError' , false ) ;
}
}
2025-06-15 21:57:27 +02:00
2025-06-26 17:24:32 +02:00
updateDayLabel ( ) {
const days = this . container . querySelector ( '#days' ) ? . value ;
const dayLabel = this . container . querySelector ( '#dayLabel' ) ;
2025-08-18 12:45:44 +02:00
const mealNote = this . container . querySelector ( '#mealNote' ) ;
2025-06-26 17:24:32 +02:00
if ( dayLabel && days ) {
const numDays = parseInt ( days ) ;
dayLabel . textContent = numDays === 1 ? 'day' : 'days' ;
}
2025-08-18 12:45:44 +02:00
if ( mealNote ) {
if ( this . showPerMeal && days ) {
const numDays = parseInt ( days ) ;
const totalMeals = numDays * this . mealsPerDay ;
mealNote . textContent = ` ( ${ totalMeals } meal ${ totalMeals === 1 ? '' : 's' } total) ` ;
mealNote . style . display = 'inline' ;
} else {
mealNote . style . display = 'none' ;
}
}
2025-06-26 17:24:32 +02:00
}
setActiveUnitButton ( unit ) {
const unitButtons = this . container . querySelectorAll ( '.dog-calculator-unit-btn' ) ;
unitButtons . forEach ( button => {
button . classList . remove ( 'active' ) ;
if ( button . dataset . unit === unit ) {
button . classList . add ( 'active' ) ;
}
} ) ;
}
2025-06-19 10:50:09 +02:00
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' ) ;
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
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 ( ) ;
this . sendHeightToParent ( ) ;
}
2025-08-18 14:36:25 +02:00
updateCupsButtonState ( ) {
const cupsButton = this . container . querySelector ( '#cupsButton' ) ;
if ( ! cupsButton ) return ;
// Check if any food source has kcal/cup selected
const hasKcalCup = this . foodSources . some ( fs =>
fs . energyUnit === 'kcalcup' && fs . energy && parseFloat ( fs . energy ) > 0
) ;
if ( hasKcalCup ) {
cupsButton . disabled = false ;
cupsButton . title = 'Show amounts in cups' ;
} else {
cupsButton . disabled = true ;
cupsButton . title = 'Available when using kcal/cup measurement' ;
// If cups was selected, switch back to grams
const unitSelect = this . container . querySelector ( '#unit' ) ;
if ( unitSelect && unitSelect . value === 'cups' ) {
unitSelect . value = 'g' ;
this . setActiveUnitButton ( 'g' ) ;
}
}
}
2025-06-19 10:50:09 +02:00
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' ) ;
2025-06-26 13:10:32 +02:00
const foodAmountsSection = this . container . querySelector ( '#foodAmountsSection' ) ;
const foodAmountsList = this . container . querySelector ( '#foodAmountsList' ) ;
const totalAmountDisplay = this . container . querySelector ( '#totalAmountDisplay' ) ;
const foodBreakdownResults = this . container . querySelector ( '#foodBreakdownResults' ) ;
const foodBreakdownList = this . container . querySelector ( '#foodBreakdownList' ) ;
2025-08-18 12:45:44 +02:00
const feedingConfig = this . container . querySelector ( '#feedingConfig' ) ;
2025-08-18 14:36:25 +02:00
// Update cups button state
this . updateCupsButtonState ( ) ;
2025-06-19 10:50:09 +02:00
2025-06-26 13:10:32 +02:00
if ( ! daysInput || ! unitSelect || ! dailyFoodResults || ! dailyFoodValue || ! foodAmountsSection ) {
2025-06-19 10:50:09 +02:00
return ;
}
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
const days = daysInput . value ;
2025-08-18 14:36:25 +02:00
let unit = unitSelect . value ;
// Failsafe: if unit is empty string but cups button is active, use 'cups'
if ( ! unit || unit === '' ) {
const activeButton = this . container . querySelector ( '.dog-calculator-unit-btn.active' ) ;
if ( activeButton ) {
unit = activeButton . dataset . unit || 'g' ;
} else {
unit = 'g' ; // Default fallback
}
}
const unitLabel = unit === 'g' ? 'g' : unit === 'kg' ? 'kg' : unit === 'oz' ? 'oz' : unit === 'lb' ? 'lb' : 'cups' ;
const decimals = unit === 'g' ? 0 : unit === 'kg' ? 2 : unit === 'cups' ? 1 : 1 ;
// Debug: log what unit is being used
console . log ( 'UpdateFoodCalculations - unit:' , unit , 'unitLabel:' , unitLabel ) ;
2025-06-19 10:50:09 +02:00
2025-08-18 12:45:44 +02:00
// Determine frequency suffix for display
const frequencySuffix = this . showPerMeal ? '/meal' : '/day' ;
2025-06-26 13:10:32 +02:00
// Clear all food source errors first
this . foodSources . forEach ( fs => {
this . showError ( ` energy-error- ${ fs . id } ` , false ) ;
} ) ;
2025-06-19 10:50:09 +02:00
this . showError ( 'daysError' , false ) ;
2025-06-26 13:10:32 +02:00
// Validate days input
2025-06-19 10:50:09 +02:00
if ( ! days || ! this . validateInput ( days , 1 , true ) ) {
if ( days ) this . showError ( 'daysError' , true ) ;
2025-06-26 13:10:32 +02:00
foodAmountsSection . style . display = 'none' ;
dailyFoodResults . style . display = 'none' ;
if ( foodBreakdownResults ) foodBreakdownResults . style . display = 'none' ;
2025-08-18 12:45:44 +02:00
if ( feedingConfig ) feedingConfig . style . display = 'none' ;
2025-06-26 17:24:32 +02:00
// Hide unit buttons when validation fails
const unitButtons = this . container . querySelector ( '#unitButtons' ) ;
if ( unitButtons ) unitButtons . style . display = 'none' ;
2025-06-19 10:50:09 +02:00
return ;
}
const numDays = parseInt ( days ) ;
2025-06-26 13:10:32 +02:00
// Calculate per-food breakdown
const foodBreakdowns = [ ] ;
let totalDailyGrams = 0 ;
let hasValidFoods = false ;
this . foodSources . forEach ( fs => {
const energyPer100g = this . getFoodSourceEnergyPer100g ( fs ) ;
if ( energyPer100g && energyPer100g > 0.1 && fs . percentage > 0 ) {
const dailyCaloriesForThisFood = ( this . currentMER * fs . percentage ) / 100 ;
2025-08-18 14:36:25 +02:00
let dailyGramsForThisFood ;
let dailyCupsForThisFood = null ;
// For kcal/cup, calculate cups directly from calories
if ( fs . energyUnit === 'kcalcup' && fs . energy ) {
const caloriesPerCup = parseFloat ( fs . energy ) ;
dailyCupsForThisFood = dailyCaloriesForThisFood / caloriesPerCup ;
// We still need grams for total calculation, use approximation
dailyGramsForThisFood = ( dailyCaloriesForThisFood / energyPer100g ) * 100 ;
console . log ( 'Cups calculation:' , {
caloriesPerCup ,
dailyCaloriesForThisFood ,
dailyCupsForThisFood ,
dailyGramsForThisFood
} ) ;
} else {
// For other units, calculate grams normally
dailyGramsForThisFood = ( dailyCaloriesForThisFood / energyPer100g ) * 100 ;
}
2025-06-26 13:10:32 +02:00
2025-08-18 12:45:44 +02:00
// Calculate per-meal amounts if needed
const displayGrams = this . showPerMeal ? dailyGramsForThisFood / this . mealsPerDay : dailyGramsForThisFood ;
2025-08-18 14:36:25 +02:00
const displayCups = dailyCupsForThisFood !== null ?
( this . showPerMeal ? dailyCupsForThisFood / this . mealsPerDay : dailyCupsForThisFood ) : null ;
2025-08-18 12:45:44 +02:00
const displayCalories = this . showPerMeal ? dailyCaloriesForThisFood / this . mealsPerDay : dailyCaloriesForThisFood ;
2025-06-26 13:10:32 +02:00
foodBreakdowns . push ( {
name : fs . name ,
percentage : fs . percentage ,
dailyGrams : dailyGramsForThisFood ,
2025-08-18 12:45:44 +02:00
displayGrams : displayGrams ,
2025-08-18 14:36:25 +02:00
dailyCups : dailyCupsForThisFood ,
displayCups : displayCups ,
2025-06-26 13:10:32 +02:00
calories : dailyCaloriesForThisFood ,
2025-08-18 12:45:44 +02:00
displayCalories : displayCalories ,
2025-06-26 17:24:32 +02:00
isLocked : fs . isLocked ,
2025-08-18 14:36:25 +02:00
hasEnergyContent : true ,
foodSource : fs // Store reference for cups conversion
2025-06-26 13:10:32 +02:00
} ) ;
totalDailyGrams += dailyGramsForThisFood ;
hasValidFoods = true ;
2025-06-26 17:24:32 +02:00
} else if ( fs . percentage > 0 ) {
// Include food sources without energy content but show them as needing energy content
foodBreakdowns . push ( {
name : fs . name ,
percentage : fs . percentage ,
dailyGrams : 0 ,
2025-08-18 12:45:44 +02:00
displayGrams : 0 ,
2025-08-18 14:36:25 +02:00
dailyCups : null ,
displayCups : null ,
2025-06-26 17:24:32 +02:00
calories : 0 ,
2025-08-18 12:45:44 +02:00
displayCalories : 0 ,
2025-06-26 17:24:32 +02:00
isLocked : fs . isLocked ,
2025-08-18 14:36:25 +02:00
hasEnergyContent : false ,
foodSource : fs // Store reference for cups conversion
2025-06-26 17:24:32 +02:00
} ) ;
2025-06-26 13:10:32 +02:00
}
} ) ;
if ( ! hasValidFoods ) {
// Show errors for invalid food sources
this . foodSources . forEach ( fs => {
const energyInput = document . getElementById ( ` energy- ${ fs . id } ` ) ;
if ( energyInput && energyInput . value && ( ! this . getFoodSourceEnergyPer100g ( fs ) || this . getFoodSourceEnergyPer100g ( fs ) <= 0.1 ) ) {
this . showError ( ` energy-error- ${ fs . id } ` , true ) ;
}
} ) ;
dailyFoodResults . style . display = 'none' ;
if ( foodBreakdownResults ) foodBreakdownResults . style . display = 'none' ;
2025-08-18 12:45:44 +02:00
if ( feedingConfig ) feedingConfig . style . display = 'none' ;
2025-06-26 17:24:32 +02:00
// Hide unit buttons when no valid foods
const unitButtons = this . container . querySelector ( '#unitButtons' ) ;
if ( unitButtons ) unitButtons . style . display = 'none' ;
// If we have any food sources without energy content, still show the breakdown section
if ( foodBreakdowns . length > 0 ) {
// Show food amounts section with warnings for missing energy content
const unitLabel = unit === 'g' ? 'g' : unit === 'kg' ? 'kg' : unit === 'oz' ? 'oz' : 'lb' ;
const foodAmountsHTML = foodBreakdowns . map ( breakdown => {
const lockIndicator = breakdown . isLocked ? '<span class="dog-calculator-lock-indicator">🔒</span>' : '' ;
return `
< div class = "dog-calculator-food-amount-item" >
< div class = "dog-calculator-food-amount-label" >
< span > $ { breakdown . name } < / s p a n >
< span class = "dog-calculator-food-percentage" > $ { breakdown . percentage } % < / s p a n >
$ { lockIndicator }
< / d i v >
< div class = "dog-calculator-food-amount-value dog-calculator-warning" title = "Enter energy content to calculate amount" >
⚠ ️
< / d i v >
< / d i v >
` ;
} ) . join ( '' ) ;
if ( foodAmountsList ) {
foodAmountsList . innerHTML = foodAmountsHTML ;
}
if ( totalAmountDisplay ) {
totalAmountDisplay . textContent = "Enter energy content for all foods" ;
}
foodAmountsSection . style . display = 'block' ;
this . sendHeightToParent ( ) ;
} else {
foodAmountsSection . style . display = 'none' ;
}
2025-06-26 13:10:32 +02:00
return ;
}
2025-06-26 17:24:32 +02:00
// Update daily food results (total) - will be updated with proper units later
2025-06-19 10:50:09 +02:00
dailyFoodResults . style . display = 'block' ;
2025-06-26 17:24:32 +02:00
2025-08-18 12:45:44 +02:00
// Show feeding configuration when we have valid foods
2025-08-18 14:36:25 +02:00
if ( feedingConfig ) {
feedingConfig . style . display = 'block' ;
// Ensure "Per day" is checked when feeding config becomes visible
const showDaily = this . container . querySelector ( '#showDaily' ) ;
if ( showDaily && ! showDaily . checked && ! this . container . querySelector ( '#showPerMeal' ) . checked ) {
showDaily . checked = true ;
}
}
2025-08-18 12:45:44 +02:00
2025-06-26 17:24:32 +02:00
// Show unit buttons when daily results are shown
const unitButtons = this . container . querySelector ( '#unitButtons' ) ;
if ( unitButtons ) unitButtons . style . display = 'flex' ;
2025-06-26 13:10:32 +02:00
// Update per-food breakdown
if ( foodBreakdownList && foodBreakdowns . length > 1 ) {
2025-06-26 17:24:32 +02:00
const breakdownHTML = foodBreakdowns . map ( breakdown => {
2025-08-18 14:36:25 +02:00
let valueContent ;
if ( breakdown . hasEnergyContent ) {
if ( unit === 'cups' ) {
// For cups, use the pre-calculated cups value if available
if ( breakdown . displayCups !== null ) {
valueContent = ` ${ this . formatNumber ( breakdown . displayCups , decimals ) } ${ unitLabel } ${ frequencySuffix } ` ;
} else {
valueContent = ` <span class="dog-calculator-warning" title="Cups only available for foods with kcal/cup measurement">N/A</span> ` ;
}
} else {
valueContent = ` ${ this . formatNumber ( this . convertUnits ( breakdown . displayGrams , unit ) , decimals ) } ${ unitLabel } ${ frequencySuffix } ` ;
}
} else {
valueContent = ` <span class="dog-calculator-warning" title="Enter energy content to calculate amount">⚠️</span> ` ;
}
2025-06-26 17:24:32 +02:00
return `
< div class = "dog-calculator-food-result-item" >
< span class = "dog-calculator-food-result-label" > $ { breakdown . name } ( $ { breakdown . percentage } % $ { breakdown . isLocked ? ' - locked' : '' } ) : < / s p a n >
< span class = "dog-calculator-food-result-value" > $ { valueContent } < / s p a n >
< / d i v >
` ;
} ) . join ( '' ) ;
2025-06-26 13:10:32 +02:00
foodBreakdownList . innerHTML = breakdownHTML ;
if ( foodBreakdownResults ) foodBreakdownResults . style . display = 'block' ;
} else {
if ( foodBreakdownResults ) foodBreakdownResults . style . display = 'none' ;
}
// Generate individual food amount breakdown
2025-06-26 17:24:32 +02:00
// Update daily food value with correct units
2025-08-18 12:45:44 +02:00
const displayTotal = this . showPerMeal ? totalDailyGrams / this . mealsPerDay : totalDailyGrams ;
2025-08-18 14:36:25 +02:00
let convertedTotal ;
let totalDisplayText ;
if ( unit === 'cups' ) {
console . log ( 'Unit is cups, checking validity...' ) ;
// For cups, we can only show total if all foods with percentage > 0 have kcal/cup
const validForCups = foodBreakdowns . filter ( b => b . percentage > 0 )
. every ( b => b . displayCups !== null && b . displayCups !== undefined ) ;
console . log ( 'Valid for cups?' , validForCups , 'Breakdowns:' , foodBreakdowns ) ;
if ( validForCups ) {
// Calculate total cups using pre-calculated values
let totalCups = 0 ;
foodBreakdowns . forEach ( breakdown => {
if ( breakdown . percentage > 0 && breakdown . displayCups !== null ) {
totalCups += breakdown . displayCups ;
}
} ) ;
console . log ( 'Total cups display:' , {
totalCups ,
displayTotal ,
foodBreakdowns : foodBreakdowns . map ( b => ( { name : b . name , displayCups : b . displayCups } ) )
} ) ;
totalDisplayText = this . formatNumber ( totalCups , decimals ) + ` ${ unitLabel } ${ frequencySuffix } ` ;
} else {
totalDisplayText = 'Mixed units - see breakdown' ;
}
} else {
convertedTotal = this . convertUnits ( displayTotal , unit ) ;
totalDisplayText = this . formatNumber ( convertedTotal , decimals ) + ` ${ unitLabel } ${ frequencySuffix } ` ;
}
dailyFoodValue . textContent = totalDisplayText ;
2025-06-19 10:50:09 +02:00
2025-06-26 13:10:32 +02:00
// Build HTML for individual food amounts
const foodAmountsHTML = foodBreakdowns . map ( breakdown => {
const lockIndicator = breakdown . isLocked ? '<span class="dog-calculator-lock-indicator">🔒</span>' : '' ;
2025-06-26 17:24:32 +02:00
if ( ! breakdown . hasEnergyContent ) {
// Show warning for food sources without energy content
return `
< div class = "dog-calculator-food-amount-item" >
< div class = "dog-calculator-food-amount-label" >
< span > $ { breakdown . name } < / s p a n >
< span class = "dog-calculator-food-percentage" > $ { breakdown . percentage } % < / s p a n >
$ { lockIndicator }
< / d i v >
< div class = "dog-calculator-food-amount-value dog-calculator-warning" title = "Enter energy content to calculate amount" >
⚠ ️
< / d i v >
2025-06-26 13:10:32 +02:00
< / d i v >
2025-06-26 17:24:32 +02:00
` ;
} else {
2025-08-18 12:45:44 +02:00
// For multi-day calculations: show total amount for all days
2025-08-18 14:36:25 +02:00
let amountDisplay ;
if ( unit === 'cups' ) {
// For cups, use pre-calculated cups value
if ( breakdown . dailyCups !== null ) {
const totalCupsForDays = breakdown . dailyCups * numDays ;
amountDisplay = ` ${ this . formatNumber ( totalCupsForDays , decimals ) } ${ unitLabel } ` ;
} else {
amountDisplay = ` <span class="dog-calculator-warning" title="Cups only available for foods with kcal/cup measurement">N/A</span> ` ;
}
} else {
// For other units, calculate from grams
const totalGramsForDays = this . showPerMeal
? ( breakdown . dailyGrams / this . mealsPerDay ) * numDays * this . mealsPerDay
: breakdown . dailyGrams * numDays ;
const convertedAmount = this . convertUnits ( totalGramsForDays , unit ) ;
amountDisplay = ` ${ this . formatNumber ( convertedAmount , decimals ) } ${ unitLabel } ` ;
}
2025-06-26 17:24:32 +02:00
return `
< div class = "dog-calculator-food-amount-item" >
< div class = "dog-calculator-food-amount-label" >
< span > $ { breakdown . name } < / s p a n >
< span class = "dog-calculator-food-percentage" > $ { breakdown . percentage } % < / s p a n >
$ { lockIndicator }
< / d i v >
< div class = "dog-calculator-food-amount-value" >
2025-08-18 14:36:25 +02:00
$ { amountDisplay }
2025-06-26 17:24:32 +02:00
< / d i v >
2025-06-26 13:10:32 +02:00
< / d i v >
2025-06-26 17:24:32 +02:00
` ;
}
2025-06-26 13:10:32 +02:00
} ) . join ( '' ) ;
// Calculate and display total
const totalFoodGrams = totalDailyGrams * numDays ;
// Update the display
if ( foodAmountsList ) {
foodAmountsList . innerHTML = foodAmountsHTML ;
}
if ( totalAmountDisplay ) {
2025-08-18 14:36:25 +02:00
if ( unit === 'cups' ) {
// For cups total, check if all foods can be converted
const validForCups = foodBreakdowns . filter ( b => b . percentage > 0 )
. every ( b => b . dailyCups !== null && b . dailyCups !== undefined ) ;
if ( validForCups ) {
// Calculate total cups using pre-calculated values
let totalCups = 0 ;
foodBreakdowns . forEach ( breakdown => {
if ( breakdown . percentage > 0 && breakdown . dailyCups !== null ) {
totalCups += breakdown . dailyCups * numDays ;
}
} ) ;
totalAmountDisplay . textContent = ` ${ this . formatNumber ( totalCups , decimals ) } ${ unitLabel } ` ;
} else {
totalAmountDisplay . textContent = 'Mixed units - see individual amounts' ;
}
} else {
const totalConverted = this . convertUnits ( totalFoodGrams , unit ) ;
totalAmountDisplay . textContent = ` ${ this . formatNumber ( totalConverted , decimals ) } ${ unitLabel } ` ;
}
2025-06-26 13:10:32 +02:00
}
foodAmountsSection . style . display = 'block' ;
2025-06-19 10:50:09 +02:00
this . sendHeightToParent ( ) ;
}
2025-06-26 13:10:32 +02:00
getFoodSourceEnergyPer100g ( foodSource ) {
if ( ! foodSource . energy || ! foodSource . energyUnit ) return null ;
const energy = parseFloat ( foodSource . energy ) ;
if ( isNaN ( energy ) ) return null ;
const unit = foodSource . energyUnit ;
// 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 ;
}
}
2025-06-19 10:50:09 +02:00
setupIframeResize ( ) {
// Send height to parent window for iframe auto-resize
this . sendHeightToParent ( ) ;
// Monitor for content changes that might affect height
const observer = new MutationObserver ( ( ) => {
setTimeout ( ( ) => this . sendHeightToParent ( ) , 100 ) ;
} ) ;
observer . observe ( document . body , {
childList : true ,
subtree : true ,
attributes : true
} ) ;
// Send height on window resize
window . addEventListener ( 'resize' , ( ) => this . sendHeightToParent ( ) ) ;
}
sendHeightToParent ( ) {
const height = Math . max ( document . body . scrollHeight , document . documentElement . scrollHeight ) ;
if ( window . parent && window . parent !== window ) {
window . parent . postMessage ( {
type : 'dogCalculatorResize' ,
height : height
} , '*' ) ;
}
}
// Modal functionality
showShareModal ( ) {
const modal = this . container . querySelector ( '#shareModal' ) ;
const shareUrl = this . container . querySelector ( '#shareUrl' ) ;
if ( modal && shareUrl ) {
shareUrl . value = window . location . href ;
modal . style . display = 'block' ;
}
}
hideShareModal ( ) {
const modal = this . container . querySelector ( '#shareModal' ) ;
if ( modal ) modal . style . display = 'none' ;
}
showEmbedModal ( ) {
const modal = this . container . querySelector ( '#embedModal' ) ;
const widgetCode = this . container . querySelector ( '#widgetCode' ) ;
const iframeCode = this . container . querySelector ( '#iframeCode' ) ;
if ( modal && widgetCode && iframeCode ) {
// Build embed URL
const baseUrl = window . location . protocol + '//embed.' + window . location . hostname ;
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
// Create widget code using createElement to avoid quote issues
const scriptTag = document . createElement ( 'script' ) ;
scriptTag . src = baseUrl + '/dog-calorie-calculator/dog-food-calculator-widget.js' ;
const divTag = document . createElement ( 'div' ) ;
divTag . id = 'dog-calorie-calculator' ;
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
const widgetHtml = scriptTag . outerHTML + '\n' + divTag . outerHTML ;
widgetCode . textContent = widgetHtml ;
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
// Create iframe code using createElement
const iframe = document . createElement ( 'iframe' ) ;
iframe . src = baseUrl + '/dog-calorie-calculator/iframe.html' ;
iframe . width = '100%' ;
iframe . height = '600' ;
iframe . frameBorder = '0' ;
iframe . title = 'Dog Calorie Calculator' ;
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
iframeCode . textContent = iframe . outerHTML ;
modal . style . display = 'block' ;
}
}
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
hideEmbedModal ( ) {
const modal = this . container . querySelector ( '#embedModal' ) ;
if ( modal ) modal . style . display = 'none' ;
}
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
shareToFacebook ( ) {
const url = encodeURIComponent ( window . location . href ) ;
window . open ( 'https://www.facebook.com/sharer/sharer.php?u=' + url , '_blank' , 'width=600,height=400' ) ;
}
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
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 = this . container . querySelector ( '#shareUrl' ) ;
const copyBtn = this . container . querySelector ( '#shareCopy' ) ;
2025-06-15 21:57:27 +02:00
2025-06-19 10:50:09 +02:00
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' ) ;
2025-06-15 21:57:27 +02:00
}
}
}
2025-06-19 10:50:09 +02:00
async copyEmbedCode ( type ) {
const codeElement = document . getElementById ( type === 'widget' ? 'widgetCode' : 'iframeCode' ) ;
const copyBtn = document . getElementById ( 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' ) ;
}
}
2025-06-15 21:57:27 +02:00
}
}
2025-06-19 10:50:09 +02:00
// Initialize calculator when DOM is ready
2025-06-15 21:57:27 +02:00
// 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 ;
} ) ( ) ;