// LOBOR FX Get Quote Page - Calculator with Live Exchange Rate API + Global Language Selector console.log('Calculator script loaded'); // Global Language Selector Class if (!window.LoborLanguageSelector) { class LoborLanguageSelector { constructor() { this.currentLanguage = localStorage.getItem('loborLanguage') || 'es'; this.languageMenuButton = null; this.languageDropdown = null; this.initialized = false; // Bind methods to preserve context this.toggleLanguageMenu = this.toggleLanguageMenu.bind(this); this.selectLanguage = this.selectLanguage.bind(this); this.handleOutsideClick = this.handleOutsideClick.bind(this); this.reinitialize = this.reinitialize.bind(this); } init() { if (this.initialized) { console.log('LanguageSelector already initialized, cleaning up first...'); this.cleanup(); } console.log('Initializing Enhanced Language Selector...'); // Set initial body class for language document.body.className = document.body.className.replace(/lang-\w+/g, '').trim(); document.body.classList.add(`lang-${this.currentLanguage}`); // Use MutationObserver to detect DOM changes this.observeDOM(); setTimeout(() => { this.setupElements(); this.attachEventListeners(); this.applyCurrentLanguage(); this.initialized = true; console.log('Language Selector initialized successfully'); }, 300); } observeDOM() { // Create an observer to watch for DOM changes (page navigations) if (this.observer) { this.observer.disconnect(); } this.observer = new MutationObserver((mutations) => { let shouldReinit = false; mutations.forEach((mutation) => { if (mutation.type === 'childList') { // Check if navigation elements were added/removed const hasLanguageElements = document.querySelector('#language-menu-button, .language-option'); if (hasLanguageElements && !this.languageMenuButton) { shouldReinit = true; } } }); if (shouldReinit) { console.log('DOM changes detected, reinitializing language selector...'); setTimeout(this.reinitialize, 200); } }); // Start observing this.observer.observe(document.body, { childList: true, subtree: true }); } reinitialize() { if (!this.initialized) { this.init(); } else { this.setupElements(); this.attachEventListeners(); this.applyCurrentLanguage(); } } setupElements() { // Desktop elements this.languageMenuButton = document.getElementById('language-menu-button'); this.languageDropdown = document.getElementById('language-dropdown'); console.log('Desktop - Language button found:', !!this.languageMenuButton); console.log('Desktop - Language dropdown found:', !!this.languageDropdown); } attachEventListeners() { // Remove existing listeners first this.removeEventListeners(); // Desktop dropdown toggle if (this.languageMenuButton) { this.languageMenuButton.addEventListener('click', this.toggleLanguageMenu); console.log('Added click listener to desktop language button'); } // Get all language options (both desktop and mobile) const allLanguageOptions = document.querySelectorAll('.language-option'); console.log('Found language options:', allLanguageOptions.length); // Store references to remove listeners later this.languageOptions = Array.from(allLanguageOptions); // Add listeners to all language options this.languageOptions.forEach((option, index) => { // Add multiple event types for better compatibility option.addEventListener('click', this.selectLanguage, { passive: false }); option.addEventListener('touchstart', this.selectLanguage, { passive: false }); console.log(`Added listeners to language option ${index}:`, option.dataset.lang); }); // Close dropdown when clicking outside (desktop only) document.addEventListener('click', this.handleOutsideClick); console.log('Event listeners attached successfully'); } removeEventListeners() { if (this.languageMenuButton) { this.languageMenuButton.removeEventListener('click', this.toggleLanguageMenu); } if (this.languageOptions) { this.languageOptions.forEach(option => { option.removeEventListener('click', this.selectLanguage); option.removeEventListener('touchstart', this.selectLanguage); }); } document.removeEventListener('click', this.handleOutsideClick); } toggleLanguageMenu(event) { event.preventDefault(); event.stopPropagation(); console.log('Toggle language menu clicked'); if (this.languageDropdown) { const isHidden = this.languageDropdown.classList.contains('hidden'); if (isHidden) { this.languageDropdown.classList.remove('hidden'); console.log('Showing dropdown'); } else { this.languageDropdown.classList.add('hidden'); console.log('Hiding dropdown'); } } } selectLanguage(event) { event.preventDefault(); event.stopPropagation(); let target = event.target; console.log('selectLanguage called, target:', target.tagName, target.textContent?.substring(0, 20)); // Find the closest language-option element while (target && !target.classList.contains('language-option')) { target = target.parentElement; if (!target || target === document.body) { console.log('No language-option found in parent hierarchy'); return; } } if (!target) { console.log('No target found'); return; } const selectedLang = target.dataset.lang; console.log('Language selected:', selectedLang, 'Current:', this.currentLanguage); if (selectedLang && selectedLang !== this.currentLanguage) { this.currentLanguage = selectedLang; localStorage.setItem('loborLanguage', selectedLang); this.updateCurrentLanguageDisplay(); this.switchPageLanguage(); console.log('Language switched to:', this.currentLanguage); // Dispatch custom event for other components to listen window.dispatchEvent(new CustomEvent('languageChanged', { detail: { language: selectedLang } })); } // Close dropdown if it exists (desktop only) if (this.languageDropdown) { this.languageDropdown.classList.add('hidden'); } } updateCurrentLanguageDisplay() { const currentLangElement = document.getElementById('current-language'); if (currentLangElement) { currentLangElement.textContent = this.currentLanguage.toUpperCase(); console.log('Updated language display to:', this.currentLanguage.toUpperCase()); } } switchPageLanguage() { console.log('Switching page language to:', this.currentLanguage); // Remove existing language classes and add new one document.body.className = document.body.className.replace(/lang-\w+/g, '').trim(); document.body.classList.add(`lang-${this.currentLanguage}`); console.log('Applied body class:', `lang-${this.currentLanguage}`); // Fallback method using the old system for compatibility const allSpanishContent = document.querySelectorAll('.es-content'); const allEnglishContent = document.querySelectorAll('.en-content'); console.log('Found Spanish elements:', allSpanishContent.length); console.log('Found English elements:', allEnglishContent.length); if (this.currentLanguage === 'es') { // Show Spanish content using style allSpanishContent.forEach((element) => { element.style.display = ''; element.classList.remove('hidden'); }); // Hide English content using style allEnglishContent.forEach((element) => { element.style.display = 'none'; element.classList.add('hidden'); }); } else { // Show English content using style allEnglishContent.forEach((element) => { element.style.display = ''; element.classList.remove('hidden'); }); // Hide Spanish content using style allSpanishContent.forEach((element) => { element.style.display = 'none'; element.classList.add('hidden'); }); } console.log('Language switch completed using both methods'); } applyCurrentLanguage() { console.log('Applying current language:', this.currentLanguage); this.updateCurrentLanguageDisplay(); this.switchPageLanguage(); } handleOutsideClick(event) { if (this.languageDropdown && this.languageMenuButton && !this.languageDropdown.contains(event.target) && !this.languageMenuButton.contains(event.target)) { this.languageDropdown.classList.add('hidden'); } } cleanup() { console.log('Cleaning up Language Selector...'); this.removeEventListeners(); if (this.observer) { this.observer.disconnect(); } this.initialized = false; } } window.LoborLanguageSelector = new LoborLanguageSelector(); } // Enhanced calculator with live exchange rate API function initEnhancedCalculator() { console.log('Initializing enhanced calculator with live API...'); const API_KEY = 'f13af1e60efa6bcf5b8e0260'; const BASE_URL = 'https://v6.exchangerate-api.com/v6'; let currentExchangeRate = 19.50; // Fallback rate let lastUpdated = null; // Wait for DOM const transactionInput = document.getElementById('transaction-amount'); const exchangeRateInput = document.getElementById('exchange-rate'); const calculateButton = document.getElementById('calculate-savings'); const resultsSection = document.getElementById('results-section'); const rateInfoElement = document.getElementById('exchange-rate-info'); console.log('Elements found:', { transactionInput: !!transactionInput, exchangeRateInput: !!exchangeRateInput, calculateButton: !!calculateButton, resultsSection: !!resultsSection, rateInfoElement: !!rateInfoElement }); if (!transactionInput || !calculateButton) { console.error('Critical elements not found'); return; } // Load live exchange rate async function loadLiveExchangeRate() { if (rateInfoElement) { rateInfoElement.innerHTML = 'Obteniendo tipo de cambio en tiempo real...'; } try { const response = await fetch(`${BASE_URL}/${API_KEY}/pair/USD/MXN`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); if (data.result === 'success') { currentExchangeRate = data.conversion_rate; lastUpdated = new Date(data.time_last_update_utc); if (rateInfoElement) { const timeString = lastUpdated.toLocaleString('es-ES', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); rateInfoElement.innerHTML = ` Tipo de cambio oficial: $${currentExchangeRate.toFixed(4)} MXN/USD
Actualizado: ${timeString} UTC - Fuente: ExchangeRate-API
`; } if (exchangeRateInput) { exchangeRateInput.placeholder = currentExchangeRate.toFixed(4); } console.log('Live exchange rate loaded successfully:', currentExchangeRate); } else { throw new Error(`API error: ${data['error-type']}`); } } catch (error) { console.error('Error loading exchange rate:', error); if (rateInfoElement) { rateInfoElement.innerHTML = ` Usando tipo de cambio estimado: $${currentExchangeRate.toFixed(4)} MXN/USD
No se pudo obtener la tasa en vivo. Ingrese la de su banco para mayor precisión.
`; } } } // Load exchange rate on init loadLiveExchangeRate(); // Format numbers with commas function formatNumber(num) { return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } // Parse input removing commas function parseInput(str) { if (!str) return 0; return parseFloat(str.replace(/,/g, '')) || 0; } // Format transaction amount input function formatTransactionInput(event) { let value = event.target.value.replace(/[^\d]/g, ''); if (value) { value = formatNumber(parseInt(value)); } event.target.value = value; } // Format exchange rate input function formatRateInput(event) { let value = event.target.value.replace(/[^\d.]/g, ''); const parts = value.split('.'); if (parts.length > 2) { value = parts[0] + '.' + parts[1]; } if (parts[1] && parts[1].length > 4) { value = parts[0] + '.' + parts[1].substring(0, 4); } event.target.value = value; } // Main calculation function with live rates function calculateSavings() { console.log('Calculate button clicked with live rates'); const button = calculateButton; button.disabled = true; // Get current language from body class const currentLang = document.body.classList.contains('lang-en') ? 'en' : 'es'; button.innerHTML = currentLang === 'es' ? 'CALCULANDO CON TASAS REALES...' : 'CALCULATING WITH REAL RATES...'; try { // Get values const transactionAmount = parseInput(transactionInput.value); let exchangeRate = parseInput(exchangeRateInput.value); console.log('Values:', { transactionAmount, exchangeRate, liveRate: currentExchangeRate }); // Validation if (!transactionAmount || transactionAmount <= 0) { const alertMessage = currentLang === 'es' ? 'Por favor ingrese un monto válido' : 'Please enter a valid amount'; alert(alertMessage); transactionInput.focus(); return; } // Use live rate if empty, otherwise use user's bank rate if (!exchangeRate) { exchangeRate = currentExchangeRate; } // Calculate costs using live exchange rate for more precision const bankCostUSD = transactionAmount * 0.018; // 1.8% bank cost const loborCostUSD = transactionAmount * 0.008; // 0.8% LOBOR FX cost const savingsUSD = bankCostUSD - loborCostUSD; // Convert to MXN using the rate const bankCostMXN = bankCostUSD * exchangeRate; const loborCostMXN = loborCostUSD * exchangeRate; const savingsMXN = savingsUSD * exchangeRate; const savingsPercent = ((savingsUSD / bankCostUSD) * 100).toFixed(1); const annualSavingsUSD = savingsUSD * 12; const annualSavingsMXN = annualSavingsUSD * exchangeRate; console.log('Enhanced Results:', { bankCostUSD, loborCostUSD, savingsUSD, bankCostMXN, loborCostMXN, savingsMXN, exchangeRate, savingsPercent }); // Update results with both USD and MXN const updates = { 'savings-amount': `$${formatNumber(Math.round(savingsUSD))} USD / $${formatNumber(Math.round(savingsMXN))} MXN`, 'bank-cost': `~$${formatNumber(Math.round(bankCostUSD))} USD / ~$${formatNumber(Math.round(bankCostMXN))} MXN`, 'lobor-cost': `$${formatNumber(Math.round(loborCostUSD))} USD / $${formatNumber(Math.round(loborCostMXN))} MXN`, 'transaction-display': `$${formatNumber(transactionAmount)} USD`, 'savings-percentage': `${savingsPercent}%`, 'annual-savings': `$${formatNumber(Math.round(annualSavingsUSD))} USD / $${formatNumber(Math.round(annualSavingsMXN))} MXN` }; // Apply updates for (const [id, value] of Object.entries(updates)) { const element = document.getElementById(id); if (element) { element.innerHTML = value.replace(' / ', '
'); if (value.includes(' / ')) { element.innerHTML += ''; } console.log(`Updated ${id}: ${value}`); } else { console.warn(`Element ${id} not found`); } } // Show exchange rate used in results const rateUsedText = exchangeRate === currentExchangeRate ? `Tipo de cambio oficial: $${exchangeRate.toFixed(4)}` : `Tipo de cambio de su banco: $${exchangeRate.toFixed(4)}`; // Add rate info to results if there's a place for it const transactionDetails = document.getElementById('transaction-display'); if (transactionDetails && transactionDetails.parentNode) { const rateInfo = transactionDetails.parentNode.querySelector('.rate-info'); if (!rateInfo) { const rateDiv = document.createElement('div'); rateDiv.className = 'rate-info text-xs opacity-75 mt-1'; rateDiv.textContent = rateUsedText; transactionDetails.parentNode.appendChild(rateDiv); } } // Show results if (resultsSection) { resultsSection.classList.remove('hidden'); setTimeout(() => { resultsSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 300); } console.log('Enhanced calculation completed successfully'); } catch (error) { console.error('Calculation error:', error); const alertMessage = currentLang === 'es' ? 'Error en el cálculo: ' + error.message : 'Calculation error: ' + error.message; alert(alertMessage); } finally { // Re-enable button setTimeout(() => { button.disabled = false; const buttonText = currentLang === 'es' ? { long: 'CALCULAR MI AHORRO AHORA', short: 'CALCULAR AHORRO' } : { long: 'CALCULATE MY SAVINGS NOW', short: 'CALCULATE SAVINGS' }; button.innerHTML = ` ${buttonText.short} `; }, 1500); } } // Add event listeners transactionInput.addEventListener('input', formatTransactionInput); if (exchangeRateInput) { exchangeRateInput.addEventListener('input', formatRateInput); } // Multiple event types for better mobile support calculateButton.addEventListener('click', calculateSavings); calculateButton.addEventListener('touchend', function(e) { e.preventDefault(); setTimeout(calculateSavings, 100); }); // Enter key support transactionInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') { calculateSavings(); } }); if (exchangeRateInput) { exchangeRateInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') { calculateSavings(); } }); } // Auto-refresh exchange rate every 5 minutes setInterval(loadLiveExchangeRate, 5 * 60 * 1000); console.log('Enhanced calculator initialized successfully'); } // Initialize immediately when script loads export function init() { console.log('Initializing Get Quote page with language selector and enhanced calculator...'); // Initialize global language selector window.LoborLanguageSelector.init(); // Initialize enhanced calculator if (document.readyState === 'complete') { initEnhancedCalculator(); } else if (document.readyState === 'interactive') { setTimeout(initEnhancedCalculator, 100); } else { document.addEventListener('DOMContentLoaded', initEnhancedCalculator); // Backup timer in case DOMContentLoaded doesn't fire setTimeout(initEnhancedCalculator, 1000); } // Listen for language change events window.addEventListener('languageChanged', (event) => { console.log('Language changed event received:', event.detail.language); }); } // Cleanup export function teardown() { console.log('Tearing down Get Quote page...'); // Don't cleanup the global selector, let it persist }