Time Value of Money & Digital Assets Calculator

Master financial decisions with traditional and digital assets. Calculate present value, future value, payments, and returns for USD, Bitcoin, Ethereum, and major cryptocurrencies.

5-Key Time Value of Money Calculator with Digital Assets

Enter any 4 values and solve for the 5th. Works with traditional currency and major cryptocurrencies including Bitcoin, Ethereum, and more.

USD ($)
Bitcoin (BTC)
Ethereum (ETH)
Solana (SOL)
WIA (WIA)
XRP (XRP)
Present Value (PV)
Future Value (FV)
Payment (PMT)
Interest Rate (I/Y)
Periods (N)
Traditional assets: 3-12%, Crypto: 5-30%
Ordinary Annuity (End)
Annuity Due (Begin)

Loan Payment Calculator

Calculate monthly payments, total interest, and amortization schedules for mortgages, auto loans, and personal loans.

Annuity Value Calculator

Calculate present and future values of ordinary annuities and annuities due. Perfect for retirement planning and investment analysis.

Investment Analysis Calculator

Compare investment options by calculating returns, required rates, and break-even points. Ideal for investment decision-making.

Net Present Value Analysis

Evaluate project profitability by calculating NPV and IRR. Enter cash flows for comprehensive investment analysis.

Enter positive values for cash inflows, negative for outflows

Quick Start Scenarios

30-Year Mortgage
$300K loan at 6.5% for 30 years
Retirement Planning
$1K monthly for 25 years at 8%
College Fund
$25K today grows to $100K in 18 years
Auto Loan
$40K car loan at 5.5% for 5 years
Bitcoin Investment
1 BTC grows at 15% for 5 years
Ethereum DCA
0.1 ETH monthly for 3 years at 20%
WIA Investment
1000 WIA + 100 monthly for 10 years

Celebrating Your Financial Success? Time to Treat Yourself!

Smart financial planning deserves a reward - discover amazing travel deals worldwide

✨ Smart Nation's Core Infrastructure: WIA Code ✨

Droneβ€’Robot delivery, autonomous driving, emergency rescue and more - Experience the future in 30 days, completely free for your nation!

Learn More About WIA Code

πŸ€– Choose Your AI Assistant

πŸ’¬ ChatGPT
Most versatile β€’ Best for general tasks
🧠 Claude
Best reasoning β€’ Perfect for analysis
✨ Gemini FREE
Free daily limits β€’ Built-in chat
, expectedReturn: { min: 3, max: 12, default: 8 }, riskLevel: 'Low' }, 'BTC': { name: 'Bitcoin', symbol: 'BTC', expectedReturn: { min: 5, max: 40, default: 15 }, riskLevel: 'Very High', currentPrice: 97000 // Mock price - in production would use API }, 'ETH': { name: 'Ethereum', symbol: 'ETH', expectedReturn: { min: 8, max: 35, default: 20 }, riskLevel: 'Very High', currentPrice: 3400 }, 'SOL': { name: 'Solana', symbol: 'SOL', expectedReturn: { min: 10, max: 45, default: 25 }, riskLevel: 'Extremely High', currentPrice: 195 }, 'ADA': { name: 'Cardano', symbol: 'ADA', expectedReturn: { min: 5, max: 30, default: 18 }, riskLevel: 'Very High', currentPrice: 1.15 }, 'XRP': { name: 'XRP', symbol: 'XRP', expectedReturn: { min: 8, max: 35, default: 22 }, riskLevel: 'Very High', currentPrice: 3.20 } }; // Asset Type Selection function setAssetType(assetType) { document.querySelectorAll('[data-asset]').forEach(option => { option.classList.remove('active'); }); event.target.classList.add('active'); currentAssetType = assetType; updateAssetLabels(); updatePriceDisplay(); updateRateHints(); toggleCryptoDisclaimer(); } // Update labels based on asset type function updateAssetLabels() { const asset = CRYPTO_ASSETS[currentAssetType]; const symbol = currentAssetType === 'USD' ? ' // Tab Management function switchTab(tabName) { document.querySelectorAll('.tab-content').forEach(tab => { tab.classList.remove('active'); }); document.querySelectorAll('.tab-button').forEach(btn => { btn.classList.remove('active'); }); document.getElementById(tabName + 'Tab').classList.add('active'); event.target.classList.add('active'); } // Solve For Selection function setSolveFor(solveType) { document.querySelectorAll('.solve-for-option').forEach(option => { option.classList.remove('active'); }); event.target.classList.add('active'); currentSolveFor = solveType; // Clear the field we're solving for const fieldMap = { 'pv': 'presentValue', 'fv': 'futureValue', 'pmt': 'payment', 'rate': 'interestRate', 'periods': 'periods' }; document.getElementById(fieldMap[solveType]).value = ''; } // Annuity Type Selection function setAnnuityType(type) { document.querySelectorAll('.toggle-button').forEach(btn => { btn.classList.remove('active'); }); event.target.classList.add('active'); currentAnnuityType = type; } // Main TVM Calculation function calculateTVM() { try { const pv = parseFloat(document.getElementById('presentValue').value) || 0; const fv = parseFloat(document.getElementById('futureValue').value) || 0; const pmt = parseFloat(document.getElementById('payment').value) || 0; const rate = parseFloat(document.getElementById('interestRate').value) || 0; const periods = parseFloat(document.getElementById('periods').value) || 0; const compFreq = parseInt(document.getElementById('compoundingFreq').value) || 12; if (rate <= 0 && currentSolveFor !== 'rate') { showNotification('Interest rate must be greater than 0', 'error'); return; } if (periods <= 0 && currentSolveFor !== 'periods') { showNotification('Number of periods must be greater than 0', 'error'); return; } const result = solveTVM(pv, fv, pmt, rate, periods, compFreq, currentSolveFor, currentAnnuityType); displayTVMResult(result); showOTA(); trackEvent('tvm_calculated', { solveFor: currentSolveFor }); } catch (error) { showNotification(`Calculation error: ${error.message}`, 'error'); } } // Core TVM Solver function solveTVM(pv, fv, pmt, rate, periods, compFreq, solveFor, annuityType) { const periodicRate = rate / 100 / compFreq; const totalPeriods = periods * compFreq; let result = {}; switch (solveFor) { case 'pv': result.value = calculatePresentValue(fv, pmt, periodicRate, totalPeriods, annuityType); result.label = 'Present Value'; break; case 'fv': result.value = calculateFutureValue(pv, pmt, periodicRate, totalPeriods, annuityType); result.label = 'Future Value'; break; case 'pmt': result.value = calculatePayment(pv, fv, periodicRate, totalPeriods, annuityType); result.label = 'Payment Amount'; break; case 'rate': const solvedRate = calculateInterestRate(pv, fv, pmt, totalPeriods, annuityType); result.value = solvedRate * compFreq * 100; result.label = 'Annual Interest Rate'; result.isPercentage = true; break; case 'periods': const solvedPeriods = calculatePeriods(pv, fv, pmt, periodicRate, annuityType); result.value = solvedPeriods / compFreq; result.label = 'Number of Periods'; result.isYears = true; break; } // Calculate additional details const effectiveRate = Math.pow(1 + periodicRate, compFreq) - 1; const totalPayments = Math.abs(pmt) * totalPeriods; result.effectiveRate = effectiveRate * 100; result.totalPayments = totalPayments; if (solveFor === 'pv') { result.totalInterest = Math.abs(result.value) - Math.abs(fv) + totalPayments; } else if (solveFor === 'fv') { result.totalInterest = Math.abs(result.value) - Math.abs(pv) - totalPayments; } else { result.totalInterest = totalPayments - Math.abs(pv); } return result; } // Present Value Calculation function calculatePresentValue(fv, pmt, rate, periods, type) { let pv = 0; if (fv !== 0) { pv += fv / Math.pow(1 + rate, periods); } if (pmt !== 0 && rate !== 0) { const annuityPV = pmt * (1 - Math.pow(1 + rate, -periods)) / rate; if (type === 'due') { pv += annuityPV * (1 + rate); } else { pv += annuityPV; } } else if (pmt !== 0 && rate === 0) { pv += pmt * periods; } return -pv; // Financial calculator convention } // Future Value Calculation function calculateFutureValue(pv, pmt, rate, periods, type) { let fv = 0; if (pv !== 0) { fv += -pv * Math.pow(1 + rate, periods); } if (pmt !== 0 && rate !== 0) { const annuityFV = pmt * (Math.pow(1 + rate, periods) - 1) / rate; if (type === 'due') { fv += annuityFV * (1 + rate); } else { fv += annuityFV; } } else if (pmt !== 0 && rate === 0) { fv += pmt * periods; } return fv; } // Payment Calculation function calculatePayment(pv, fv, rate, periods, type) { if (rate === 0) { return -(pv + fv) / periods; } const pvFactor = pv * Math.pow(1 + rate, periods); const numerator = -(pvFactor + fv); const denominator = (Math.pow(1 + rate, periods) - 1) / rate; let pmt = numerator / denominator; if (type === 'due') { pmt = pmt / (1 + rate); } return pmt; } // Interest Rate Calculation (Newton-Raphson method) function calculateInterestRate(pv, fv, pmt, periods, type) { if (pv === 0 && pmt === 0) return 0; if (fv === 0 && pmt === 0) return 0; let rate = 0.1; // Initial guess const tolerance = 1e-8; const maxIterations = 100; for (let i = 0; i < maxIterations; i++) { const f = evaluateTVMFunction(pv, fv, pmt, rate, periods, type); const df = evaluateTVMDerivative(pv, fv, pmt, rate, periods, type); if (Math.abs(df) < tolerance) break; const newRate = rate - f / df; if (Math.abs(newRate - rate) < tolerance) { return newRate; } rate = newRate; } return rate; } // TVM Function Evaluation function evaluateTVMFunction(pv, fv, pmt, rate, periods, type) { if (rate === 0) { return pv + fv + pmt * periods; } const pvTerm = pv; const fvTerm = fv / Math.pow(1 + rate, periods); let pmtTerm = 0; if (pmt !== 0) { pmtTerm = pmt * (1 - Math.pow(1 + rate, -periods)) / rate; if (type === 'due') { pmtTerm *= (1 + rate); } } return pvTerm + fvTerm + pmtTerm; } // TVM Derivative Evaluation function evaluateTVMDerivative(pv, fv, pmt, rate, periods, type) { const h = 1e-8; const f1 = evaluateTVMFunction(pv, fv, pmt, rate + h, periods, type); const f2 = evaluateTVMFunction(pv, fv, pmt, rate - h, periods, type); return (f1 - f2) / (2 * h); } // Periods Calculation function calculatePeriods(pv, fv, pmt, rate, type) { if (rate === 0) { return -(pv + fv) / pmt; } // Simplified calculation for common cases if (pmt === 0) { return Math.log(-fv / pv) / Math.log(1 + rate); } // For annuity calculations, use iterative method let periods = 10; // Initial guess const tolerance = 1e-8; const maxIterations = 100; for (let i = 0; i < maxIterations; i++) { const calculatedFV = calculateFutureValue(pv, pmt, rate, periods, type); const error = Math.abs(calculatedFV - fv); if (error < tolerance) break; // Adjust periods based on error if (calculatedFV < fv) { periods += 0.1; } else { periods -= 0.1; } if (periods <= 0) { periods = 0.1; } } return periods; } // Display TVM Results function displayTVMResult(result) { const resultContainer = document.getElementById('tvmResult'); const resultLabel = document.getElementById('resultLabel'); const resultValue = document.getElementById('resultValue'); const effectiveRate = document.getElementById('effectiveRate'); const totalPayments = document.getElementById('totalPayments'); const totalInterest = document.getElementById('totalInterest'); resultLabel.textContent = result.label; if (result.isPercentage) { resultValue.textContent = result.value.toFixed(2) + '%'; } else if (result.isYears) { resultValue.textContent = result.value.toFixed(1) + ' years'; } else { resultValue.textContent = formatCurrency(result.value); } effectiveRate.textContent = result.effectiveRate.toFixed(2) + '%'; totalPayments.textContent = formatCurrency(result.totalPayments); totalInterest.textContent = formatCurrency(result.totalInterest); resultContainer.style.display = 'block'; } // Loan Calculator function calculateLoan() { const principal = parseFloat(document.getElementById('loanAmount').value) || 0; const rate = parseFloat(document.getElementById('loanRate').value) || 0; const years = parseFloat(document.getElementById('loanTerm').value) || 0; const freq = parseInt(document.getElementById('paymentFreq').value) || 12; if (principal <= 0 || rate <= 0 || years <= 0) { showNotification('Please enter valid loan parameters', 'error'); return; } const periodicRate = rate / 100 / freq; const totalPayments = years * freq; // Calculate monthly payment using PMT formula const payment = principal * (periodicRate * Math.pow(1 + periodicRate, totalPayments)) / (Math.pow(1 + periodicRate, totalPayments) - 1); const totalOfPayments = payment * totalPayments; const totalInterest = totalOfPayments - principal; const interestPercentage = (totalInterest / principal) * 100; // Display results document.getElementById('loanPayment').textContent = formatCurrency(payment); document.getElementById('totalLoanPayments').textContent = formatCurrency(totalOfPayments); document.getElementById('totalLoanInterest').textContent = formatCurrency(totalInterest); document.getElementById('interestPercentage').textContent = interestPercentage.toFixed(1) + '%'; document.getElementById('loanResult').style.display = 'block'; // Generate amortization table generateAmortizationTable(principal, payment, periodicRate, 12); showOTA(); } // Generate Amortization Table function generateAmortizationTable(principal, payment, periodicRate, numPayments) { const container = document.getElementById('amortizationData'); let balance = principal; let html = `
Payment #
Payment
Interest
Principal
Balance
`; for (let i = 1; i <= numPayments; i++) { const interestPayment = balance * periodicRate; const principalPayment = payment - interestPayment; balance -= principalPayment; html += `
${i}
${formatCurrency(payment)}
${formatCurrency(interestPayment)}
${formatCurrency(principalPayment)}
${formatCurrency(balance)}
`; } container.innerHTML = html; document.getElementById('amortizationTable').style.display = 'block'; } // Annuity Calculator function calculateAnnuity() { const payment = parseFloat(document.getElementById('annuityPayment').value) || 0; const rate = parseFloat(document.getElementById('annuityRate').value) || 0; const periods = parseInt(document.getElementById('annuityPeriods').value) || 0; const timing = document.getElementById('annuityTiming').value; if (payment <= 0 || rate <= 0 || periods <= 0) { showNotification('Please enter valid annuity parameters', 'error'); return; } const periodicRate = rate / 100 / 12; // Assuming monthly payments // Calculate present value let pv = payment * (1 - Math.pow(1 + periodicRate, -periods)) / periodicRate; // Calculate future value let fv = payment * (Math.pow(1 + periodicRate, periods) - 1) / periodicRate; // Adjust for annuity due if (timing === 'due') { pv *= (1 + periodicRate); fv *= (1 + periodicRate); } const totalPayments = payment * periods; const interestEarned = fv - totalPayments; const effectiveRate = Math.pow(1 + periodicRate, 12) - 1; // Display results document.getElementById('annuityPV').textContent = formatCurrency(pv); document.getElementById('annuityFV').textContent = formatCurrency(fv); document.getElementById('totalAnnuityPayments').textContent = formatCurrency(totalPayments); document.getElementById('annuityInterest').textContent = formatCurrency(interestEarned); document.getElementById('annuityEffectiveRate').textContent = (effectiveRate * 100).toFixed(2) + '%'; document.getElementById('annuityResult').style.display = 'block'; showOTA(); } // Investment Analysis function calculateInvestment() { const initial = parseFloat(document.getElementById('initialInvestment').value) || 0; const target = parseFloat(document.getElementById('targetAmount').value) || 0; const years = parseFloat(document.getElementById('investmentPeriod').value) || 0; const annual = parseFloat(document.getElementById('annualContribution').value) || 0; if (initial <= 0 || target <= 0 || years <= 0) { showNotification('Please enter valid investment parameters', 'error'); return; } // Calculate required return rate const totalInvested = initial + (annual * years); // Use iterative method to find required rate let rate = 0.05; // Initial guess for (let i = 0; i < 100; i++) { const futureValue = initial * Math.pow(1 + rate, years) + annual * (Math.pow(1 + rate, years) - 1) / rate; if (Math.abs(futureValue - target) < 1) break; if (futureValue < target) { rate += 0.001; } else { rate -= 0.001; } } const growth = target - totalInvested; const breakEven = Math.log(initial * 2 / initial) / Math.log(1 + rate); // Display results document.getElementById('requiredReturn').textContent = (rate * 100).toFixed(2) + '%'; document.getElementById('totalInvested').textContent = formatCurrency(totalInvested); document.getElementById('investmentGrowth').textContent = formatCurrency(growth); document.getElementById('breakEvenTime').textContent = breakEven.toFixed(1) + ' years'; document.getElementById('investmentResult').style.display = 'block'; showOTA(); } // NPV Calculator function calculateNPV() { const initial = parseFloat(document.getElementById('npvInitial').value) || 0; const discountRate = parseFloat(document.getElementById('discountRate').value) || 0; const cashFlowsText = document.getElementById('cashFlows').value; if (discountRate <= 0) { showNotification('Please enter a valid discount rate', 'error'); return; } // Parse cash flows const cashFlows = cashFlowsText.split(',').map(cf => parseFloat(cf.trim())).filter(cf => !isNaN(cf)); if (cashFlows.length === 0) { showNotification('Please enter valid cash flows', 'error'); return; } const rate = discountRate / 100; // Calculate NPV let npv = initial; const discountedCashFlows = []; for (let i = 0; i < cashFlows.length; i++) { const discounted = cashFlows[i] / Math.pow(1 + rate, i + 1); npv += discounted; discountedCashFlows.push(discounted); } // Calculate IRR (approximate) const irr = calculateIRR([initial, ...cashFlows]) * 100; // Calculate payback period let cumulative = initial; let paybackPeriod = 0; for (let i = 0; i < cashFlows.length; i++) { cumulative += cashFlows[i]; if (cumulative >= 0) { paybackPeriod = i + 1; break; } } // Calculate profitability index const presentValueInflows = discountedCashFlows.reduce((sum, cf) => sum + cf, 0); const profitabilityIndex = presentValueInflows / Math.abs(initial); // Investment decision const decision = npv > 0 ? 'Accept (NPV > 0)' : 'Reject (NPV < 0)'; // Display results document.getElementById('npvValue').textContent = formatCurrency(npv); document.getElementById('irrValue').textContent = irr.toFixed(2) + '%'; document.getElementById('paybackPeriod').textContent = paybackPeriod + ' years'; document.getElementById('profitabilityIndex').textContent = profitabilityIndex.toFixed(2); document.getElementById('investmentDecision').textContent = decision; document.getElementById('npvResult').style.display = 'block'; // Generate NPV breakdown generateNPVBreakdown([initial, ...cashFlows], discountedCashFlows, rate); showOTA(); } // Calculate IRR using Newton-Raphson method function calculateIRR(cashFlows) { let rate = 0.1; // Initial guess const tolerance = 1e-8; const maxIterations = 100; for (let i = 0; i < maxIterations; i++) { let npv = 0; let dnpv = 0; for (let j = 0; j < cashFlows.length; j++) { npv += cashFlows[j] / Math.pow(1 + rate, j); if (j > 0) { dnpv -= j * cashFlows[j] / Math.pow(1 + rate, j + 1); } } if (Math.abs(npv) < tolerance) break; if (Math.abs(dnpv) < tolerance) break; rate = rate - npv / dnpv; } return rate; } // Generate NPV Breakdown function generateNPVBreakdown(cashFlows, discountedCashFlows, rate) { const container = document.getElementById('npvData'); let html = `
Year
Cash Flow
Discount Factor
Present Value
`; for (let i = 0; i < cashFlows.length; i++) { const discountFactor = 1 / Math.pow(1 + rate, i); const presentValue = i === 0 ? cashFlows[i] : discountedCashFlows[i - 1]; html += `
${i}
${formatCurrency(cashFlows[i])}
${discountFactor.toFixed(4)}
${formatCurrency(presentValue)}
`; } container.innerHTML = html; document.getElementById('npvBreakdown').style.display = 'block'; } // Sample Scenarios function useSample(scenario) { const scenarios = { mortgage: () => { switchTab('loan'); document.querySelector('[onclick="switchTab(\'loan\')"]').click(); document.getElementById('loanAmount').value = 300000; document.getElementById('loanRate').value = 6.5; document.getElementById('loanTerm').value = 30; document.getElementById('paymentFreq').value = 12; }, retirement: () => { switchTab('annuity'); document.querySelector('[onclick="switchTab(\'annuity\')"]').click(); document.getElementById('annuityPayment').value = 1000; document.getElementById('annuityRate').value = 8; document.getElementById('annuityPeriods').value = 300; // 25 years * 12 months document.getElementById('annuityTiming').value = 'ordinary'; }, college: () => { switchTab('basic'); document.querySelector('[onclick="switchTab(\'basic\')"]').click(); setSolveFor('rate'); document.getElementById('presentValue').value = 25000; document.getElementById('futureValue').value = 100000; document.getElementById('payment').value = 0; document.getElementById('periods').value = 18; document.getElementById('interestRate').value = ''; }, carloan: () => { switchTab('loan'); document.querySelector('[onclick="switchTab(\'loan\')"]').click(); document.getElementById('loanAmount').value = 40000; document.getElementById('loanRate').value = 5.5; document.getElementById('loanTerm').value = 5; document.getElementById('paymentFreq').value = 12; }, investment: () => { switchTab('investment'); document.querySelector('[onclick="switchTab(\'investment\')"]').click(); document.getElementById('initialInvestment').value = 50000; document.getElementById('targetAmount').value = 100000; document.getElementById('investmentPeriod').value = 6; document.getElementById('annualContribution').value = 0; }, annuity: () => { switchTab('annuity'); document.querySelector('[onclick="switchTab(\'annuity\')"]').click(); document.getElementById('annuityPayment').value = 50000; document.getElementById('annuityRate').value = 6; document.getElementById('annuityPeriods').value = 240; // 20 years * 12 months document.getElementById('annuityTiming').value = 'ordinary'; } }; if (scenarios[scenario]) { scenarios[scenario](); // Smooth scroll to calculate button setTimeout(() => { const calculateBtn = document.querySelector('.tab-content.active .btn'); if (calculateBtn) { calculateBtn.scrollIntoView({ behavior: 'smooth', block: 'center' }); // Highlight button calculateBtn.style.animation = 'pulse 0.5s ease-in-out'; setTimeout(() => calculateBtn.style.animation = '', 500); } }, 100); } } // Utility Functions function formatCurrency(amount) { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(Math.abs(amount)); } function showNotification(message, type = 'success') { const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.remove(); }, 3000); } function showOTA() { const otaContainer = document.getElementById('otaContainer'); if (otaContainer && (otaContainer.style.display === 'none' || !otaContainer.style.display)) { otaContainer.style.display = 'block'; setTimeout(() => { const otaHeader = document.querySelector('.ota-header h3'); if (otaHeader) { otaHeader.style.animation = 'pulse 1s ease-in-out'; } }, 100); } } function trackEvent(eventName, data = {}) { if (typeof gtag !== 'undefined') { gtag('event', eventName, { 'event_category': TOOL_CONFIG.category, 'event_label': TOOL_CONFIG.name, ...data }); } } // AI Assistant Functions function openAIModal() { const modal = document.getElementById('aiModal'); modal.classList.add('show'); } function closeAIModal() { const modal = document.getElementById('aiModal'); modal.classList.remove('show'); } function selectAI(aiType) { switch(aiType) { case 'chatgpt': const toolContext = `I need help with Time Value of Money calculations using the WIA Code TVM Calculator. Please help me understand present value, future value, annuities, loan calculations, and investment analysis.`; const chatUrl = `https://chat.openai.com/?q=${encodeURIComponent(toolContext)}`; window.open(chatUrl, '_blank'); closeAIModal(); trackEvent('ai_selection', { ai_type: 'chatgpt' }); break; case 'claude': const claudeContext = `I need help with Time Value of Money calculations using the WIA Code TVM Calculator. Please help me understand present value, future value, annuities, loan calculations, and investment analysis.`; const claudeUrl = `https://claude.ai/chat?q=${encodeURIComponent(claudeContext)}`; window.open(claudeUrl, '_blank'); closeAIModal(); trackEvent('ai_selection', { ai_type: 'claude' }); break; case 'gemini': showNotification('Gemini integration coming soon!', 'warning'); trackEvent('ai_selection', { ai_type: 'gemini' }); break; } } // Event Listeners document.addEventListener('DOMContentLoaded', function() { // Enter key support for inputs document.querySelectorAll('input[type="number"], input[type="text"]').forEach(input => { input.addEventListener('keypress', function(e) { if (e.key === 'Enter') { const activeTab = document.querySelector('.tab-content.active'); const calculateBtn = activeTab.querySelector('.btn'); if (calculateBtn) { calculateBtn.click(); } } }); }); // AI button event document.getElementById('aiBtn').addEventListener('click', openAIModal); // Modal outside click close document.getElementById('aiModal').addEventListener('click', function(e) { if (e.target === this) { closeAIModal(); } }); // ESC key to close modal document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { closeAIModal(); } }); updateCurrentYear(); updateToolCount(); }); // Dynamic Tool Count async function updateToolCount() { try { const response = await fetch('/api/tool-count.php'); const data = await response.json(); document.querySelectorAll('.dynamic-tools-count').forEach(el => { el.textContent = `${data.count}+ free online tools in 206 languages. No signup, no fees, just tools that work.`; }); document.querySelectorAll('.dynamic-count').forEach(el => { const prefix = el.getAttribute('data-text') || ''; const suffix = el.getAttribute('data-suffix') || ''; const icon = el.textContent.split(' ')[0] || ''; el.textContent = `${icon} ${prefix} ${data.count}+ ${suffix}`; }); } catch (error) { const fallbackCount = 333; document.querySelectorAll('.dynamic-tools-count').forEach(el => { el.textContent = `${fallbackCount}+ free online tools in 206 languages. No signup, no fees, just tools that work.`; }); document.querySelectorAll('.dynamic-count').forEach(el => { const prefix = el.getAttribute('data-text') || ''; const suffix = el.getAttribute('data-suffix') || ''; const icon = el.textContent.split(' ')[0] || ''; el.textContent = `${icon} ${prefix} ${fallbackCount}+ ${suffix}`; }); } } function updateCurrentYear() { const currentYear = new Date().getFullYear(); document.querySelectorAll('.current-year').forEach(el => { el.textContent = currentYear; }); } // Google Analytics window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-XXXXXXXXX'); trackEvent('page_view', { tool: TOOL_CONFIG.name, category: TOOL_CONFIG.category }); : asset.symbol; document.getElementById('pvLabel').textContent = `Present Value (PV) ${symbol}`; document.getElementById('fvLabel').textContent = `Future Value (FV) ${symbol}`; document.getElementById('pmtLabel').textContent = `Payment (PMT) ${symbol}`; } // Update crypto price display function updatePriceDisplay() { const priceDisplay = document.getElementById('cryptoPriceDisplay'); const priceTitle = document.getElementById('cryptoPriceTitle'); const priceValue = document.getElementById('cryptoPriceValue'); if (currentAssetType === 'USD') { priceDisplay.style.display = 'none'; } else { const asset = CRYPTO_ASSETS[currentAssetType]; priceDisplay.style.display = 'block'; priceTitle.textContent = `${asset.name} Current Price`; priceValue.textContent = `1 ${asset.symbol} = ${asset.currentPrice.toLocaleString()} USD`; } } // Update rate hints based on asset type function updateRateHints() { const asset = CRYPTO_ASSETS[currentAssetType]; const rateHint = document.getElementById('rateHint'); if (currentAssetType === 'USD') { rateHint.textContent = 'Traditional assets: 3-12%, Enter your expected return'; } else { rateHint.textContent = `${asset.name} - Risk: ${asset.riskLevel}, Enter your expected return`; // Clear any auto-filled rate to let user decide const rateField = document.getElementById('interestRate'); if (rateField.value == 8) { rateField.value = ''; } } } // Toggle crypto disclaimer function toggleCryptoDisclaimer() { const disclaimer = document.getElementById('cryptoDisclaimer'); if (currentAssetType === 'USD') { disclaimer.style.display = 'none'; } else { disclaimer.style.display = 'block'; } } // Tab Management function switchTab(tabName) { document.querySelectorAll('.tab-content').forEach(tab => { tab.classList.remove('active'); }); document.querySelectorAll('.tab-button').forEach(btn => { btn.classList.remove('active'); }); document.getElementById(tabName + 'Tab').classList.add('active'); event.target.classList.add('active'); } // Solve For Selection function setSolveFor(solveType) { document.querySelectorAll('.solve-for-option').forEach(option => { option.classList.remove('active'); }); event.target.classList.add('active'); currentSolveFor = solveType; // Clear the field we're solving for const fieldMap = { 'pv': 'presentValue', 'fv': 'futureValue', 'pmt': 'payment', 'rate': 'interestRate', 'periods': 'periods' }; document.getElementById(fieldMap[solveType]).value = ''; } // Annuity Type Selection function setAnnuityType(type) { document.querySelectorAll('.toggle-button').forEach(btn => { btn.classList.remove('active'); }); event.target.classList.add('active'); currentAnnuityType = type; } // Main TVM Calculation function calculateTVM() { try { const pv = parseFloat(document.getElementById('presentValue').value) || 0; const fv = parseFloat(document.getElementById('futureValue').value) || 0; const pmt = parseFloat(document.getElementById('payment').value) || 0; const rate = parseFloat(document.getElementById('interestRate').value) || 0; const periods = parseFloat(document.getElementById('periods').value) || 0; const compFreq = parseInt(document.getElementById('compoundingFreq').value) || 12; if (rate <= 0 && currentSolveFor !== 'rate') { showNotification('Interest rate must be greater than 0', 'error'); return; } if (periods <= 0 && currentSolveFor !== 'periods') { showNotification('Number of periods must be greater than 0', 'error'); return; } const result = solveTVM(pv, fv, pmt, rate, periods, compFreq, currentSolveFor, currentAnnuityType); displayTVMResult(result); showOTA(); trackEvent('tvm_calculated', { solveFor: currentSolveFor }); } catch (error) { showNotification(`Calculation error: ${error.message}`, 'error'); } } // Core TVM Solver function solveTVM(pv, fv, pmt, rate, periods, compFreq, solveFor, annuityType) { const periodicRate = rate / 100 / compFreq; const totalPeriods = periods * compFreq; let result = {}; switch (solveFor) { case 'pv': result.value = calculatePresentValue(fv, pmt, periodicRate, totalPeriods, annuityType); result.label = 'Present Value'; break; case 'fv': result.value = calculateFutureValue(pv, pmt, periodicRate, totalPeriods, annuityType); result.label = 'Future Value'; break; case 'pmt': result.value = calculatePayment(pv, fv, periodicRate, totalPeriods, annuityType); result.label = 'Payment Amount'; break; case 'rate': const solvedRate = calculateInterestRate(pv, fv, pmt, totalPeriods, annuityType); result.value = solvedRate * compFreq * 100; result.label = 'Annual Interest Rate'; result.isPercentage = true; break; case 'periods': const solvedPeriods = calculatePeriods(pv, fv, pmt, periodicRate, annuityType); result.value = solvedPeriods / compFreq; result.label = 'Number of Periods'; result.isYears = true; break; } // Calculate additional details const effectiveRate = Math.pow(1 + periodicRate, compFreq) - 1; const totalPayments = Math.abs(pmt) * totalPeriods; result.effectiveRate = effectiveRate * 100; result.totalPayments = totalPayments; if (solveFor === 'pv') { result.totalInterest = Math.abs(result.value) - Math.abs(fv) + totalPayments; } else if (solveFor === 'fv') { result.totalInterest = Math.abs(result.value) - Math.abs(pv) - totalPayments; } else { result.totalInterest = totalPayments - Math.abs(pv); } return result; } // Present Value Calculation function calculatePresentValue(fv, pmt, rate, periods, type) { let pv = 0; if (fv !== 0) { pv += fv / Math.pow(1 + rate, periods); } if (pmt !== 0 && rate !== 0) { const annuityPV = pmt * (1 - Math.pow(1 + rate, -periods)) / rate; if (type === 'due') { pv += annuityPV * (1 + rate); } else { pv += annuityPV; } } else if (pmt !== 0 && rate === 0) { pv += pmt * periods; } return -pv; // Financial calculator convention } // Future Value Calculation function calculateFutureValue(pv, pmt, rate, periods, type) { let fv = 0; if (pv !== 0) { fv += -pv * Math.pow(1 + rate, periods); } if (pmt !== 0 && rate !== 0) { const annuityFV = pmt * (Math.pow(1 + rate, periods) - 1) / rate; if (type === 'due') { fv += annuityFV * (1 + rate); } else { fv += annuityFV; } } else if (pmt !== 0 && rate === 0) { fv += pmt * periods; } return fv; } // Payment Calculation function calculatePayment(pv, fv, rate, periods, type) { if (rate === 0) { return -(pv + fv) / periods; } const pvFactor = pv * Math.pow(1 + rate, periods); const numerator = -(pvFactor + fv); const denominator = (Math.pow(1 + rate, periods) - 1) / rate; let pmt = numerator / denominator; if (type === 'due') { pmt = pmt / (1 + rate); } return pmt; } // Interest Rate Calculation (Newton-Raphson method) function calculateInterestRate(pv, fv, pmt, periods, type) { if (pv === 0 && pmt === 0) return 0; if (fv === 0 && pmt === 0) return 0; let rate = 0.1; // Initial guess const tolerance = 1e-8; const maxIterations = 100; for (let i = 0; i < maxIterations; i++) { const f = evaluateTVMFunction(pv, fv, pmt, rate, periods, type); const df = evaluateTVMDerivative(pv, fv, pmt, rate, periods, type); if (Math.abs(df) < tolerance) break; const newRate = rate - f / df; if (Math.abs(newRate - rate) < tolerance) { return newRate; } rate = newRate; } return rate; } // TVM Function Evaluation function evaluateTVMFunction(pv, fv, pmt, rate, periods, type) { if (rate === 0) { return pv + fv + pmt * periods; } const pvTerm = pv; const fvTerm = fv / Math.pow(1 + rate, periods); let pmtTerm = 0; if (pmt !== 0) { pmtTerm = pmt * (1 - Math.pow(1 + rate, -periods)) / rate; if (type === 'due') { pmtTerm *= (1 + rate); } } return pvTerm + fvTerm + pmtTerm; } // TVM Derivative Evaluation function evaluateTVMDerivative(pv, fv, pmt, rate, periods, type) { const h = 1e-8; const f1 = evaluateTVMFunction(pv, fv, pmt, rate + h, periods, type); const f2 = evaluateTVMFunction(pv, fv, pmt, rate - h, periods, type); return (f1 - f2) / (2 * h); } // Periods Calculation function calculatePeriods(pv, fv, pmt, rate, type) { if (rate === 0) { return -(pv + fv) / pmt; } // Simplified calculation for common cases if (pmt === 0) { return Math.log(-fv / pv) / Math.log(1 + rate); } // For annuity calculations, use iterative method let periods = 10; // Initial guess const tolerance = 1e-8; const maxIterations = 100; for (let i = 0; i < maxIterations; i++) { const calculatedFV = calculateFutureValue(pv, pmt, rate, periods, type); const error = Math.abs(calculatedFV - fv); if (error < tolerance) break; // Adjust periods based on error if (calculatedFV < fv) { periods += 0.1; } else { periods -= 0.1; } if (periods <= 0) { periods = 0.1; } } return periods; } // Display TVM Results function displayTVMResult(result) { const resultContainer = document.getElementById('tvmResult'); const resultLabel = document.getElementById('resultLabel'); const resultValue = document.getElementById('resultValue'); const effectiveRate = document.getElementById('effectiveRate'); const totalPayments = document.getElementById('totalPayments'); const totalInterest = document.getElementById('totalInterest'); resultLabel.textContent = result.label; if (result.isPercentage) { resultValue.textContent = result.value.toFixed(2) + '%'; } else if (result.isYears) { resultValue.textContent = result.value.toFixed(1) + ' years'; } else { resultValue.textContent = formatCurrency(result.value); } effectiveRate.textContent = result.effectiveRate.toFixed(2) + '%'; totalPayments.textContent = formatCurrency(result.totalPayments); totalInterest.textContent = formatCurrency(result.totalInterest); resultContainer.style.display = 'block'; } // Loan Calculator function calculateLoan() { const principal = parseFloat(document.getElementById('loanAmount').value) || 0; const rate = parseFloat(document.getElementById('loanRate').value) || 0; const years = parseFloat(document.getElementById('loanTerm').value) || 0; const freq = parseInt(document.getElementById('paymentFreq').value) || 12; if (principal <= 0 || rate <= 0 || years <= 0) { showNotification('Please enter valid loan parameters', 'error'); return; } const periodicRate = rate / 100 / freq; const totalPayments = years * freq; // Calculate monthly payment using PMT formula const payment = principal * (periodicRate * Math.pow(1 + periodicRate, totalPayments)) / (Math.pow(1 + periodicRate, totalPayments) - 1); const totalOfPayments = payment * totalPayments; const totalInterest = totalOfPayments - principal; const interestPercentage = (totalInterest / principal) * 100; // Display results document.getElementById('loanPayment').textContent = formatCurrency(payment); document.getElementById('totalLoanPayments').textContent = formatCurrency(totalOfPayments); document.getElementById('totalLoanInterest').textContent = formatCurrency(totalInterest); document.getElementById('interestPercentage').textContent = interestPercentage.toFixed(1) + '%'; document.getElementById('loanResult').style.display = 'block'; // Generate amortization table generateAmortizationTable(principal, payment, periodicRate, 12); showOTA(); } // Generate Amortization Table function generateAmortizationTable(principal, payment, periodicRate, numPayments) { const container = document.getElementById('amortizationData'); let balance = principal; let html = `
Payment #
Payment
Interest
Principal
Balance
`; for (let i = 1; i <= numPayments; i++) { const interestPayment = balance * periodicRate; const principalPayment = payment - interestPayment; balance -= principalPayment; html += `
${i}
${formatCurrency(payment)}
${formatCurrency(interestPayment)}
${formatCurrency(principalPayment)}
${formatCurrency(balance)}
`; } container.innerHTML = html; document.getElementById('amortizationTable').style.display = 'block'; } // Annuity Calculator function calculateAnnuity() { const payment = parseFloat(document.getElementById('annuityPayment').value) || 0; const rate = parseFloat(document.getElementById('annuityRate').value) || 0; const periods = parseInt(document.getElementById('annuityPeriods').value) || 0; const timing = document.getElementById('annuityTiming').value; if (payment <= 0 || rate <= 0 || periods <= 0) { showNotification('Please enter valid annuity parameters', 'error'); return; } const periodicRate = rate / 100 / 12; // Assuming monthly payments // Calculate present value let pv = payment * (1 - Math.pow(1 + periodicRate, -periods)) / periodicRate; // Calculate future value let fv = payment * (Math.pow(1 + periodicRate, periods) - 1) / periodicRate; // Adjust for annuity due if (timing === 'due') { pv *= (1 + periodicRate); fv *= (1 + periodicRate); } const totalPayments = payment * periods; const interestEarned = fv - totalPayments; const effectiveRate = Math.pow(1 + periodicRate, 12) - 1; // Display results document.getElementById('annuityPV').textContent = formatCurrency(pv); document.getElementById('annuityFV').textContent = formatCurrency(fv); document.getElementById('totalAnnuityPayments').textContent = formatCurrency(totalPayments); document.getElementById('annuityInterest').textContent = formatCurrency(interestEarned); document.getElementById('annuityEffectiveRate').textContent = (effectiveRate * 100).toFixed(2) + '%'; document.getElementById('annuityResult').style.display = 'block'; showOTA(); } // Investment Analysis function calculateInvestment() { const initial = parseFloat(document.getElementById('initialInvestment').value) || 0; const target = parseFloat(document.getElementById('targetAmount').value) || 0; const years = parseFloat(document.getElementById('investmentPeriod').value) || 0; const annual = parseFloat(document.getElementById('annualContribution').value) || 0; if (initial <= 0 || target <= 0 || years <= 0) { showNotification('Please enter valid investment parameters', 'error'); return; } // Calculate required return rate const totalInvested = initial + (annual * years); // Use iterative method to find required rate let rate = 0.05; // Initial guess for (let i = 0; i < 100; i++) { const futureValue = initial * Math.pow(1 + rate, years) + annual * (Math.pow(1 + rate, years) - 1) / rate; if (Math.abs(futureValue - target) < 1) break; if (futureValue < target) { rate += 0.001; } else { rate -= 0.001; } } const growth = target - totalInvested; const breakEven = Math.log(initial * 2 / initial) / Math.log(1 + rate); // Display results document.getElementById('requiredReturn').textContent = (rate * 100).toFixed(2) + '%'; document.getElementById('totalInvested').textContent = formatCurrency(totalInvested); document.getElementById('investmentGrowth').textContent = formatCurrency(growth); document.getElementById('breakEvenTime').textContent = breakEven.toFixed(1) + ' years'; document.getElementById('investmentResult').style.display = 'block'; showOTA(); } // NPV Calculator function calculateNPV() { const initial = parseFloat(document.getElementById('npvInitial').value) || 0; const discountRate = parseFloat(document.getElementById('discountRate').value) || 0; const cashFlowsText = document.getElementById('cashFlows').value; if (discountRate <= 0) { showNotification('Please enter a valid discount rate', 'error'); return; } // Parse cash flows const cashFlows = cashFlowsText.split(',').map(cf => parseFloat(cf.trim())).filter(cf => !isNaN(cf)); if (cashFlows.length === 0) { showNotification('Please enter valid cash flows', 'error'); return; } const rate = discountRate / 100; // Calculate NPV let npv = initial; const discountedCashFlows = []; for (let i = 0; i < cashFlows.length; i++) { const discounted = cashFlows[i] / Math.pow(1 + rate, i + 1); npv += discounted; discountedCashFlows.push(discounted); } // Calculate IRR (approximate) const irr = calculateIRR([initial, ...cashFlows]) * 100; // Calculate payback period let cumulative = initial; let paybackPeriod = 0; for (let i = 0; i < cashFlows.length; i++) { cumulative += cashFlows[i]; if (cumulative >= 0) { paybackPeriod = i + 1; break; } } // Calculate profitability index const presentValueInflows = discountedCashFlows.reduce((sum, cf) => sum + cf, 0); const profitabilityIndex = presentValueInflows / Math.abs(initial); // Investment decision const decision = npv > 0 ? 'Accept (NPV > 0)' : 'Reject (NPV < 0)'; // Display results document.getElementById('npvValue').textContent = formatCurrency(npv); document.getElementById('irrValue').textContent = irr.toFixed(2) + '%'; document.getElementById('paybackPeriod').textContent = paybackPeriod + ' years'; document.getElementById('profitabilityIndex').textContent = profitabilityIndex.toFixed(2); document.getElementById('investmentDecision').textContent = decision; document.getElementById('npvResult').style.display = 'block'; // Generate NPV breakdown generateNPVBreakdown([initial, ...cashFlows], discountedCashFlows, rate); showOTA(); } // Calculate IRR using Newton-Raphson method function calculateIRR(cashFlows) { let rate = 0.1; // Initial guess const tolerance = 1e-8; const maxIterations = 100; for (let i = 0; i < maxIterations; i++) { let npv = 0; let dnpv = 0; for (let j = 0; j < cashFlows.length; j++) { npv += cashFlows[j] / Math.pow(1 + rate, j); if (j > 0) { dnpv -= j * cashFlows[j] / Math.pow(1 + rate, j + 1); } } if (Math.abs(npv) < tolerance) break; if (Math.abs(dnpv) < tolerance) break; rate = rate - npv / dnpv; } return rate; } // Generate NPV Breakdown function generateNPVBreakdown(cashFlows, discountedCashFlows, rate) { const container = document.getElementById('npvData'); let html = `
Year
Cash Flow
Discount Factor
Present Value
`; for (let i = 0; i < cashFlows.length; i++) { const discountFactor = 1 / Math.pow(1 + rate, i); const presentValue = i === 0 ? cashFlows[i] : discountedCashFlows[i - 1]; html += `
${i}
${formatCurrency(cashFlows[i])}
${discountFactor.toFixed(4)}
${formatCurrency(presentValue)}
`; } container.innerHTML = html; document.getElementById('npvBreakdown').style.display = 'block'; } // Sample Scenarios function useSample(scenario) { const scenarios = { mortgage: () => { switchTab('loan'); document.querySelector('[onclick="switchTab(\'loan\')"]').click(); document.getElementById('loanAmount').value = 300000; document.getElementById('loanRate').value = 6.5; document.getElementById('loanTerm').value = 30; document.getElementById('paymentFreq').value = 12; }, retirement: () => { switchTab('annuity'); document.querySelector('[onclick="switchTab(\'annuity\')"]').click(); document.getElementById('annuityPayment').value = 1000; document.getElementById('annuityRate').value = 8; document.getElementById('annuityPeriods').value = 300; // 25 years * 12 months document.getElementById('annuityTiming').value = 'ordinary'; }, college: () => { switchTab('basic'); document.querySelector('[onclick="switchTab(\'basic\')"]').click(); setSolveFor('rate'); document.getElementById('presentValue').value = 25000; document.getElementById('futureValue').value = 100000; document.getElementById('payment').value = 0; document.getElementById('periods').value = 18; document.getElementById('interestRate').value = ''; }, carloan: () => { switchTab('loan'); document.querySelector('[onclick="switchTab(\'loan\')"]').click(); document.getElementById('loanAmount').value = 40000; document.getElementById('loanRate').value = 5.5; document.getElementById('loanTerm').value = 5; document.getElementById('paymentFreq').value = 12; }, investment: () => { switchTab('investment'); document.querySelector('[onclick="switchTab(\'investment\')"]').click(); document.getElementById('initialInvestment').value = 50000; document.getElementById('targetAmount').value = 100000; document.getElementById('investmentPeriod').value = 6; document.getElementById('annualContribution').value = 0; }, annuity: () => { switchTab('annuity'); document.querySelector('[onclick="switchTab(\'annuity\')"]').click(); document.getElementById('annuityPayment').value = 50000; document.getElementById('annuityRate').value = 6; document.getElementById('annuityPeriods').value = 240; // 20 years * 12 months document.getElementById('annuityTiming').value = 'ordinary'; } }; if (scenarios[scenario]) { scenarios[scenario](); // Smooth scroll to calculate button setTimeout(() => { const calculateBtn = document.querySelector('.tab-content.active .btn'); if (calculateBtn) { calculateBtn.scrollIntoView({ behavior: 'smooth', block: 'center' }); // Highlight button calculateBtn.style.animation = 'pulse 0.5s ease-in-out'; setTimeout(() => calculateBtn.style.animation = '', 500); } }, 100); } } // Utility Functions function formatCurrency(amount) { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(Math.abs(amount)); } function showNotification(message, type = 'success') { const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.remove(); }, 3000); } function showOTA() { const otaContainer = document.getElementById('otaContainer'); if (otaContainer && (otaContainer.style.display === 'none' || !otaContainer.style.display)) { otaContainer.style.display = 'block'; setTimeout(() => { const otaHeader = document.querySelector('.ota-header h3'); if (otaHeader) { otaHeader.style.animation = 'pulse 1s ease-in-out'; } }, 100); } } function trackEvent(eventName, data = {}) { if (typeof gtag !== 'undefined') { gtag('event', eventName, { 'event_category': TOOL_CONFIG.category, 'event_label': TOOL_CONFIG.name, ...data }); } } // AI Assistant Functions function openAIModal() { const modal = document.getElementById('aiModal'); modal.classList.add('show'); } function closeAIModal() { const modal = document.getElementById('aiModal'); modal.classList.remove('show'); } function selectAI(aiType) { switch(aiType) { case 'chatgpt': const toolContext = `I need help with Time Value of Money calculations using the WIA Code TVM Calculator. Please help me understand present value, future value, annuities, loan calculations, and investment analysis.`; const chatUrl = `https://chat.openai.com/?q=${encodeURIComponent(toolContext)}`; window.open(chatUrl, '_blank'); closeAIModal(); trackEvent('ai_selection', { ai_type: 'chatgpt' }); break; case 'claude': const claudeContext = `I need help with Time Value of Money calculations using the WIA Code TVM Calculator. Please help me understand present value, future value, annuities, loan calculations, and investment analysis.`; const claudeUrl = `https://claude.ai/chat?q=${encodeURIComponent(claudeContext)}`; window.open(claudeUrl, '_blank'); closeAIModal(); trackEvent('ai_selection', { ai_type: 'claude' }); break; case 'gemini': showNotification('Gemini integration coming soon!', 'warning'); trackEvent('ai_selection', { ai_type: 'gemini' }); break; } } // Event Listeners document.addEventListener('DOMContentLoaded', function() { // Enter key support for inputs document.querySelectorAll('input[type="number"], input[type="text"]').forEach(input => { input.addEventListener('keypress', function(e) { if (e.key === 'Enter') { const activeTab = document.querySelector('.tab-content.active'); const calculateBtn = activeTab.querySelector('.btn'); if (calculateBtn) { calculateBtn.click(); } } }); }); // AI button event document.getElementById('aiBtn').addEventListener('click', openAIModal); // Modal outside click close document.getElementById('aiModal').addEventListener('click', function(e) { if (e.target === this) { closeAIModal(); } }); // ESC key to close modal document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { closeAIModal(); } }); updateCurrentYear(); updateToolCount(); }); // Dynamic Tool Count async function updateToolCount() { try { const response = await fetch('/api/tool-count.php'); const data = await response.json(); document.querySelectorAll('.dynamic-tools-count').forEach(el => { el.textContent = `${data.count}+ free online tools in 206 languages. No signup, no fees, just tools that work.`; }); document.querySelectorAll('.dynamic-count').forEach(el => { const prefix = el.getAttribute('data-text') || ''; const suffix = el.getAttribute('data-suffix') || ''; const icon = el.textContent.split(' ')[0] || ''; el.textContent = `${icon} ${prefix} ${data.count}+ ${suffix}`; }); } catch (error) { const fallbackCount = 333; document.querySelectorAll('.dynamic-tools-count').forEach(el => { el.textContent = `${fallbackCount}+ free online tools in 206 languages. No signup, no fees, just tools that work.`; }); document.querySelectorAll('.dynamic-count').forEach(el => { const prefix = el.getAttribute('data-text') || ''; const suffix = el.getAttribute('data-suffix') || ''; const icon = el.textContent.split(' ')[0] || ''; el.textContent = `${icon} ${prefix} ${fallbackCount}+ ${suffix}`; }); } } function updateCurrentYear() { const currentYear = new Date().getFullYear(); document.querySelectorAll('.current-year').forEach(el => { el.textContent = currentYear; }); } // Google Analytics window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-XXXXXXXXX'); trackEvent('page_view', { tool: TOOL_CONFIG.name, category: TOOL_CONFIG.category });