PageSourceSearch

https://www.altoprofessional.com/js-search.js

js altoprofessional.com collected 2026-10-01 12:28:49 UTC 2,472 bytes, 80 lines download raw bytes

1  // Load product data once
2  let products = [];
3  
4  fetch('/products.json')
5    .then(response => response.json())
6    .then(data => {
7      products = data;
8      console.log('Products loaded:', products.length);
9    })
10    .catch(err => {
11      console.error('Failed to load products.json', err);
12    });
13
14  const searchInput = document.getElementById('search-input');
15  const searchResults = document.getElementById('search-results');
16
17  // Escape special regex characters
18  function escapeRegex(str) {
19    return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
20  }
21
22  // Highlight matches in text
23  function highlightMatch(text, term) {
24    if (!term) return text;
25    const escapedTerm = escapeRegex(term);
26    const regex = new RegExp(`(${escapedTerm})`, 'gi');
27    return text.replace(regex, '<span class="highlight">$1</span>');
28  }
29
30  // Search logic
31  function performSearch(query) {
32    if (!query.trim() || products.length === 0) {
33      searchResults.style.display = 'none';
34      return;
35    }
36
37    const term = query.toLowerCase();
38    const results = products.filter(product => {
39      const searchable = 
40        product.title.toLowerCase() +
41        ' ' +
42        product.description.toLowerCase() +
43        ' ' +
44        product.keywords.toLowerCase();
45      return searchable.includes(term);
46    }).slice(0, 8); // Limit to top 8 results
47
48    // Render results
49    if (results.length > 0) {
50      searchResults.innerHTML = results.map(product => `
51        <a href="${product.url}" class="search-result-item">
52          <div class="search-result-title">${highlightMatch(product.title, query)}</div>
53          <div class="search-result-desc">${highlightMatch(product.description, query)}</div>
54        </a>
55      `).join('');
56      searchResults.style.display = 'block';
57    } else {
58      searchResults.innerHTML = '<div class="search-result-item">No products found</div>';
59      searchResults.style.display = 'block';
60    }
61  }
62
63  // Event listeners
64  searchInput.addEventListener('input', () => {
65    performSearch(searchInput.value);
66  });
67
68  // Hide results when clicking outside
69  document.addEventListener('click', (e) => {
70    if (!searchInput.contains(e.target) && !searchResults.contains(e.target)) {
71      searchResults.style.display = 'none';
72    }
73  });
74
75  // Navigate with keyboard (optional enhancement)
76  searchInput.addEventListener('keydown', (e) => {
77    if (e.key === 'Escape') {
78      searchResults.style.display = 'none';
79    }
80  });

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.