`;
companiesGrid.appendChild(card);
});
}
function updateSummaryMetrics(dataToSummarize) {
if (!avgOwnershipChangeEl || !netShareChangeEl || !companiesIncreasedOwnEl) {
console.error("One or more summary metric DOM elements not found.");
return;
}
if (dataToSummarize.length === 0) {
avgOwnershipChangeEl.textContent = 'N/A';
netShareChangeEl.textContent = 'N/A';
companiesIncreasedOwnEl.textContent = '0';
return;
}
let totalOwnershipChangePercent = 0;
let totalNetShareChange = 0;
let companiesIncreasedCount = 0;
dataToSummarize.forEach(company => {
const ownershipChange = company.currentOwnershipPercent - company.previousOwnershipPercent;
const shareChangeVal = company.currentShares - company.previousShares;
totalOwnershipChangePercent += ownershipChange;
totalNetShareChange += shareChangeVal;
if (ownershipChange > 0) {
companiesIncreasedCount++;
}
});
const avgChange = totalOwnershipChangePercent / dataToSummarize.length;
const avgChangeSign = avgChange > 0 ? '+' : (avgChange < 0 ? '' : '');
avgOwnershipChangeEl.textContent = `${avgChangeSign}${avgChange.toFixed(2)}%`;
avgOwnershipChangeEl.className = `font-bold ${getChangeClass(avgChange)}`;
const netShareSign = totalNetShareChange > 0 ? '+' : (totalNetShareChange < 0 ? '' : '');
netShareChangeEl.textContent = `${netShareSign}${formatNumber(totalNetShareChange)} shares`;
netShareChangeEl.className = `font-bold ${getChangeClass(totalNetShareChange / 1e9)}`; // Normalize for color
companiesIncreasedOwnEl.textContent = companiesIncreasedCount;
}
function filterCompanies() {
if (!searchInput) {
console.error("Error: searchInput element not found.");
renderCompanies(companiesOwnershipData);
updateSummaryMetrics(companiesOwnershipData);
return;
}
const searchTerm = searchInput.value.toLowerCase().trim();
const filtered = companiesOwnershipData.filter(company =>
company.name.toLowerCase().includes(searchTerm) ||
company.ticker.toLowerCase().includes(searchTerm)
);
renderCompanies(filtered);
updateSummaryMetrics(filtered);
}
function generatePdf() {
if (!pdfContentEl) {
console.error("Error: pdfContent element not found. Cannot generate PDF.");
const msgBox = document.createElement('div');
msgBox.textContent = "Error preparing PDF content. Please try again.";
msgBox.style.cssText = "position:fixed; top:20px; left:50%; transform:translateX(-50%); background:red; color:white; padding:10px; border-radius:5px; z-index:1000;";
document.body.appendChild(msgBox);
setTimeout(() => msgBox.remove(), 3000);
return;
}
const opt = {
margin: [0.5, 0.5, 0.5, 0.5], // inches
filename: 'institutional_ownership_report.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2, logging: false, useCORS: true, letterRendering: true, scrollY: 0 },
jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' },
pagebreak: { mode: ['avoid-all', 'css', 'legacy'] }
};
const pdfTitleElement = pdfContentEl.querySelector('.print\\:block');
if (pdfTitleElement) {
pdfTitleElement.classList.remove('hidden');
}
const contentCloneForPdf = pdfContentEl.cloneNode(true);
html2pdf().from(contentCloneForPdf).set(opt).save().then(() => {
if (pdfTitleElement) {
pdfTitleElement.classList.add('hidden');
}
}).catch(err => {
console.error("Error generating PDF:", err);
if (pdfTitleElement) {
pdfTitleElement.classList.add('hidden');
}
const errorBox = document.createElement('div');
errorBox.textContent = "Failed to generate PDF. See console for details.";
errorBox.style.cssText = "position:fixed; top:20px; left:50%; transform:translateX(-50%); background:red; color:white; padding:10px; border-radius:5px; z-index:1000;";
document.body.appendChild(errorBox);
setTimeout(() => errorBox.remove(), 3000);
});
}
// --- Event Listeners ---
if (searchInput) {
searchInput.addEventListener('input', filterCompanies);
} else {
console.warn("Search input element not found. Search functionality will be disabled.");
}
if (downloadPdfButton) {
downloadPdfButton.addEventListener('click', generatePdf);
} else {
console.warn("PDF download button not found. PDF functionality will be disabled.");
}
// --- Initial Render ---
renderCompanies(companiesOwnershipData);
updateSummaryMetrics(companiesOwnershipData);
});