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_U }], action: [ actions.TABS_REOPEN]},
|
||||||
{ keys: [{ code: KeyboardEvent.DOM_VK_H }], action: [ actions.TABS_PREV, 1 ]},
|
{ 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_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 {
|
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 * as scrolls from './scrolls';
|
||||||
import FooterLine from './footer-line';
|
import FooterLine from './footer-line';
|
||||||
|
import Follow from './follow';
|
||||||
import * as actions from '../shared/actions';
|
import * as actions from '../shared/actions';
|
||||||
|
|
||||||
var footer = null;
|
var footer = null;
|
||||||
|
@ -52,6 +53,9 @@ const invokeEvent = (action) => {
|
||||||
case actions.SCROLL_BOTTOM:
|
case actions.SCROLL_BOTTOM:
|
||||||
scrolls.scrollBottom(window, action[1]);
|
scrolls.scrollBottom(window, action[1]);
|
||||||
break;
|
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_DOWN = 'scroll.down';
|
||||||
export const SCROLL_TOP = 'scroll.top';
|
export const SCROLL_TOP = 'scroll.top';
|
||||||
export const SCROLL_BOTTOM = 'scroll.bottom';
|
export const SCROLL_BOTTOM = 'scroll.bottom';
|
||||||
|
export const FOLLOW_START = 'follow.start';
|
||||||
|
|
||||||
const BACKGROUND_ACTION_SET = new Set([
|
const BACKGROUND_ACTION_SET = new Set([
|
||||||
TABS_CLOSE,
|
TABS_CLOSE,
|
||||||
|
@ -22,7 +23,8 @@ const CONTENT_ACTION_SET = new Set([
|
||||||
SCROLL_UP,
|
SCROLL_UP,
|
||||||
SCROLL_DOWN,
|
SCROLL_DOWN,
|
||||||
SCROLL_TOP,
|
SCROLL_TOP,
|
||||||
SCROLL_BOTTOM
|
SCROLL_BOTTOM,
|
||||||
|
FOLLOW_START
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const isBackgroundAction = (action) => {
|
export const isBackgroundAction = (action) => {
|
||||||
|
|
Reference in a new issue