← Files Investment BankingARCHIVED FILE
shared/dashboard/dashboard.js
14.9 KB · Oct 5, 2026 · 18:28 UTC
(function () {
function ready(fn) {
if (document.readyState !== "loading") fn();
else document.addEventListener("DOMContentLoaded", fn);
}
function copyTextFor(block) {
var clone = block.cloneNode(true);
clone.querySelectorAll("[data-copy-button], .citation-popover, .table-action-bar, .dashboard-utility-bar").forEach(function (node) {
node.remove();
});
clone.querySelectorAll(".citation-badge, .citation-chip, .citation-link, .source-id").forEach(function (node) {
var label = (node.innerText || node.textContent || "").trim();
node.replaceWith(document.createTextNode(label ? " " + label : ""));
});
clone.querySelectorAll("table").forEach(function (table) {
var lines = [];
var headers = Array.prototype.map.call(table.querySelectorAll("thead th"), function (th) {
return th.innerText.trim();
});
if (headers.length) lines.push(headers.join("\t"));
table.querySelectorAll("tbody tr").forEach(function (tr) {
var cells = Array.prototype.map.call(tr.querySelectorAll("td"), function (td) {
return td.innerText.replace(/\s+/g, " ").trim();
});
if (cells.length) lines.push(cells.join("\t"));
});
var pre = document.createElement("pre");
pre.textContent = lines.join("\n");
table.replaceWith(pre);
});
return clone.innerText.replace(/\n{3,}/g, "\n\n").trim();
}
function fallbackCopy(text) {
var area = document.createElement("textarea");
area.value = text;
area.setAttribute("readonly", "");
area.style.position = "fixed";
area.style.left = "-9999px";
document.body.appendChild(area);
area.select();
var ok = document.execCommand("copy");
area.remove();
return Promise.resolve(ok);
}
function writeClipboard(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
return navigator.clipboard.writeText(text);
}
return fallbackCopy(text);
}
function fullReportText() {
var root = document.querySelector("[data-report-copy-root]") || document.querySelector("main");
if (!root) return "";
var clone = root.cloneNode(true);
clone.querySelectorAll(".toc-list, #sources, [data-report-copy-exclude]").forEach(function (node) {
node.remove();
});
return copyTextFor(clone);
}
function tableForButton(button) {
var target = button.getAttribute("data-table-target");
if (target) {
var found = document.getElementById(target);
if (found) return found;
}
var shell = button.closest("[data-table-export-wrapper]");
return shell ? shell.querySelector("table[data-table-export]") : null;
}
function cleanCellText(cell) {
var clone = cell.cloneNode(true);
clone.querySelectorAll(".citation-popover").forEach(function (node) {
node.remove();
});
clone.querySelectorAll(".citation-badge, .citation-chip, .citation-link, .source-id").forEach(function (node) {
var label = (node.innerText || node.textContent || "").trim();
node.replaceWith(document.createTextNode(label ? " " + label : ""));
});
return clone.innerText.replace(/\s+/g, " ").trim();
}
function tableToMatrix(table) {
var rows = [];
var headers = Array.prototype.map.call(table.querySelectorAll("thead th"), cleanCellText);
if (headers.length) rows.push(headers);
table.querySelectorAll("tbody tr").forEach(function (tr) {
var cells = Array.prototype.map.call(tr.querySelectorAll("td"), cleanCellText);
if (cells.length) rows.push(cells);
});
return rows;
}
function tableToTSV(table) {
return tableToMatrix(table).map(function (row) {
return row.join("\t");
}).join("\n");
}
function csvEscape(value) {
var raw = String(value == null ? "" : value);
if (/[",\n\r]/.test(raw)) {
return '"' + raw.replace(/"/g, '""') + '"';
}
return raw;
}
function tableToCSV(table) {
return tableToMatrix(table).map(function (row) {
return row.map(csvEscape).join(",");
}).join("\n");
}
function safeFilename(value, fallback) {
var raw = String(value || fallback || "dashboard-table.csv")
.replace(/[\\/:*?"<>|]+/g, "-")
.replace(/\s+/g, "_")
.replace(/_+/g, "_")
.replace(/^-+|-+$/g, "");
if (!raw) raw = fallback || "dashboard-table.csv";
if (!/\.csv$/i.test(raw)) raw += ".csv";
return raw;
}
function downloadBlob(filename, content, type) {
var blob = new Blob([content], { type: type || "text/plain;charset=utf-8" });
var url = URL.createObjectURL(blob);
var link = document.createElement("a");
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
link.remove();
window.setTimeout(function () {
URL.revokeObjectURL(url);
}, 250);
}
function markCopied(button) {
var original = button.getAttribute("data-label-original") || button.textContent;
button.setAttribute("data-label-original", original);
button.classList.add("is-copied");
var textNode = Array.prototype.find.call(button.childNodes, function (node) {
return node.nodeType === Node.TEXT_NODE;
});
if (!textNode) {
textNode = document.createTextNode("");
button.appendChild(textNode);
}
textNode.nodeValue = "Copied";
window.setTimeout(function () {
button.classList.remove("is-copied");
textNode.nodeValue = original.trim() || "Copy";
}, 1300);
}
function escapeHtml(value) {
return String(value == null ? "" : value)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
var citationPopover;
var citationHideTimer;
var sourceData = window.DASHBOARD_SOURCE_DATA || {};
var CITATION_SELECTOR = ".citation-chip[data-citation-id], .citation-chip[data-citation-title], .citation-badge[data-citation-id], .citation-badge[data-citation-title], .citation-link[data-citation-id], .citation-link[data-citation-title]";
function citationField(label, value) {
if (!value) return "";
return '<div class="citation-popover-label">' + escapeHtml(label) + '</div><div class="citation-popover-value">' + escapeHtml(value) + '</div>';
}
function ensureCitationPopover() {
if (citationPopover) return citationPopover;
citationPopover = document.createElement("div");
citationPopover.className = "citation-popover";
citationPopover.id = "citation-popover";
citationPopover.setAttribute("role", "tooltip");
document.body.appendChild(citationPopover);
return citationPopover;
}
function positionCitationPopover(popover, chip) {
var rect = chip.getBoundingClientRect();
var popRect = popover.getBoundingClientRect();
var margin = 12;
var left = rect.left + rect.width / 2 - popRect.width / 2;
left = Math.max(margin, Math.min(left, window.innerWidth - popRect.width - margin));
var top = rect.top - popRect.height - 10;
if (top < margin) top = rect.bottom + 10;
popover.style.left = left + "px";
popover.style.top = top + "px";
}
function sourceForChip(chip) {
var id = chip && chip.getAttribute("data-citation-id");
if (id && sourceData[id]) return sourceData[id];
if (chip && chip.getAttribute("data-citation-needed")) {
return {
id: "Needs source",
title: "Citation needed",
status: "Missing inline source id",
notes: chip.getAttribute("data-tooltip") || "Attach citation_ids to this claim, number, table row, or module."
};
}
if (chip && chip.getAttribute("data-citation-missing")) {
return {
id: "Missing source",
title: "Source id not found",
status: "Missing source record",
notes: chip.getAttribute("data-tooltip") || "Add this id to the source register."
};
}
if (chip && chip.getAttribute("data-citation-external")) {
return {
id: "External source",
title: chip.getAttribute("data-citation-title") || "External source",
type: "External URL",
date: chip.getAttribute("data-citation-date") || "",
detail: chip.getAttribute("data-citation-detail") || "",
notes: chip.getAttribute("href") || chip.getAttribute("data-tooltip") || ""
};
}
var title = chip && chip.getAttribute("data-citation-title");
if (title) {
return {
id: id || "source",
title: title,
type: "External source",
date: chip.getAttribute("data-citation-date") || "",
detail: chip.getAttribute("data-citation-detail") || "",
notes: chip.getAttribute("href") || ""
};
}
return null;
}
function showCitationPopover(chip) {
var source = sourceForChip(chip);
if (!source) return;
window.clearTimeout(citationHideTimer);
var id = source.id || chip.getAttribute("data-citation-id") || "Source";
var title = source.title || "Source";
var popover = ensureCitationPopover();
popover.innerHTML =
'<div class="citation-popover-title"><span class="citation-popover-id">' + escapeHtml(id) + '</span>' + escapeHtml(title) + '</div>' +
'<div class="citation-popover-grid">' +
citationField("Type", source.type) +
citationField("Quality", source.quality || source.status) +
citationField("Date", source.date) +
citationField("Workbook", source.workbook) +
citationField("Sheet", source.sheet) +
citationField("Range", source.range || source.workbook_ref) +
citationField("Value", source.value) +
citationField("Formula", source.formula) +
citationField("Detail", source.detail) +
citationField("URL", source.url) +
'</div>' +
(source.notes ? '<div class="citation-popover-note">' + escapeHtml(source.notes) + '</div>' : '<div class="citation-popover-note">' + (source.workbook_ref ? 'Click to open the workbook. If the viewer does not jump automatically, use the sheet/range shown here.' : 'Click the citation to jump to the full source row.') + '</div>');
chip.setAttribute("aria-describedby", "citation-popover");
popover.classList.add("show");
positionCitationPopover(popover, chip);
}
function hideCitationPopover() {
window.clearTimeout(citationHideTimer);
citationHideTimer = window.setTimeout(function () {
document.querySelectorAll("[aria-describedby='citation-popover']").forEach(function (node) {
node.removeAttribute("aria-describedby");
});
if (citationPopover) citationPopover.classList.remove("show");
}, 120);
}
ready(function () {
var tocLinks = Array.prototype.slice.call(document.querySelectorAll(".toc-link"));
var sections = tocLinks
.map(function (link) { return document.querySelector(link.getAttribute("href")); })
.filter(Boolean);
function setActive(id) {
tocLinks.forEach(function (link) {
link.classList.toggle("is-active", link.getAttribute("href") === "#" + id);
});
}
if ("IntersectionObserver" in window && sections.length) {
var observer = new IntersectionObserver(function (entries) {
var visible = entries
.filter(function (entry) { return entry.isIntersecting; })
.sort(function (a, b) { return b.intersectionRatio - a.intersectionRatio; })[0];
if (visible && visible.target) setActive(visible.target.id);
}, { rootMargin: "-18% 0px -70% 0px", threshold: [0.1, 0.35, 0.6] });
sections.forEach(function (section) { observer.observe(section); });
}
tocLinks.forEach(function (link) {
link.addEventListener("click", function () {
setActive((link.getAttribute("href") || "").replace("#", ""));
});
});
document.addEventListener("click", function (event) {
var tsvButton = event.target.closest("[data-copy-table-tsv]");
if (tsvButton) {
var tsvTable = tableForButton(tsvButton);
if (!tsvTable) return;
var tsv = tableToTSV(tsvTable);
if (!tsv) return;
writeClipboard(tsv).then(function () {
markCopied(tsvButton);
}).catch(function () {
tsvButton.classList.add("copy-failed");
});
return;
}
var csvButton = event.target.closest("[data-download-table-csv]");
if (csvButton) {
var csvTable = tableForButton(csvButton);
if (!csvTable) return;
var csv = tableToCSV(csvTable);
if (!csv) return;
downloadBlob(safeFilename(csvTable.getAttribute("data-download-filename"), "dashboard-table.csv"), csv, "text/csv;charset=utf-8");
markCopied(csvButton);
return;
}
var fullReportButton = event.target.closest("[data-copy-full-report]");
if (fullReportButton) {
var reportText = fullReportText();
if (!reportText) return;
writeClipboard(reportText).then(function () {
markCopied(fullReportButton);
}).catch(function () {
fullReportButton.classList.add("copy-failed");
});
return;
}
var printButton = event.target.closest("[data-print-dashboard]");
if (printButton) {
window.print();
return;
}
var button = event.target.closest("[data-copy-button]");
if (!button) return;
var block = button.closest("[data-copy-block]");
if (!block) return;
var text = copyTextFor(block);
if (!text) return;
writeClipboard(text).then(function () {
markCopied(button);
}).catch(function () {
button.classList.add("copy-failed");
});
});
document.addEventListener("mouseover", function (event) {
var chip = event.target.closest(CITATION_SELECTOR);
if (chip) showCitationPopover(chip);
});
document.addEventListener("focusin", function (event) {
var chip = event.target.closest(CITATION_SELECTOR);
if (chip) showCitationPopover(chip);
});
document.addEventListener("mouseout", function (event) {
if (event.target.closest(CITATION_SELECTOR)) hideCitationPopover();
});
document.addEventListener("focusout", function (event) {
if (event.target.closest(CITATION_SELECTOR)) hideCitationPopover();
});
document.addEventListener("keydown", function (event) {
if (event.key === "Escape") {
hideCitationPopover();
if (document.activeElement && document.activeElement.matches && document.activeElement.matches(CITATION_SELECTOR)) {
document.activeElement.blur();
}
}
});
if (window.location.hash) {
var target = document.querySelector(window.location.hash);
if (target) window.setTimeout(function () { target.scrollIntoView(); }, 50);
}
if ("ResizeObserver" in window) {
var ro = new ResizeObserver(function (entries) {
entries.forEach(function (entry) {
entry.target.dispatchEvent(new CustomEvent("dashboard:resize", { bubbles: false }));
});
});
document.querySelectorAll("[data-responsive-chart]").forEach(function (el) { ro.observe(el); });
}
});
})();
SHA-256: b81f21b4fe5541d7c6cbfc4b4c2811c3191d8eb088252819257d4dcdaee4691f