spb/social-runtime-crawler
Public
TypeScript 91.8%
HTML 3.2%
JavaScript 3%
SQL 1.4%
CSS 0.7%
1// In-page DOM snapshot. Kept as plain JavaScript (not TS) so no bundler helper (`__name`) leaks into the page.2// Evaluated by PageSummarizer.ts as `(SOURCE)(MAX)`; must remain a single function expression.3(MAX) => {4 const clean = (s) => (s ?? "").replace(/\s+/g, " ").trim();5 const vh = window.innerHeight;6 const regionOf = (node) => {7 let el = node;8 let hops = 0;9 while (el && hops++ < 30) {10 const role = el.getAttribute("role") ?? "";11 const tag = el.tagName.toLowerCase();12 if (role === "dialog" || el.getAttribute("aria-modal") === "true") return "dialog";13 if (tag === "ytd-comments" || /comment/i.test(el.id) || role === "comment") return "comments";14 if (role === "feed") return "feed";15 if (role === "main" || tag === "main" || el.id === "primary" || el.id === "contents" || el.id === "main-content") return "main";16 if (role === "navigation" || tag === "nav" || el.id === "guide") return "navigation";17 if (role === "complementary" || tag === "aside" || el.id === "secondary" || el.id === "related") return "sidebar";18 if (tag === "header" || role === "banner" || el.id === "masthead") return "header";19 el = el.parentElement;20 }21 return "unknown";22 };23 const cssPath = (el) => {24 const href = el.getAttribute("href");25 const tag = el.tagName.toLowerCase();26 if (href) return `${tag}[href="${href.replace(/"/g, '\\"')}"]`;27 const aria = el.getAttribute("aria-label");28 if (aria) return `${tag}[aria-label="${aria.replace(/"/g, '\\"').slice(0, 80)}"]`;29 if (el.id) return `#${CSS.escape(el.id)}`;30 const parts = [];31 let cur = el;32 let hops = 0;33 while (cur && hops++ < 6 && cur !== document.body) {34 const p = cur.parentElement;35 const idx = p ? Array.from(p.children).indexOf(cur) + 1 : 1;36 parts.unshift(`${cur.tagName.toLowerCase()}:nth-child(${idx})`);37 cur = p;38 }39 return parts.join(">");40 };41 const isVisible = (el) => {42 const r = el.getBoundingClientRect();43 const st = getComputedStyle(el);44 return r.width > 0 && r.height > 0 && st.visibility !== "hidden" && st.display !== "none";45 };46 const cardMeta = (el) => {47 let card = el;48 let hops = 0;49 const isCard = (n) => n.tagName === "ARTICLE" || n.getAttribute("role") === "article" || n.tagName === "LI" || /-(renderer|item|lockup|post)$|^shreddit-post$|view-model$/i.test(n.tagName) || /(^|\s)(card|post|feed-item|lockup)(\s|$)/i.test(typeof n.className === "string" ? n.className : "");50 while (card && hops++ < 6 && !isCard(card)) card = card.parentElement;51 // Only trust a card scope of bounded size; a huge container would bleed another item's numbers into this one.52 const scope = card && clean(card.textContent).length < 1500 ? card : el;53 const text = clean(scope.textContent).slice(0, 1200);54 const out = {};55 const dur = text.match(/(?:^|\s)(\d{1,2}:\d{2}(?::\d{2})?)(?=\s|$)/);56 if (dur) out.duration = dur[1];57 const views = text.match(/([\d.,\s]+\s?[kKmMbB]?)\s*(vues|views|visionnements|upvotes|points|votes|likes|j'aime)/i);58 if (views) out.views = views[0];59 const comments = text.match(/([\d.,\s]+\s?[kKmMbB]?)\s*(comment|commentaire|repl|répon)/i);60 if (comments) out.comments = comments[0];61 const ago = text.match(/(il y a\s+[\w\s]+?|\d+\s+(seconds?|minutes?|hours?|days?|weeks?|months?|years?|h|min|j|sem|mois|ans?)\s+ago|\b\d+\s*(h|min|j|d|w|mo|y)\b)/i);62 if (ago) out.published = ago[0];63 const authorLink = scope.querySelector('a[href*="/@"], a[href^="/user/"], a[href^="/u/"], a[href*="/channel/"], a[href^="/c/"], a[href*="/r/"]');64 if (authorLink && authorLink !== el) out.author = clean(authorLink.textContent) || (authorLink.getAttribute("href") ?? "");65 return out;66 };6768 const seen = new Set();69 const candidates = [];70 let index = 0;71 const push = (c) => {72 const key = c.kind + "|" + (c.href ?? "") + "|" + c.text.slice(0, 80);73 if (seen.has(key)) return;74 seen.add(key);75 candidates.push({ ...c, index: index++ });76 };7778 for (const a of Array.from(document.querySelectorAll("a[href]"))) {79 if (candidates.length >= MAX) break;80 if (!isVisible(a)) continue;81 const href = a.href;82 if (!href || href.startsWith("javascript:")) continue;83 const img = a.querySelector("img");84 const text = clean(a.textContent) || clean(a.getAttribute("aria-label")) || clean(a.getAttribute("title")) || clean(img ? img.alt : "");85 if (!text && !/watch|shorts|comments|status|reel|video|\/p\//.test(href)) continue;86 const r = a.getBoundingClientRect();87 push({ kind: "link", href, text: text.slice(0, 200), aria_label: clean(a.getAttribute("aria-label")) || undefined, role: a.getAttribute("role") ?? undefined, region: regionOf(a), visible: r.top < vh && r.bottom > 0, top: Math.round(r.top + window.scrollY), meta: cardMeta(a), locator_hint: cssPath(a) });88 }89 for (const art of Array.from(document.querySelectorAll("article,[role=article],shreddit-post,ytd-rich-item-renderer,ytd-video-renderer,ytd-compact-video-renderer,ytd-reel-item-renderer,ytd-comment-thread-renderer"))) {90 if (candidates.length >= MAX) break;91 if (!isVisible(art)) continue;92 const r = art.getBoundingClientRect();93 const link = art.querySelector("a[href]");94 push({ kind: "article", href: link ? link.href : undefined, text: clean(art.textContent).slice(0, 300), role: art.getAttribute("role") ?? art.tagName.toLowerCase(), region: regionOf(art), visible: r.top < vh && r.bottom > 0, top: Math.round(r.top + window.scrollY), meta: cardMeta(art), locator_hint: cssPath(link ?? art) });95 }96 for (const h of Array.from(document.querySelectorAll("h1,h2,[role=heading]")).slice(0, 30)) {97 if (!isVisible(h)) continue;98 const r = h.getBoundingClientRect();99 const text = clean(h.textContent);100 if (!text) continue;101 push({ kind: "heading", text: text.slice(0, 200), region: regionOf(h), visible: r.top < vh && r.bottom > 0, top: Math.round(r.top + window.scrollY), locator_hint: cssPath(h) });102 }103 for (const s of Array.from(document.querySelectorAll('input[type=search],input[name*=search i],input[placeholder*=search i],input[placeholder*=recherch i],[role=searchbox],[role=combobox][aria-label*=search i],[role=combobox][aria-label*=recherch i]'))) {104 if (!isVisible(s)) continue;105 const r = s.getBoundingClientRect();106 push({ kind: "search", text: clean(s.getAttribute("placeholder") ?? s.getAttribute("aria-label")) || "search", region: regionOf(s), visible: r.top < vh, top: Math.round(r.top + window.scrollY), locator_hint: cssPath(s) });107 }108 for (const b of Array.from(document.querySelectorAll("button,[role=button],summary,[aria-expanded]"))) {109 if (candidates.length >= MAX) break;110 if (!isVisible(b)) continue;111 const label = clean(b.getAttribute("aria-label")) || clean(b.textContent);112 if (!/more|plus|show|afficher|voir|replies|réponses|comments|commentaires|expand|load|charger|read more|lire la suite|\.\.\./i.test(label)) continue;113 const r = b.getBoundingClientRect();114 push({ kind: "button", text: label.slice(0, 120), aria_label: clean(b.getAttribute("aria-label")) || undefined, role: b.getAttribute("role") ?? "button", region: regionOf(b), visible: r.top < vh && r.bottom > 0, top: Math.round(r.top + window.scrollY), locator_hint: cssPath(b) });115 }116117 const videos = Array.from(document.querySelectorAll("video")).map((v) => ({118 src: v.getAttribute("src") ?? undefined,119 current_src: v.currentSrc || undefined,120 duration: Number.isFinite(v.duration) ? v.duration : undefined,121 width: v.videoWidth,122 height: v.videoHeight,123 playing: !v.paused && !v.ended && v.readyState > 2,124 poster: v.poster || undefined,125 }));126127 const main = document.querySelector("main,[role=main],#primary,#main-content,#contents") ?? document.body;128 const landmarks = Array.from(document.querySelectorAll("[role],main,nav,aside,header,footer,form")).map((e) => e.getAttribute("role") ?? e.tagName.toLowerCase());129 const h1 = document.querySelector("h1");130 return {131 url: location.href,132 title: document.title,133 lang: document.documentElement.lang || undefined,134 h1: clean(h1 ? h1.textContent : "") || undefined,135 landmarks: Array.from(new Set(landmarks)).slice(0, 25),136 candidates,137 video_elements: videos,138 text_excerpt: clean(main.textContent).slice(0, 600),139 scroll: { y: window.scrollY, height: document.documentElement.scrollHeight, viewport: vh },140 has_login_form: !!document.querySelector("input[type=password]"),141 dialog_open: !!document.querySelector("[role=dialog]:not([aria-hidden=true]),[aria-modal=true]"),142 };143}144