// Seeded PRNG (mulberry32) function mulberry32(seed) { return function() { seed |= 0; seed = seed + 0x6D2B79F5 | 0; let t = Math.imul(seed ^ seed >>> 15, 1 | seed); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296; }; } const rng = mulberry32(Date.now()); const loadedBundles = new Set(); const container = document.getElementById("tab-container"); const loadingEl = document.getElementById("loading"); // Detect browser and set class on body for tab styling function detectBrowser() { const ua = navigator.userAgent; if (ua.includes("Firefox")) return "firefox"; if (ua.includes("Safari") && !ua.includes("Chrome")) return "safari"; return "chrome"; // Chrome, Edge, Opera, Brave, etc. } document.body.classList.add(`browser-${detectBrowser()}`); // How many tabs fit in one row? function tabsPerRow() { const tabWidth = 160; // avg tab width in px (min 100, max 200) return Math.ceil(window.innerWidth / tabWidth) + 4; // extra for marquee overflow } // How many rows fill the viewport? function rowsPerScreen() { const rowHeight = 40; return Math.ceil(window.innerHeight / rowHeight) + 4; // buffer } // Pick a random bundle index we haven't loaded yet function pickBundle() { if (loadedBundles.size >= TOTAL_BUNDLES) return -1; let idx; do { idx = Math.floor(rng() * TOTAL_BUNDLES); } while (loadedBundles.has(idx)); loadedBundles.add(idx); return idx; } // Fetch a bundle JSON async function fetchBundle(idx) { const padded = String(idx).padStart(4, "0"); const resp = await fetch(`tabs/${padded}.json`); if (!resp.ok) throw new Error(`Failed to fetch bundle ${padded}`); const data = await resp.json(); return data.entries; } // Create a tab DOM element function createTab(entry) { const tab = document.createElement("div"); tab.className = "tab"; if (!entry.iframe_ok) { tab.classList.add("tab-external"); } if (entry._isAbout) { // About tab gets a rotating icon like the site favicon const img = document.createElement("img"); img.className = "tab-icon"; img.alt = ""; aboutIcons.push(img); tab.appendChild(img); } else if (entry.icon) { const img = document.createElement("img"); img.className = "tab-icon"; img.src = `data:image/png;base64,${entry.icon}`; img.alt = ""; img.loading = "lazy"; tab.appendChild(img); if (loadedIcons.length < 100) loadedIcons.push(entry.icon); } const title = document.createElement("span"); title.className = "tab-title"; title.textContent = entry.title || entry.url; tab.appendChild(title); tab.title = entry.title || entry.url; // Click handler tab.addEventListener("click", () => { if (entry._isAbout) { openInlineViewer(tab, entry, "/about.html"); return; } if (entry.iframe_ok) { openInlineViewer(tab, entry, entry.url); } else { window.open(entry.url, "_blank", "noopener"); } }); return tab; } // Create a row of tabs with marquee animation function createRow(entries, rowIndex) { const row = document.createElement("div"); row.className = "tab-row"; const speed = 90 + (rng() * 60); // 90-150s per cycle row.style.setProperty("--speed", `${speed}s`); // Random direction const goLeft = rng() > 0.5; row.classList.add(goLeft ? "scroll-left" : "scroll-right"); // Stagger start so rows aren't synchronized row.style.animationDelay = `${-rng() * speed}s`; // Add tabs twice so the marquee loops seamlessly (translate -50% = one full set) for (let copy = 0; copy < 2; copy++) { for (const entry of entries) { row.appendChild(createTab(entry)); } } return row; } // Render entries into rows function renderEntries(entries) { const perRow = tabsPerRow(); let rowIndex = container.children.length; for (let i = 0; i < entries.length; i += perRow) { const rowEntries = entries.slice(i, i + perRow); if (rowEntries.length < 3) continue; // skip tiny last rows container.appendChild(createRow(rowEntries, rowIndex)); rowIndex++; } } // Load enough bundles to fill the screen async function loadMore() { const neededRows = rowsPerScreen(); const entriesNeeded = neededRows * tabsPerRow(); let entries = []; while (entries.length < entriesNeeded) { const idx = pickBundle(); if (idx === -1) break; try { const bundleEntries = await fetchBundle(idx); entries = entries.concat(bundleEntries); } catch (e) { console.error("Failed to load bundle:", e); } } if (entries.length > 0) { // Inject the "About EveryTab" tab at a random position in the first load if (container.children.length === 0) { const aboutEntry = { url: "https://everytab.site/about.html", title: "About EveryTab", icon: "", iframe_ok: true, _isAbout: true, }; const pos = Math.floor(rng() * Math.min(entries.length, tabsPerRow() * 3)); entries.splice(pos, 0, aboutEntry); } renderEntries(entries); loadingEl.style.display = "none"; } } // Rotating site favicon — picks a random loaded icon every second const faviconLink = document.createElement("link"); faviconLink.rel = "icon"; faviconLink.type = "image/png"; document.head.appendChild(faviconLink); const loadedIcons = []; const aboutIcons = []; // img elements for "About" tabs — rotate their icons too setInterval(() => { if (loadedIcons.length === 0) return; const icon = loadedIcons[Math.floor(Math.random() * loadedIcons.length)]; const dataUri = `data:image/png;base64,${icon}`; faviconLink.href = dataUri; for (const img of aboutIcons) { img.src = dataUri; } }, 1000); // Infinite scroll let loading = false; window.addEventListener("scroll", async () => { if (loading) return; const scrollBottom = window.innerHeight + window.scrollY; const docHeight = document.documentElement.scrollHeight; if (scrollBottom >= docHeight - 500) { loading = true; await loadMore(); loading = false; } }); // Inline iframe viewer let activeViewer = null; let activeTab = null; function openInlineViewer(tabEl, entry, url) { // Close existing viewer closeInlineViewer(); // Mark tab as active tabEl.classList.add("tab-active"); activeTab = tabEl; // Find the row this tab belongs to const row = tabEl.closest(".tab-row"); // Build the viewer const viewer = document.createElement("div"); viewer.className = "iframe-viewer"; const header = document.createElement("div"); header.className = "iframe-header"; if (entry.icon) { const icon = document.createElement("img"); icon.className = "tab-icon"; icon.src = `data:image/png;base64,${entry.icon}`; header.appendChild(icon); } const title = document.createElement("span"); title.className = "tab-title"; title.textContent = entry.title || entry.url; header.appendChild(title); const link = document.createElement("a"); link.href = url; link.target = "_blank"; link.rel = "noopener"; link.textContent = entry.url + " ↗"; header.appendChild(link); const close = document.createElement("button"); close.className = "iframe-close"; close.textContent = "✕"; close.addEventListener("click", closeInlineViewer); header.appendChild(close); const iframe = document.createElement("iframe"); iframe.sandbox = "allow-scripts allow-same-origin allow-forms"; iframe.src = url; viewer.appendChild(header); viewer.appendChild(iframe); // Insert after the row row.after(viewer); activeViewer = viewer; // Scroll so the viewer is visible viewer.scrollIntoView({ behavior: "smooth", block: "start" }); } function closeInlineViewer() { if (activeViewer) { activeViewer.remove(); activeViewer = null; } if (activeTab) { activeTab.classList.remove("tab-active"); activeTab = null; } } document.addEventListener("keydown", (e) => { if (e.key === "Escape") closeInlineViewer(); }); // Initial load loadMore();