add follow fot a tags
This commit is contained in:
parent
99c1b83133
commit
355da18d3d
4 changed files with 119 additions and 1 deletions
|
@ -13,6 +13,8 @@ const DEFAULT_KEYMAP = [
|
|||
{ keys: [{ code: KeyboardEvent.DOM_VK_U }], action: [ actions.TABS_REOPEN]},
|
||||
{ keys: [{ code: KeyboardEvent.DOM_VK_H }], action: [ actions.TABS_PREV, 1 ]},
|
||||
{ keys: [{ code: KeyboardEvent.DOM_VK_L }], action: [ actions.TABS_NEXT, 1 ]},
|
||||
{ keys: [{ code: KeyboardEvent.DOM_VK_F }], action: [ actions.FOLLOW_START, false ]},
|
||||
{ keys: [{ code: KeyboardEvent.DOM_VK_F, shift: true }], action: [ actions.FOLLOW_START, true ]},
|
||||
]
|
||||
|
||||
export default class KeyQueue {
|
||||
|
|
110
src/content/follow.js
Normal file
110
src/content/follow.js
Normal file
|
@ -0,0 +1,110 @@
|
|||
import Hint from './hint';
|
||||
import HintKeyProducer from './hint-key-producer';
|
||||
|
||||
const DEFAULT_HINT_CHARSET = 'abcdefghijklmnopqrstuvwxyz'
|
||||
|
||||
export default class Follow {
|
||||
constructor(doc) {
|
||||
this.doc = doc;
|
||||
this.hintElements = {};
|
||||
this.keys = [];
|
||||
|
||||
// TODO activate input elements and push button elements
|
||||
let links = Follow.getTargetElements(doc);
|
||||
|
||||
this.addHints(links);
|
||||
|
||||
this.boundKeydown = this.handleKeydown.bind(this);
|
||||
doc.addEventListener('keydown', this.boundKeydown);
|
||||
}
|
||||
|
||||
addHints(elements) {
|
||||
let producer = new HintKeyProducer(DEFAULT_HINT_CHARSET);
|
||||
Array.prototype.forEach.call(elements, (ele) => {
|
||||
let keys = producer.produce();
|
||||
let hint = new Hint(ele, keys)
|
||||
|
||||
this.hintElements[keys] = hint;
|
||||
});
|
||||
}
|
||||
|
||||
handleKeydown(e) {
|
||||
let keyCode = e.keyCode;
|
||||
if (keyCode === KeyboardEvent.DOM_VK_ESCAPE) {
|
||||
this.remove();
|
||||
return;
|
||||
} else if (keyCode === KeyboardEvent.DOM_VK_ENTER ||
|
||||
keyCode === KeyboardEvent.DOM_VK_RETURN) {
|
||||
this.openUrl(this.keys);
|
||||
return;
|
||||
} else if (Follow.availableKey(keyCode)) {
|
||||
this.keys.push(keyCode);
|
||||
} else if (keyCode === KeyboardEvent.DOM_VK_BACK_SPACE ||
|
||||
keyCode === KeyboardEvent.DOM_VK_DELETE) {
|
||||
this.keys.pop();
|
||||
}
|
||||
|
||||
|
||||
let keysAsString = Follow.codeChars(this.keys);
|
||||
let shown = Object.keys(this.hintElements).filter((key) => {
|
||||
return key.startsWith(keysAsString);
|
||||
});
|
||||
let hidden = Object.keys(this.hintElements).filter((key) => {
|
||||
return !key.startsWith(keysAsString);
|
||||
});
|
||||
if (shown.length == 0) {
|
||||
this.remove();
|
||||
return;
|
||||
} else if (shown.length == 1) {
|
||||
this.openUrl(this.keys);
|
||||
return;
|
||||
}
|
||||
|
||||
shown.forEach((key) => {
|
||||
this.hintElements[key].show();
|
||||
});
|
||||
hidden.forEach((key) => {
|
||||
this.hintElements[key].hide();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
remove() {
|
||||
this.doc.removeEventListener("keydown", this.boundKeydown);
|
||||
Object.keys(this.hintElements).forEach((key) => {
|
||||
this.hintElements[key].remove();
|
||||
});
|
||||
}
|
||||
|
||||
openUrl(keys) {
|
||||
let chars = Follow.codeChars(keys);
|
||||
this.hintElements[chars].activate();
|
||||
}
|
||||
|
||||
static availableKey(keyCode) {
|
||||
return (
|
||||
KeyboardEvent.DOM_VK_0 <= keyCode && keyCode <= KeyboardEvent.DOM_VK_9 ||
|
||||
KeyboardEvent.DOM_VK_A <= keyCode && keyCode <= KeyboardEvent.DOM_VK_Z
|
||||
);
|
||||
}
|
||||
|
||||
static codeChars(codes) {
|
||||
const CHARCODE_ZERO = '0'.charCodeAt(0);
|
||||
const CHARCODE_A = 'a'.charCodeAt(0);
|
||||
|
||||
let chars = '';
|
||||
|
||||
for (let code of codes) {
|
||||
if (KeyboardEvent.DOM_VK_0 <= code && code <= KeyboardEvent.DOM_VK_9) {
|
||||
chars += String.fromCharCode(code - KeyboardEvent.DOM_VK_0 + CHARCODE_ZERO);
|
||||
} else if (KeyboardEvent.DOM_VK_A <= code && code <= KeyboardEvent.DOM_VK_Z) {
|
||||
chars += String.fromCharCode(code - KeyboardEvent.DOM_VK_A + CHARCODE_A);
|
||||
}
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
|
||||
static getTargetElements(doc) {
|
||||
return doc.querySelectorAll('a')
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import * as scrolls from './scrolls';
|
||||
import FooterLine from './footer-line';
|
||||
import Follow from './follow';
|
||||
import * as actions from '../shared/actions';
|
||||
|
||||
var footer = null;
|
||||
|
@ -52,6 +53,9 @@ const invokeEvent = (action) => {
|
|||
case actions.SCROLL_BOTTOM:
|
||||
scrolls.scrollBottom(window, action[1]);
|
||||
break;
|
||||
case actions.FOLLOW_START:
|
||||
new Follow(window.document, action[1] || false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ export const SCROLL_UP = 'scroll.up';
|
|||
export const SCROLL_DOWN = 'scroll.down';
|
||||
export const SCROLL_TOP = 'scroll.top';
|
||||
export const SCROLL_BOTTOM = 'scroll.bottom';
|
||||
export const FOLLOW_START = 'follow.start';
|
||||
|
||||
const BACKGROUND_ACTION_SET = new Set([
|
||||
TABS_CLOSE,
|
||||
|
@ -22,7 +23,8 @@ const CONTENT_ACTION_SET = new Set([
|
|||
SCROLL_UP,
|
||||
SCROLL_DOWN,
|
||||
SCROLL_TOP,
|
||||
SCROLL_BOTTOM
|
||||
SCROLL_BOTTOM,
|
||||
FOLLOW_START
|
||||
]);
|
||||
|
||||
export const isBackgroundAction = (action) => {
|
||||
|
|
Reference in a new issue