$(document).ready(function() { highlightResult('top')(); $('.result').on('click', function() { highlightResult($(this))(); }); 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, 'top'), des: 'scroll to the top of the page', cat: 'Navigation' }, 86: { key: 'v', fun: scrollPageTo(document.body.scrollHeight, 'bottom'), des: 'scroll to the bottom of the page', cat: 'Navigation' }, 75: { key: 'k', fun: highlightResult('up'), des: 'select previous search result', cat: 'Results' }, 74: { key: 'j', fun: highlightResult('down'), 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).keydown(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) { e.preventDefault(); vimKeys[e.keyCode].fun(); } } } }); function nextResult(current, direction) { var next = current[direction](); while (!next.is('.result') && next.length !== 0) { next = next[direction](); } return next } 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': 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 = nextResult(current, 'next'); if (next.length === 0) { next = $('.result:first'); } break; case 'up': next = nextResult(current, 'prev'); if (next.length === 0) { next = $('.result:last'); } break; case 'bottom': next = $('.result:last'); break; case 'top': default: next = $('.result:first'); } } if (next) { current.removeAttr('data-vim-selected').removeClass('well well-sm'); next.attr('data-vim-selected', 'true').addClass('well well-sm'); scrollPageToSelected(); } } } 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 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); highlightResult('visible')(); } } function scrollPageTo(position, nav) { return function() { window.scrollTo(0, position); highlightResult(nav)(); } } function searchInputFocus() { $('input#q').focus(); } 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() { 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); } });