// NOTE: The scripts is used for all iconography pages and for demo purposes, if you wish you may use any part of the code for your own project // Global variables (add new ones) let currentSvgWeight = 'sa-thin'; // Default weight for SVG icons let isNoFill = false; // Default no-fill state for SVG icons // Global variables let allIcons = []; let searchHistory = new Set(); let searchTimeout = null; let currentToast = null; let iconMappings = {}; let currentSearchTerm = ''; let currentIconSet = 'sa'; // Semantic matches function findSemanticMatches(searchTerm) { searchTerm = searchTerm.toLowerCase().replace(/\s+/g, '-'); if (iconMappings[searchTerm]) return iconMappings[searchTerm]; const partialMatches = Object.keys(iconMappings).filter(key => { const normalizedKey = key.toLowerCase().replace(/\s+/g, '-').replace(/^-/, ''); // Strip leading hyphen return normalizedKey.includes(searchTerm) || searchTerm.includes(normalizedKey); }); if (partialMatches.length > 0) return partialMatches.flatMap(key => iconMappings[key]); for (const category in iconMappings) { const categoryMatches = Object.keys(iconMappings[category]).filter(key => { const normalizedKey = key.toLowerCase().replace(/\s+/g, '-').replace(/^-/, ''); // Strip leading hyphen return normalizedKey.includes(searchTerm) || searchTerm.includes(normalizedKey); }); if (categoryMatches.length > 0) return categoryMatches.flatMap(key => iconMappings[category][key]); } return null; } // Suggestions with Levenshtein distance function findSuggestions(searchTerm) { if (!searchTerm || searchTerm.length < 2) return []; searchTerm = searchTerm.replace(/\s+/g, '-').replace(/^-/, ''); // Strip leading hyphen const semanticMatches = findSemanticMatches(searchTerm); if (semanticMatches) { return [...new Set(semanticMatches)].filter(icon => allIcons.includes(`-${icon}`) || allIcons.includes(icon) ).map(icon => icon.replace(/^-/, '')); // Clean icons in results } return allIcons .map(icon => { const iconName = icon.startsWith('-') ? icon.substring(1) : icon; return { name: iconName, distance: levenshteinDistance(searchTerm.toLowerCase(), iconName.toLowerCase()) }; }) .filter(item => { const maxDistance = Math.min(Math.floor(searchTerm.length * 0.4), 3); return item.distance > 0 && item.distance <= maxDistance; }) .sort((a, b) => a.distance - b.distance) .slice(0, 3) .map(item => item.name); } function levenshteinDistance(a, b) { if (a.length === 0) return b.length; if (b.length === 0) return a.length; const matrix = Array(b.length + 1).fill(null).map(() => Array(a.length + 1).fill(null)); for (let i = 0; i <= b.length; i++) matrix[i][0] = i; for (let j = 0; j <= a.length; j++) matrix[0][j] = j; for (let i = 1; i <= b.length; i++) { for (let j = 1; j <= a.length; j++) { matrix[i][j] = b.charAt(i - 1) === a.charAt(j - 1) ? matrix[i - 1][j - 1] : Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1); } } return matrix[b.length][a.length]; } // Filter logic function initializeFilter() { const searchInput = document.getElementById('searchIcons'); searchInput.addEventListener('input', function () { currentSearchTerm = this.value.trim().replace(/^-/, ''); // Strip leading hyphen filterIcons(); }); } function filterIcons() { const searchTerms = currentSearchTerm.toLowerCase().split(/\s+/).filter(term => term.length > 0).map(term => term.replace(/^-/, '')); // Strip leading hyphens document.querySelectorAll('#iconList li').forEach(item => { const text = item.textContent.toLowerCase().replace(/^-/, ''); // Strip leading hyphen const matches = searchTerms.every(term => text.includes(term)); item.classList.toggle('js-filter-hide', !matches); }); updateVisibleCount(); if (searchTimeout) clearTimeout(searchTimeout); searchTimeout = setTimeout(() => updateSearchHistory(currentSearchTerm), 1000); const visibleIcons = document.querySelectorAll('#iconList li:not(.js-filter-hide)').length; const suggestionsContainer = document.getElementById('suggestions'); if (currentSearchTerm.length >= 2 && visibleIcons < 10) { const suggestions = findSuggestions(currentSearchTerm); suggestionsContainer.innerHTML = suggestions.length > 0 ? `Did you mean? ${suggestions.map(s => `${s}`).join(' ')}` : ''; } else { suggestionsContainer.innerHTML = ''; } } // Load icon set async function loadIconSet(iconSet = 'sa') { const iconSets = { 'sa': { icons: 'json/sa-icons.json', mappings: 'json/sa-mappings.json', prefix: 'sa' }, 'base': { icons: 'json/sa-base.json', mappings: 'json/sa-mappings.json', prefix: 'sa' }, 'svg': { icons: 'json/sa-svg-icons.json', mappings: 'json/sa-svg-mappings.json', prefix: 'svg' }, 'fal': { icons: 'json/fa-icons.json', mappings: 'json/fa-mappings.json', prefix: 'fal' }, 'fas': { icons: 'json/fa-icons.json', mappings: 'json/fa-mappings.json', prefix: 'fas' }, 'far': { icons: 'json/fa-icons.json', mappings: 'json/fa-mappings.json', prefix: 'far' }, 'fad': { icons: 'json/fa-duotone.json', mappings: 'json/fa-mappings.json', prefix: 'fad' }, 'fab': { icons: 'json/fa-brands.json', mappings: 'json/fa-mappings.json', prefix: 'fab' }, 'material': { icons: 'json/material-icons.json', mappings: 'json/material-mappings.json', prefix: 'material' } }; const selectedSet = iconSets[iconSet] || iconSets['sa']; // If first load or switching between different icon families, fetch new data if (!allIcons.length || (currentIconSet === 'sa' || currentIconSet === 'svg') !== (iconSet === 'sa' || iconSet === 'svg')) { try { const [iconsResponse, mappingsResponse] = await Promise.all([ fetch(selectedSet.icons), fetch(selectedSet.mappings) ]); if (!iconsResponse.ok || !mappingsResponse.ok) throw new Error('Failed to load resources'); allIcons = (await iconsResponse.json()).map(icon => icon.replace(/^-/, '')); // Strip leading hyphen from all icons iconMappings = await mappingsResponse.json(); currentIconSet = iconSet; generateIconList(allIcons, selectedSet.prefix); initializeFilter(); } catch (error) { console.error('Error loading icon set:', error); } } else { // Update style for same icon family currentIconSet = iconSet; document.querySelectorAll('#iconList li').forEach(item => { const iconName = item.dataset.iconName.replace(/^-/, ''); // Strip leading hyphen const iconElement = item.querySelector('.icon-container'); iconElement.innerHTML = getIconClass(iconSet, iconName); }); } // Re-apply filter if search term exists if (currentSearchTerm) filterIcons(); updateVisibleCount(); } // Generate icon list function generateIconList(icons, iconPrefix) { const iconList = document.getElementById('iconList'); iconList.innerHTML = icons.map(icon => { const cleanIconName = icon.replace(/^-/, ''); // Ensure no leading hyphen const iconClass = getIconClass(iconPrefix, cleanIconName); const displayName = cleanIconName; const isSvg = iconPrefix === 'svg'; return `