aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/content/actions/setting.js16
-rw-r--r--src/content/components/common/follow.js2
-rw-r--r--src/content/components/common/input.js21
-rw-r--r--src/content/components/common/keymapper.js23
-rw-r--r--src/content/components/top-content/follow-controller.js2
-rw-r--r--src/content/reducers/input.js6
-rw-r--r--src/content/reducers/setting.js2
-rw-r--r--src/shared/utils/keys.js86
8 files changed, 127 insertions, 31 deletions
diff --git a/src/content/actions/setting.js b/src/content/actions/setting.js
index c874294..353dd24 100644
--- a/src/content/actions/setting.js
+++ b/src/content/actions/setting.js
@@ -1,9 +1,23 @@
import actions from 'content/actions';
+import * as keyUtils from 'shared/utils/keys';
const set = (value) => {
+ let maps = new Map();
+ if (value.keymaps) {
+ let entries = Object.entries(value.keymaps).map((entry) => {
+ return [
+ keyUtils.fromMapKeys(entry[0]),
+ entry[1],
+ ];
+ });
+ maps = new Map(entries);
+ }
+
return {
type: actions.SETTING_SET,
- value,
+ value: Object.assign({}, value, {
+ keymaps: maps,
+ })
};
};
diff --git a/src/content/components/common/follow.js b/src/content/components/common/follow.js
index 83aeb0a..7a35105 100644
--- a/src/content/components/common/follow.js
+++ b/src/content/components/common/follow.js
@@ -46,7 +46,7 @@ export default class Follow {
}
this.win.parent.postMessage(JSON.stringify({
type: messages.FOLLOW_KEY_PRESS,
- key,
+ key: key.key,
}), '*');
return true;
}
diff --git a/src/content/components/common/input.js b/src/content/components/common/input.js
index 8b1d35d..22b0a91 100644
--- a/src/content/components/common/input.js
+++ b/src/content/components/common/input.js
@@ -1,22 +1,5 @@
import * as dom from 'shared/utils/dom';
-
-const modifierdKeyName = (name) => {
- if (name.length === 1) {
- return name.toUpperCase();
- } else if (name === 'Escape') {
- return 'Esc';
- }
- return name;
-};
-
-const mapKey = (e) => {
- if (e.ctrlKey) {
- return '<C-' + modifierdKeyName(e.key) + '>';
- } else if (e.shiftKey && e.key.length !== 1) {
- return '<S-' + modifierdKeyName(e.key) + '>';
- }
- return e.key;
-};
+import * as keys from 'shared/utils/keys';
export default class InputComponent {
constructor(target) {
@@ -64,7 +47,7 @@ export default class InputComponent {
return;
}
- let key = mapKey(e);
+ let key = keys.fromKeyboardEvent(e);
for (let listener of this.onKeyListeners) {
let stop = listener(key);
diff --git a/src/content/components/common/keymapper.js b/src/content/components/common/keymapper.js
index 1da3c0d..0abbc91 100644
--- a/src/content/components/common/keymapper.js
+++ b/src/content/components/common/keymapper.js
@@ -1,6 +1,19 @@
import * as inputActions from 'content/actions/input';
import * as operationActions from 'content/actions/operation';
import operations from 'shared/operations';
+import * as keyUtils from 'shared/utils/keys';
+
+const mapStartsWith = (mapping, keys) => {
+ if (mapping.length < keys.length) {
+ return false;
+ }
+ for (let i = 0; i < keys.length; ++i) {
+ if (!keyUtils.equals(mapping[i], keys[i])) {
+ return false;
+ }
+ }
+ return true;
+};
export default class KeymapperComponent {
constructor(store) {
@@ -14,14 +27,14 @@ export default class KeymapperComponent {
let input = state.input;
let keymaps = state.setting.keymaps;
- let matched = Object.keys(keymaps).filter((keyStr) => {
- return keyStr.startsWith(input.keys);
+ let matched = Array.from(keymaps.keys()).filter((mapping) => {
+ return mapStartsWith(mapping, input.keys);
});
if (!state.addon.enabled) {
// available keymaps are only ADDON_ENABLE and ADDON_TOGGLE_ENABLED if
// the addon disabled
matched = matched.filter((keys) => {
- let type = keymaps[keys].type;
+ let type = keymaps.get(keys).type;
return type === operations.ADDON_ENABLE ||
type === operations.ADDON_TOGGLE_ENABLED;
});
@@ -30,10 +43,10 @@ export default class KeymapperComponent {
this.store.dispatch(inputActions.clearKeys());
return false;
} else if (matched.length > 1 ||
- matched.length === 1 && input.keys !== matched[0]) {
+ matched.length === 1 && input.keys.length < matched[0].length) {
return true;
}
- let operation = keymaps[matched];
+ let operation = keymaps.get(matched[0]);
this.store.dispatch(operationActions.exec(operation));
this.store.dispatch(inputActions.clearKeys());
return true;
diff --git a/src/content/components/top-content/follow-controller.js b/src/content/components/top-content/follow-controller.js
index 38869e6..d373177 100644
--- a/src/content/components/top-content/follow-controller.js
+++ b/src/content/components/top-content/follow-controller.js
@@ -76,7 +76,7 @@ export default class FollowController {
this.activate();
this.store.dispatch(followControllerActions.disable());
break;
- case 'Escape':
+ case 'Esc':
this.store.dispatch(followControllerActions.disable());
break;
case 'Backspace':
diff --git a/src/content/reducers/input.js b/src/content/reducers/input.js
index 9457604..134aa95 100644
--- a/src/content/reducers/input.js
+++ b/src/content/reducers/input.js
@@ -1,18 +1,18 @@
import actions from 'content/actions';
const defaultState = {
- keys: ''
+ keys: []
};
export default function reducer(state = defaultState, action = {}) {
switch (action.type) {
case actions.INPUT_KEY_PRESS:
return Object.assign({}, state, {
- keys: state.keys + action.key
+ keys: state.keys.concat([action.key]),
});
case actions.INPUT_CLEAR_KEYS:
return Object.assign({}, state, {
- keys: '',
+ keys: [],
});
default:
return state;
diff --git a/src/content/reducers/setting.js b/src/content/reducers/setting.js
index b6f6c58..a54f5a3 100644
--- a/src/content/reducers/setting.js
+++ b/src/content/reducers/setting.js
@@ -1,7 +1,7 @@
import actions from 'content/actions';
const defaultState = {
- keymaps: {},
+ keymaps: new Map(),
};
export default function reducer(state = defaultState, action = {}) {
diff --git a/src/shared/utils/keys.js b/src/shared/utils/keys.js
new file mode 100644
index 0000000..fba8ce3
--- /dev/null
+++ b/src/shared/utils/keys.js
@@ -0,0 +1,86 @@
+const modifierdKeyName = (name) => {
+ if (name.length === 1) {
+ return name;
+ } else if (name === 'Escape') {
+ return 'Esc';
+ }
+ return name;
+};
+
+const fromKeyboardEvent = (e) => {
+ let key = modifierdKeyName(e.key);
+ let shift = e.shiftKey;
+ if (key.length === 1 && key.toUpperCase() === key.toLowerCase()) {
+ // make shift false for symbols to enable key bindings by symbold keys.
+ // But this limits key bindings by symbol keys with Shift (such as Shift+$>.
+ shift = false;
+ }
+
+ return {
+ key: modifierdKeyName(e.key),
+ shiftKey: shift,
+ ctrlKey: e.ctrlKey,
+ altKey: e.altKey,
+ metaKey: e.metaKey,
+ };
+};
+
+const fromMapKey = (key) => {
+ if (key.startsWith('<') && key.endsWith('>')) {
+ let inner = key.slice(1, -1);
+ let shift = inner.includes('S-');
+ let base = inner.slice(inner.lastIndexOf('-') + 1);
+ if (shift && base.length === 1) {
+ base = base.toUpperCase();
+ } else if (!shift && base.length === 1) {
+ base = base.toLowerCase();
+ }
+ return {
+ key: base,
+ shiftKey: inner.includes('S-'),
+ ctrlKey: inner.includes('C-'),
+ altKey: inner.includes('A-'),
+ metaKey: inner.includes('M-'),
+ };
+ }
+ return {
+ key: key,
+ shiftKey: key.toLowerCase() !== key,
+ ctrlKey: false,
+ altKey: false,
+ metaKey: false,
+ };
+};
+
+const fromMapKeys = (keys) => {
+ const fromMapKeysRecursive = (remainings, mappedKeys) => {
+ if (remainings.length === 0) {
+ return mappedKeys;
+ }
+
+ let nextPos = 1;
+ if (remainings.startsWith('<')) {
+ let ltPos = remainings.indexOf('>');
+ if (ltPos > 0) {
+ nextPos = ltPos + 1;
+ }
+ }
+
+ return fromMapKeysRecursive(
+ remainings.slice(nextPos),
+ mappedKeys.concat([fromMapKey(remainings.slice(0, nextPos))])
+ );
+ };
+
+ return fromMapKeysRecursive(keys, []);
+};
+
+const equals = (e1, e2) => {
+ return e1.key === e2.key &&
+ e1.ctrlKey === e2.ctrlKey &&
+ e1.metaKey === e2.metaKey &&
+ e1.altKey === e2.altKey &&
+ e1.shiftKey === e2.shiftKey;
+};
+
+export { fromKeyboardEvent, fromMapKey, fromMapKeys, equals };