diff options
author | elioat <elioat@tilde.institute> | 2025-03-15 21:08:52 -0400 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2025-03-15 21:08:52 -0400 |
commit | 7b380bc6f12771c07f7c70b6a9c747fd6703f92e (patch) | |
tree | 6e95a0e1b93a80825025bd281efe70da4488f17d | |
parent | cd6f63bbf351e458f15613e11839bddc17bc5157 (diff) | |
download | tour-7b380bc6f12771c07f7c70b6a9c747fd6703f92e.tar.gz |
*
-rw-r--r-- | html/web-font-vacuum/app.js | 195 |
1 files changed, 85 insertions, 110 deletions
diff --git a/html/web-font-vacuum/app.js b/html/web-font-vacuum/app.js index 2dc91ff..8a04356 100644 --- a/html/web-font-vacuum/app.js +++ b/html/web-font-vacuum/app.js @@ -44,6 +44,8 @@ const CORS_PROXIES = [ // Keep track of which proxy worked last let lastWorkingProxyIndex = 0; +let proxyFailureCount = 0; +const MAX_PROXY_FAILURES = 3; async function fetchWithProxies(url, attempt = 0, isBinary = false) { // Start with the last working proxy @@ -59,29 +61,31 @@ async function fetchWithProxies(url, attempt = 0, isBinary = false) { const fetchOptions = { headers: { - 'Accept': isBinary ? '*/*' : 'text/html,application/xhtml+xml,text/css' - } + 'Accept': isBinary ? '*/*' : 'text/html,application/xhtml+xml,text/css', + 'Origin': window.location.origin + }, + mode: 'cors' }; - // For binary data, set responseType to arraybuffer if supported - if (isBinary) { - fetchOptions.mode = 'cors'; - } - const response = await fetch(proxy.urlFormatter(url), fetchOptions); if (response.ok) { lastWorkingProxyIndex = proxyIndex; + proxyFailureCount = 0; return response; } } catch (error) { console.log(`Proxy ${proxy.name} failed:`, error); - // Continue to next proxy + proxyFailureCount++; + + // If we've had too many failures, wait a bit before continuing + if (proxyFailureCount >= MAX_PROXY_FAILURES) { + await new Promise(resolve => setTimeout(resolve, 1000)); + proxyFailureCount = 0; + } } } - // DO NOT PASS GO! DO NOT COLLECT $200 DOLLARS - // All proxies failed throw new Error('All proxies failed to fetch the resource'); } @@ -350,7 +354,7 @@ document.addEventListener('DOMContentLoaded', () => { if (fontUrls.size === 0) { resultsDiv.innerHTML = '<p>No web fonts (WOFF/TTF/WOFF2/OTF) were found on this page.</p>'; } else { - displayFontUrls(Array.from(fontUrls)); + displayFontUrls(fontUrls); } } catch (error) { @@ -372,78 +376,71 @@ document.addEventListener('DOMContentLoaded', () => { const response = await fetchWithProxies(cssUrl); const css = await response.text(); - // Use the CSS URL as the base URL for resolving font URLs + // Extract font URLs from the CSS content + extractFontUrlsFromCss(css, cssUrl, fontUrls); } catch (error) { console.error(`Error processing CSS from ${cssUrl}:`, error); } } /** - * Extracts font URLs from CSS by the power of regex. - * - Font URLs - * - Font family names - * - Full CSS rules for reference + * Extracts font URLs from CSS content * - * @param {string} css - The CSS content to parse - * @param {string} baseUrl - Base URL for resolving relative paths + * @param {string} css - The CSS content to process + * @param {string} cssUrl - The URL of the CSS file (for resolving relative paths) * @param {Set} fontUrls - Set to store found font URLs */ - function extractFontUrlsFromCss(css, baseUrl, fontUrls) { + function extractFontUrlsFromCss(css, cssUrl, fontUrls) { + // Get the base URL for resolving relative paths + const baseUrl = new URL(cssUrl).origin; + + // Match @font-face blocks const fontFaceRegex = /@font-face\s*{[^}]*}/g; const urlRegex = /url\(['"]?([^'"\)]+)['"]?\)/g; - const fontFamilyRegex = /font-family\s*:\s*['"]?([^'";]+)['"]?/; + const fontFamilyRegex = /font-family\s*:\s*['"]?([^'";]*)['"]?/; - let fontFaceRules = css.match(fontFaceRegex) || []; - - fontFaceRules.forEach(rule => { - let urls = []; - let match; - - // Extract URLs from the rule - while ((match = urlRegex.exec(rule)) !== null) { - let fontUrl = match[1].trim(); - - // Handle relative URLs - if (!fontUrl.startsWith('http') && !fontUrl.startsWith('data:')) { - fontUrl = new URL(fontUrl, baseUrl).href; - } - - // Check if it's a font file - if (fontUrl.match(/\.(woff2?|ttf|otf|eot)($|\?)/i)) { - urls.push(fontUrl); - } - } + let fontFaceMatch; + while ((fontFaceMatch = fontFaceRegex.exec(css)) !== null) { + const fontFaceBlock = fontFaceMatch[0]; // Extract font-family name - const familyMatch = rule.match(fontFamilyRegex); - const familyName = familyMatch ? familyMatch[1].trim() : 'Unknown Font'; - const cleanRule = rule.replace(/\s+/g, ' ').trim(); + const familyMatch = fontFaceBlock.match(fontFamilyRegex); + const fontFamily = familyMatch ? familyMatch[1].trim() : 'Unknown Font'; - urls.forEach(url => { - const filename = url.split('/').pop().split('?')[0]; - fontUrls.add({ - url, - family: familyName, - filename, - cssRule: cleanRule - }); - }); - }); - - // Also look for other font references in the CSS - const fontRegex = /font(-family)?\s*:\s*['"]?([^'";]+)['"]?/g; - while ((match = fontRegex.exec(css)) !== null) { - if (!match[0].includes('@font-face')) { - const rule = match[0].trim(); - const context = css.substring(Math.max(0, match.index - 50), - Math.min(css.length, match.index + rule.length + 50)); - - // Store the context for any existing font entries that match the family - const fontFamily = match[2].split(',')[0].trim(); - for (let fontData of fontUrls) { - if (fontData.family === fontFamily) { - fontData.usageExample = context.trim(); + // Extract all URLs from this @font-face block + let urlMatch; + while ((urlMatch = urlRegex.exec(fontFaceBlock)) !== null) { + try { + let fontUrl = urlMatch[1].trim(); + + // Skip data: URLs + if (fontUrl.startsWith('data:')) { + console.log('Skipping data: URL font'); + continue; } + + // Only process known font file types + if (!fontUrl.match(/\.(woff2?|ttf|otf|eot)(\?.*)?$/i)) { + continue; + } + + // Resolve relative URLs + if (fontUrl.startsWith('//')) { + fontUrl = 'https:' + fontUrl; + } else if (!fontUrl.startsWith('http')) { + fontUrl = new URL(fontUrl, cssUrl).href; + } + + const filename = fontUrl.split('/').pop().split('?')[0]; + console.log(`Found font in CSS: ${fontUrl} (${fontFamily})`); + + fontUrls.add({ + url: fontUrl, + family: fontFamily, + filename: filename + }); + } catch (error) { + console.error('Error processing font URL:', urlMatch[1], error); } } } @@ -461,51 +458,27 @@ document.addEventListener('DOMContentLoaded', () => { */ function extractDirectFontLinks(doc, baseUrl, fontUrls) { // Check for preload links - doc.querySelectorAll('link[rel="preload"][as="font"]').forEach(link => { - const href = link.getAttribute('href'); - if (href && href.match(/\.(woff|woff2|ttf|otf)(\?.*)?$/i)) { - try { - const absoluteUrl = new URL(href, baseUrl).href; - const filename = href.split('/').pop().split('?')[0]; - - // Try to get font-family from the link - let fontFamilyName = 'Unknown Font'; - if (link.dataset.fontFamily) { - fontFamilyName = link.dataset.fontFamily; - } - - console.log(`Found preloaded font: ${absoluteUrl} (${fontFamilyName})`); - fontUrls.add({ - url: absoluteUrl, - family: fontFamilyName, - filename: filename - }); - } catch (error) { - console.error('Error resolving font URL:', href, error); - } - } - }); - - // Check for any links that might be fonts - doc.querySelectorAll('a[href]').forEach(link => { + doc.querySelectorAll('link[rel="preload"][as="font"], link[rel="stylesheet"]').forEach(link => { const href = link.getAttribute('href'); - if (href && href.match(/\.(woff|woff2|ttf|otf)(\?.*)?$/i)) { + if (href && href.match(/\.(woff|woff2|ttf|otf|css)(\?.*)?$/i)) { try { const absoluteUrl = new URL(href, baseUrl).href; - const filename = href.split('/').pop().split('?')[0]; - - // Use link text as potential font name if available - let fontFamilyName = link.textContent.trim() || 'Unknown Font'; - if (fontFamilyName === filename) { - fontFamilyName = 'Unknown Font'; + if (href.match(/\.css(\?.*)?$/i)) { + processCssUrl(absoluteUrl, fontUrls, baseUrl); + } else { + const filename = href.split('/').pop().split('?')[0]; + let fontFamilyName = 'Unknown Font'; + if (link.dataset.fontFamily) { + fontFamilyName = link.dataset.fontFamily; + } + + console.log(`Found preloaded font: ${absoluteUrl} (${fontFamilyName})`); + fontUrls.add({ + url: absoluteUrl, + family: fontFamilyName, + filename: filename + }); } - - console.log(`Found linked font: ${absoluteUrl} (${fontFamilyName})`); - fontUrls.add({ - url: absoluteUrl, - family: fontFamilyName, - filename: filename - }); } catch (error) { console.error('Error resolving font URL:', href, error); } @@ -529,7 +502,10 @@ document.addEventListener('DOMContentLoaded', () => { const resultsDiv = document.getElementById('results'); resultsDiv.innerHTML = ''; - if (urls.size === 0) { + // Convert urls to array if it's a Set, or use as is if already array + const fontsArray = Array.isArray(urls) ? urls : Array.from(urls); + + if (fontsArray.length === 0) { resultsDiv.innerHTML = '<p>No fonts found on this webpage.</p>'; return; } @@ -541,7 +517,6 @@ document.addEventListener('DOMContentLoaded', () => { container.style.maxWidth = '800px'; container.style.margin = '0 auto'; - const fontsArray = Array.from(urls); const shouldAutoPreview = fontsArray.length < 4; for (let fontData of fontsArray) { |