`;
}
window.rce_openTab = function(event, tabId) {
if (!tabContents || !tabLinks) { console.error("RCE: Tab elements not found."); return; }
tabContents.forEach(tc => tc.style.display = 'none');
tabLinks.forEach(tl => tl.classList.remove('rce-active'));
const tabElement = document.getElementById(tabId);
if(tabElement) tabElement.style.display = 'block';
else console.error(`RCE: Tab element for id ${tabId} not found.`);
if (event && event.currentTarget) event.currentTarget.classList.add('rce-active');
else {
const activeBtn = Array.from(tabLinks).find(btn => btn.getAttribute('onclick').includes(tabId));
if(activeBtn) activeBtn.classList.add('rce-active');
}
currentTabIndex = Array.from(tabLinks).findIndex(tl => tl.classList.contains('rce-active'));
rce_updateNavButtons();
if (tabId === 'rceTabSummary') rce_calculateAndDisplaySummary();
}
window.rce_navigateTab = function(direction) {
let newIndex = currentTabIndex;
if (direction === 'next' && currentTabIndex < tabLinks.length - 1) newIndex++;
else if (direction === 'prev' && currentTabIndex > 0) newIndex--;
if(tabLinks[newIndex]) tabLinks[newIndex].click();
}
function rce_updateNavButtons() {
if(prevTabButton) prevTabButton.disabled = (currentTabIndex === 0);
if(nextTabButton) nextTabButton.disabled = (currentTabIndex === tabLinks.length - 1);
}
function rce_generateTableForPDF(title, categoryKey) {
let html = `
';
return { html, total: categoryTotal };
}
window.rce_downloadResultsAsPDF = async function() {
if (!rce_jsPDFLoaded || !rce_html2canvasLoaded) {
alert("PDF libraries are still loading. Please wait a moment and try again."); return;
}
if (typeof html2canvas === 'undefined' || typeof jspdf === 'undefined' || typeof jspdf.jsPDF === 'undefined') {
alert("PDF generation library is not available. Please ensure an internet connection.");
console.error("RCE: jsPDF or html2canvas is undefined."); return;
}
const { jsPDF } = jspdf;
const pdfDoc = new jsPDF({ unit: 'pt', format: 'a4', orientation: 'portrait' });
const pdfContentTarget = document.getElementById('rce-pdfContentContainer');
if (!pdfContentTarget) { console.error("RCE: PDF content target not found."); return; }
const currentAge = budgetData.profile.childAge;
const projectYears = budgetData.profile.yearsToProject;
document.getElementById('rce-pdfProfileInfo').innerHTML = `
`; const oneTimeResult = rce_generateTableForPDF("Initial & One-Time Costs", "oneTimeCosts"); document.getElementById('rce-pdfOneTimeCosts').innerHTML = oneTimeResult.html; const monthlyResult = rce_generateTableForPDF("Ongoing Monthly Expenses", "monthlyCosts"); document.getElementById('rce-pdfMonthlyCosts').innerHTML = monthlyResult.html; const annualResult = rce_generateTableForPDF("Annual & Irregular Expenses", "annualCosts"); document.getElementById('rce-pdfAnnualCosts').innerHTML = annualResult.html; const monthlyEquivalentAnnualPdf = annualResult.total > 0 ? annualResult.total / 12 : 0; const grandTotalMonthlyPdf = monthlyResult.total + monthlyEquivalentAnnualPdf; const firstYearTotalCostPdf = oneTimeResult.total + (grandTotalMonthlyPdf * 12); const projectedTotalCostPdf = grandTotalMonthlyPdf * 12 * projectYears; document.getElementById('rce-pdfOverallSummary').innerHTML = `
${title}
`; const items = budgetData[categoryKey]; if (!items || items.length === 0) { html += "No items entered for this section.
"; return { html, total: 0 }; } html += '| Item | Cost ($) |
|---|---|
| ${item.name} | ${parseFloat(item.cost).toFixed(2)} |
| No costs entered for items in this section. | |
| Total ${title.toLowerCase()} | $${categoryTotal.toFixed(2)} |
Child's Profile
Child's Current Age: ${currentAge} years
Projection Period: ${projectYears} years
`; const oneTimeResult = rce_generateTableForPDF("Initial & One-Time Costs", "oneTimeCosts"); document.getElementById('rce-pdfOneTimeCosts').innerHTML = oneTimeResult.html; const monthlyResult = rce_generateTableForPDF("Ongoing Monthly Expenses", "monthlyCosts"); document.getElementById('rce-pdfMonthlyCosts').innerHTML = monthlyResult.html; const annualResult = rce_generateTableForPDF("Annual & Irregular Expenses", "annualCosts"); document.getElementById('rce-pdfAnnualCosts').innerHTML = annualResult.html; const monthlyEquivalentAnnualPdf = annualResult.total > 0 ? annualResult.total / 12 : 0; const grandTotalMonthlyPdf = monthlyResult.total + monthlyEquivalentAnnualPdf; const firstYearTotalCostPdf = oneTimeResult.total + (grandTotalMonthlyPdf * 12); const projectedTotalCostPdf = grandTotalMonthlyPdf * 12 * projectYears; document.getElementById('rce-pdfOverallSummary').innerHTML = `
Overall Estimated Summary
| Total Initial & One-Time Costs: | $${oneTimeResult.total.toFixed(2)} |
| Total Ongoing Monthly Expenses: | $${monthlyResult.total.toFixed(2)} |
| Total Annual & Irregular Expenses: | $${annualResult.total.toFixed(2)} |
| (Monthly Equivalent of Annual Costs): | $${monthlyEquivalentAnnualPdf.toFixed(2)} |
| GRAND TOTAL ESTIMATED MONTHLY COST: | $${grandTotalMonthlyPdf.toFixed(2)} |
| Estimated First Full Year Cost: | $${firstYearTotalCostPdf.toFixed(2)} |
| PROJECTED TOTAL COST OVER ${projectYears} YEARS: | $${projectedTotalCostPdf.toFixed(2)} |
Projection Disclaimer: This is a simple multiplication based on current estimates and does not account for inflation or changes in needs as the child ages.
`; pdfContentTarget.style.display = 'block'; try { const canvas = await html2canvas(pdfContentTarget, { scale: 2, useCORS: true, backgroundColor: '#ffffff', onclone: (clonedDoc) => { const headers = clonedDoc.querySelectorAll('.rce-results-table th'); headers.forEach(th => { th.style.backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--tool-table-header-bg').trim() || '#ebf5fb'; th.style.color = getComputedStyle(document.documentElement).getPropertyValue('--tool-primary-color').trim() || '#3498db'; }); clonedDoc.querySelectorAll('#rce-pdfContentContainer h2, #rce-pdfContentContainer h3, #rce-pdfContentContainer h4').forEach(h => { h.style.color = getComputedStyle(document.documentElement).getPropertyValue('--tool-primary-color').trim() || '#3498db'; }); } }); pdfContentTarget.style.display = 'none'; const imgData = canvas.toDataURL('image/png'); const imgProps = pdfDoc.getImageProperties(imgData); const pdfPageWidth = pdfDoc.internal.pageSize.getWidth(); const pdfPageHeight = pdfDoc.internal.pageSize.getHeight(); const margin = 40; // pt const contentWidth = pdfPageWidth - 2 * margin; const contentHeight = (imgProps.height * contentWidth) / imgProps.width; let currentPos = margin; let remainingImgHeight = contentHeight; pdfDoc.addImage(imgData, 'PNG', margin, currentPos, contentWidth, contentHeight); remainingImgHeight -= (pdfPageHeight - 2 * margin); while (remainingImgHeight > 0) { currentPos -= (pdfPageHeight - 2 * margin); pdfDoc.addPage(); pdfDoc.addImage(imgData, 'PNG', margin, currentPos, contentWidth, contentHeight); remainingImgHeight -= (pdfPageHeight - 2 * margin); } pdfDoc.save('Raising_Child_Expense_Estimate.pdf'); } catch (error) { console.error("RCE: Error generating PDF:", error); alert("An error occurred while generating the PDF."); pdfContentTarget.style.display = 'none'; } } // Initialization rce_populateInitialDefaultRows(); if(tabLinks.length > 0) rce_openTab(null, tabContents[0].id); else console.error("RCE: No tab links found."); rce_updateNavButtons(); rce_calculateAndDisplaySummary(); // Initial calculation // Attach listeners for profile inputs if(childAgeInput) childAgeInput.addEventListener('change', rce_calculateAndDisplaySummary); if(yearsToProjectInput) yearsToProjectInput.addEventListener('change', rce_calculateAndDisplaySummary); // Final null checks for critical elements if (!childAgeInput || !yearsToProjectInput || !budgetSummaryOutput) { console.error("RCE: One or more critical initial profile/summary elements are missing."); } const downloadButton = document.getElementById('rce-downloadPdfButton'); if (!downloadButton) { console.error("RCE: PDF download button not found."); } else if (!rce_jsPDFLoaded || !rce_html2canvasLoaded) { downloadButton.disabled = true; } });