/.
const clean = DOMPurify.sanitize("");
if (!clean) return [];
@@ -2837,30 +2881,43 @@ Copy to clipboard
return select ? importSanitizedNodes(select.childNodes) : [];
}
- /** Assign HTML after sanitization so static analysis (e.g. CodeQL) sees a guarded DOM sink. */
+ function htmlNodesForElement(el, raw) {
+ const tag = el.tagName;
+ if (tag === "THEAD") {
+ return window.DOMPurify
+ ? nodesFromSanitizedTableSection("thead", raw)
+ : nodesFromTrustedTableSection("thead", raw);
+ }
+ if (tag === "TBODY") {
+ return window.DOMPurify
+ ? nodesFromSanitizedTableSection("tbody", raw)
+ : nodesFromTrustedTableSection("tbody", raw);
+ }
+ if (tag === "SELECT") {
+ return window.DOMPurify ? nodesFromSanitizedSelectOptions(raw) : nodesFromTrustedSelectOptions(raw);
+ }
+ return window.DOMPurify ? nodesFromSanitizedBodyHtml(raw) : nodesFromTrustedBodyHtml(raw);
+ }
+
+ /** Assign HTML after sanitization (or trusted DOMParser fallback when DOMPurify is unavailable). */
function setHtml(el, html) {
if (!el) return;
- if (!window.DOMPurify) {
- el.replaceChildren();
- return;
- }
const raw = html == null ? "" : String(html);
- const tag = el.tagName;
- let nodes;
- if (tag === "THEAD") nodes = nodesFromSanitizedTableSection("thead", raw);
- else if (tag === "TBODY") nodes = nodesFromSanitizedTableSection("tbody", raw);
- else if (tag === "SELECT") nodes = nodesFromSanitizedSelectOptions(raw);
- else nodes = nodesFromSanitizedBodyHtml(raw);
+ const nodes = htmlNodesForElement(el, raw);
el.replaceChildren(...nodes);
}
function insertAdjacentHtmlSafe(el, position, html) {
- if (!el || !window.DOMPurify) return;
+ if (!el) return;
const raw = html == null ? "" : String(html);
const nodes =
el.tagName === "TBODY" && position === "beforeend"
- ? nodesFromSanitizedTableSection("tbody", raw)
- : nodesFromSanitizedBodyHtml(raw);
+ ? window.DOMPurify
+ ? nodesFromSanitizedTableSection("tbody", raw)
+ : nodesFromTrustedTableSection("tbody", raw)
+ : window.DOMPurify
+ ? nodesFromSanitizedBodyHtml(raw)
+ : nodesFromTrustedBodyHtml(raw);
if (!nodes.length) return;
if (position === "beforeend") el.append(...nodes);
else if (position === "afterbegin") el.prepend(...nodes);
@@ -2888,6 +2945,8 @@ Copy to clipboard
let linkLensFetchPromise = null;
/** Filled on main data load so we can re-render after lazy linklens arrives. */
let pageContext = null;
+ /** "full" (default table) or "popular" (curated hand-picked links). */
+ let listViewMode = "full";
function syncOfflineExportGlobals() {
try {
@@ -4353,7 +4412,76 @@ Copy to clipboard
}
function shouldHideMostOpenedSection() {
- return !!(viewingFolderId || savedLinksViewOnly);
+ return !!(viewingFolderId || savedLinksViewOnly || listViewMode === "popular");
+ }
+
+ function setListViewMode(mode) {
+ const next = mode === "popular" ? "popular" : "full";
+ if (listViewMode === next) {
+ applyListViewModeUi();
+ return;
+ }
+ listViewMode = next;
+ applyListViewModeUi();
+ if (listViewMode === "full" && pageContext) {
+ void render(pageContext.links, pageContext.meta, pageContext.link_check);
+ }
+ }
+
+ function applyListViewModeUi() {
+ const popular = listViewMode === "popular";
+ const sec = $("popularLinksSection");
+ const table = $("tableWrap");
+ const statsEl = $("stats");
+ const navBtn = $("popularLinksNavBtn");
+ if (sec) sec.hidden = !popular;
+ if (table) table.hidden = popular;
+ if (statsEl) statsEl.hidden = popular;
+ if (popular) hideMostOpenedSection();
+ if (navBtn) {
+ navBtn.classList.toggle("btn-primary", popular);
+ navBtn.setAttribute("aria-pressed", popular ? "true" : "false");
+ }
+ const backMain = $("sidebarBackToMainList");
+ if (backMain) {
+ const onPlainMainList = !viewingFolderId && !savedLinksViewOnly && !popular;
+ backMain.hidden = onPlainMainList;
+ }
+ }
+
+ function renderPopularLinksSection(meta) {
+ const sec = $("popularLinksSection");
+ const noteEl = $("popularLinksNote");
+ const thead = $("popularLinksThead");
+ const tbody = $("popularLinksTableBody");
+ const emptyEl = $("popularLinksEmptyState");
+ const mount = $("popularLinksTableMount");
+ if (!sec || !thead || !tbody) return;
+ const rows = Array.isArray(meta && meta.popular_links) ? meta.popular_links.filter((r) => r && r.link) : [];
+ const note = meta && meta.popular_note ? String(meta.popular_note).trim() : "";
+ if (noteEl) {
+ noteEl.textContent = note || "Hand-picked links that tend to work well — not ranked by site traffic.";
+ noteEl.hidden = false;
+ }
+ if (!rows.length) {
+ setHtml(thead, "");
+ setHtml(tbody, "");
+ if (emptyEl) {
+ setHtml(emptyEl, emptyStateNoResultsInnerHtml());
+ emptyEl.hidden = false;
+ }
+ if (mount) mount.hidden = true;
+ if (listViewMode === "popular") sec.hidden = false;
+ return;
+ }
+ if (emptyEl) {
+ emptyEl.hidden = true;
+ setHtml(emptyEl, "");
+ }
+ if (mount) mount.hidden = false;
+ setHtml(thead, linkTableTheadRowHtml());
+ setHtml(tbody, rows.map((r) => buildRowTrHtml(r)).join(""));
+ if (listViewMode === "popular") sec.hidden = false;
}
function hideMostOpenedSection() {
@@ -4432,7 +4560,7 @@ Copy to clipboard
}
const backMain = $("sidebarBackToMainList");
if (backMain) {
- const onPlainMainList = !viewingFolderId && !savedLinksViewOnly;
+ const onPlainMainList = !viewingFolderId && !savedLinksViewOnly && listViewMode !== "popular";
backMain.hidden = onPlainMainList;
}
renderFolderChromeIntoHost();
@@ -10599,6 +10727,36 @@ Copy to clipboard
};
}
+ async function normalizePayloadAsync(json) {
+ if (window.ProxyListData && typeof window.ProxyListData.normalizePayload === "function") {
+ const normalized = window.ProxyListData.normalizePayload(json);
+ if (normalized.compact && typeof window.ProxyListData.expandAllLinksAsync === "function") {
+ const expanded = await window.ProxyListData.expandAllLinksAsync(
+ {
+ format: 2,
+ providers: normalized.compact.providers,
+ contributors: normalized.compact.contributors,
+ links: normalized.compact.links,
+ },
+ {
+ onProgress: function (ratio) {
+ const pct = 38 + Math.round(Math.max(0, Math.min(1, Number(ratio) || 0)) * 14);
+ reportContentLoadingProgress(pct, "Preparing list…");
+ },
+ }
+ );
+ return {
+ meta: normalized.meta,
+ link_check: normalized.link_check,
+ links: expanded,
+ failing_links: normalized.failing_links,
+ };
+ }
+ return normalizePayload(json);
+ }
+ return normalizePayload(json);
+ }
+
let offlineExportScriptPromise = null;
function ensureOfflineExportScript() {
if (window.ProxyListOfflineExport) return Promise.resolve(window.ProxyListOfflineExport);
@@ -10615,6 +10773,7 @@ Copy to clipboard
}
setContentLoading(true, { progress: 0, message: "Getting data..." });
+ applyImportantNotices({});
bindClickLoadSlowPromptButtons();
initSavedLinksView();
initEmbedMode();
@@ -10662,8 +10821,11 @@ Copy to clipboard
])
.then(([, json, checkedDomainsText]) => {
reportContentLoadingProgress(35, "Getting data...");
+ renderPopularLinksSection((json && json.meta) || {});
+ return normalizePayloadAsync(json).then((normalized) => {
initFolderReportModal();
reportContentLoadingProgress(55, "Preparing list…");
+ const { meta, link_check, links } = normalized;
rebuildLinkLensIndexes();
populateKnownFilters();
updateSelectedFilterUI();
@@ -10674,7 +10836,6 @@ Copy to clipboard
.map((s) => s.trim().toLowerCase().replace(/^www\./, ""))
.filter(Boolean)
);
- const { meta, link_check, links } = normalizePayload(json);
pageContext = { links, meta, link_check };
syncOfflineExportGlobals();
currentListVersionStamp = versionStamp(meta) || "";
@@ -10685,6 +10846,8 @@ Copy to clipboard
failThreshold = meta.fail_threshold;
}
applyImportantNotices(meta);
+ renderPopularLinksSection(meta);
+ applyListViewModeUi();
setFooter(meta);
syncMostOpenedProviderOptions(links);
const moSortSel = $("mostOpenedSortSelect");
@@ -11156,6 +11319,21 @@ Copy to clipboard
setSiteUpdateBannerVisible(false);
});
}
+ const popularLinksNavBtn = $("popularLinksNavBtn");
+ if (popularLinksNavBtn) {
+ popularLinksNavBtn.addEventListener("click", () => {
+ setListViewMode(listViewMode === "popular" ? "full" : "popular");
+ });
+ }
+ const sidebarBackToMainList = $("sidebarBackToMainList");
+ if (sidebarBackToMainList) {
+ sidebarBackToMainList.addEventListener("click", (ev) => {
+ if (listViewMode === "popular") {
+ ev.preventDefault();
+ setListViewMode("full");
+ }
+ });
+ }
$("backToTopBtn").addEventListener("click", () => {
window.scrollTo({ top: 0, behavior: "smooth" });
});
@@ -11178,6 +11356,7 @@ Copy to clipboard
finishContentLoading();
if ($("folderTrustedResults")) refreshFolderTrustedSearchUI(folderTrustedSearchLastQuery);
});
+ });
})
.then((rendered) => {
void ensureFirebaseSdk()
diff --git a/docs/stats/index.html b/docs/stats/index.html
index 04a58b0..4c55f25 100644
--- a/docs/stats/index.html
+++ b/docs/stats/index.html
@@ -811,10 +811,7 @@ Active users per month
-
-
-
-
+
diff --git a/docs/stats/stats.js b/docs/stats/stats.js
index 5102960..0e94779 100644
--- a/docs/stats/stats.js
+++ b/docs/stats/stats.js
@@ -1513,6 +1513,65 @@
});
}
+ function loadExternalScript(src) {
+ return new Promise(function (resolve, reject) {
+ var s = document.createElement("script");
+ s.src = src;
+ s.onload = function () {
+ resolve();
+ };
+ s.onerror = function () {
+ reject(new Error("Could not load " + src));
+ };
+ document.head.appendChild(s);
+ });
+ }
+
+ var firebaseSdkPromise = null;
+ var FIREBASE_COMPAT_BASE = "https://www.gstatic.com/firebasejs/10.7.1/";
+
+ function ensureFirebaseSdk() {
+ if (typeof firebase !== "undefined") return Promise.resolve(true);
+ if (firebaseSdkPromise) return firebaseSdkPromise;
+ firebaseSdkPromise = ["firebase-app-compat.js", "firebase-auth-compat.js", "firebase-firestore-compat.js"]
+ .reduce(function (chain, file) {
+ return chain.then(function () {
+ return loadExternalScript(FIREBASE_COMPAT_BASE + file);
+ });
+ }, Promise.resolve())
+ .then(function () {
+ return typeof firebase !== "undefined";
+ })
+ .catch(function (err) {
+ console.warn("[stats] Firebase SDK did not load", err);
+ firebaseSdkPromise = null;
+ return false;
+ });
+ return firebaseSdkPromise;
+ }
+
+ function fetchDocsAsset(name) {
+ if (window.ProxyListData && typeof ProxyListData.fetchJsonAsset === "function") {
+ return ProxyListData.fetchJsonAsset(name, { fetchInit: { cache: "no-cache" }, timeoutMs: 20000 });
+ }
+ return fetch("../" + name, { cache: "no-cache" }).then(function (res) {
+ if (!res.ok) throw new Error(name + " HTTP " + res.status);
+ return res.json();
+ });
+ }
+
+ async function resolveExpandedLinksAsync(json, normalized) {
+ if (normalized && normalized.compact && typeof ProxyListData.expandAllLinksAsync === "function") {
+ return ProxyListData.expandAllLinksAsync({
+ format: 2,
+ providers: normalized.compact.providers,
+ contributors: normalized.compact.contributors,
+ links: normalized.compact.links,
+ });
+ }
+ return ProxyListData.resolveExpandedLinks(normalized && normalized.compact ? normalized : json);
+ }
+
function initFirebase() {
try {
var cfg = window.__FIREBASE_CONFIG__;
@@ -1948,13 +2007,18 @@
}
showPanel(panelFromHash(), { skipHash: true });
+ if (typeof Chart === "undefined") {
+ setNotice("Chart.js did not load — graphs are unavailable in this browser or proxy.", "err");
+ }
+
var data;
try {
- var json = await ProxyListData.fetchListPayload({ fetchInit: { cache: "no-cache" } });
+ var json = await ProxyListData.fetchListPayload({ fetchInit: { cache: "no-cache" }, timeoutMs: 20000 });
var normalized = ProxyListData.normalizePayload(json);
+ var links = await resolveExpandedLinksAsync(json, normalized);
data = {
meta: normalized.meta || (json && json.meta) || {},
- links: ProxyListData.resolveExpandedLinks(normalized.compact ? normalized : json),
+ links: links,
};
} catch (err) {
setNotice((err && err.message) || "Failed to load list data.", "err");
@@ -1983,31 +2047,23 @@
});
try {
- var contribRes = await fetch("../contributor_link_totals.json", { cache: "no-cache" });
- if (contribRes.ok) {
- renderContributors(await contribRes.json());
- }
+ renderContributors(await fetchDocsAsset("contributor_link_totals.json"));
} catch (_) {}
try {
- var filterRes = await fetch("../filter_stats.json", { cache: "no-cache" });
- if (filterRes.ok) {
- renderFilterStats(await filterRes.json());
- } else {
- setText("statFilters", "—");
- setText("statFilterChecked", "—");
- var filterBody = $("filterTableBody");
- if (filterBody) {
- filterBody.innerHTML =
- ' |