(() => { const urlInput = document.getElementById('urlInput'); const lazyLoad = document.getElementById('lazyLoad'); const openBtn = document.getElementById('openBtn'); const extractBtn = document.getElementById('extractBtn'); const urlCount = document.getElementById('urlCount'); const status = document.getElementById('status'); const modeBadge = document.getElementById('modeBadgeInner'); // ── Helpers ──────────────────────────────────────────────── function parseURLs(text) { return text .split(/\r?\n/) .map(l => l.trim()) .filter(l => { if (!l) return false; try { new URL(l); return true; } catch { return false; } }); } // Extracts every URL from arbitrary text (emails, HTML, markdown, prose…) function extractURLs(text) { // Match http/https URLs, including those wrapped in <>, (), [], quotes, or at end of sentence const raw = text.match(/https?:\/\/[^\s<>"')\]},;]+/gi) || []; // Deduplicate while preserving order const seen = new Set(); return raw .map(u => { // Strip common trailing punctuation that isn't part of the URL return u.replace(/[.,;:!?)"'\]}>]+$/, ''); }) .filter(u => { if (seen.has(u)) return false; seen.add(u); try { new URL(u); return true; } catch { return false; } }); } function setStatus(msg, type = '') { status.className = 'status' + (type ? ' ' + type : ''); status.innerHTML = msg ? `${msg}` : ''; } function updateCount() { const urls = parseURLs(urlInput.value); const n = urls.length; urlCount.textContent = n === 1 ? '1 URL' : `${n} URLs`; urlCount.className = 'url-count' + (n > 0 ? ' has-urls' : ''); openBtn.disabled = n === 0; } function updateBadge() { if (lazyLoad.checked) { modeBadge.textContent = 'lazy'; modeBadge.className = 'mode-badge'; } else { modeBadge.textContent = 'eager'; modeBadge.className = 'mode-badge off'; } } // ── Event listeners ──────────────────────────────────────── urlInput.addEventListener('input', () => { updateCount(); setStatus(''); }); lazyLoad.addEventListener('change', updateBadge); extractBtn.addEventListener('click', () => { const urls = extractURLs(urlInput.value); if (!urls.length) { setStatus('No URLs found in text', 'error'); return; } urlInput.value = urls.join('\n'); updateCount(); setStatus(`Extracted ${urls.length} URL${urls.length !== 1 ? 's' : ''}`, 'success'); // Brief visual flash on the button extractBtn.classList.add('flash'); setTimeout(() => extractBtn.classList.remove('flash'), 600); }); openBtn.addEventListener('click', async () => { const urls = parseURLs(urlInput.value); if (!urls.length) return; openBtn.disabled = true; setStatus(''); const useLazy = lazyLoad.checked; let opened = 0; for (const url of urls) { try { let targetUrl = url; if (useLazy) { // Build the extension loader URL with the target as ?url=… const loaderBase = chrome.runtime.getURL('loader.html'); targetUrl = `${loaderBase}?url=${encodeURIComponent(url)}`; } await chrome.tabs.create({ url: targetUrl, active: false }); opened++; } catch (err) { console.warn('Failed to open', url, err); } } updateCount(); if (opened === urls.length) { setStatus(`Opened ${opened} tab${opened !== 1 ? 's' : ''}`, 'success'); } else { setStatus(`Opened ${opened} / ${urls.length} tabs`, 'error'); } }); // ── Init ─────────────────────────────────────────────────── updateCount(); updateBadge(); urlInput.focus(); })();