From 6dfb92e1646ebd34e1ae7989a7e51845c4ecc47d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 18 Aug 2026 13:45:15 +0000 Subject: [PATCH 1/3] Fix gray list, popular links, and stats on web proxies. When DOMPurify failed to load inside Scramjet/Ultraviolet, setHtml cleared the table and left a blank gray panel. Fall back to trusted DOMParser markup for self-generated HTML. Add a Popular links sidebar view backed by meta.popular_links so curated links render without Firebase. Expand compact list rows asynchronously to keep UV/Scramjet responsive. Vendor Chart.js locally and lazy-load Firebase on the statistics page with proxy-safe asset URLs. Co-authored-by: yourworstnightmare_ --- docs/data-loader.js | 65 +++++++++++ docs/index.html | 219 +++++++++++++++++++++++++++++++---- docs/stats/index.html | 5 +- docs/stats/stats.js | 92 ++++++++++++--- docs/unsorted.html | 10 +- docs/vendor/chart.umd.min.js | 20 ++++ 6 files changed, 367 insertions(+), 44 deletions(-) create mode 100644 docs/vendor/chart.umd.min.js diff --git a/docs/data-loader.js b/docs/data-loader.js index 188f3d2..f79634d 100644 --- a/docs/data-loader.js +++ b/docs/data-loader.js @@ -244,6 +244,69 @@ return out; } + function yieldToMainThread() { + return new Promise(function (resolve) { + if (typeof requestAnimationFrame === "function") { + requestAnimationFrame(function () { + setTimeout(resolve, 0); + }); + } else { + setTimeout(resolve, 0); + } + }); + } + + /** Expand compact rows in chunks so web proxies and low-end devices stay responsive. */ + async function expandAllLinksAsync(payload, options) { + if (!isCompactPayload(payload)) { + return Array.isArray(payload) ? payload : payload.links || []; + } + var opts = Object.assign({ shareProviderArrays: true, yieldEvery: 500 }, options || {}); + var total = payload.links.length; + var out = new Array(total); + var step = Math.max(50, Number(opts.yieldEvery) || 500); + for (var i = 0; i < total; i++) { + out[i] = expandRow(payload, payload.links[i], opts); + if (i > 0 && i % step === 0) { + if (typeof opts.onProgress === "function") { + try { + opts.onProgress(i / total); + } catch (_) {} + } + await yieldToMainThread(); + } + } + if (typeof opts.onProgress === "function") { + try { + opts.onProgress(1); + } catch (_) {} + } + return out; + } + + async function fetchJsonAsset(name, options) { + var opts = options || {}; + var base = opts.baseUrl != null ? opts.baseUrl : listAssetBaseUrl(); + var timeoutMs = opts.timeoutMs != null ? opts.timeoutMs : 20000; + var errors = []; + var candidates = listAssetUrlCandidates(name, base); + for (var i = 0; i < candidates.length; i++) { + var url = candidates[i]; + try { + var res = await fetchWithTimeout(url, opts.fetchInit || {}, timeoutMs); + if (!res.ok) { + errors.push(url + " HTTP " + res.status); + continue; + } + return readJsonResponse(res); + } catch (err) { + errors.push(url + ": " + (err && err.message ? err.message : String(err))); + } + } + var detail = errors.length ? errors.join("; ") : "no URLs attempted"; + throw new Error("Could not load " + name + " (" + detail + ")"); + } + function linkCount(normalized) { if (normalized.compact) return normalized.compact.links.length; return (normalized.links || []).length; @@ -330,6 +393,8 @@ normalizePayload: normalizePayload, resolveExpandedLinks: resolveExpandedLinks, expandAllLinks: expandAllLinks, + expandAllLinksAsync: expandAllLinksAsync, + fetchJsonAsset: fetchJsonAsset, linkCount: linkCount, isRelativeUrl: isRelativeUrl, directoryOfSrc: directoryOfSrc, diff --git a/docs/index.html b/docs/index.html index 3f41536..3d24a5a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2087,6 +2087,10 @@

Proxy list

Unsorted links +