|
1 | 1 | ;(function () { |
2 | 2 | 'use strict' |
3 | 3 |
|
4 | | - activateSearch(require('docsearch.js/dist/cdn/docsearch.js'), document.getElementById('search-script').dataset) |
| 4 | + var CTRL_KEY_CODE = 17 |
| 5 | + var S_KEY_CODE = 83 |
| 6 | + var SOLIDUS_KEY_CODE = 191 |
5 | 7 |
|
6 | | - var F_KEY = 70 |
7 | | - var S_KEY = 83 |
| 8 | + activateSearch(require('docsearch.js/dist/cdn/docsearch.js'), document.getElementById('search-script').dataset) |
8 | 9 |
|
9 | 10 | function activateSearch (docsearch, config) { |
10 | 11 | appendStylesheet(config.stylesheet) |
|
13 | 14 | advancedSyntax: true, |
14 | 15 | advancedSyntaxFeatures: ['exactPhrase'], |
15 | 16 | } |
16 | | - var searchForm = document.querySelector('form.search') |
| 17 | + var searchField = document.querySelector('form.search') |
17 | 18 | var controller = docsearch({ |
18 | 19 | appId: config.appId, |
19 | 20 | apiKey: config.apiKey, |
20 | 21 | indexName: config.indexName, |
21 | | - inputSelector: '#search-query', |
22 | | - autocompleteOptions: { autoselect: true, debug: true, hint: false, keyboardShortcuts: [], minLength: 2 }, |
| 22 | + inputSelector: 'form.search #search-query', |
| 23 | + autocompleteOptions: { autoselect: false, debug: true, hint: false, keyboardShortcuts: [], minLength: 2 }, |
23 | 24 | algoliaOptions: algoliaOptions, |
24 | | - transformData: transformData, |
| 25 | + transformData: protectHitOrder, |
25 | 26 | }) |
26 | 27 | var input = controller.input |
27 | | - var autocomplete = input.autocomplete |
28 | | - autocomplete.setVal() |
29 | | - input.on('autocomplete:selected', disableClose) |
30 | | - input.data('aaAutocomplete').dropdown._ensureVisible = ensureVisible |
31 | | - searchForm.addEventListener('click', confineEvent) |
32 | | - document.documentElement.addEventListener('click', resetSearch.bind(autocomplete)) |
33 | | - document.documentElement.addEventListener('keydown', handleShortcuts.bind(input)) |
| 28 | + var typeahead = input.data('aaAutocomplete') |
| 29 | + var dropdown = typeahead.dropdown |
| 30 | + var menu = dropdown.$menu |
| 31 | + typeahead.setVal() // clear value on page reload |
| 32 | + input.on('autocomplete:closed', clearSearch.bind(typeahead)) |
| 33 | + input.on('autocomplete:selected', onSuggestionSelected) |
| 34 | + input.on('autocomplete:updated', onResultsUpdated.bind(typeahead)) |
| 35 | + dropdown._ensureVisible = ensureVisible |
| 36 | + menu.off('mousedown.aa') |
| 37 | + menu.off('mouseenter.aa') |
| 38 | + menu.off('mouseleave.aa') |
| 39 | + var suggestionSelector = '.' + dropdown.cssClasses.prefix + dropdown.cssClasses.suggestion |
| 40 | + menu.on('mousedown.aa', suggestionSelector, onSuggestionMouseDown.bind(dropdown)) |
| 41 | + monitorCtrlKey.call(typeahead) |
| 42 | + searchField.addEventListener('click', confineEvent) |
| 43 | + document.documentElement.addEventListener('click', clearSearch.bind(typeahead)) |
| 44 | + document.addEventListener('keydown', handleShortcuts.bind(typeahead)) |
34 | 45 | if (input.attr('autofocus') != null) input.focus() |
35 | 46 | } |
36 | 47 |
|
37 | 48 | function appendStylesheet (href) { |
38 | 49 | document.head.appendChild(Object.assign(document.createElement('link'), { rel: 'stylesheet', href: href })) |
39 | 50 | } |
40 | 51 |
|
41 | | - function confineEvent (e) { |
42 | | - e.stopPropagation() |
| 52 | + function onResultsUpdated () { |
| 53 | + if (!isClosed(this)) getScrollableResultsContainer(this.dropdown).scrollTop(0) |
43 | 54 | } |
44 | 55 |
|
45 | | - function disableClose (e) { |
46 | | - e.isDefaultPrevented = function () { |
47 | | - return true |
48 | | - } |
| 56 | + function confineEvent (e) { |
| 57 | + e.stopPropagation() |
49 | 58 | } |
50 | 59 |
|
51 | 60 | function ensureVisible (el) { |
52 | | - var item = el.get(0) |
53 | | - var container = item |
54 | | - while ((container = container.parentNode) && container !== document.documentElement) { |
55 | | - if (window.getComputedStyle(container).overflowY === 'auto') break |
56 | | - } |
57 | | - if (!container || container.scrollHeight === container.offsetHeight) return |
| 61 | + var container = getScrollableResultsContainer(this)[0] |
| 62 | + if (container.scrollHeight === container.offsetHeight) return |
58 | 63 | var delta |
| 64 | + var item = el[0] |
59 | 65 | if ((delta = 15 + item.offsetTop + item.offsetHeight - (container.offsetHeight + container.scrollTop)) > 0) { |
60 | 66 | container.scrollTop += delta |
61 | 67 | } |
|
64 | 70 | } |
65 | 71 | } |
66 | 72 |
|
| 73 | + function getScrollableResultsContainer (dropdown) { |
| 74 | + var suggestionsSelector = '.' + dropdown.cssClasses.prefix + dropdown.cssClasses.suggestions |
| 75 | + return dropdown.datasets[0].$el.find(suggestionsSelector) |
| 76 | + } |
| 77 | + |
67 | 78 | function handleShortcuts (e) { |
68 | | - if (e.altKey || e.metaKey || e.ctrlKey || e.shiftKey) return |
69 | | - var keyCode = e.keyCode |
70 | | - if (keyCode === F_KEY || keyCode === S_KEY) { |
71 | | - this.focus() |
| 79 | + var target = e.target || {} |
| 80 | + if (e.altKey || e.shiftKey || target.isContentEditable || 'disabled' in target) return |
| 81 | + if (e.ctrlKey ? e.keyCode === SOLIDUS_KEY_CODE : e.keyCode === S_KEY_CODE) { |
| 82 | + this.$input.focus() |
72 | 83 | e.preventDefault() |
| 84 | + e.stopPropagation() |
| 85 | + } |
| 86 | + } |
| 87 | + |
| 88 | + function isClosed (typeahead) { |
| 89 | + var query = typeahead.getVal() |
| 90 | + return !query || query !== typeahead.dropdown.datasets[0].query |
| 91 | + } |
| 92 | + |
| 93 | + function monitorCtrlKey () { |
| 94 | + this.$input.on('keydown', onCtrlKeyDown.bind(this)) |
| 95 | + this.dropdown.$container.on('keyup', onCtrlKeyUp.bind(this)) |
| 96 | + } |
| 97 | + |
| 98 | + function onCtrlKeyDown (e) { |
| 99 | + if (e.keyCode !== CTRL_KEY_CODE) return |
| 100 | + var dropdown = this.dropdown |
| 101 | + var container = getScrollableResultsContainer(dropdown) |
| 102 | + var prevScrollTop = container.scrollTop() |
| 103 | + dropdown.getCurrentCursor().find('a').focus() |
| 104 | + container.scrollTop(prevScrollTop) // calling focus can cause the container to scroll, so restore it |
| 105 | + } |
| 106 | + |
| 107 | + function onCtrlKeyUp (e) { |
| 108 | + if (e.keyCode !== CTRL_KEY_CODE) return |
| 109 | + this.$input.focus() |
| 110 | + } |
| 111 | + |
| 112 | + function onSuggestionMouseDown (e) { |
| 113 | + var dropdown = this |
| 114 | + var suggestion = dropdown._getSuggestions().filter('#' + e.currentTarget.id) |
| 115 | + if (suggestion[0] === dropdown._getCursor()[0]) return |
| 116 | + dropdown._removeCursor() |
| 117 | + dropdown._setCursor(suggestion, false) |
| 118 | + } |
| 119 | + |
| 120 | + function onSuggestionSelected (e) { |
| 121 | + e.isDefaultPrevented = function () { |
| 122 | + return true |
73 | 123 | } |
74 | 124 | } |
75 | 125 |
|
76 | | - function resetSearch () { |
77 | | - this.close() |
| 126 | + function clearSearch () { |
78 | 127 | this.setVal() |
79 | 128 | } |
80 | 129 |
|
81 | | - // qualify separate occurrences of the same lvl0 title so that the order of results is preserved |
82 | | - function transformData (hits) { |
83 | | - var prevLvl0Title |
84 | | - var qualifiers = {} |
| 130 | + // preserves the original order of results by qualifying unique occurrences of the same lvl0 and lvl1 values |
| 131 | + function protectHitOrder (hits) { |
| 132 | + var prevLvl0 |
| 133 | + var lvl0Qualifiers = {} |
| 134 | + var lvl1Qualifiers = {} |
85 | 135 | return hits.map(function (hit) { |
86 | | - var lvl0Title = hit.hierarchy.lvl0 |
87 | | - var qualifier = qualifiers[lvl0Title] |
88 | | - if (lvl0Title !== prevLvl0Title) qualifiers[lvl0Title] = qualifier == null ? '' : (qualifier += ' ') |
89 | | - if (qualifier) hit.hierarchy.lvl0 = lvl0Title + qualifier |
90 | | - prevLvl0Title = lvl0Title |
| 136 | + var lvl0 = hit.hierarchy.lvl0 |
| 137 | + var lvl1 = hit.hierarchy.lvl1 |
| 138 | + if (!lvl0) lvl0 = hit.hierarchy.lvl0 = hit.component + (hit.version === 'master' ? '' : ' ' + hit.version) |
| 139 | + if (!lvl1) lvl1 = hit.hierarchy.lvl1 = lvl0 |
| 140 | + var lvl0Qualifier = lvl0Qualifiers[lvl0] |
| 141 | + if (lvl0 !== prevLvl0) { |
| 142 | + lvl0Qualifiers[lvl0] = lvl0Qualifier == null ? (lvl0Qualifier = '') : (lvl0Qualifier += ' ') |
| 143 | + lvl1Qualifiers = {} |
| 144 | + } |
| 145 | + if (lvl0Qualifier) hit.hierarchy.lvl0 = lvl0 + lvl0Qualifier |
| 146 | + if (lvl1 in lvl1Qualifiers) { |
| 147 | + hit.hierarchy.lvl1 = lvl1 + (lvl1Qualifiers[lvl1] += ' ') |
| 148 | + } else { |
| 149 | + lvl1Qualifiers[lvl1] = '' |
| 150 | + } |
| 151 | + prevLvl0 = lvl0 |
91 | 152 | return hit |
92 | 153 | }) |
93 | 154 | } |
|
0 commit comments