about summary refs log tree commit diff stats
path: root/html
diff options
context:
space:
mode:
Diffstat (limited to 'html')
-rw-r--r--html/web-font-vacuum/app.js356
-rw-r--r--html/web-font-vacuum/index.html151
2 files changed, 351 insertions, 156 deletions
diff --git a/html/web-font-vacuum/app.js b/html/web-font-vacuum/app.js
index 82f877e..c9d5f36 100644
--- a/html/web-font-vacuum/app.js
+++ b/html/web-font-vacuum/app.js
@@ -335,44 +335,67 @@ document.addEventListener('DOMContentLoaded', () => {
     }
 
     function extractFontUrlsFromCss(css, baseUrl, fontUrls) {
+        // Regular expression to match @font-face rules
         const fontFaceRegex = /@font-face\s*{[^}]*}/g;
-        const urlRegex = /url\(['"]?([^'")]+)['"]?\)/g;
-        const fontFamilyRegex = /font-family\s*:\s*['"]?([^'";]*)['"]?/i;
+        const urlRegex = /url\(['"]?([^'"\)]+)['"]?\)/g;
+        const fontFamilyRegex = /font-family\s*:\s*['"]?([^'";]+)['"]?/;
         
-        const fontFaces = css.match(fontFaceRegex) || [];
+        let fontFaceRules = css.match(fontFaceRegex) || [];
         
-        fontFaces.forEach(fontFace => {
-            // Extract font-family name
-            let fontFamilyName = 'Unknown Font';
-            const fontFamilyMatch = fontFace.match(fontFamilyRegex);
-            if (fontFamilyMatch && fontFamilyMatch[1]) {
-                fontFamilyName = fontFamilyMatch[1].trim();
+        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);
+                }
             }
             
-            // Extract URLs
-            let match;
-            while ((match = urlRegex.exec(fontFace)) !== null) {
-                let fontUrl = match[1];
-                fontUrl = fontUrl.replace(/['"]/g, '');
+            // Extract font-family name
+            const familyMatch = rule.match(fontFamilyRegex);
+            const familyName = familyMatch ? familyMatch[1].trim() : 'Unknown Font';
+            
+            // Clean up the CSS rule for display
+            const cleanRule = rule.replace(/\s+/g, ' ').trim();
+            
+            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));
                 
-                if (fontUrl.match(/\.(woff|woff2|ttf|otf)(\?.*)?$/i)) {
-                    try {
-                        // Properly resolve the font URL against the CSS URL (baseUrl)
-                        const absoluteUrl = new URL(fontUrl, baseUrl).href;
-                        console.log(`Found font URL: ${absoluteUrl} (${fontFamilyName})`);
-                        
-                        // Store both the URL and the font family name
-                        fontUrls.add({
-                            url: absoluteUrl,
-                            family: fontFamilyName,
-                            filename: fontUrl.split('/').pop().split('?')[0]
-                        });
-                    } catch (error) {
-                        console.error('Error resolving font URL:', fontUrl, error);
+                // 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();
                     }
                 }
             }
-        });
+        }
     }
 
     // Function to check for direct font links in the HTML
@@ -431,114 +454,191 @@ document.addEventListener('DOMContentLoaded', () => {
     }
 
     async function displayFontUrls(urls) {
-        resultsDiv.innerHTML = '<h2>Found Font Files:</h2>';
-        
-        // Create a container for all font items
-        const fontsContainer = document.createElement('div');
-        fontsContainer.className = 'fonts-container';
-        
-        for (const fontData of urls) {
+        const resultsDiv = document.getElementById('results');
+        resultsDiv.innerHTML = '';
+
+        if (urls.size === 0) {
+            resultsDiv.innerHTML = '<p>No fonts found on this webpage.</p>';
+            return;
+        }
+
+        const container = document.createElement('div');
+        container.style.display = 'flex';
+        container.style.flexDirection = 'column';
+        container.style.gap = '2rem';
+        container.style.maxWidth = '800px';
+        container.style.margin = '0 auto';
+
+        // Convert urls Set to Array for length check and iteration
+        const fontsArray = Array.from(urls);
+        const shouldAutoPreview = fontsArray.length < 4;
+
+        for (let fontData of fontsArray) {
             const fontItem = document.createElement('div');
-            fontItem.className = 'font-item';
+            fontItem.style.border = '2px solid var(--dark)';
+            fontItem.style.padding = '1rem';
+            fontItem.style.background = 'var(--beige)';
+            fontItem.style.position = 'relative';
+
+            // Add accent bar at the top
+            const accentBar = document.createElement('div');
+            accentBar.style.position = 'absolute';
+            accentBar.style.top = '0';
+            accentBar.style.left = '0';
+            accentBar.style.right = '0';
+            accentBar.style.height = '4px';
+            accentBar.style.background = 'var(--accent)';
+            fontItem.appendChild(accentBar);
+
+            // Font info section
+            const fontInfo = document.createElement('div');
+            fontInfo.style.marginTop = '0.5rem';
             
-            const fontUrl = fontData.url;
-            const fontName = fontData.filename;
-            const fontFamily = fontData.family;
-            const extension = fontName.split('.').pop().toUpperCase();
-            const previewFontFamily = `preview-${Math.random().toString(36).substr(2, 9)}`;
+            const fontName = document.createElement('h3');
+            fontName.style.margin = '0';
+            fontName.style.textTransform = 'uppercase';
+            fontName.textContent = fontData.family;
+            fontInfo.appendChild(fontName);
+
+            const fontType = document.createElement('div');
+            fontType.style.display = 'inline-block';
+            fontType.style.background = 'var(--dark)';
+            fontType.style.color = 'var(--beige)';
+            fontType.style.padding = '0.2rem 0.5rem';
+            fontType.style.marginTop = '0.5rem';
+            fontType.style.fontSize = '0.8rem';
+            fontType.textContent = fontData.filename.split('.').pop().toUpperCase();
+            fontInfo.appendChild(fontType);
+
+            // CSS Rule section
+            if (fontData.cssRule) {
+                const ruleContainer = document.createElement('div');
+                ruleContainer.style.marginTop = '1rem';
+                ruleContainer.style.padding = '0.5rem';
+                ruleContainer.style.background = 'rgba(0,0,0,0.05)';
+                ruleContainer.style.border = '1px dashed var(--dark)';
+                ruleContainer.style.fontFamily = 'monospace';
+                ruleContainer.style.fontSize = '0.8rem';
+                ruleContainer.style.overflowX = 'auto';
+                
+                const ruleLabel = document.createElement('div');
+                ruleLabel.style.fontWeight = 'bold';
+                ruleLabel.style.marginBottom = '0.5rem';
+                ruleLabel.textContent = '@font-face Rule:';
+                ruleContainer.appendChild(ruleLabel);
+                
+                const ruleText = document.createElement('pre');
+                ruleText.style.margin = '0';
+                ruleText.style.whiteSpace = 'pre-wrap';
+                ruleText.textContent = fontData.cssRule;
+                ruleContainer.appendChild(ruleText);
+                
+                fontInfo.appendChild(ruleContainer);
+            }
+
+            // Preview section
+            const previewContainer = document.createElement('div');
+            previewContainer.style.marginTop = '1rem';
+            previewContainer.style.padding = '1rem';
+            previewContainer.style.border = '1px dashed var(--dark)';
             
-            // Create the font item HTML structure
-            fontItem.innerHTML = `
-                <div class="font-info">
-                    <strong>${fontName}</strong>
-                    <br>
-                    <small>Family: ${fontFamily}</small>
-                    <br>
-                    <small>${extension} Font</small>
-                </div>
-                <div class="font-preview" style="display: none;">
-                    <div class="preview-text" style="font-family: '${previewFontFamily}', sans-serif;">
-                        ${PREVIEW_TEXT}
-                    </div>
-                    <div class="preview-sizes">
-                        <div style="font-size: 12px;">${PREVIEW_TEXT}</div>
-                        <div style="font-size: 18px;">${PREVIEW_TEXT}</div>
-                        <div style="font-size: 24px;">${PREVIEW_TEXT}</div>
-                    </div>
-                </div>
-                <div class="font-actions">
-                    <button onclick="downloadFont('${fontUrl}', '${fontName}')">Download</button>
-                    <button class="preview-btn">Preview</button>
-                </div>
-            `;
+            // Hide preview container initially if not auto-previewing
+            if (!shouldAutoPreview) {
+                previewContainer.style.display = 'none';
+            }
             
-            // Add the font item to the container
-            fontsContainer.appendChild(fontItem);
+            const previewLabel = document.createElement('div');
+            previewLabel.style.fontWeight = 'bold';
+            previewLabel.style.marginBottom = '0.5rem';
+            previewLabel.textContent = 'Preview:';
+            previewContainer.appendChild(previewLabel);
+
+            const preview = document.createElement('div');
+            preview.style.marginBottom = '1rem';
+            preview.id = `preview-${fontData.filename}`;
+            preview.textContent = 'The quick brown fox jumps over the lazy dog 0123456789';
+            previewContainer.appendChild(preview);
+
+            // Size variations
+            const sizeVariations = document.createElement('div');
+            sizeVariations.style.borderTop = '1px solid var(--dark)';
+            sizeVariations.style.paddingTop = '0.5rem';
+            sizeVariations.style.marginTop = '0.5rem';
             
-            // Add preview button functionality
-            const previewBtn = fontItem.querySelector('.preview-btn');
-            const previewDiv = fontItem.querySelector('.font-preview');
-            let fontLoaded = false;
+            [12, 18, 24].forEach(size => {
+                const sizePreview = document.createElement('div');
+                sizePreview.style.fontSize = `${size}px`;
+                sizePreview.textContent = `${size}px - AaBbCc 123`;
+                sizeVariations.appendChild(sizePreview);
+            });
             
-            previewBtn.addEventListener('click', async () => {
-                if (!fontLoaded) {
-                    previewBtn.textContent = 'Loading...';
-                    fontLoaded = await previewFont(fontUrl, previewFontFamily);
-                    previewBtn.textContent = 'Preview';
-                }
+            previewContainer.appendChild(sizeVariations);
+
+            // Button container
+            const buttonContainer = document.createElement('div');
+            buttonContainer.style.display = 'flex';
+            buttonContainer.style.gap = '0.5rem';
+            buttonContainer.style.marginTop = '1rem';
+
+            // Download button
+            const downloadBtn = document.createElement('button');
+            downloadBtn.textContent = '⬇ Download';
+            downloadBtn.style.flex = '1';
+            downloadBtn.addEventListener('click', () => downloadFont(fontData.url, fontData.filename));
+            buttonContainer.appendChild(downloadBtn);
+
+            // Preview button (only for 5 or more fonts)
+            if (!shouldAutoPreview) {
+                const previewBtn = document.createElement('button');
+                previewBtn.textContent = '👁 Preview';
+                previewBtn.style.flex = '1';
                 
-                if (fontLoaded) {
-                    const isShowing = previewDiv.style.display !== 'none';
-                    previewDiv.style.display = isShowing ? 'none' : 'block';
-                    previewBtn.textContent = isShowing ? 'Preview' : 'Hide Preview';
-                } else {
-                    showError('Failed to load font for preview');
-                }
-            });
-        }
-        
-        // Add the container to the results
-        resultsDiv.appendChild(fontsContainer);
-        
-        // Add CSS for the new preview features
-        const style = document.createElement('style');
-        style.textContent = `
-            .fonts-container {
-                display: grid;
-                gap: 1rem;
-            }
-            .font-item {
-                background: white;
-                padding: 1rem;
-                border-radius: 4px;
-                box-shadow: 0 1px 3px rgba(0,0,0,0.1);
-            }
-            .font-info {
-                margin-bottom: 1rem;
-            }
-            .font-preview {
-                margin: 1rem 0;
-                padding: 1rem;
-                background: #f8f9fa;
-                border-radius: 4px;
-            }
-            .preview-sizes {
-                margin-top: 1rem;
-                display: grid;
-                gap: 0.5rem;
-            }
-            .font-actions {
-                display: flex;
-                gap: 0.5rem;
-            }
-            .preview-btn {
-                background: #6c757d;
+                let isPreviewVisible = false;
+                previewBtn.addEventListener('click', async () => {
+                    if (!isPreviewVisible) {
+                        previewContainer.style.display = 'block';
+                        const previewElement = document.getElementById(`preview-${fontData.filename}`);
+                        if (await previewFont(fontData.url, fontData.family)) {
+                            previewElement.style.fontFamily = fontData.family;
+                            sizeVariations.querySelectorAll('div').forEach(div => {
+                                div.style.fontFamily = fontData.family;
+                            });
+                            previewBtn.textContent = '👁 Hide Preview';
+                            isPreviewVisible = true;
+                        } else {
+                            // If preview fails, hide the container again
+                            previewContainer.style.display = 'none';
+                        }
+                    } else {
+                        previewContainer.style.display = 'none';
+                        previewBtn.textContent = '👁 Preview';
+                        isPreviewVisible = false;
+                    }
+                });
+                buttonContainer.appendChild(previewBtn);
             }
-            .preview-btn:hover {
-                background: #5a6268;
+
+            fontItem.appendChild(fontInfo);
+            fontItem.appendChild(previewContainer);
+            fontItem.appendChild(buttonContainer);
+            container.appendChild(fontItem);
+
+            // Auto-preview for fewer than 4 fonts
+            if (shouldAutoPreview) {
+                // Use setTimeout to ensure the DOM is ready
+                setTimeout(async () => {
+                    const previewElement = document.getElementById(`preview-${fontData.filename}`);
+                    if (await previewFont(fontData.url, fontData.family)) {
+                        previewElement.style.fontFamily = fontData.family;
+                        sizeVariations.querySelectorAll('div').forEach(div => {
+                            div.style.fontFamily = fontData.family;
+                        });
+                    }
+                }, 100);
             }
-        `;
-        document.head.appendChild(style);
+        }
+
+        resultsDiv.appendChild(container);
     }
 
     function showError(message) {
diff --git a/html/web-font-vacuum/index.html b/html/web-font-vacuum/index.html
index 8c4aad1..41cc7d7 100644
--- a/html/web-font-vacuum/index.html
+++ b/html/web-font-vacuum/index.html
@@ -5,70 +5,165 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Web Font Vacuum</title>
     <style>
+        :root {
+            --beige: #f5f2e9;
+            --dark: #111111;
+            --accent: #ff4d00;
+            --grid-line: #ccbea7;
+            --container-bg: #ffffff;
+            --focus-outline: #2563eb;
+        }
+        
         body {
-            font-family: system-ui, -apple-system, sans-serif;
-            max-width: 800px;
-            margin: 2rem auto;
-            padding: 0 1rem;
+            font-family: 'Courier New', monospace;
+            max-width: 900px;
+            margin: 0 auto;
+            padding: 1rem;
             line-height: 1.5;
-            background: #f0f2f5;
+            background: var(--beige);
+            color: var(--dark);
+            background-image: 
+                linear-gradient(var(--grid-line) 1px, transparent 1px),
+                linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
+            background-size: 20px 20px;
         }
+        
+        h1, h2 {
+            text-transform: uppercase;
+            letter-spacing: 2px;
+            border-bottom: 3px solid var(--accent);
+            padding-bottom: 0.5rem;
+            font-weight: 900;
+        }
+        
         .container {
-            background: white;
+            background: var(--container-bg);
             padding: 2rem;
-            border-radius: 8px;
-            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+            border: 3px solid var(--dark);
+            box-shadow: 8px 8px 0 var(--dark);
+            margin-top: 2rem;
         }
+        
         .input-group {
             display: flex;
             gap: 1rem;
             margin-bottom: 2rem;
+            border: 2px solid var(--dark);
+            padding: 1rem;
+            background: var(--beige);
+        }
+        
+        .sr-only {
+            position: absolute;
+            width: 1px;
+            height: 1px;
+            padding: 0;
+            margin: -1px;
+            overflow: hidden;
+            clip: rect(0, 0, 0, 0);
+            white-space: nowrap;
+            border: 0;
         }
+        
         input[type="url"] {
             flex: 1;
             padding: 0.75rem;
             font-size: 1rem;
-            border: 1px solid #ddd;
-            border-radius: 4px;
+            border: 2px solid var(--dark);
+            background: var(--beige);
+            font-family: 'Courier New', monospace;
         }
+        
+        input[type="url"]:focus {
+            outline: 3px solid var(--focus-outline);
+            outline-offset: 2px;
+        }
+        
         button {
             padding: 0.75rem 1.5rem;
             font-size: 1rem;
-            background: #007bff;
-            color: white;
-            border: none;
-            border-radius: 4px;
+            background: var(--dark);
+            color: var(--beige);
+            border: 2px solid var(--dark);
             cursor: pointer;
-            transition: background-color 0.2s;
+            text-transform: uppercase;
+            font-weight: bold;
+            font-family: 'Courier New', monospace;
+            transition: all 0.2s;
+        }
+        
+        button:hover,
+        button:focus-visible {
+            background: var(--accent);
+            transform: translateY(-2px);
+            outline: 3px solid var(--focus-outline);
+            outline-offset: 2px;
         }
-        button:hover {
-            background: #0056b3;
+        
+        button:focus:not(:focus-visible) {
+            outline: none;
         }
+        
         .error {
-            color: #dc3545;
+            color: var(--accent);
             padding: 1rem;
-            background: #f8d7da;
-            border-radius: 4px;
+            background: rgba(255, 77, 0, 0.1);
+            border: 2px solid var(--accent);
             margin-top: 1rem;
             display: none;
+            font-weight: bold;
+            role: "alert";
         }
+        
         #results {
             margin-top: 1rem;
         }
+
+        /* Skip link for keyboard users */
+        .skip-link {
+            position: absolute;
+            top: -40px;
+            left: 0;
+            background: var(--dark);
+            color: var(--beige);
+            padding: 8px;
+            z-index: 100;
+            transition: top 0.2s;
+        }
+
+        .skip-link:focus {
+            top: 0;
+            outline: 3px solid var(--focus-outline);
+        }
     </style>
 </head>
 <body>
-    <div class="container">
+    <a href="#main-content" class="skip-link">Skip to main content</a>
+    <div class="container" id="main-content">
         <h1>Web Font Vacuum</h1>
-        <p>Enter a URL to find and download web fonts (WOFF/TTF/WOFF2) from any website.</p>
+        <p>Enter a URL to find and download web fonts (WOFF/TTF/WOFF2/OTF) from any website.</p>
         
-        <div class="input-group">
-            <input type="url" id="urlInput" placeholder="Enter website URL (e.g., https://example.com)" required>
-            <button id="analyzeBtn">Analyze Fonts</button>
-        </div>
+        <form class="input-group" role="search" aria-label="Website URL search form" onsubmit="event.preventDefault();">
+            <label for="urlInput" class="sr-only">Website URL</label>
+            <input 
+                type="url" 
+                id="urlInput" 
+                name="urlInput"
+                placeholder="Enter website URL (e.g., https://example.com)" 
+                required
+                aria-required="true"
+                aria-describedby="urlHint"
+            >
+            <span id="urlHint" class="sr-only">Enter the full website URL including https:// or http://</span>
+            <button 
+                id="analyzeBtn" 
+                type="submit"
+                aria-label="Analyze website for fonts"
+            >Analyze Fonts</button>
+        </form>
 
-        <div id="error" class="error"></div>
-        <div id="results"></div>
+        <div id="error" class="error" role="alert" aria-live="polite"></div>
+        <div id="results" role="region" aria-label="Font analysis results"></div>
     </div>
     <script src="app.js"></script>
 </body>