|
| 1 | +{{- $lazy := site.Params.modules.flexsearch.lazyLoad | default false -}} |
1 | 2 | const search = document.querySelector('.search-input') |
2 | 3 | const suggestions = document.querySelector('.search-suggestions') |
3 | 4 | const background = document.querySelector('.search-background') |
@@ -33,57 +34,57 @@ var index = new FlexSearch.Document({ |
33 | 34 | } |
34 | 35 | }); |
35 | 36 |
|
| 37 | +{{ if $lazy -}} |
| 38 | +/* |
| 39 | + Lazy mode: the index data is fetched from a standalone JSON resource the |
| 40 | + first time the user interacts with search, instead of being bundled into |
| 41 | + every page. The data URL is read from the search input's data-search-index |
| 42 | + attribute (set by the search-input / ModalSearch layouts). |
| 43 | +*/ |
| 44 | +let indexStatus = 'idle'; // idle | loading | ready | error |
| 45 | + |
| 46 | +function loadIndex() { |
| 47 | + if (indexStatus !== 'idle') return; |
| 48 | + indexStatus = 'loading'; |
| 49 | + |
| 50 | + const url = search.dataset.searchIndex; |
| 51 | + if (!url) { |
| 52 | + indexStatus = 'error'; |
| 53 | + console.error('flexsearch: missing data-search-index URL on the search input'); |
| 54 | + return; |
| 55 | + } |
| 56 | + |
| 57 | + fetch(url) |
| 58 | + .then((response) => { |
| 59 | + if (!response.ok) throw new Error(`HTTP ${response.status}`); |
| 60 | + return response.json(); |
| 61 | + }) |
| 62 | + .then((docs) => { |
| 63 | + for (const doc of docs) index.add(doc); |
| 64 | + indexStatus = 'ready'; |
| 65 | + search.addEventListener('input', showResults, true); |
| 66 | + // Honor a query typed while the index was still loading. |
| 67 | + if (search.value) showResults.call(search); |
| 68 | + }) |
| 69 | + .catch((err) => { |
| 70 | + indexStatus = 'error'; |
| 71 | + console.error('flexsearch: failed to load search index', err); |
| 72 | + }); |
| 73 | +} |
| 74 | +{{- else -}} |
36 | 75 | /* |
37 | 76 | Source: |
38 | 77 | - https://github.com/nextapps-de/flexsearch#index-documents-field-search |
39 | 78 | - https://raw.githack.com/nextapps-de/flexsearch/master/demo/autocomplete.html |
40 | 79 | */ |
41 | 80 | function initIndex() { |
42 | | - // https://discourse.gohugo.io/t/range-length-or-last-element/3803/2 |
43 | | - // Note: pages without a title (such as browserconfig.xml) are excluded |
44 | | - {{ $sections := where (where site.Pages "Kind" "section") "Title" "!=" "" }} |
45 | | - {{ $list := where site.RegularPages "Title" "!=" "" | append $sections }} |
46 | | - {{ $list = where $list ".Params.searchExclude" "!=" true }} |
47 | | - {{ $len := (len $list) -}} |
48 | | - |
49 | | - {{ if gt $len 0 }} |
50 | | - {{ range $index, $element := sort $list "Title" "asc" -}} |
51 | | - {{ $url := .RelPermalink }} |
52 | | - {{ if site.Params.modules.flexsearch.canonifyURLs }}{{ $url = .Permalink }}{{ end }} |
53 | | - {{ $title := or .Params.indexTitle .LinkTitle .Title }} |
54 | | - {{ if gt (strings.RuneCount $title) 33 }} |
55 | | - {{ $title = print (substr $title 0 30) "..." }} |
56 | | - {{ end }} |
57 | | - {{ if and site.Params.main.titleCase (not $element.Params.exact) }}{{ $title = title $title }}{{ end }} |
58 | | - {{ $content := "" }} |
59 | | - {{ if site.Params.modules.flexsearch.summaryOnly -}} |
60 | | - {{ $content = replaceRE "[{}]" "" (.Summary | plainify) }} |
61 | | - {{- else -}} |
62 | | - {{ $content = replaceRE "[{}]" "" .Plain }} |
63 | | - {{- end }} |
64 | | - {{ if site.Params.modules.flexsearch.frontmatter }} |
65 | | - {{ $key := site.Params.modules.flexsearch.filter | default "params" }} |
66 | | - {{ $val := slice }} |
67 | | - {{ if ne $key "params" }}{{ $val = index .Params $key }}{{ else }}{{ $val = .Params }}{{ end }} |
68 | | - {{ $content = printf "%s %s" (partial "assets/search-meta.html" (dict "key" $key "val" $val)) $content }} |
69 | | - {{ end }} |
70 | | - index.add({ |
71 | | - id: {{ $index }}, |
72 | | - href: "{{ $url }}", |
73 | | - title: {{ $title | jsonify }}, |
74 | | - {{ with .Description -}} |
75 | | - description: {{ . | jsonify }}, |
76 | | - {{- else -}} |
77 | | - description: {{ .Summary | plainify | jsonify }}, |
78 | | - {{- end }} |
79 | | - content: {{ trim $content " \r\n" | jsonify }} |
80 | | - }); |
81 | | - {{ end -}} |
82 | | - {{ end }} |
83 | | - |
| 81 | + {{- range $doc := partial "utilities/GetSearchDocs.html" . }} |
| 82 | + index.add({{ $doc | jsonify }}); |
| 83 | + {{- end }} |
84 | 84 | search.addEventListener('input', showResults, true); |
85 | 85 | } |
86 | | - |
| 86 | +{{- end }} |
| 87 | + |
87 | 88 | function hideSuggestions(e) { |
88 | 89 | var isClickInsideElement = suggestions.contains(e.target); |
89 | 90 |
|
@@ -134,7 +135,7 @@ function suggestionFocus(e) { |
134 | 135 | focusableSuggestions[nextIndex].focus(); |
135 | 136 | } |
136 | 137 | } |
137 | | - |
| 138 | + |
138 | 139 | /* |
139 | 140 | Source: |
140 | 141 | - https://github.com/nextapps-de/flexsearch#index-documents-field-search |
@@ -168,7 +169,7 @@ function showResults() { |
168 | 169 |
|
169 | 170 | suggestions.innerHTML = ""; |
170 | 171 | suggestions.classList.remove('d-none'); |
171 | | - |
| 172 | + |
172 | 173 | // inform user that no results were found |
173 | 174 | if (flatResults.size === 0 && searchQuery) { |
174 | 175 | const msg = suggestions.dataset.noResults; |
@@ -204,17 +205,24 @@ function showResults() { |
204 | 205 | if (suggestions.childElementCount == maxResult) break; |
205 | 206 | } |
206 | 207 | } |
207 | | - |
| 208 | + |
208 | 209 | if (search !== null && suggestions !== null) { |
209 | 210 | document.addEventListener('keydown', inputFocus); |
210 | | - document.addEventListener('keydown', suggestionFocus); |
| 211 | + document.addEventListener('keydown', suggestionFocus); |
211 | 212 | document.addEventListener('click', hideSuggestions); |
| 213 | + {{ if $lazy -}} |
| 214 | + search.addEventListener('focus', loadIndex, { once: true }); |
| 215 | + search.addEventListener('click', loadIndex, { once: true }); |
| 216 | + {{- else -}} |
212 | 217 | initIndex(); |
| 218 | + {{- end }} |
213 | 219 | } |
214 | 220 |
|
215 | 221 | const searchModal = document.getElementById('search-modal') |
216 | 222 | if (searchModal !== null) { |
217 | 223 | searchModal.addEventListener('shown.bs.modal', function () { |
| 224 | + {{ if $lazy }}loadIndex(); |
| 225 | + {{ end -}} |
218 | 226 | const searchInput = document.getElementById('search-input-modal') |
219 | 227 | if (searchInput !== null) { |
220 | 228 | searchInput.focus({ focusVisible: true }) |
|
0 commit comments