opendoc-theme 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/_includes/directory.html +95 -0
- data/_includes/document-title.txt +58 -0
- data/_includes/toc.html +107 -0
- data/_includes/toolbar.html +53 -0
- data/_includes/welcome.html +5 -0
- data/_layouts/default.html +147 -0
- data/_layouts/home.html +4 -0
- data/_layouts/iframe.html +13 -0
- data/_layouts/page.html +4 -0
- data/_layouts/print.html +27 -0
- data/_sass/_base.scss +381 -0
- data/_sass/_constants.scss +87 -0
- data/_sass/_iframe.scss +7 -0
- data/_sass/_layout.scss +419 -0
- data/_sass/_nav.scss +592 -0
- data/_sass/_print.scss +43 -0
- data/_sass/_syntax-highlighting.scss +61 -0
- data/_sass/_toolbar.scss +372 -0
- data/_sass/_welcome.scss +41 -0
- data/assets/export.md +30 -0
- data/assets/images/chevron-up-white.svg +1 -0
- data/assets/images/chevron-up.svg +1 -0
- data/assets/images/close.svg +17 -0
- data/assets/images/favicon.ico +0 -0
- data/assets/images/feedback-hover.svg +3 -0
- data/assets/images/feedback-mobile.svg +1 -0
- data/assets/images/feedback.svg +1 -0
- data/assets/images/github-hover.svg +3 -0
- data/assets/images/github.svg +1 -0
- data/assets/images/home.svg +14 -0
- data/assets/images/index-img.png +0 -0
- data/assets/images/logo.png +0 -0
- data/assets/images/menu.svg +1 -0
- data/assets/images/opendoc-logo-full.svg +10 -0
- data/assets/images/pdf-hover.svg +11 -0
- data/assets/images/pdf.svg +9 -0
- data/assets/images/search-icon-dark.svg +19 -0
- data/assets/images/search-icon-white.svg +12 -0
- data/assets/images/share.svg +1 -0
- data/assets/images/sidebar-hover.svg +3 -0
- data/assets/images/sidebar.svg +1 -0
- data/assets/images/vertical-dots.svg +1 -0
- data/assets/images/x-mobile.svg +1 -0
- data/assets/index.html +5 -0
- data/assets/js/banner.js +20 -0
- data/assets/js/google_analytics.js +11 -0
- data/assets/js/header.js +31 -0
- data/assets/js/helpers.js +24 -0
- data/assets/js/lunr.min.js +6 -0
- data/assets/js/navigation.js +202 -0
- data/assets/js/page-index.js +57 -0
- data/assets/js/pqueue.js +373 -0
- data/assets/js/pre-loader.js +43 -0
- data/assets/js/search.js +580 -0
- data/assets/js/toolbar.js +130 -0
- data/assets/siteIndex.json +56 -0
- data/assets/startup/build.sh +18 -0
- data/assets/startup/docprint.html +20 -0
- data/assets/startup/pdf-gen.js +309 -0
- data/assets/startup/prebuild-lunr-index.js +52 -0
- data/assets/styles/main.scss +13 -0
- data/assets/styles/normalize.css +427 -0
- data/assets/vendor/babel-polyfill.min.js +3 -0
- data/assets/vendor/dom4.js +2 -0
- data/assets/vendor/fetch.umd.js +531 -0
- data/assets/vendor/headroom.min.js +7 -0
- data/assets/vendor/jump.min.js +2 -0
- data/assets/vendor/mark.min.js +7 -0
- data/assets/vendor/popper.min.js +5 -0
- data/assets/vendor/web-share-shim.bundle.min.js +2 -0
- metadata +158 -0
data/assets/js/search.js
ADDED
@@ -0,0 +1,580 @@
|
|
1
|
+
---
|
2
|
+
---
|
3
|
+
(function () {
|
4
|
+
// Search Box Element
|
5
|
+
// =============================================================================
|
6
|
+
var siteSearchElement = document.getElementsByClassName('search-box')[0]
|
7
|
+
var searchBoxElement = document.getElementById('search-box')
|
8
|
+
var clearButton = document.getElementsByClassName('clear-button')[0]
|
9
|
+
var main = document.getElementsByTagName('main')[0]
|
10
|
+
var searchFilter = document.getElementsByClassName('search-filter')[0]
|
11
|
+
var searchResults = document.getElementsByClassName('search-results')[0]
|
12
|
+
|
13
|
+
searchBoxElement.oninput = function (event) {
|
14
|
+
if (searchBoxElement.value && searchBoxElement.value.trim().length > 0) {
|
15
|
+
siteSearchElement.classList.add('filled')
|
16
|
+
onSearchChangeDebounced()
|
17
|
+
} else {
|
18
|
+
siteSearchElement.classList.remove('filled')
|
19
|
+
onSearchChange()
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
searchBoxElement.onfocus = function () {
|
24
|
+
siteSearchElement.classList.add('focused')
|
25
|
+
if (siteSearchElement.classList.contains('filled')) {
|
26
|
+
searchResults.classList.add('visible')
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
searchBoxElement.onblur = function() {
|
31
|
+
siteSearchElement.classList.remove('focused')
|
32
|
+
}
|
33
|
+
|
34
|
+
document.body.addEventListener('click', function (event) {
|
35
|
+
var target = event.target
|
36
|
+
if (target.id !== 'search-box' && !target.classList.contains('search-btn') && !target.parentNode.classList.contains('search-btn')) {
|
37
|
+
searchResults.classList.remove('visible')
|
38
|
+
}
|
39
|
+
})
|
40
|
+
|
41
|
+
clearButton.onclick = function () {
|
42
|
+
searchBoxElement.value = ''
|
43
|
+
searchBoxElement.dispatchEvent(new Event('input', {
|
44
|
+
'bubbles': true,
|
45
|
+
'cancelable': true
|
46
|
+
}))
|
47
|
+
}
|
48
|
+
|
49
|
+
// Assign search endpoint based on env config
|
50
|
+
// ===========================================================================
|
51
|
+
var endpoint = null
|
52
|
+
var env = '{{ jekyll.environment }}'
|
53
|
+
var elasticSearchIndex = '{{site.github.owner_name}}-{{site.github.repository_name}}'
|
54
|
+
|
55
|
+
if (env === 'production') {
|
56
|
+
endpoint = '{{ site.server_PROD | append: '/' }}' + elasticSearchIndex
|
57
|
+
} else {
|
58
|
+
// Allow overriding of search index in dev env
|
59
|
+
var configElasticSearchIndex = '{{site.elastic_search_index}}'
|
60
|
+
if (configElasticSearchIndex) {
|
61
|
+
elasticSearchIndex = configElasticSearchIndex
|
62
|
+
}
|
63
|
+
endpoint = '{{ site.server_DEV | append: '/' }}' + elasticSearchIndex
|
64
|
+
}
|
65
|
+
|
66
|
+
var search_endpoint = endpoint + '/search'
|
67
|
+
|
68
|
+
|
69
|
+
// Global Variables
|
70
|
+
// =============================================================================
|
71
|
+
|
72
|
+
var wordsToHighlight = []
|
73
|
+
var sectionIndex = {}
|
74
|
+
var minQueryLength = 3
|
75
|
+
var lunrIndex = null
|
76
|
+
// Begin Lunr Indexing
|
77
|
+
// =============================================================================
|
78
|
+
function getLunrIndex() {
|
79
|
+
return fetch('/assets/lunrIndex.json')
|
80
|
+
.then(function (res) {
|
81
|
+
return res.json()
|
82
|
+
})
|
83
|
+
.then(function (json) {
|
84
|
+
lunrIndex = lunr.Index.load(json.index)
|
85
|
+
lunrIndex.pipeline.remove(lunr.stemmer)
|
86
|
+
sectionIndex = json.sectionIndex
|
87
|
+
})
|
88
|
+
.catch(function (err) {
|
89
|
+
console.error('Fetch failed to read the Lunr index: ' + err)
|
90
|
+
})
|
91
|
+
}
|
92
|
+
|
93
|
+
// Load Lunr Index if set
|
94
|
+
// ============================================================================
|
95
|
+
var searchSetOffline = "{{ site.offline_search_only }}" === "true" || false
|
96
|
+
|
97
|
+
if (searchSetOffline) {
|
98
|
+
getLunrIndex()
|
99
|
+
}
|
100
|
+
|
101
|
+
// Search
|
102
|
+
// =============================================================================
|
103
|
+
// Helper function to translate lunr search results
|
104
|
+
// Returns a simple { title, content, link } array
|
105
|
+
var snippetSpace = 40
|
106
|
+
var maxSnippets = 4
|
107
|
+
var maxResults = 10
|
108
|
+
var translateLunrResults = function (allLunrResults) {
|
109
|
+
var lunrResults = allLunrResults.slice(0, maxResults)
|
110
|
+
return lunrResults.map(function (result) {
|
111
|
+
var matchedDocument = sectionIndex[result.ref]
|
112
|
+
var contentSnippets = []
|
113
|
+
var titleSnippets = []
|
114
|
+
var snippetsRangesByFields = {}
|
115
|
+
// Loop over matching terms
|
116
|
+
var rangesByFields = {}
|
117
|
+
// Group ranges according to field type(text / title)
|
118
|
+
for (var term in result.matchData.metadata) {
|
119
|
+
// To highlight the main body later
|
120
|
+
wordsToHighlight.push(term)
|
121
|
+
var fields = result.matchData.metadata[term]
|
122
|
+
for (var field in fields) {
|
123
|
+
positions = fields[field].position
|
124
|
+
rangesByFields[field] = rangesByFields[field] ? rangesByFields[field].concat(positions) : positions
|
125
|
+
}
|
126
|
+
}
|
127
|
+
var snippetCount = 0
|
128
|
+
// Sort according to ascending snippet range
|
129
|
+
for (var field in rangesByFields) {
|
130
|
+
var ranges = rangesByFields[field]
|
131
|
+
.map(function (a) {
|
132
|
+
return [a[0] - snippetSpace, a[0] + a[1] + snippetSpace, a[0], a[0] + a[1]]
|
133
|
+
})
|
134
|
+
.sort(function (a, b) {
|
135
|
+
return a[0] - b[0]
|
136
|
+
})
|
137
|
+
// Merge contiguous ranges
|
138
|
+
var startIndex = ranges[0][0]
|
139
|
+
var endIndex = ranges[0][1]
|
140
|
+
var mergedRanges = []
|
141
|
+
var highlightRanges = []
|
142
|
+
for (rangeIndex in ranges) {
|
143
|
+
var range = ranges[rangeIndex]
|
144
|
+
snippetCount++
|
145
|
+
if (range[0] <= endIndex) {
|
146
|
+
endIndex = Math.max(range[1], endIndex)
|
147
|
+
highlightRanges = highlightRanges.concat([range[2], range[3]])
|
148
|
+
} else {
|
149
|
+
mergedRanges.push([startIndex].concat(highlightRanges).concat([endIndex]))
|
150
|
+
startIndex = range[0]
|
151
|
+
endIndex = range[1]
|
152
|
+
highlightRanges = [range[2], range[3]]
|
153
|
+
}
|
154
|
+
if (snippetCount >= maxSnippets) {
|
155
|
+
mergedRanges.push([startIndex].concat(highlightRanges).concat([endIndex]))
|
156
|
+
snippetsRangesByFields[field] = mergedRanges
|
157
|
+
break
|
158
|
+
}
|
159
|
+
if (+rangeIndex === ranges.length - 1) {
|
160
|
+
if (snippetCount + 1 < maxSnippets) {
|
161
|
+
snippetCount++
|
162
|
+
}
|
163
|
+
mergedRanges.push([startIndex].concat(highlightRanges).concat([endIndex]))
|
164
|
+
snippetsRangesByFields[field] = mergedRanges
|
165
|
+
if (snippetCount >= maxSnippets) {
|
166
|
+
break
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
// Extract snippets and add highlights to search results
|
172
|
+
for (var field in snippetsRangesByFields) {
|
173
|
+
positions = snippetsRangesByFields[field]
|
174
|
+
positions.forEach(function (position) {
|
175
|
+
matchedText = matchedDocument[field]
|
176
|
+
var snippet = ''
|
177
|
+
// If start of matched text dont use ellipsis
|
178
|
+
if (position[0] > 0) {
|
179
|
+
snippet += '...'
|
180
|
+
}
|
181
|
+
snippet += matchedText.substring(position[0], position[1])
|
182
|
+
for (var i = 1; i <= position.length - 2; i++) {
|
183
|
+
if (i % 2 == 1) {
|
184
|
+
snippet += '<mark>'
|
185
|
+
} else {
|
186
|
+
snippet += '</mark>'
|
187
|
+
}
|
188
|
+
snippet += matchedText.substring(position[i], position[i + 1])
|
189
|
+
}
|
190
|
+
if (field === 'title') {
|
191
|
+
titleSnippets.push(snippet)
|
192
|
+
} else {
|
193
|
+
snippet += '...'
|
194
|
+
contentSnippets.push(snippet)
|
195
|
+
}
|
196
|
+
})
|
197
|
+
}
|
198
|
+
var joinHighlights = function (str) {
|
199
|
+
if (str) {
|
200
|
+
return str.replace(/<\/mark> <mark>/g, ' ')
|
201
|
+
}
|
202
|
+
}
|
203
|
+
// Build a simple flat object per lunr result
|
204
|
+
return {
|
205
|
+
title: joinHighlights(titleSnippets.length === 0 ? matchedDocument.title: titleSnippets.join(' ')),
|
206
|
+
documentTitle: joinHighlights(matchedDocument.documentTitle),
|
207
|
+
content: joinHighlights(contentSnippets.join(' ')),
|
208
|
+
url: matchedDocument.url
|
209
|
+
}
|
210
|
+
})
|
211
|
+
}
|
212
|
+
|
213
|
+
// Displays the search results in HTML
|
214
|
+
// Takes an array of objects with "title" and "content" properties
|
215
|
+
var renderSearchResultsFromLunr = function (searchResults) {
|
216
|
+
var container = document.getElementsByClassName('search-results')[0]
|
217
|
+
container.scrollTop = 0
|
218
|
+
container.innerHTML = ''
|
219
|
+
if (!searchResults || searchResults.length === 0) {
|
220
|
+
var error = generateErrorHTML()
|
221
|
+
container.append(error)
|
222
|
+
} else {
|
223
|
+
searchResults.forEach(function (result, i) {
|
224
|
+
var element = generateResultHTML(result, i)
|
225
|
+
container.appendChild(element)
|
226
|
+
})
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
230
|
+
var renderSearchResultsFromServer = function (searchResults) {
|
231
|
+
var container = document.getElementsByClassName('search-results')[0]
|
232
|
+
container.scrollTop = 0
|
233
|
+
container.innerHTML = ''
|
234
|
+
if (typeof searchResults.hits === 'undefined') {
|
235
|
+
var error = document.createElement('p')
|
236
|
+
error.classList.add('not-found')
|
237
|
+
error.innerHTML = searchResults
|
238
|
+
container.appendChild(error)
|
239
|
+
// Check if there are hits and max_score is more than 0
|
240
|
+
// Max score is checked as well as filter will always return something
|
241
|
+
} else if (searchResults.hits.hits.length === 0 || searchResults.hits['max_score'] === 0) {
|
242
|
+
var error = generateErrorHTML()
|
243
|
+
container.appendChild(error)
|
244
|
+
} else {
|
245
|
+
searchResults.hits.hits.forEach(function (result, i) {
|
246
|
+
if (result._score) {
|
247
|
+
var formatted = formatResult(result, i)
|
248
|
+
var element = generateResultHTML(formatted)
|
249
|
+
container.appendChild(element)
|
250
|
+
}
|
251
|
+
});
|
252
|
+
highlightBody()
|
253
|
+
}
|
254
|
+
}
|
255
|
+
|
256
|
+
var generateErrorHTML = function () {
|
257
|
+
var error = document.createElement('p')
|
258
|
+
error.innerHTML = 'Results matching your query were not found.'
|
259
|
+
error.classList.add('not-found')
|
260
|
+
return error
|
261
|
+
}
|
262
|
+
|
263
|
+
var generateResultHTML = function (result, i) {
|
264
|
+
var element = document.createElement('a')
|
265
|
+
element.className = 'search-link nav-link'
|
266
|
+
var urlParts = ('{{site.baseurl}}' + result.url).split('/')
|
267
|
+
urlParts = urlParts.filter(function (part) {
|
268
|
+
return part !== ''
|
269
|
+
})
|
270
|
+
element.href = '/' + urlParts.join('/')
|
271
|
+
var searchResult = document.createElement('div')
|
272
|
+
var searchTitle = document.createElement('p')
|
273
|
+
searchTitle.className = 'search-title'
|
274
|
+
searchTitle.innerHTML = result.documentTitle || '{{ site.title }}'
|
275
|
+
searchResult.appendChild(searchTitle)
|
276
|
+
var searchSubtitle = document.createElement('p')
|
277
|
+
searchSubtitle.className = 'search-subtitle'
|
278
|
+
searchSubtitle.innerHTML = result.title
|
279
|
+
searchResult.appendChild(searchSubtitle)
|
280
|
+
var searchContent = document.createElement('p')
|
281
|
+
searchContent.className = 'search-content'
|
282
|
+
searchContent.innerHTML = result.content
|
283
|
+
searchResult.appendChild(searchContent)
|
284
|
+
element.onmouseup = function() {
|
285
|
+
searchResults.classList.remove('visible')
|
286
|
+
}
|
287
|
+
element.appendChild(searchResult)
|
288
|
+
return element
|
289
|
+
}
|
290
|
+
|
291
|
+
formatResult = function (result) {
|
292
|
+
var content = null
|
293
|
+
var title = result._source.title
|
294
|
+
var url = result._source.url;
|
295
|
+
var documentTitle = result._source.documentTitle;
|
296
|
+
var regex = /<mark>(.*?)<\/mark>/g
|
297
|
+
var joinHighlights = function (str) {
|
298
|
+
if (str) {
|
299
|
+
return str.replace(/<\/mark> <mark>/g, ' ')
|
300
|
+
}
|
301
|
+
}
|
302
|
+
if (result.highlight) {
|
303
|
+
['title', 'content'].forEach(function (field) {
|
304
|
+
var curr, match, term;
|
305
|
+
if (result.highlight[field]) {
|
306
|
+
var curr = result.highlight[field].join('...')
|
307
|
+
// trimLeft not supported in IE
|
308
|
+
var curr = curr.replace(/^\s+/, "")
|
309
|
+
var curr = joinHighlights(curr)
|
310
|
+
var match = true
|
311
|
+
while (match) {
|
312
|
+
match = regex.exec(curr)
|
313
|
+
if (match) {
|
314
|
+
var term = match[1].toLowerCase()
|
315
|
+
if ((wordsToHighlight.indexOf(term)) < 0) {
|
316
|
+
wordsToHighlight.push(term)
|
317
|
+
}
|
318
|
+
}
|
319
|
+
}
|
320
|
+
}
|
321
|
+
})
|
322
|
+
if (result.highlight.content) {
|
323
|
+
content = joinHighlights(result.highlight.content.slice(0, Math.min(3, result.highlight.content.length)).join('...'))
|
324
|
+
}
|
325
|
+
if (result.highlight.title) {
|
326
|
+
title = joinHighlights(result.highlight.title[0])
|
327
|
+
}
|
328
|
+
}
|
329
|
+
return {
|
330
|
+
url: url,
|
331
|
+
content: content ? '...' + content + '...' : '',
|
332
|
+
title: title,
|
333
|
+
documentTitle: documentTitle
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
var debounce = function (func, threshold, execAsap) {
|
338
|
+
var timeout = null;
|
339
|
+
return function () {
|
340
|
+
var args = 1 <= arguments.length ? slice.call(arguments, 0) : []
|
341
|
+
obj = this
|
342
|
+
var delayed = function () {
|
343
|
+
if (!execAsap) {
|
344
|
+
func.apply(obj, args)
|
345
|
+
}
|
346
|
+
timeout = null
|
347
|
+
}
|
348
|
+
if (timeout) {
|
349
|
+
clearTimeout(timeout)
|
350
|
+
} else if (execAsap) {
|
351
|
+
func.apply(obj, args)
|
352
|
+
}
|
353
|
+
timeout = setTimeout(delayed, threshold || 100)
|
354
|
+
}
|
355
|
+
}
|
356
|
+
|
357
|
+
|
358
|
+
var createEsQuery = function (queryStr) {
|
359
|
+
var source = ['title', 'url', 'documentTitle']
|
360
|
+
var title_automcomplete_q = {
|
361
|
+
'match_phrase_prefix': {
|
362
|
+
'title': {
|
363
|
+
'query': queryStr,
|
364
|
+
'max_expansions': 20,
|
365
|
+
'boost': 100,
|
366
|
+
'slop': 10
|
367
|
+
}
|
368
|
+
}
|
369
|
+
}
|
370
|
+
var content_automcomplete_q = {
|
371
|
+
'match_phrase_prefix': {
|
372
|
+
'content': {
|
373
|
+
'query': queryStr,
|
374
|
+
'max_expansions': 20,
|
375
|
+
'boost': 60,
|
376
|
+
'slop': 10
|
377
|
+
}
|
378
|
+
}
|
379
|
+
}
|
380
|
+
var title_keyword_q = {
|
381
|
+
'match': {
|
382
|
+
'title': {
|
383
|
+
'query': queryStr,
|
384
|
+
'fuzziness': 'AUTO',
|
385
|
+
'max_expansions': 10,
|
386
|
+
'boost': 20,
|
387
|
+
'analyzer': 'stop'
|
388
|
+
}
|
389
|
+
}
|
390
|
+
}
|
391
|
+
var content_keyword_q = {
|
392
|
+
'match': {
|
393
|
+
'content': {
|
394
|
+
'query': queryStr,
|
395
|
+
'fuzziness': 'AUTO',
|
396
|
+
'max_expansions': 10,
|
397
|
+
'analyzer': 'stop'
|
398
|
+
}
|
399
|
+
}
|
400
|
+
}
|
401
|
+
|
402
|
+
var bool_q = {
|
403
|
+
'bool': {
|
404
|
+
'should': [title_automcomplete_q, content_automcomplete_q, title_keyword_q, content_keyword_q],
|
405
|
+
}
|
406
|
+
}
|
407
|
+
|
408
|
+
// If document filter is present
|
409
|
+
var page = pageIndex[window.location.pathname]
|
410
|
+
if (!searchFilter.classList.contains('hidden') && page && page.documentInfo[0]) {
|
411
|
+
// documentId is the alphanumeric and lowercase version of document title
|
412
|
+
// used as a keyword filter to search within the document
|
413
|
+
var documentId = page.documentInfo[0].replace(/[^\w]/g, '').toLowerCase()
|
414
|
+
var filter_by_document = {
|
415
|
+
'term': {
|
416
|
+
'documentId': documentId
|
417
|
+
}
|
418
|
+
}
|
419
|
+
bool_q.bool.filter = filter_by_document
|
420
|
+
}
|
421
|
+
|
422
|
+
var highlight = {}
|
423
|
+
highlight.require_field_match = false
|
424
|
+
highlight.fields = {}
|
425
|
+
highlight.fields['content'] = {
|
426
|
+
'fragment_size': 80,
|
427
|
+
'number_of_fragments': 6,
|
428
|
+
'pre_tags': ['<mark>'],
|
429
|
+
'post_tags': ['</mark>']
|
430
|
+
}
|
431
|
+
highlight.fields['title'] = {
|
432
|
+
'fragment_size': 80,
|
433
|
+
'number_of_fragments': 6,
|
434
|
+
'pre_tags': ['<mark>'],
|
435
|
+
'post_tags': ['</mark>']
|
436
|
+
}
|
437
|
+
return {
|
438
|
+
'_source': source,
|
439
|
+
'query': bool_q,
|
440
|
+
'highlight': highlight
|
441
|
+
}
|
442
|
+
}
|
443
|
+
|
444
|
+
// Call the API
|
445
|
+
esSearch = function (query) {
|
446
|
+
var esQuery = createEsQuery(query)
|
447
|
+
fetch(search_endpoint, {
|
448
|
+
method: 'POST',
|
449
|
+
headers: {
|
450
|
+
'Content-Type': 'application/json'
|
451
|
+
},
|
452
|
+
body: JSON.stringify(esQuery)
|
453
|
+
})
|
454
|
+
.then(checkStatus)
|
455
|
+
.then(parseJSON)
|
456
|
+
.then(function (data) {
|
457
|
+
renderSearchResultsFromServer(data.body)
|
458
|
+
})
|
459
|
+
.catch(function (err) {
|
460
|
+
console.error(err)
|
461
|
+
renderSearchResultsFromServer('Failed to fetch search results')
|
462
|
+
})
|
463
|
+
}
|
464
|
+
|
465
|
+
var lunrSearch = function (query) {
|
466
|
+
// Add wildcard before and after
|
467
|
+
var queryTerm = refineLunrSearchQuery(query)
|
468
|
+
if (lunrIndex !== null) {
|
469
|
+
var lunrResults = lunrIndex.search(queryTerm)
|
470
|
+
var results = translateLunrResults(lunrResults)
|
471
|
+
highlightBody()
|
472
|
+
renderSearchResultsFromLunr(results)
|
473
|
+
}
|
474
|
+
}
|
475
|
+
|
476
|
+
var refineLunrSearchQuery = function(query) {
|
477
|
+
FUZZY_FACTOR = 4 // range: 1 to INF. Lower is fuzzier *relative to term length*.
|
478
|
+
var addFuzzyOperator = function(term, fuzziness) {
|
479
|
+
return term + '~' +
|
480
|
+
Math.floor(term.length / Math.max(1, fuzziness)).toString()
|
481
|
+
}
|
482
|
+
var stringIsLettersOnly = function(str) {
|
483
|
+
return /^[a-zA-Z]+$/.test(str)
|
484
|
+
}
|
485
|
+
|
486
|
+
var terms = query.split(' ')
|
487
|
+
terms = terms.map(function(term) {
|
488
|
+
if (stringIsLettersOnly(term)) {
|
489
|
+
return addFuzzyOperator(term, FUZZY_FACTOR)
|
490
|
+
}
|
491
|
+
return term
|
492
|
+
})
|
493
|
+
return terms.join(' ')
|
494
|
+
}
|
495
|
+
|
496
|
+
|
497
|
+
var onSearchChange = function () {
|
498
|
+
var query = searchBoxElement.value.trim()
|
499
|
+
// Clear highlights
|
500
|
+
wordsToHighlight = []
|
501
|
+
if (query.length < minQueryLength) {
|
502
|
+
searchResults.classList.remove('visible')
|
503
|
+
highlightBody()
|
504
|
+
return
|
505
|
+
}
|
506
|
+
searchResults.classList.add('visible')
|
507
|
+
|
508
|
+
if (searchSetOffline) {
|
509
|
+
lunrSearch(query)
|
510
|
+
} else {
|
511
|
+
esSearch(query)
|
512
|
+
if (env === 'production' && window.ga) {
|
513
|
+
window.ga('send', 'pageview', '/search?query=' + encodeURIComponent(query))
|
514
|
+
}
|
515
|
+
}
|
516
|
+
}
|
517
|
+
|
518
|
+
var onSearchChangeDebounced = debounce(onSearchChange, 500, false)
|
519
|
+
|
520
|
+
var isBackspaceFirstPress = true
|
521
|
+
var isBackspacePressedOnEmpty = false
|
522
|
+
// Detect that backspace is not part of longpress
|
523
|
+
searchBoxElement.onkeydown = function (e) {
|
524
|
+
searchResults.classList.remove('hidden')
|
525
|
+
if (isBackspaceFirstPress && e.keyCode === 8) {
|
526
|
+
isBackspaceFirstPress = false
|
527
|
+
if (searchBoxElement.value === '') {
|
528
|
+
isBackspacePressedOnEmpty = true
|
529
|
+
}
|
530
|
+
}
|
531
|
+
}
|
532
|
+
|
533
|
+
clearSearchFilter = function () {
|
534
|
+
searchFilter.classList.add('hidden')
|
535
|
+
}
|
536
|
+
|
537
|
+
searchFilter.onclick = clearSearchFilter
|
538
|
+
|
539
|
+
searchBoxElement.onkeyup = function (e) {
|
540
|
+
// flash search results on enter
|
541
|
+
if (e.keyCode === 13) {
|
542
|
+
var container = document.getElementsByClassName('search-results')[0]
|
543
|
+
container.style.opacity = 0
|
544
|
+
return setTimeout(function () {
|
545
|
+
return container.style.opacity = 1
|
546
|
+
}, 100)
|
547
|
+
}
|
548
|
+
// Delete filter on backspace when input is empty and not part of longpress
|
549
|
+
if (e.keyCode === 8) {
|
550
|
+
isBackspaceFirstPress = true
|
551
|
+
if (searchBoxElement.value === '' && isBackspacePressedOnEmpty) {
|
552
|
+
clearSearchFilter()
|
553
|
+
isBackspacePressedOnEmpty = false
|
554
|
+
return
|
555
|
+
}
|
556
|
+
}
|
557
|
+
}
|
558
|
+
|
559
|
+
|
560
|
+
// Highlighting
|
561
|
+
// ============================================================================
|
562
|
+
window.highlightBody = function () {
|
563
|
+
// Check if Mark.js script is already imported
|
564
|
+
if (Mark) {
|
565
|
+
var instance = new Mark(main)
|
566
|
+
instance.unmark()
|
567
|
+
if (wordsToHighlight.length > 0) {
|
568
|
+
instance.mark(wordsToHighlight, {
|
569
|
+
exclude: ['h1'],
|
570
|
+
accuracy: {
|
571
|
+
value: 'exactly',
|
572
|
+
limiters: [',', '.', '(', ')', '-', '\'', '[', ']', '?', '/', '\\', ':', '*', '!', '@', '&']
|
573
|
+
},
|
574
|
+
separateWordSearch: false
|
575
|
+
})
|
576
|
+
}
|
577
|
+
}
|
578
|
+
}
|
579
|
+
|
580
|
+
})()
|