]> git.evergreen-ils.org Git - Evergreen.git/blob - docs-antora/ui/ui-lunr/js/vendor/search.js
LP#1848524: remove old docs...
[Evergreen.git] / docs-antora / ui / ui-lunr / js / vendor / search.js
1 /* eslint-env browser */
2 window.antoraLunr = (function (lunr) {
3   var searchInput = document.getElementById('search-input')
4   var searchResult = document.createElement('div')
5   searchResult.classList.add('search-result-dropdown-menu')
6   searchInput.parentNode.appendChild(searchResult)
7
8   function highlightText (doc, position) {
9     var hits = []
10     var start = position[0]
11     var length = position[1]
12
13     var text = doc.text
14     var highlightSpan = document.createElement('span')
15     highlightSpan.classList.add('search-result-highlight')
16     highlightSpan.innerText = text.substr(start, length)
17
18     var end = start + length
19     var textEnd = text.length - 1
20     var contextOffset = 15
21     var contextAfter = end + contextOffset > textEnd ? textEnd : end + contextOffset
22     var contextBefore = start - contextOffset < 0 ? 0 : start - contextOffset
23     if (start === 0 && end === textEnd) {
24       hits.push(highlightSpan)
25     } else if (start === 0) {
26       hits.push(highlightSpan)
27       hits.push(document.createTextNode(text.substr(end, contextAfter)))
28     } else if (end === textEnd) {
29       hits.push(document.createTextNode(text.substr(0, start)))
30       hits.push(highlightSpan)
31     } else {
32       hits.push(document.createTextNode('...' + text.substr(contextBefore, start - contextBefore)))
33       hits.push(highlightSpan)
34       hits.push(document.createTextNode(text.substr(end, contextAfter - end) + '...'))
35     }
36     return hits
37   }
38
39   function highlightTitle (hash, doc, position) {
40     var hits = []
41     var start = position[0]
42     var length = position[1]
43
44     var highlightSpan = document.createElement('span')
45     highlightSpan.classList.add('search-result-highlight')
46     var title
47     if (hash) {
48       title = doc.titles.filter(function (item) {
49         return item.id === hash
50       })[0].text
51     } else {
52       title = doc.title
53     }
54     highlightSpan.innerText = title.substr(start, length)
55
56     var end = start + length
57     var titleEnd = title.length - 1
58     if (start === 0 && end === titleEnd) {
59       hits.push(highlightSpan)
60     } else if (start === 0) {
61       hits.push(highlightSpan)
62       hits.push(document.createTextNode(title.substr(length, titleEnd)))
63     } else if (end === titleEnd) {
64       hits.push(document.createTextNode(title.substr(0, start)))
65       hits.push(highlightSpan)
66     } else {
67       hits.push(document.createTextNode(title.substr(0, start)))
68       hits.push(highlightSpan)
69       hits.push(document.createTextNode(title.substr(end, titleEnd)))
70     }
71     return hits
72   }
73
74   function highlightHit (metadata, hash, doc) {
75     var hits = []
76     for (var token in metadata) {
77       var fields = metadata[token]
78       for (var field in fields) {
79         var positions = fields[field]
80         if (positions.position) {
81           var position = positions.position[0] // only higlight the first match
82           if (field === 'title') {
83             hits = highlightTitle(hash, doc, position)
84           } else if (field === 'text') {
85             hits = highlightText(doc, position)
86           }
87         }
88       }
89     }
90     return hits
91   }
92
93   function createSearchResult(result, store, searchResultDataset) {
94     result.forEach(function (item) {
95       var url = item.ref
96       var hash
97       if (url.includes('#')) {
98         hash = url.substring(url.indexOf('#') + 1)
99         url = url.replace('#' + hash, '')
100       }
101       var doc = store[url]
102       var metadata = item.matchData.metadata
103       var hits = highlightHit(metadata, hash, doc)
104       searchResultDataset.appendChild(createSearchResultItem(doc, item, hits))
105     })
106   }
107
108   function createSearchResultItem (doc, item, hits) {
109     var documentTitle = document.createElement('div')
110     documentTitle.classList.add('search-result-document-title')
111     documentTitle.innerText = doc.title
112     var documentHit = document.createElement('div')
113     documentHit.classList.add('search-result-document-hit')
114     var documentHitLink = document.createElement('a')
115     var rootPath = window.antora.basePath
116     documentHitLink.href = rootPath + item.ref
117     documentHit.appendChild(documentHitLink)
118     hits.forEach(function (hit) {
119       documentHitLink.appendChild(hit)
120     })
121     var searchResultItem = document.createElement('div')
122     searchResultItem.classList.add('search-result-item')
123     searchResultItem.appendChild(documentTitle)
124     searchResultItem.appendChild(documentHit)
125     searchResultItem.addEventListener('mousedown', function (e) {
126       e.preventDefault()
127     })
128     return searchResultItem
129   }
130
131   function createNoResult (text) {
132     var searchResultItem = document.createElement('div')
133     searchResultItem.classList.add('search-result-item')
134     var documentHit = document.createElement('div')
135     documentHit.classList.add('search-result-document-hit')
136     var message = document.createElement('strong')
137     message.innerText = 'No results found for query "' + text + '"'
138     documentHit.appendChild(message)
139     searchResultItem.appendChild(documentHit)
140     return searchResultItem
141   }
142
143   function search (index, text) {
144     // execute an exact match search
145     var result = index.search(text)
146     if (result.length > 0) {
147       return result
148     }
149     // no result, use a begins with search
150     result = index.search(text + '*')
151     if (result.length > 0) {
152       return result
153     }
154     // no result, use a contains search
155     result = index.search('*' + text + '*')
156     return result
157   }
158
159   function searchIndex (index, store, text) {
160     // reset search result
161     while (searchResult.firstChild) {
162       searchResult.removeChild(searchResult.firstChild)
163     }
164     if (text.trim() === '') {
165       return
166     }
167     var result = search(index, text)
168     var searchResultDataset = document.createElement('div')
169     searchResultDataset.classList.add('search-result-dataset')
170     searchResult.appendChild(searchResultDataset)
171     if (result.length > 0) {
172       createSearchResult(result, store, searchResultDataset)
173     } else {
174       searchResultDataset.appendChild(createNoResult(text))
175     }
176   }
177
178   function debounce (func, wait, immediate) {
179     var timeout
180     return function () {
181       var context = this
182       var args = arguments
183       var later = function () {
184         timeout = null
185         if (!immediate) func.apply(context, args)
186       }
187       var callNow = immediate && !timeout
188       clearTimeout(timeout)
189       timeout = setTimeout(later, wait)
190       if (callNow) func.apply(context, args)
191     }
192   }
193
194   function init (data) {
195     var index = Object.assign({index: lunr.Index.load(data.index), store: data.store})
196     var search = debounce(function () {
197       searchIndex(index.index, index.store, searchInput.value)
198     }, 100)
199     searchInput.addEventListener('keydown', search)
200
201     // this is prevented in case of mousedown attached to SearchResultItem
202     searchInput.addEventListener('blur', function (e) {
203       while (searchResult.firstChild) {
204         searchResult.removeChild(searchResult.firstChild)
205       }
206     })
207   }
208
209   return {
210     init: init,
211   }
212 })(window.lunr)