`;
inputsHtml += `
Real Estate (${inputs.re.type.replace(/_/g,' ')}) Scenario Inputs:
Est. Annual Appreciation: ${pmre_formatPercentage(inputs.re.expAppreciation)}
${inputs.re.type === 'rental_property' ? `
Est. Gross Rental Yield: ${pmre_formatPercentage(inputs.re.grossRentalYield)}
Est. OpEx as ${inputs.re.opExPercentType === 'rent' ? '% of Rent' : '% of Prop. Value'}: ${pmre_formatPercentage(inputs.re.opExPercentValue)}
` : `
Annual Holding Costs: ${inputs.re.holdingCostsNonRentalType === 'percent' ? pmre_formatPercentage(inputs.re.holdingCostsNonRentalValue) : pmre_formatCurrency(inputs.re.holdingCostsNonRentalValue)}
`}
Purchase Closing Costs: ${pmre_formatPercentage(inputs.re.purchaseClosingCostsPercent)}
Selling Costs: ${pmre_formatPercentage(inputs.re.sellingCostsPercent)}
Perceived Liquidity: ${inputs.re.liquidity}
`;
const comparisonTableHtml = `
| Metric | Precious Metals | Real Estate |
| Initial Investment Outlay | ${pmre_formatCurrency(inputs.initialInvestment + ((inputs.pm.purchaseCostsType === 'percent') ? inputs.initialInvestment * (inputs.pm.purchaseCostsValue/100) : inputs.pm.purchaseCostsValue))} | ${pmre_formatCurrency(inputs.initialInvestment + (inputs.initialInvestment * inputs.re.purchaseClosingCostsPercent/100))} |
| Est. Net Future Value | ${pmre_formatCurrency(results.pm.netFutureValue)} | ${pmre_formatCurrency(results.re.netFutureValue)} |
| Est. Net Profit | ${pmre_formatCurrency(results.pm.netProfit)} | ${pmre_formatCurrency(results.re.netProfit)} |
| Est. Total Costs Incurred | ${pmre_formatCurrency(results.pm.totalCosts)} | ${pmre_formatCurrency(results.re.totalCostsForDisplayRE)} |
| Est. Simple ROI | ${pmre_formatPercentage(results.pm.roi)} | ${pmre_formatPercentage(results.re.roi)} |
| Est. Annualized ROI (CAGR %) | ${pmre_formatPercentage(results.pm.cagr)} | ${pmre_formatPercentage(results.re.cagr)} |
${inputs.re.type === 'rental_property' ? `| Accumulated Net Rental Income (RE) | N/A | ${pmre_formatCurrency(results.re.accumulatedNetRentalIncome)} |
` : ''}
`;
// Basic cleaning of qualitative HTML for PDF
let cleanedQualitativeHtml = qualitative.replace(/
/g, '
').replace(/<\/h4>/g, '
')
.replace(//g, '
').replace(//g,'')
.replace(//g, '').replace(/- /g, '
- ');
const disclaimer_pdf = `Estimates are based on user inputs which are speculative. Actual outcomes can vary significantly. Both asset classes have unique risks. This is NOT financial advice. DYOR.`;
pdfContentEl.innerHTML = `
Precious Metals vs. Real Estate Investment Comparison
I. Input Parameters
${inputsHtml}
II. Projected Financial Comparison (Over ${inputs.timeHorizon} Years)
III. Qualitative Alignment & Considerations
${cleanedQualitativeHtml}
Disclaimer: ${disclaimer_pdf}
`;
document.body.appendChild(pdfContentEl);
html2canvas(pdfContentEl, { scale: 2, useCORS: true, logging:true, windowWidth: pdfContentEl.scrollWidth, windowHeight: pdfContentEl.scrollHeight }).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const imgProps = pdf.getImageProperties(imgData);
const pageMargin = 40;
const contentWidth = pdfWidth - 2 * pageMargin;
// Calculate how much of the canvas height corresponds to one PDF page height
const singlePageCanvasHeightEquivalent = (pdfHeight - 2 * pageMargin) * (imgProps.width / contentWidth);
let numPages = Math.ceil(imgProps.height / singlePageCanvasHeightEquivalent);
for (let i = 0; i < numPages; i++) {
if (i > 0) pdf.addPage();
let sourceY = i * singlePageCanvasHeightEquivalent;
let sourceHeight = Math.min(singlePageCanvasHeightEquivalent, imgProps.height - sourceY);
const pageCanvas = document.createElement('canvas');
pageCanvas.width = imgProps.width;
pageCanvas.height = sourceHeight;
const ctx = pageCanvas.getContext('2d');
ctx.drawImage(canvas, 0, sourceY, imgProps.width, sourceHeight, 0, 0, imgProps.width, sourceHeight);
const pageImgData = pageCanvas.toDataURL('image/png');
const pageImgHeight = (sourceHeight * contentWidth) / imgProps.width;
pdf.addImage(pageImgData, 'PNG', pageMargin, pageMargin, contentWidth, pageImgHeight, undefined, 'FAST');
}
pdf.save('PM_vs_RE_Comparison.pdf');
if(document.body.contains(pdfContentEl)) document.body.removeChild(pdfContentEl);
}).catch(err => {
console.error("PM vs RE PDF Error:", err); alert("Error generating Comparison PDF. See console.");
if(document.body.contains(pdfContentEl)) document.body.removeChild(pdfContentEl);
});
}
document.addEventListener('DOMContentLoaded', function() {
pmre_openTab({}, 'pmre_tab_overview');
pmre_toggleREInputs(); // Initialize RE input visibility based on default select
// Set some example defaults for quicker testing
const initialInvEl = document.getElementById('pmre_initialInvestment');
if(initialInvEl && !initialInvEl.value) initialInvEl.value = '100000';
const timeHorizonEl = document.getElementById('pmre_timeHorizon');
if(timeHorizonEl && !timeHorizonEl.value) timeHorizonEl.value = '10';
// PM Defaults
const pmAppEl = document.getElementById('pmre_pm_expAppreciation');
if(pmAppEl && !pmAppEl.value) pmAppEl.value = '3';
const pmPurchCostEl = document.getElementById('pmre_pm_purchaseCostsValue');
if(pmPurchCostEl && !pmPurchCostEl.value) pmPurchCostEl.value = '5'; // Assuming %
const pmHoldCostEl = document.getElementById('pmre_pm_holdingCostValue');
if(pmHoldCostEl && !pmHoldCostEl.value) pmHoldCostEl.value = '0.5'; // Assuming %
const pmSellCostEl = document.getElementById('pmre_pm_sellingCostValue');
if(pmSellCostEl && !pmSellCostEl.value) pmSellCostEl.value = '2'; // Assuming %
// RE Defaults
const reAppEl = document.getElementById('pmre_re_expAppreciation');
if(reAppEl && !reAppEl.value) reAppEl.value = '4';
const reYieldEl = document.getElementById('pmre_re_grossRentalYield');
if(reYieldEl && !reYieldEl.value) reYieldEl.value = '6';
const reOpExEl = document.getElementById('pmre_re_opExPercentValue');
if(reOpExEl && !reOpExEl.value) reOpExEl.value = '40'; // Assuming % of rent
const rePurchCostEl = document.getElementById('pmre_re_purchaseClosingCostsPercent');
if(rePurchCostEl && !rePurchCostEl.value) rePurchCostEl.value = '3';
const reSellCostEl = document.getElementById('pmre_re_sellingCostsPercent');
if(reSellCostEl && !reSellCostEl.value) reSellCostEl.value = '6';
if (!document.getElementById('pmre_initialInvestment')) {
console.error("Critical input 'pmre_initialInvestment' not found.");
}
});