/** * Input Utilities Module * Handles input formatting, validation, and parsing */ class InputUtils { static formatNumberWithCommas(num, currency = null) { try { // Get current currency if not provided if (!currency) { const currencyElement = document.getElementById('currency'); currency = currencyElement ? currencyElement.value : 'CHF'; } // Use appropriate locale for number formatting const locale = currency === 'EUR' ? 'de-DE' : 'de-CH'; return parseInt(num).toLocaleString(locale); } catch (error) { console.error('Error formatting number with commas:', error); return String(num); } } static parseFormattedNumber(str) { if (typeof str !== 'string') { return 0; } try { // Remove all non-numeric characters except decimal points and commas const cleaned = str.replace(/[^\d,.-]/g, ''); // Handle empty string if (!cleaned) { return 0; } // Remove commas and parse as float const result = parseFloat(cleaned.replace(/,/g, '')); // Return 0 for invalid numbers or NaN return isNaN(result) ? 0 : result; } catch (error) { console.error('Error parsing formatted number:', error); return 0; } } static handleInvestmentAmountInput(input) { if (!input || typeof input.value !== 'string') { console.error('Invalid input element provided to handleInvestmentAmountInput'); return; } try { // Allow only digits, no immediate formatting during typing let value = input.value.replace(/[^\d]/g, ''); // Handle empty input if (!value) { input.setAttribute('data-value', '0'); return; } // Parse the numeric value let numericValue = parseInt(value) || 0; // Update the data attribute with the raw numeric value (no limits during typing) input.setAttribute('data-value', numericValue.toString()); // Update the input value (keep it clean, no commas during typing) input.value = value; // Update the slider if it exists (with limits) const slider = document.getElementById('investment-slider'); if (slider) { const minValue = parseInt(slider.min) || 100000; const maxValue = parseInt(slider.max) || 2000000; const sliderValue = Math.max(minValue, Math.min(maxValue, numericValue)); slider.value = sliderValue; } // Trigger calculations with debouncing if (window.ROICalculatorApp && window.ROICalculatorApp.calculator) { clearTimeout(input._calculationTimeout); input._calculationTimeout = setTimeout(() => { window.ROICalculatorApp.calculator.updateCalculations(); }, 300); // 300ms delay to avoid excessive calculations during typing } } catch (error) { console.error('Error handling investment amount input:', error); // Set a safe default value input.setAttribute('data-value', '500000'); input.value = '500000'; } } static handleInvestmentAmountBlur(input) { if (!input) return; try { // Get the numeric value let numericValue = parseInt(input.getAttribute('data-value')) || 500000; // Enforce min/max limits on blur const minValue = 100000; const maxValue = 2000000; if (numericValue < minValue) { numericValue = minValue; } else if (numericValue > maxValue) { numericValue = maxValue; } // Update the data attribute with the corrected value input.setAttribute('data-value', numericValue.toString()); // Format and display the value with commas on blur input.value = InputUtils.formatNumberWithCommas(numericValue); // Update the slider const slider = document.getElementById('investment-slider'); if (slider) { slider.value = numericValue; } // Trigger immediate calculation on blur if (window.ROICalculatorApp && window.ROICalculatorApp.calculator) { clearTimeout(input._calculationTimeout); window.ROICalculatorApp.calculator.updateCalculations(); } } catch (error) { console.error('Error handling investment amount blur:', error); // Set a safe default value input.setAttribute('data-value', '500000'); input.value = '500,000'; } } static handleInvestmentAmountFocus(input) { if (!input) return; try { // Remove commas when focusing for easier editing const numericValue = input.getAttribute('data-value') || '500000'; input.value = numericValue; } catch (error) { console.error('Error handling investment amount focus:', error); } } static getCSRFToken() { try { // Try to get CSRF token from meta tag first const metaTag = document.querySelector('meta[name="csrf-token"]'); if (metaTag) { return metaTag.getAttribute('content'); } // Fallback to cookie method const cookies = document.cookie.split(';'); for (let cookie of cookies) { const [name, value] = cookie.trim().split('='); if (name === 'csrftoken') { return decodeURIComponent(value); } } // If no CSRF token found, return empty string (will cause server error, but won't break JS) console.error('CSRF token not found'); return ''; } catch (error) { console.error('Error getting CSRF token:', error); return ''; } } }