diff --git a/build.js b/build.js index f44130c..3b5f19c 100644 --- a/build.js +++ b/build.js @@ -100,340 +100,43 @@ function transformHTMLForWidget(html) { } /** - * Create production-ready widget JavaScript + * Create production-ready widget JavaScript that uses the ACTUAL extracted JS from iframe.html */ function createWidgetJS(css, html, js) { - // Use original CSS and HTML without transformation for consistency - const widgetCSS = css; - const widgetHTML = html; + // Transform the extracted JavaScript from iframe.html to work as a widget - const widgetCode = `/** - * Dog Calorie Calculator Widget - * Embeddable JavaScript widget for websites - * - * Usage: - * - *
- * - * Or with options: - * - * - * By Canine Nutrition and Wellness - * https://caninenutritionandwellness.com - */ - -(function() { - 'use strict'; - - // Inject widget styles with proper namespacing - const CSS_STYLES = \`${widgetCSS}\`; - - function injectStyles() { - if (document.getElementById('dog-calculator-styles')) return; - - const style = document.createElement('style'); - style.id = 'dog-calculator-styles'; - style.textContent = CSS_STYLES; - document.head.appendChild(style); - } - - // Main widget class - class DogCalorieCalculatorWidget { - constructor(container, options = {}) { + // Replace the iframe's DOMContentLoaded listener with widget initialization + let transformedJS = js + // Replace the iframe class name with widget class name + .replace(/class DogCalorieCalculator/g, 'class DogCalorieCalculatorWidget') + // Replace document.getElementById with scoped selectors within the widget + .replace(/document\.getElementById\('([^']+)'\)/g, 'this.container.querySelector(\'#$1\')') + // Replace direct document queries in the class with container-scoped queries + .replace(/document\.querySelector\(/g, 'this.container.querySelector(') + .replace(/document\.querySelectorAll\(/g, 'this.container.querySelectorAll(') + // Remove the DOMContentLoaded listener and class instantiation - we'll handle this in the widget wrapper + .replace(/document\.addEventListener\('DOMContentLoaded'.*?\n.*?new DogCalorieCalculator.*?\n.*?\}\);/s, '') + // Add widget initialization methods + .replace(/constructor\(\) \{/, `constructor(container, options = {}) { this.container = container; this.options = { theme: options.theme || this.getThemeFromURL() || 'system', scale: options.scale || this.getScaleFromURL() || 1.0, ...options - }; - this.init(); - } - - init() { - // Insert the transformed calculator HTML - this.container.innerHTML = \`${widgetHTML}\`; + };`) + // Replace the init() method to inject HTML and apply widget settings + .replace(/init\(\) \{/, `init() { + // Inject the calculator HTML into the container + this.container.innerHTML = \`${html}\`; // Apply widget-specific settings this.applyTheme(); this.applyScale(); - // Initialize the calculator with the same functionality as iframe - this.initCalculator(); - } - - initCalculator() { - const container = this.container; - - // Helper functions to scope DOM queries to this widget - const $ = (selector) => container.querySelector(selector); - const $$ = (selector) => container.querySelectorAll(selector); - - // Create calculator instance with exact same logic as iframe - const calc = { - currentMER: 0, - isImperial: false, - theme: this.options.theme, - scale: this.options.scale, - - // Exact same calculation methods from iframe - calculateRER: (weightKg) => 70 * Math.pow(weightKg, 0.75), - calculateMER: (rer, factor) => rer * factor, - - formatNumber: (num, decimals = 0) => { - if (decimals === 0) return Math.round(num).toString(); - return num.toFixed(decimals).replace(/\\.?0+$/, ''); - }, - - 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; - }, - - convertUnits: (grams, unit) => { - switch (unit) { - case 'kg': return grams / 1000; - case 'oz': return grams / 28.3495; - case 'lb': return grams / 453.592; - default: return grams; - } - }, - - getWeightInKg: () => { - const weightInput = $('#weight'); - if (!weightInput || !weightInput.value) return null; - const weight = parseFloat(weightInput.value); - if (isNaN(weight)) return null; - return calc.isImperial ? weight / 2.20462 : weight; - }, - - getFoodEnergyPer100g: () => { - const foodEnergyInput = $('#foodEnergy'); - const energyUnitSelect = $('#energyUnit'); - if (!foodEnergyInput || !foodEnergyInput.value || !energyUnitSelect) return null; - - const energy = parseFloat(foodEnergyInput.value); - if (isNaN(energy)) return null; - - const unit = energyUnitSelect.value; - switch (unit) { - case 'kcal100g': return energy; - case 'kcalkg': return energy / 10; - case 'kcalcup': return energy / 1.2; - case 'kcalcan': return energy / 4.5; - default: return energy; - } - }, - - showError: (elementId, show = true) => { - const errorElement = $('#' + elementId); - if (errorElement) { - if (show) { - errorElement.classList.remove('dog-calculator-hidden'); - } else { - errorElement.classList.add('dog-calculator-hidden'); - } - } - }, - - updateCalorieCalculations: () => { - const dogTypeSelect = $('#dogType'); - const calorieResults = $('#calorieResults'); - const rerValue = $('#rerValue'); - const merValue = $('#merValue'); - - if (!dogTypeSelect || !calorieResults || !rerValue || !merValue) return; - - const weightKg = calc.getWeightInKg(); - const dogTypeFactor = dogTypeSelect.value; - - calc.showError('weightError', false); - - if (!weightKg || weightKg < 0.1) { - const weightInput = $('#weight'); - if (weightInput && weightInput.value) calc.showError('weightError', true); - calorieResults.style.display = 'none'; - return; - } - - if (!dogTypeFactor) { - calorieResults.style.display = 'none'; - return; - } - - const factor = parseFloat(dogTypeFactor); - const rer = calc.calculateRER(weightKg); - const mer = calc.calculateMER(rer, factor); - - calc.currentMER = mer; - - rerValue.textContent = calc.formatNumber(rer, 0) + ' cal/day'; - merValue.textContent = calc.formatNumber(mer, 0) + ' cal/day'; - calorieResults.style.display = 'block'; - - calc.updateFoodCalculations(); - }, - - updateFoodCalculations: () => { - if (calc.currentMER === 0) return; - - const daysInput = $('#days'); - const unitSelect = $('#unit'); - const dailyFoodResults = $('#dailyFoodResults'); - const dailyFoodValue = $('#dailyFoodValue'); - const totalFoodDisplay = $('#totalFoodDisplay'); - - if (!daysInput || !unitSelect || !dailyFoodResults || !dailyFoodValue || !totalFoodDisplay) return; - - const energyPer100g = calc.getFoodEnergyPer100g(); - const days = daysInput.value; - const unit = unitSelect.value; - - calc.showError('foodEnergyError', false); - calc.showError('daysError', false); - - if (!energyPer100g || energyPer100g < 0.1) { - const foodEnergyInput = $('#foodEnergy'); - if (foodEnergyInput && foodEnergyInput.value) calc.showError('foodEnergyError', true); - dailyFoodResults.style.display = 'none'; - totalFoodDisplay.value = ''; - return; - } - - if (!days || !calc.validateInput(days, 1, true)) { - if (days) calc.showError('daysError', true); - totalFoodDisplay.value = ''; - return; - } - - const numDays = parseInt(days); - const dailyFoodGrams = (calc.currentMER / energyPer100g) * 100; - const totalFoodGrams = dailyFoodGrams * numDays; - - dailyFoodValue.textContent = calc.formatNumber(dailyFoodGrams, 1) + ' g/day'; - dailyFoodResults.style.display = 'block'; - - const convertedAmount = calc.convertUnits(totalFoodGrams, unit); - const unitLabel = unit === 'g' ? 'g' : unit === 'kg' ? 'kg' : unit === 'oz' ? 'oz' : 'lb'; - const decimals = unit === 'g' ? 0 : unit === 'kg' ? 2 : 1; - - totalFoodDisplay.value = calc.formatNumber(convertedAmount, decimals) + ' ' + unitLabel; - }, - - // Unit switching methods (missing from previous version!) - toggleUnits: () => { - const toggle = $('#unitToggle'); - calc.isImperial = toggle.checked; - - calc.updateUnitLabels(); - calc.convertExistingValues(); - calc.updateCalorieCalculations(); - }, - - updateUnitLabels: () => { - const metricLabel = $('#metricLabel'); - const imperialLabel = $('#imperialLabel'); - const weightLabel = $('#weightLabel'); - const weightInput = $('#weight'); - const unitSelect = $('#unit'); - const energyUnitSelect = $('#energyUnit'); - - if (metricLabel && imperialLabel) { - metricLabel.classList.toggle('active', !calc.isImperial); - imperialLabel.classList.toggle('active', calc.isImperial); - } - - if (calc.isImperial) { - if (weightLabel) weightLabel.textContent = "Dog's Weight (lbs):"; - if (weightInput) { - weightInput.placeholder = "Enter weight in lbs"; - weightInput.min = "0.2"; - weightInput.step = "0.1"; - } - if (unitSelect) { - unitSelect.innerHTML = '' + - '' + - '' + - ''; - } - // Set energy unit to kcal/cup for imperial - if (energyUnitSelect && energyUnitSelect.value === 'kcal100g') { - energyUnitSelect.value = 'kcalcup'; - } - } else { - if (weightLabel) weightLabel.textContent = "Dog's Weight (kg):"; - if (weightInput) { - weightInput.placeholder = "Enter weight in kg"; - weightInput.min = "0.1"; - weightInput.step = "0.1"; - } - if (unitSelect) { - unitSelect.innerHTML = '' + - '' + - '' + - ''; - } - // Set energy unit to kcal/100g for metric - if (energyUnitSelect && energyUnitSelect.value === 'kcalcup') { - energyUnitSelect.value = 'kcal100g'; - } - } - }, - - convertExistingValues: () => { - const weightInput = $('#weight'); - - if (weightInput && weightInput.value) { - const currentWeight = parseFloat(weightInput.value); - if (!isNaN(currentWeight)) { - if (calc.isImperial) { - weightInput.value = calc.formatNumber(currentWeight * 2.20462, 1); - } else { - weightInput.value = calc.formatNumber(currentWeight / 2.20462, 1); - } - } - } - }, - - bindEvents: () => { - const weightInput = $('#weight'); - const dogTypeSelect = $('#dogType'); - const foodEnergyInput = $('#foodEnergy'); - const daysInput = $('#days'); - const unitSelect = $('#unit'); - const energyUnitSelect = $('#energyUnit'); - const unitToggle = $('#unitToggle'); - - if (weightInput) { - weightInput.addEventListener('input', () => calc.updateCalorieCalculations()); - } - if (dogTypeSelect) dogTypeSelect.addEventListener('change', () => calc.updateCalorieCalculations()); - if (foodEnergyInput) { - foodEnergyInput.addEventListener('input', () => calc.updateFoodCalculations()); - } - if (energyUnitSelect) energyUnitSelect.addEventListener('change', () => calc.updateFoodCalculations()); - if (daysInput) { - daysInput.addEventListener('input', () => calc.updateFoodCalculations()); - } - if (unitSelect) unitSelect.addEventListener('change', () => calc.updateFoodCalculations()); - if (unitToggle) unitToggle.addEventListener('change', () => calc.toggleUnits()); - }, - - init: () => { - calc.bindEvents(); - calc.updateUnitLabels(); // Initialize unit labels - const calcContainer = $('#dogCalculator'); - if (calcContainer) { - calcContainer.classList.add('loaded'); - } - } - }; - - calc.init(); - return calc; - } - + // Continue with original init logic`); + + // Add widget-specific methods before the class closing brace + transformedJS = transformedJS.replace(/(\s+)(\}\s*$)/, `$1 getThemeFromURL() { const urlParams = new URLSearchParams(window.location.search); const theme = urlParams.get('theme'); @@ -458,9 +161,44 @@ function createWidgetJS(css, html, js) { this.container.style.transform = \`scale(\${scale})\`; this.container.style.transformOrigin = 'top left'; } - } + }$2`); + + const widgetCode = `/** + * Dog Calorie Calculator Widget + * Embeddable JavaScript widget for websites + * + * THIS CODE IS AUTO-GENERATED FROM iframe.html - DO NOT EDIT MANUALLY + * Edit iframe.html and run 'node build.js' to update this file + * + * Usage: + * + * + * + * Or with options: + * + * + * By Canine Nutrition and Wellness + * https://caninenutritionandwellness.com + */ + +(function() { + 'use strict'; + + // Inject widget styles + const CSS_STYLES = \`${css}\`; + + 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); } + // ACTUAL JavaScript from iframe.html (transformed for widget use) + ${transformedJS} + // Auto-initialize widgets on page load function initializeWidget() { injectStyles(); diff --git a/sundog-dog-food-calculator.js b/sundog-dog-food-calculator.js index 7f9e8aa..1b78972 100644 --- a/sundog-dog-food-calculator.js +++ b/sundog-dog-food-calculator.js @@ -2,6 +2,9 @@ * Dog Calorie Calculator Widget * Embeddable JavaScript widget for websites * + * THIS CODE IS AUTO-GENERATED FROM iframe.html - DO NOT EDIT MANUALLY + * Edit iframe.html and run 'node build.js' to update this file + * * Usage: * * @@ -16,7 +19,7 @@ (function() { 'use strict'; - // Inject widget styles with proper namespacing + // Inject widget styles const CSS_STYLES = `/* Sundog Dog Food Calorie Calculator Styles */ body { @@ -928,20 +931,29 @@ document.head.appendChild(style); } - // Main widget class - class DogCalorieCalculatorWidget { - constructor(container, options = {}) { + // ACTUAL JavaScript from iframe.html (transformed for widget use) + /** + * 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.init(); - } - - init() { - // Insert the transformed calculator HTML + this.currentMER = 0; + this.isImperial = false; + this.theme = this.getThemeFromURL() || 'system'; + this.scale = this.getScaleFromURL() || 1.0; + this.init(); + } + + init() { + // Inject the calculator HTML into the container this.container.innerHTML = `