PageSourceSearch

https://www.sba.gov/app/themes/sbagov/assets/js/search-suggestions.js?ver=1788983491

js sba.gov collected 2026-09-24 06:03:17 UTC 4,243 bytes, 143 lines download raw bytes

1(function () {
2    const config = window.sbagovSearchSuggestions;
3    const modal = document.getElementById('sba-header-search-modal');
4
5    if (!config || !modal) {
6        return;
7    }
8
9    const searchBlock = modal.querySelector('.sba-modal-core-search');
10    const input = searchBlock
11        ? searchBlock.querySelector('input[type="search"], .wp-block-search__input, input[name="s"]')
12        : null;
13    const resultsLabel = modal.querySelector('[data-search-results-label]');
14    const resultsList = modal.querySelector('[data-search-results]');
15    const status = modal.querySelector('#sba-search-suggestions-status');
16
17    if (!input || !resultsLabel || !resultsList || !status) {
18        return;
19    }
20
21    let abortController;
22    let debounceTimer;
23
24    const decodeEntities = (value) => {
25        const doc = new DOMParser().parseFromString(String(value), 'text/html');
26        return doc.body.textContent || '';
27    };
28
29    input.setAttribute('aria-controls', 'sba-search-suggestions');
30    input.setAttribute('aria-describedby', 'sba-search-suggestions-status');
31    input.setAttribute('autocomplete', 'off');
32
33    const clearResults = () => {
34        resultsList.innerHTML = '';
35        resultsLabel.hidden = true;
36        resultsList.hidden = true;
37        status.textContent = '';
38    };
39
40    const announceMessage = (message) => {
41        clearResults();
42        status.textContent = message;
43    };
44
45    const renderResults = (results) => {
46        resultsList.innerHTML = '';
47        resultsLabel.hidden = false;
48
49        results.forEach((result) => {
50            const item = document.createElement('li');
51            item.className = 'sba-search-suggestions__item';
52
53            const link = document.createElement('a');
54            const title = decodeEntities((result.title || '').trim());
55
56            link.className = 'sba-search-suggestions__link';
57            link.href = result.url;
58
59            const titleSpan = document.createElement('span');
60            titleSpan.className = 'sba-search-suggestions__title';
61            titleSpan.textContent = title || input.value.trim();
62            link.appendChild(titleSpan);
63
64            item.appendChild(link);
65            resultsList.appendChild(item);
66        });
67
68        resultsList.hidden = false;
69        status.textContent = config.messages.results;
70    };
71
72    const fetchResults = async (term) => {
73        if (abortController) {
74            abortController.abort();
75        }
76
77        abortController = new AbortController();
78        status.textContent = config.messages.searching;
79
80        try {
81            const url = new URL(config.endpoint, window.location.origin);
82            url.searchParams.set('search', term);
83            url.searchParams.set('per_page', String(config.perPage));
84
85            const response = await fetch(url.toString(), {
86                signal: abortController.signal,
87                headers: {
88                    Accept: 'application/json',
89                },
90            });
91
92            if (!response.ok) {
93                throw new Error('Search request failed');
94            }
95
96            const results = await response.json();
97
98            if (!Array.isArray(results) || results.length === 0) {
99                announceMessage(config.messages.noResults);
100                return;
101            }
102
103            renderResults(results);
104        } catch (error) {
105            if (error.name === 'AbortError') {
106                return;
107            }
108
109            clearResults();
110        }
111    };
112
113    input.addEventListener('input', () => {
114        const term = input.value.trim();
115
116        window.clearTimeout(debounceTimer);
117
118        if (term.length < config.minChars) {
119            if (abortController) {
120                abortController.abort();
121            }
122
123            clearResults();
124            return;
125        }
126
127        debounceTimer = window.setTimeout(() => {
128            fetchResults(term);
129        }, 250);
130    });
131
132    modal.addEventListener('click', (event) => {
133        if (event.target.closest('[data-close-modal]')) {
134            clearResults();
135        }
136    });
137
138    modal.addEventListener('keydown', (event) => {
139        if (event.key === 'Escape') {
140            clearResults();
141        }
142    });
143})();

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.