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)
8 function highlightText (doc, position) {
10 var start = position[0]
11 var length = position[1]
14 var highlightSpan = document.createElement('span')
15 highlightSpan.classList.add('search-result-highlight')
16 highlightSpan.innerText = text.substr(start, length)
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)
32 hits.push(document.createTextNode('...' + text.substr(contextBefore, start - contextBefore)))
33 hits.push(highlightSpan)
34 hits.push(document.createTextNode(text.substr(end, contextAfter - end) + '...'))
39 function highlightTitle (hash, doc, position) {
41 var start = position[0]
42 var length = position[1]
44 var highlightSpan = document.createElement('span')
45 highlightSpan.classList.add('search-result-highlight')
48 title = doc.titles.filter(function (item) {
49 return item.id === hash
54 highlightSpan.innerText = title.substr(start, length)
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)
67 hits.push(document.createTextNode(title.substr(0, start)))
68 hits.push(highlightSpan)
69 hits.push(document.createTextNode(title.substr(end, titleEnd)))
74 function highlightHit (metadata, hash, doc) {
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)
93 function createSearchResult(result, store, searchResultDataset) {
94 result.forEach(function (item) {
97 if (url.includes('#')) {
98 hash = url.substring(url.indexOf('#') + 1)
99 url = url.replace('#' + hash, '')
102 var metadata = item.matchData.metadata
103 var hits = highlightHit(metadata, hash, doc)
104 searchResultDataset.appendChild(createSearchResultItem(doc, item, hits))
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)
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) {
128 return searchResultItem
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
143 function search (index, text) {
144 // execute an exact match search
145 var result = index.search(text)
146 if (result.length > 0) {
149 // no result, use a begins with search
150 result = index.search(text + '*')
151 if (result.length > 0) {
154 // no result, use a contains search
155 result = index.search('*' + text + '*')
159 function searchIndex (index, store, text) {
160 // reset search result
161 while (searchResult.firstChild) {
162 searchResult.removeChild(searchResult.firstChild)
164 if (text.trim() === '') {
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)
174 searchResultDataset.appendChild(createNoResult(text))
178 function debounce (func, wait, immediate) {
183 var later = function () {
185 if (!immediate) func.apply(context, args)
187 var callNow = immediate && !timeout
188 clearTimeout(timeout)
189 timeout = setTimeout(later, wait)
190 if (callNow) func.apply(context, args)
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)
199 searchInput.addEventListener('keydown', search)
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)