(function() { // Prevent multiple instances from running at once if (window.immoralFontVacuum) { alert('Web Font Vacuum is already running!'); return; } window.immoralFontVacuum = true; const logCollector = { logs: [], group: function(label) { this.logs.push(`\n### ${label}`); }, groupEnd: function() { this.logs.push(`### End Group\n`); }, log: function(...args) { this.logs.push(args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : arg ).join(' ')); }, warn: function(...args) { this.logs.push(`⚠️ ${args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : arg ).join(' ')}`); }, error: function(...args) { this.logs.push(`❌ ${args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : arg ).join(' ')}`); }, getReport: function() { return `Font Vacuum Report ================== Time: ${new Date().toISOString()} URL: ${window.location.href} ${this.logs.join('\n')}`; } }; const styleRoot = document.createElement('div'); styleRoot.className = 'fv-root'; styleRoot.style.all = 'initial'; // Reset all styles const style = document.createElement('style'); style.textContent = ` .fv-root { font: 16px system-ui, -apple-system, sans-serif; color: #333333; line-height: 1.4; box-sizing: border-box; } .fv-root * { box-sizing: inherit; font-family: inherit; line-height: inherit; color: inherit; } .fv-container { position: fixed; top: 20px; right: 20px; width: 400px; max-height: 90vh; background: #f5f5f5; z-index: 999999; overflow-y: auto; display: flex; flex-direction: column; border: 3px solid #333; box-shadow: 8px 8px 0 #ff4d00; } .fv-header { padding: 1rem; background: #333333; color: #f5f5f5; display: flex; justify-content: space-between; align-items: center; cursor: move; user-select: none; flex-shrink: 0; } .fv-header h1 { margin: 0; font-size: 1.1rem; line-height: 1; } .fv-close { background: none; border: none; color: #f5f5f5; cursor: pointer; font-size: 1.5rem; padding: 0; margin: 0; line-height: 1; display: flex; align-items: center; } .fv-content { padding: 1rem; overflow-y: auto; flex-grow: 1; } .fv-footer { padding: 0.75rem 1rem; background: #333333; color: #f5f5f5; display: flex; justify-content: flex-end; align-items: center; flex-shrink: 0; } .fv-footer-button { background: #555; color: #f5f5f5; border: none; padding: 0.5rem 1rem; cursor: pointer; font-size: 0.9em; display: flex; align-items: center; gap: 0.5rem; } .fv-footer-button:hover { background: #666; } .fv-font-item { margin-bottom: 1rem; padding: 1rem; border: 1px solid #ddd; background: #ffffff; } .fv-font-item h3 { margin: 0 0 1rem 0; padding: 0; font-size: 1.1em; font-weight: 600; } .fv-preview { margin: 1rem 0; padding: 1rem; border: 1px dashed #333; background: #ffffff; } .fv-button { background: #333; color: #f5f5f5; border: none; padding: 0.5rem 1rem; cursor: pointer; margin: 0.25rem 0.5rem 0.25rem 0; font-size: 0.9em; } .fv-button:last-child { margin-right: 0; } .fv-button:hover { background: #444; } `; styleRoot.appendChild(style); document.body.appendChild(styleRoot); const container = document.createElement('div'); container.className = 'fv-container'; styleRoot.appendChild(container); let isDragging = false; let currentX; let currentY; let initialX; let initialY; let xOffset = 0; let yOffset = 0; const header = document.createElement('header'); header.className = 'fv-header'; header.innerHTML = `

Web Font Vacuum

`; header.addEventListener('mousedown', dragStart); document.addEventListener('mousemove', drag); document.addEventListener('mouseup', dragEnd); function dragStart(e) { initialX = e.clientX - xOffset; initialY = e.clientY - yOffset; if (e.target === header) { isDragging = true; } } function drag(e) { if (isDragging) { e.preventDefault(); currentX = e.clientX - initialX; currentY = e.clientY - initialY; xOffset = currentX; yOffset = currentY; container.style.transform = `translate(${currentX}px, ${currentY}px)`; } } function dragEnd() { isDragging = false; } header.querySelector('.fv-close').addEventListener('click', () => { document.body.removeChild(styleRoot); window.immoralFontVacuum = false; }); const content = document.createElement('div'); content.className = 'fv-content'; function extractFontUrls(cssText, baseUrl) { const fontUrls = []; const fontFaceRegex = /@font-face\s*{[^}]*}/g; const urlRegex = /url\(['"]?([^'"\)]+)['"]?\)/g; const fontFamilyRegex = /font-family\s*:\s*['"]?([^'";]*)['"]?/; function resolveUrl(url, base) { try { // Protocol-relative URLs if (url.startsWith('//')) { return `${location.protocol}${url}`; } // Absolute URLs if (url.match(/^https?:\/\//)) { return url; } // Root-relative URLs if (url.startsWith('/')) { return `${location.origin}${url}`; } // Relative URLs - use stylesheet URL as base if available return new URL(url, base || location.href).href; } catch (e) { console.warn('Failed to resolve URL:', url, e); return url; } } let fontFaceMatch; while ((fontFaceMatch = fontFaceRegex.exec(cssText)) !== null) { const fontFaceBlock = fontFaceMatch[0]; const familyMatch = fontFaceBlock.match(fontFamilyRegex); const fontFamily = familyMatch ? familyMatch[1].trim() : 'Unknown Font'; let urlMatch; while ((urlMatch = urlRegex.exec(fontFaceBlock)) !== null) { let fontUrl = urlMatch[1].trim(); // Skip data: URLs if (fontUrl.startsWith('data:')) continue; // Only process known font file types if (!fontUrl.match(/\.(woff2?|ttf|otf|eot)(\?.*)?$/i)) continue; // Resolve the URL relative to the stylesheet's URL fontUrl = resolveUrl(fontUrl, baseUrl); fontUrls.push({ family: fontFamily, url: fontUrl, filename: fontUrl.split('/').pop().split('?')[0], cssRule: fontFaceBlock }); } } return fontUrls; } function findFonts() { const fonts = new Map(); logCollector.group('Font Vacuum: Scanning Stylesheets'); logCollector.log(`Found ${document.styleSheets.length} stylesheets`); for (const sheet of document.styleSheets) { try { const baseUrl = sheet.href; logCollector.group(`Stylesheet: ${baseUrl || 'inline'}`); const cssRules = sheet.cssRules || sheet.rules; logCollector.log(`- Rules found: ${cssRules.length}`); let cssText = ''; let fontFaceCount = 0; for (const rule of cssRules) { if (rule.constructor.name === 'CSSFontFaceRule') { fontFaceCount++; } cssText += rule.cssText + '\n'; } logCollector.log(`- @font-face rules found: ${fontFaceCount}`); const fontUrls = extractFontUrls(cssText, baseUrl); logCollector.log(`- Font URLs extracted: ${fontUrls.length}`); fontUrls.forEach(font => { logCollector.log(` • ${font.family}: ${font.url}`); if (!fonts.has(font.family)) { fonts.set(font.family, { variants: [], cssRule: font.cssRule }); } fonts.get(font.family).variants.push(font); }); logCollector.groupEnd(); } catch (e) { logCollector.warn(`Could not access stylesheet:`, sheet.href, e); logCollector.groupEnd(); } } const results = Array.from(fonts.entries()).map(([family, data]) => ({ family, variants: data.variants, cssRule: data.cssRule })); logCollector.log('Final Results:', { totalFamilies: results.length, families: results.map(f => ({ family: f.family, variants: f.variants.length, urls: f.variants.map(v => v.url) })) }); logCollector.groupEnd(); return results; } async function downloadFont(url, filename) { try { logCollector.group(`Font Vacuum: Downloading ${filename} from ${url}`); logCollector.log('Searching for existing font-face rule...'); const existingFontRule = Array.from(document.styleSheets) .flatMap(sheet => { try { return Array.from(sheet.cssRules); } catch (e) { return []; } }) .find(rule => rule.constructor.name === 'CSSFontFaceRule' && rule.cssText.includes(url) ); logCollector.log('Existing font-face rule found:', !!existingFontRule); let response; if (existingFontRule) { logCollector.log('Attempting to fetch using existing rule credentials...'); const fontBlob = await fetch(url, { mode: 'cors', credentials: 'include', headers: { 'Origin': window.location.origin } }).then(r => r.blob()); response = new Response(fontBlob); } else { logCollector.log('No existing rule found, attempting direct fetch...'); response = await fetch(url, { mode: 'cors', credentials: 'include', headers: { 'Origin': window.location.origin } }); } if (!response.ok) { throw new Error(`Network response was not ok. Status: ${response.status}`); } logCollector.log('Font fetched successfully, preparing download...'); const blob = await response.blob(); logCollector.log('Font blob size:', blob.size, 'bytes'); const objectUrl = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = objectUrl; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); setTimeout(() => URL.revokeObjectURL(objectUrl), 100); logCollector.log('Download initiated successfully'); logCollector.groupEnd(); return true; } catch (error) { logCollector.error('Error downloading font:', error); logCollector.groupEnd(); alert(`Error downloading font: ${error.message}\n\nTroubleshooting tips:\n1. Check the console for detailed logs\n2. Try using your browser's developer tools Network tab to find and download the font file directly\n3. Some sites may block direct font downloads`); return false; } } async function previewFont(url, fontFamily) { try { logCollector.group(`Font Vacuum: Previewing ${fontFamily} from ${url}`); const existingFontRule = Array.from(document.styleSheets) .flatMap(sheet => { try { return Array.from(sheet.cssRules); } catch (e) { return []; } }) .find(rule => rule.constructor.name === 'CSSFontFaceRule' && rule.cssText.includes(url) ); if (existingFontRule) { logCollector.log('Using existing font-face rule for preview'); logCollector.groupEnd(); return true; } logCollector.log('No existing rule found, attempting to load font...'); const response = await fetch(url, { mode: 'cors', credentials: 'include', headers: { 'Origin': window.location.origin } }); if (!response.ok) { throw new Error(`Network response was not ok. Status: ${response.status}`); } const blob = await response.blob(); logCollector.log('Font blob size:', blob.size, 'bytes'); const fontUrl = URL.createObjectURL(blob); const fontFace = new FontFace(fontFamily, `url(${fontUrl})`, { style: 'normal', weight: '400', display: 'swap' }); const loadedFont = await fontFace.load(); document.fonts.add(loadedFont); URL.revokeObjectURL(fontUrl); logCollector.log('Font loaded successfully'); logCollector.groupEnd(); return true; } catch (error) { logCollector.error('Error loading font:', error); logCollector.groupEnd(); return false; } } const fonts = findFonts(); if (fonts.length === 0) { content.innerHTML = '

No web fonts found on this page.

'; } else { fonts.forEach(fontData => { const fontItem = document.createElement('div'); fontItem.className = 'fv-font-item'; const fontName = document.createElement('h3'); fontName.style.margin = '0 0 1rem 0'; fontName.textContent = fontData.family; fontItem.appendChild(fontName); const preview = document.createElement('div'); preview.className = 'fv-preview'; preview.innerHTML = '0123456789

Society for me my misery
Since Gift of Thee --

The quick brown fox jumps over the lazy dog!?'; fontItem.appendChild(preview); const uniqueDownloads = new Map(); fontData.variants.forEach(variant => { if (!uniqueDownloads.has(variant.url)) { uniqueDownloads.set(variant.url, { filename: variant.filename, url: variant.url }); } }); const buttonContainer = document.createElement('div'); buttonContainer.style.marginTop = '1rem'; uniqueDownloads.forEach(({filename, url}) => { const downloadBtn = document.createElement('button'); downloadBtn.className = 'fv-button'; downloadBtn.textContent = `⬇ Download ${filename}`; downloadBtn.addEventListener('click', () => downloadFont(url, filename)); buttonContainer.appendChild(downloadBtn); }); fontItem.appendChild(buttonContainer); fontData.variants.forEach(async (variant) => { if (await previewFont(variant.url, fontData.family)) { preview.style.fontFamily = fontData.family; if (variant.cssRule) { const fontStyle = variant.cssRule.match(/font-style:\s*([^;]+)/); const fontWeight = variant.cssRule.match(/font-weight:\s*([^;]+)/); if (fontStyle) preview.style.fontStyle = fontStyle[1]; if (fontWeight) preview.style.fontWeight = fontWeight[1]; } } }); content.appendChild(fontItem); }); } const footer = document.createElement('div'); footer.className = 'fv-footer'; const reportBtn = document.createElement('button'); reportBtn.className = 'fv-footer-button'; reportBtn.innerHTML = '📋Copy Debug Report'; reportBtn.addEventListener('click', () => { const report = logCollector.getReport(); navigator.clipboard.writeText(report).then(() => { reportBtn.innerHTML = 'Report Copied!'; setTimeout(() => { reportBtn.innerHTML = '📋Copy Debug Report'; }, 2000); }); }); footer.appendChild(reportBtn); container.appendChild(header); container.appendChild(content); container.appendChild(footer); styleRoot.appendChild(container); })();