@@ -0,0 +1,238 @@
1+$(document).ready(function() {
2+ var vimKeys = {
3+ 27: {
4+ key: 'Escape',
5+ fun: removeFocus,
6+ des: 'remove focus from the focused input',
7+ cat: 'Control'
8+ },
9+ 73: {
10+ key: 'i',
11+ fun: searchInputFocus,
12+ des: 'focus on the search input',
13+ cat: 'Control'
14+ },
15+ 66: {
16+ key: 'b',
17+ fun: scrollPage(-window.innerHeight),
18+ des: 'scroll one page up',
19+ cat: 'Navigation'
20+ },
21+ 70: {
22+ key: 'f',
23+ fun: scrollPage(window.innerHeight),
24+ des: 'scroll one page down',
25+ cat: 'Navigation'
26+ },
27+ 85: {
28+ key: 'u',
29+ fun: scrollPage(-window.innerHeight / 2),
30+ des: 'scroll half a page up',
31+ cat: 'Navigation'
32+ },
33+ 68: {
34+ key: 'd',
35+ fun: scrollPage(window.innerHeight / 2),
36+ des: 'scroll half a page down',
37+ cat: 'Navigation'
38+ },
39+ 71: {
40+ key: 'g',
41+ fun: scrollPageTo(-document.body.scrollHeight),
42+ des: 'scroll to the top of the page',
43+ cat: 'Navigation'
44+ },
45+ 86: {
46+ key: 'v',
47+ fun: scrollPageTo(document.body.scrollHeight),
48+ des: 'scroll to the bottom of the page',
49+ cat: 'Navigation'
50+ },
51+ 75: {
52+ key: 'k',
53+ fun: previousResult,
54+ des: 'select previous search result',
55+ cat: 'Results'
56+ },
57+ 74: {
58+ key: 'j',
59+ fun: nextResult,
60+ des: 'select next search result',
61+ cat: 'Results'
62+ },
63+ 80: {
64+ key: 'p',
65+ fun: pageButtonClick(0),
66+ des: 'go to previous page',
67+ cat: 'Results'
68+ },
69+ 78: {
70+ key: 'n',
71+ fun: pageButtonClick(1),
72+ des: 'go to next page',
73+ cat: 'Results'
74+ },
75+ 79: {
76+ key: 'o',
77+ fun: openResult(false),
78+ des: 'open search result',
79+ cat: 'Results'
80+ },
81+ 84: {
82+ key: 't',
83+ fun: openResult(true),
84+ des: 'open the result in a new tab',
85+ cat: 'Results'
86+ },
87+ 82: {
88+ key: 'r',
89+ fun: reloadPage,
90+ des: 'reload page from the server',
91+ cat: 'Control'
92+ },
93+ 72: {
94+ key: 'h',
95+ fun: toggleHelp,
96+ des: 'toggle help window',
97+ cat: 'Other'
98+ }
99+ };
100+
101+ $(document).keyup(function(e) {
102+ // check for modifiers so we don't break browser's hotkeys
103+ if (vimKeys.hasOwnProperty(e.keyCode)
104+ && !e.ctrlKey
105+ && !e.altKey
106+ && !e.shiftKey
107+ && !e.metaKey)
108+ {
109+ if (e.keyCode === 27) {
110+ if (e.target.tagName.toLowerCase() === 'input') {
111+ vimKeys[e.keyCode].fun();
112+ }
113+ } else {
114+ if (e.target === document.body) {
115+ vimKeys[e.keyCode].fun();
116+ }
117+ }
118+ }
119+ });
120+
121+ function reloadPage() {
122+ document.location.reload(false);
123+ }
124+
125+ function removeFocus() {
126+ if (document.activeElement) {
127+ document.activeElement.blur();
128+ }
129+ }
130+
131+ function pageButtonClick(num) {
132+ return function() {
133+ var buttons = $('div#pagination button[type="submit"]');
134+ if (buttons.length !== 2) {
135+ console.log('page navigation with this theme is not supported');
136+ return;
137+ }
138+ if (num >= 0 && num < buttons.length) {
139+ buttons[num].click();
140+ } else {
141+ console.log('pageButtonClick(): invalid argument');
142+ }
143+ }
144+ }
145+
146+ function scrollPage(amount) {
147+ return function() {
148+ window.scrollBy(0, amount);
149+ }
150+ }
151+
152+ function scrollPageTo(position) {
153+ return function() {
154+ window.scrollTo(0, position);
155+ }
156+ }
157+
158+ function searchInputFocus() {
159+ $('input#q').focus();
160+ }
161+
162+ function previousResult() {
163+ }
164+
165+ function nextResult() {
166+ }
167+
168+ function openResult(newTab) {
169+ }
170+
171+ function toggleHelp() {
172+ var helpPanel = $('#vim-hotkeys-help');
173+ if (helpPanel.length) {
174+ helpPanel.toggleClass('hidden');
175+ return;
176+ }
177+
178+ var categories = {};
179+
180+ for (var k in vimKeys) {
181+ var key = vimKeys[k];
182+ categories[key.cat] = categories[key.cat] || [];
183+ categories[key.cat].push(key);
184+ }
185+
186+ var sorted = Object.keys(categories).sort(function(a, b) {
187+ return categories[b].length - categories[a].length;
188+ });
189+
190+ if (sorted.length === 0) {
191+ return;
192+ }
193+
194+ var html = '<div id="vim-hotkeys-help" class="well vim-hotkeys-help">';
195+ html += '<div class="container-fluid">';
196+
197+ html += '<div class="row">';
198+ html += '<div class="col-sm-12">';
199+ html += '<h3>How to navigate searx with Vim-like hotkeys</h3>';
200+ html += '</div>'; // col-sm-12
201+ html += '</div>'; // row
202+
203+ for (var i = 0; i < sorted.length; i++) {
204+ var cat = categories[sorted[i]];
205+
206+ var lastCategory = i === (sorted.length - 1);
207+ var first = i % 2 === 0;
208+
209+ if (first) {
210+ html += '<div class="row">';
211+ }
212+ html += '<div class="col-sm-' + (first && lastCategory ? 12 : 6) + '">';
213+
214+ html += '<div class="panel panel-default">';
215+ html += '<div class="panel-heading">' + cat[0].cat + '</div>';
216+ html += '<div class="panel-body">';
217+ html += '<ul class="list-unstyled">';
218+
219+ for (var cj in cat) {
220+ html += '<li><kbd>' + cat[cj].key + '</kbd> ' + cat[cj].des + '</li>';
221+ }
222+
223+ html += '</ul>';
224+ html += '</div>'; // panel-body
225+ html += '</div>'; // panel
226+ html += '</div>'; // col-sm-*
227+
228+ if (!first || lastCategory) {
229+ html += '</div>'; // row
230+ }
231+ }
232+
233+ html += '</div>'; // container-fluid
234+ html += '</div>'; // vim-hotkeys-help
235+
236+ $('body').append(html);
237+ }
238+});