PageSourceSearch

https://www.fanasparebank.no/js/search.js

js fanasparebank.no collected 2026-10-02 16:49:54 UTC 7,250 bytes, 223 lines download raw bytes

1
2const endpoint = "/api/search/searchall";
3
4const focusSearch = sessionStorage.getItem('focus-search') === 'true';
5sessionStorage.removeItem('focus-search');
6
7if (window.location.hash === '#focus-search' || focusSearch) {
8    $('#search-input').trigger('focus');
9}
10
11let searchButton = $("#search-modal-button"),
12    searchResults = $("#search-results"),
13    searchInput = $("#search-input");
14    
15    // Listen for input clearing to reset results
16    searchInput.on('input', function () {
17        const val = $(this).val();
18        if (!val || val.trim().length === 0) {
19            ClearSearch();
20        }
21    });
22const searchContainer = $(".search__results");
23
24// Pagination state
25let _allSearchHits = [];
26let _currentPage = 0;
27const PAGE_SIZE = 10;
28// loader timing removed — show spinner only while request is pending
29
30function SendSearchRequest(e) {
31    const query = searchInput.val();
32    SearchRequest(query);
33}
34
35
36// The search request will be sent when user press enter
37searchInput.on("keydown", function (e) {
38    if (e.originalEvent.key == "Enter") {
39        SendSearchRequest();
40    }
41});
42
43
44// The search request will be sent when user click on the button
45searchButton.on("click", SendSearchRequest);
46
47
48//send the search request
49function SearchRequest(query) {
50
51    //clear existing search results
52    searchResults.children().remove();
53    // hide results container until we have results to show
54    if (searchContainer.length) {
55        searchContainer.addClass('hidden').attr('aria-hidden', 'true');
56    }
57    ShowLoader();
58
59    if (query.length < 1) {
60        RemoveLoader();
61        if (searchContainer.length) {
62            searchContainer.addClass('hidden').attr('aria-hidden', 'true');
63        }
64        return;
65    }
66
67    var minimalRelevanceScore = document.getElementById('minimalRelevanceScore').value;
68
69    fetch(`${endpoint}?query=${query}&score=${minimalRelevanceScore}`)
70        .then((response) => {
71            if (!response.ok) {
72                throw new Error();
73            }
74            return response.json();
75        })
76        .then((data) => {
77            // store hits and render first page
78            _allSearchHits = data.hits || [];
79            _currentPage = 0;
80            RemoveLoader();
81            renderResultsPage(_currentPage);
82        })
83        .catch(err => {
84            RemoveLoader();
85            // hide results on error
86            if (searchContainer.length) {
87                searchContainer.addClass('hidden').attr('aria-hidden', 'true');
88            }
89            searchResults.append("<li>Noe gikk gale med søket. Vennligst prøv igjen senere.</li>");
90        });
91}
92
93function ShowLoader() {
94    // Render spinner into the dedicated spinner container so it's easy to find in DOM
95    const spinnerContainer = $("#search-spinnerContainer");
96    if (spinnerContainer.length) {
97        // avoid re-adding
98        if (spinnerContainer.find('.spinner').length === 0) {
99            spinnerContainer.html('<div class="spinner" aria-hidden="true"></div>');
100        }
101    } else {
102        // fallback to previous behaviour: append inside results list
103        if (searchResults.find('.search__loader').length === 0) {
104            const loader = $(`<li class="search__result search__loader"><div class="spinner" aria-hidden="true"></div></li>`);
105            searchResults.append(loader);
106        }
107    }
108}
109
110function RemoveLoader() {
111    const spinnerContainer = $("#search-spinnerContainer");
112    if (spinnerContainer.length) {
113        spinnerContainer.empty();
114    }
115    searchResults.find('.search__loader').remove();
116}
117
118
119function renderResultsPage(page) {
120    searchResults.empty();
121
122    const total = _allSearchHits.length;
123
124    // if no results, display message
125    if (total === 0) {
126        searchContainer.removeClass('hidden').attr('aria-hidden', 'false');
127        searchResults.append(createSearchResultItem("Ingen søkeresultater"));
128        return;
129    }
130
131    // show container when we have results
132    if (searchContainer.length) {
133        searchContainer.removeClass('hidden').attr('aria-hidden', 'false');
134    }
135
136    const start = page * PAGE_SIZE;
137    const end = Math.min(start + PAGE_SIZE, total);
138
139    for (let i = start; i < end; i++) {
140        const currentItem = _allSearchHits[i];
141                const titleToShow = getResultTitle(currentItem);
142                const snippet = currentItem.excerpt || '';
143                const html = `<a class="links__link" href="${currentItem.url}">
144                                                <p class="search__result-title">${titleToShow}</p>
145                                            </a>
146                                            <p class="search__result-snippet">${snippet}</p>`;
147        searchResults.append(createSearchResultItem(html));
148    }
149
150    // Pagination controls
151    const totalPages = Math.ceil(total / PAGE_SIZE);
152
153    const pagination = $('<li class="search__result search__pagination" aria-l
153ive="polite"></li>');
154    const nav = $(`<nav class="pagination" aria-label="Søk pagination"></nav>`);
155
156    // Pagination info (e.g. "1-10 av 57")
157    const pageInfo = $('<div class="search__pagination-info"></div>');
158    const displayStart = start + 1;
159    const displayEnd = end;
160    pageInfo.text(`Viser  ${displayStart}-${displayEnd} av totalt ${total} resultater`) ;
161    pagination.append(pageInfo);
162
163    // Show Prev only if there are previous pages
164    if (page > 0) {
165        const prevBtn = $(`<button class="button button--secondary button--small mr-2" aria-label="Forrige side">Forrige</button>`);
166        prevBtn.on('click', function () {
167            _currentPage = Math.max(0, _currentPage - 1);
168            renderResultsPage(_currentPage);
169        });
170        nav.append(prevBtn);
171    }
172
173    // Show Next only if there are more pages
174    if (end < total) {
175        const nextBtn = $(`<button class="button button--secondary button--small ml-2" aria-label="Neste side">Neste</button>`);
176        nextBtn.on('click', function () {
177            _currentPage = Math.min(totalPages - 1, _currentPage + 1);
178            renderResultsPage(_currentPage);
179        });
180        nav.append(nextBtn);
181    }
182
183    pagination.append(nav);
184    searchResults.append(pagination);
185}
186
187function createSearchResultItem(resultBody) {
188    return `<li class="search__result">${resultBody}</li>`;
189}
190
191// Return the best title available for a result: prefer metaTitle, then title
192function getResultTitle(item) {
193    let raw = '';
194    if (item.metaTitle && item.metaTitle.trim().length > 0) {
195        raw = item.metaTitle;
196    } else if (item.meta_title && item.meta_title.trim().length > 0) {
197        raw = item.meta_title;
198    } else if (item.title && item.title.trim().length > 0) {
199        raw = item.title;
200    } else {
201        raw = 'Uten tittel';
202    }
203    return escapeHtml(raw);
204}
205
206function escapeHtml(unsafe) {
207    return unsafe
208        .replace(/&/g, '&amp;')
209        .replace(/</g, '&lt;')
210        .replace(/>/g, '&gt;')
211        .replace(/"/g, '&quot;')
212        .replace(/'/g, '&#039;');
213}
214
215function ClearSearch() {
216    _allSearchHits = [];
217    _currentPage = 0;
218    RemoveLoader();
219    if (searchContainer.length) {
220        searchContainer.addClass('hidden').attr('aria-hidden', 'true');
221    }
222    searchResults.empty();
223}

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.