From 3246541bdc405f269179ec737b83772b926c43a6 Mon Sep 17 00:00:00 2001 From: Kirill Isakov Date: Sat, 23 Apr 2016 22:26:02 +0600 Subject: [PATCH 1/5] [WIP] Add vim-hotkeys plugin --- searx/plugins/__init__.py | 4 +- searx/plugins/vim_hotkeys.py | 10 + searx/static/plugins/css/vim_hotkeys.css | 9 + searx/static/plugins/js/vim_hotkeys.js | 238 +++++++++++++++++++++++ 4 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 searx/plugins/vim_hotkeys.py create mode 100644 searx/static/plugins/css/vim_hotkeys.css create mode 100644 searx/static/plugins/js/vim_hotkeys.js diff --git a/searx/plugins/__init__.py b/searx/plugins/__init__.py index 87cc01382..efb9b0682 100644 --- a/searx/plugins/__init__.py +++ b/searx/plugins/__init__.py @@ -23,7 +23,8 @@ from searx.plugins import (https_rewrite, open_results_on_new_tab, self_info, search_on_category_select, - tracker_url_remover) + tracker_url_remover, + vim_hotkeys) required_attrs = (('name', str), ('description', str), @@ -77,3 +78,4 @@ plugins.register(open_results_on_new_tab) plugins.register(self_info) plugins.register(search_on_category_select) plugins.register(tracker_url_remover) +plugins.register(vim_hotkeys) diff --git a/searx/plugins/vim_hotkeys.py b/searx/plugins/vim_hotkeys.py new file mode 100644 index 000000000..e537a3ac8 --- /dev/null +++ b/searx/plugins/vim_hotkeys.py @@ -0,0 +1,10 @@ +from flask.ext.babel import gettext + +name = gettext('Vim-like hotkeys') +description = gettext('Navigate search results with Vim-like hotkeys ' + '(JavaScript required). ' + 'Press "h" key on main or result page to get help.') +default_on = False + +js_dependencies = ('plugins/js/vim_hotkeys.js',) +css_dependencies = ('plugins/css/vim_hotkeys.css',) diff --git a/searx/static/plugins/css/vim_hotkeys.css b/searx/static/plugins/css/vim_hotkeys.css new file mode 100644 index 000000000..19745d942 --- /dev/null +++ b/searx/static/plugins/css/vim_hotkeys.css @@ -0,0 +1,9 @@ +.vim-hotkeys-help { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 9999999; + overflow-y: auto; + max-height: 80%; +} diff --git a/searx/static/plugins/js/vim_hotkeys.js b/searx/static/plugins/js/vim_hotkeys.js new file mode 100644 index 000000000..3aadae288 --- /dev/null +++ b/searx/static/plugins/js/vim_hotkeys.js @@ -0,0 +1,238 @@ +$(document).ready(function() { + var vimKeys = { + 27: { + key: 'Escape', + fun: removeFocus, + des: 'remove focus from the focused input', + cat: 'Control' + }, + 73: { + key: 'i', + fun: searchInputFocus, + des: 'focus on the search input', + cat: 'Control' + }, + 66: { + key: 'b', + fun: scrollPage(-window.innerHeight), + des: 'scroll one page up', + cat: 'Navigation' + }, + 70: { + key: 'f', + fun: scrollPage(window.innerHeight), + des: 'scroll one page down', + cat: 'Navigation' + }, + 85: { + key: 'u', + fun: scrollPage(-window.innerHeight / 2), + des: 'scroll half a page up', + cat: 'Navigation' + }, + 68: { + key: 'd', + fun: scrollPage(window.innerHeight / 2), + des: 'scroll half a page down', + cat: 'Navigation' + }, + 71: { + key: 'g', + fun: scrollPageTo(-document.body.scrollHeight), + des: 'scroll to the top of the page', + cat: 'Navigation' + }, + 86: { + key: 'v', + fun: scrollPageTo(document.body.scrollHeight), + des: 'scroll to the bottom of the page', + cat: 'Navigation' + }, + 75: { + key: 'k', + fun: previousResult, + des: 'select previous search result', + cat: 'Results' + }, + 74: { + key: 'j', + fun: nextResult, + des: 'select next search result', + cat: 'Results' + }, + 80: { + key: 'p', + fun: pageButtonClick(0), + des: 'go to previous page', + cat: 'Results' + }, + 78: { + key: 'n', + fun: pageButtonClick(1), + des: 'go to next page', + cat: 'Results' + }, + 79: { + key: 'o', + fun: openResult(false), + des: 'open search result', + cat: 'Results' + }, + 84: { + key: 't', + fun: openResult(true), + des: 'open the result in a new tab', + cat: 'Results' + }, + 82: { + key: 'r', + fun: reloadPage, + des: 'reload page from the server', + cat: 'Control' + }, + 72: { + key: 'h', + fun: toggleHelp, + des: 'toggle help window', + cat: 'Other' + } + }; + + $(document).keyup(function(e) { + // check for modifiers so we don't break browser's hotkeys + if (vimKeys.hasOwnProperty(e.keyCode) + && !e.ctrlKey + && !e.altKey + && !e.shiftKey + && !e.metaKey) + { + if (e.keyCode === 27) { + if (e.target.tagName.toLowerCase() === 'input') { + vimKeys[e.keyCode].fun(); + } + } else { + if (e.target === document.body) { + vimKeys[e.keyCode].fun(); + } + } + } + }); + + function reloadPage() { + document.location.reload(false); + } + + function removeFocus() { + if (document.activeElement) { + document.activeElement.blur(); + } + } + + function pageButtonClick(num) { + return function() { + var buttons = $('div#pagination button[type="submit"]'); + if (buttons.length !== 2) { + console.log('page navigation with this theme is not supported'); + return; + } + if (num >= 0 && num < buttons.length) { + buttons[num].click(); + } else { + console.log('pageButtonClick(): invalid argument'); + } + } + } + + function scrollPage(amount) { + return function() { + window.scrollBy(0, amount); + } + } + + function scrollPageTo(position) { + return function() { + window.scrollTo(0, position); + } + } + + function searchInputFocus() { + $('input#q').focus(); + } + + function previousResult() { + } + + function nextResult() { + } + + function openResult(newTab) { + } + + function toggleHelp() { + var helpPanel = $('#vim-hotkeys-help'); + if (helpPanel.length) { + helpPanel.toggleClass('hidden'); + return; + } + + var categories = {}; + + for (var k in vimKeys) { + var key = vimKeys[k]; + categories[key.cat] = categories[key.cat] || []; + categories[key.cat].push(key); + } + + var sorted = Object.keys(categories).sort(function(a, b) { + return categories[b].length - categories[a].length; + }); + + if (sorted.length === 0) { + return; + } + + var html = '
'; + html += '
'; + + html += '
'; + html += '
'; + html += '

How to navigate searx with Vim-like hotkeys

'; + html += '
'; // col-sm-12 + html += '
'; // row + + for (var i = 0; i < sorted.length; i++) { + var cat = categories[sorted[i]]; + + var lastCategory = i === (sorted.length - 1); + var first = i % 2 === 0; + + if (first) { + html += '
'; + } + html += '
'; + + html += '
'; + html += '
' + cat[0].cat + '
'; + html += '
'; + html += '
    '; + + for (var cj in cat) { + html += '
  • ' + cat[cj].key + ' ' + cat[cj].des + '
  • '; + } + + html += '
'; + html += '
'; // panel-body + html += '
'; // panel + html += '
'; // col-sm-* + + if (!first || lastCategory) { + html += '
'; // row + } + } + + html += '
'; // container-fluid + html += '
'; // vim-hotkeys-help + + $('body').append(html); + } +}); From 1faf1b645b25745ac887529605727850e0d86d84 Mon Sep 17 00:00:00 2001 From: Kirill Isakov Date: Sun, 24 Apr 2016 03:02:33 +0600 Subject: [PATCH 2/5] Set vim-hotkeys help panels to same height --- searx/static/plugins/css/vim_hotkeys.css | 16 ++++++++++++++++ searx/static/plugins/js/vim_hotkeys.js | 6 +++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/searx/static/plugins/css/vim_hotkeys.css b/searx/static/plugins/css/vim_hotkeys.css index 19745d942..3e9eaddf0 100644 --- a/searx/static/plugins/css/vim_hotkeys.css +++ b/searx/static/plugins/css/vim_hotkeys.css @@ -7,3 +7,19 @@ overflow-y: auto; max-height: 80%; } + +.dflex { + display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */ + display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */ + display: -ms-flexbox; /* TWEENER - IE 10 */ + display: -webkit-flex; /* NEW - Chrome */ + display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */ +} + +.iflex { + -webkit-box-flex: 1; /* OLD - iOS 6-, Safari 3.1-6 */ + -moz-box-flex: 1; /* OLD - Firefox 19- */ + -webkit-flex: 1; /* Chrome */ + -ms-flex: 1; /* IE 10 */ + flex: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */ +} diff --git a/searx/static/plugins/js/vim_hotkeys.js b/searx/static/plugins/js/vim_hotkeys.js index 3aadae288..fd47463ba 100644 --- a/searx/static/plugins/js/vim_hotkeys.js +++ b/searx/static/plugins/js/vim_hotkeys.js @@ -207,11 +207,11 @@ $(document).ready(function() { var first = i % 2 === 0; if (first) { - html += '
'; + html += '
'; } - html += '
'; + html += '
'; - html += '
'; + html += '
'; html += '
' + cat[0].cat + '
'; html += '
'; html += '
    '; From 0d6625e0703e3c4edfd0cdbca815795bb20c32b3 Mon Sep 17 00:00:00 2001 From: Kirill Isakov Date: Sun, 24 Apr 2016 18:01:02 +0600 Subject: [PATCH 3/5] Add search result navigation support --- searx/static/plugins/css/vim_hotkeys.css | 1 + searx/static/plugins/js/vim_hotkeys.js | 75 +++++++++++++++++++++--- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/searx/static/plugins/css/vim_hotkeys.css b/searx/static/plugins/css/vim_hotkeys.css index 3e9eaddf0..2ccfdc1af 100644 --- a/searx/static/plugins/css/vim_hotkeys.css +++ b/searx/static/plugins/css/vim_hotkeys.css @@ -6,6 +6,7 @@ z-index: 9999999; overflow-y: auto; max-height: 80%; + box-shadow: 0 0 1em; } .dflex { diff --git a/searx/static/plugins/js/vim_hotkeys.js b/searx/static/plugins/js/vim_hotkeys.js index fd47463ba..740b78ff2 100644 --- a/searx/static/plugins/js/vim_hotkeys.js +++ b/searx/static/plugins/js/vim_hotkeys.js @@ -1,4 +1,10 @@ $(document).ready(function() { + highlightResult('top')(); + + $('.result').on('click', function() { + highlightResult($(this))(); + }); + var vimKeys = { 27: { key: 'Escape', @@ -50,13 +56,13 @@ $(document).ready(function() { }, 75: { key: 'k', - fun: previousResult, + fun: highlightResult('up'), des: 'select previous search result', cat: 'Results' }, 74: { key: 'j', - fun: nextResult, + fun: highlightResult('down'), des: 'select next search result', cat: 'Results' }, @@ -75,7 +81,7 @@ $(document).ready(function() { 79: { key: 'o', fun: openResult(false), - des: 'open search result', + des: 'open search result', cat: 'Results' }, 84: { @@ -118,6 +124,50 @@ $(document).ready(function() { } }); + function highlightResult(which) { + return function() { + var current = $('.result[data-vim-selected]'); + if (current.length === 0) { + current = $('.result:first'); + if (current.length === 0) { + return; + } + } + + var next; + + if (typeof which !== 'string') { + next = which; + } else { + switch (which) { + // case 'visible': + // TODO + case 'down': + next = current.next('.result'); + if (next.length === 0) { + next = $('.result:first'); + } + break; + case 'up': + next = current.prev('.result'); + if (next.length === 0) { + next = $('.result:last'); + } + break; + case 'bottom': + next = $('.result:last'); + break; + case 'top': + default: + next = $('.result:first'); + } + } + + current.removeAttr('data-vim-selected').removeClass('well well-sm'); + next.attr('data-vim-selected', 'true').addClass('well well-sm'); + } + } + function reloadPage() { document.location.reload(false); } @@ -146,12 +196,14 @@ $(document).ready(function() { function scrollPage(amount) { return function() { window.scrollBy(0, amount); + highlightResult('visible')(); } } function scrollPageTo(position) { return function() { window.scrollTo(0, position); + highlightResult('visible')(); } } @@ -159,13 +211,18 @@ $(document).ready(function() { $('input#q').focus(); } - function previousResult() { - } - - function nextResult() { - } - function openResult(newTab) { + return function() { + var link = $('.result[data-vim-selected] .result_header a'); + if (link.length) { + var url = link.attr('href'); + if (newTab) { + window.open(url); + } else { + window.location.href = url; + } + } + }; } function toggleHelp() { From 7b48a663503018dd47b81c94afd9c4df6d62467a Mon Sep 17 00:00:00 2001 From: Kirill Isakov Date: Sun, 24 Apr 2016 21:04:53 +0600 Subject: [PATCH 4/5] Add auto page scrolling to selected result --- searx/static/plugins/js/vim_hotkeys.js | 49 +++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/searx/static/plugins/js/vim_hotkeys.js b/searx/static/plugins/js/vim_hotkeys.js index 740b78ff2..f7f5989c2 100644 --- a/searx/static/plugins/js/vim_hotkeys.js +++ b/searx/static/plugins/js/vim_hotkeys.js @@ -140,8 +140,21 @@ $(document).ready(function() { next = which; } else { switch (which) { - // case 'visible': - // TODO + case 'visible': + var top = $(window).scrollTop(); + var bot = top + $(window).height(); + var results = $('.result'); + + for (var i = 0; i < results.length; i++) { + next = $(results[i]); + var etop = next.offset().top; + var ebot = etop + next.height(); + + if ((ebot <= bot) && (etop > top)) { + break; + } + } + break; case 'down': next = current.next('.result'); if (next.length === 0) { @@ -163,8 +176,11 @@ $(document).ready(function() { } } - current.removeAttr('data-vim-selected').removeClass('well well-sm'); - next.attr('data-vim-selected', 'true').addClass('well well-sm'); + if (next) { + current.removeAttr('data-vim-selected').removeClass('well well-sm'); + next.attr('data-vim-selected', 'true').addClass('well well-sm'); + scrollPageToSelected(); + } } } @@ -193,6 +209,31 @@ $(document).ready(function() { } } + function scrollPageToSelected() { + var sel = $('.result[data-vim-selected]'); + if (sel.length !== 1) { + return; + } + + var wnd = $(window); + + var wtop = wnd.scrollTop(); + var etop = sel.offset().top; + + var offset = 30; + + if (wtop > etop) { + wnd.scrollTop(etop - offset); + } else { + var ebot = etop + sel.height(); + var wbot = wtop + wnd.height(); + + if (wbot < ebot) { + wnd.scrollTop(ebot - wnd.height() + offset); + } + } + } + function scrollPage(amount) { return function() { window.scrollBy(0, amount); From c12e41a80f7b88e75f6cf5960948c784a316f724 Mon Sep 17 00:00:00 2001 From: Kirill Isakov Date: Sun, 24 Apr 2016 21:18:27 +0600 Subject: [PATCH 5/5] Fix result selection for top & bottom scrolling --- searx/static/plugins/js/vim_hotkeys.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/searx/static/plugins/js/vim_hotkeys.js b/searx/static/plugins/js/vim_hotkeys.js index f7f5989c2..61500d8f5 100644 --- a/searx/static/plugins/js/vim_hotkeys.js +++ b/searx/static/plugins/js/vim_hotkeys.js @@ -44,13 +44,13 @@ $(document).ready(function() { }, 71: { key: 'g', - fun: scrollPageTo(-document.body.scrollHeight), + fun: scrollPageTo(-document.body.scrollHeight, 'top'), des: 'scroll to the top of the page', cat: 'Navigation' }, 86: { key: 'v', - fun: scrollPageTo(document.body.scrollHeight), + fun: scrollPageTo(document.body.scrollHeight, 'bottom'), des: 'scroll to the bottom of the page', cat: 'Navigation' }, @@ -241,10 +241,10 @@ $(document).ready(function() { } } - function scrollPageTo(position) { + function scrollPageTo(position, nav) { return function() { window.scrollTo(0, position); - highlightResult('visible')(); + highlightResult(nav)(); } }