瀏覽代碼

[WIP] Add vim-hotkeys plugin

Kirill Isakov 9 年之前
父節點
當前提交
3246541bdc

+ 3
- 1
searx/plugins/__init__.py 查看文件

23
                            open_results_on_new_tab,
23
                            open_results_on_new_tab,
24
                            self_info,
24
                            self_info,
25
                            search_on_category_select,
25
                            search_on_category_select,
26
-                           tracker_url_remover)
26
+                           tracker_url_remover,
27
+                           vim_hotkeys)
27
 
28
 
28
 required_attrs = (('name', str),
29
 required_attrs = (('name', str),
29
                   ('description', str),
30
                   ('description', str),
77
 plugins.register(self_info)
78
 plugins.register(self_info)
78
 plugins.register(search_on_category_select)
79
 plugins.register(search_on_category_select)
79
 plugins.register(tracker_url_remover)
80
 plugins.register(tracker_url_remover)
81
+plugins.register(vim_hotkeys)

+ 10
- 0
searx/plugins/vim_hotkeys.py 查看文件

1
+from flask.ext.babel import gettext
2
+
3
+name = gettext('Vim-like hotkeys')
4
+description = gettext('Navigate search results with Vim-like hotkeys '
5
+                      '(JavaScript required). '
6
+                      'Press "h" key on main or result page to get help.')
7
+default_on = False
8
+
9
+js_dependencies = ('plugins/js/vim_hotkeys.js',)
10
+css_dependencies = ('plugins/css/vim_hotkeys.css',)

+ 9
- 0
searx/static/plugins/css/vim_hotkeys.css 查看文件

1
+.vim-hotkeys-help {
2
+    position: fixed;
3
+    top: 50%;
4
+    left: 50%;
5
+    transform: translate(-50%, -50%);
6
+    z-index: 9999999;
7
+    overflow-y: auto;
8
+    max-height: 80%;
9
+}

+ 238
- 0
searx/static/plugins/js/vim_hotkeys.js 查看文件

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
+});