From 6ed3f9e41478b59bd27a3b8ae930091430b85e5b Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 18 Sep 2018 12:34:41 +0900
Subject: [PATCH] =?UTF-8?q?=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF?=
 =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=B0=E3=81=AA=E3=81=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/client/app/common/hotkey.ts               |  94 +++++++-----
 src/client/app/common/keycode.ts              | 134 +++---------------
 .../views/components/reaction-picker.vue      |   2 +-
 .../desktop/views/components/notes.note.vue   |   2 +-
 4 files changed, 78 insertions(+), 154 deletions(-)

diff --git a/src/client/app/common/hotkey.ts b/src/client/app/common/hotkey.ts
index 62726887d1..66336fca6e 100644
--- a/src/client/app/common/hotkey.ts
+++ b/src/client/app/common/hotkey.ts
@@ -1,29 +1,42 @@
 import keyCode from './keycode';
+import { concat } from '../../../prelude/array';
 
-const getKeyMap = keymap => Object.keys(keymap).map(input => {
-	const result = {} as any;
+type pattern = {
+	which: string[];
+	ctrl?: boolean;
+	shift?: boolean;
+	alt?: boolean;
+};
 
-	const { keyup, keydown } = keymap[input];
+type action = {
+	patterns: pattern[];
 
-	input.split('+').forEach(keyName => {
-		switch (keyName.toLowerCase()) {
-			case 'ctrl':
-			case 'alt':
-			case 'shift':
-			case 'meta':
-				result[keyName] = true;
-				break;
-			default: {
-				result.keyCode = keyCode(keyName);
-				if (!Array.isArray(result.keyCode)) result.keyCode = [result.keyCode];
+	callback: Function;
+};
+
+const getKeyMap = keymap => Object.entries(keymap).map(([patterns, callback]): action => {
+	const result = {
+		patterns: [],
+		callback: callback
+	} as action;
+
+	result.patterns = patterns.split('|').map(part => {
+		const pattern = {
+			which: []
+		} as pattern;
+
+		part.trim().split('+').forEach(key => {
+			key = key.trim().toLowerCase();
+			switch (key) {
+				case 'ctrl': pattern.ctrl = true; break;
+				case 'alt': pattern.alt = true; break;
+				case 'shift': pattern.shift = true; break;
+				default: pattern.which = keyCode(key).map(k => k.toLowerCase());
 			}
-		}
-	});
+		});
 
-	result.callback = {
-		keydown: keydown || keymap[input],
-		keyup
-	};
+		return pattern;
+	});
 
 	return result;
 });
@@ -36,28 +49,39 @@ export default {
 			bind(el, binding) {
 				el._hotkey_global = binding.modifiers.global === true;
 
-				el._keymap = getKeyMap(binding.value);
+				const actions = getKeyMap(binding.value);
 
-				el.dataset.reservedKeyCodes = el._keymap.map(key => `'${key.keyCode}'`).join(' ');
+				const reservedKeys = concat(actions.map(a => a.patterns.map(p => p.which)));
+
+				el.dataset.reservedKeys = reservedKeys.map(key => `'${key}'`).join(' ');
 
 				el._keyHandler = e => {
-					const reservedKeyCodes = document.activeElement ? ((document.activeElement as any).dataset || {}).reservedKeyCodes || '' : '';
+					const key = e.code.toLowerCase();
+
+					const targetReservedKeys = document.activeElement ? ((document.activeElement as any).dataset || {}).reservedKeys || '' : '';
 					if (document.activeElement && ignoreElemens.some(el => document.activeElement.matches(el))) return;
 
-					for (const hotkey of el._keymap) {
-						if (el._hotkey_global && reservedKeyCodes.includes(`'${e.keyCode}'`)) break;
+					for (const action of actions) {
+						if (el._hotkey_global && targetReservedKeys.includes(`'${key}'`)) break;
 
-						const callback = hotkey.keyCode.includes(e.keyCode) &&
-							!!hotkey.ctrl === e.ctrlKey &&
-							!!hotkey.alt === e.altKey &&
-							!!hotkey.shift === e.shiftKey &&
-							!!hotkey.meta === e.metaKey &&
-							hotkey.callback[e.type];
+						const matched = action.patterns.some(pattern => {
+							let matched = pattern.which.includes(key);
+							if (pattern.ctrl && !e.ctrlKey) matched = false;
+							if (pattern.shift && !e.shiftKey) matched = false;
+							if (pattern.alt && !e.altKey) matched = false;
 
-						if (callback) {
-							e.preventDefault();
-							e.stopPropagation();
-							callback(e);
+							if (matched) {
+								e.preventDefault();
+								e.stopPropagation();
+								action.callback(e);
+								return true;
+							} else {
+								return false;
+							}
+						});
+
+						if (matched) {
+							break;
 						}
 					}
 				};
diff --git a/src/client/app/common/keycode.ts b/src/client/app/common/keycode.ts
index 55043ad76b..5786c1dc0a 100644
--- a/src/client/app/common/keycode.ts
+++ b/src/client/app/common/keycode.ts
@@ -1,116 +1,20 @@
-export default searchInput => {
-	// Keyboard Events
-	if (searchInput && typeof searchInput === 'object') {
-		const hasKeyCode = searchInput.which || searchInput.keyCode || searchInput.charCode;
-		if (hasKeyCode) {
-			searchInput = hasKeyCode;
-		}
+export default (input: string): string[] => {
+	if (Object.keys(aliases).some(a => a.toLowerCase() == input.toLowerCase())) {
+		const codes = aliases[input];
+		return Array.isArray(codes) ? codes : [codes];
+	} else {
+		return [input];
 	}
-
-	// Numbers
-	// if (typeof searchInput === 'number') {
-	//   return names[searchInput]
-	// }
-
-	// Everything else (cast to string)
-	const search = String(searchInput);
-
-	// check codes
-	const foundNamedKeyCodes = codes[search.toLowerCase()];
-	if (foundNamedKeyCodes) {
-		return foundNamedKeyCodes;
-	}
-
-	// check aliases
-	const foundNamedKeyAliases = aliases[search.toLowerCase()];
-	if (foundNamedKeyAliases) {
-		return foundNamedKeyAliases;
-	}
-
-	// weird character?
-	if (search.length === 1) {
-		return search.charCodeAt(0);
-	}
-
-	return undefined;
 };
 
-/**
- * Get by name
- *
- *   exports.code['enter'] // => 13
- */
-
-export const codes = {
-	'backspace': 8,
-	'tab': 9,
-	'enter': 13,
-	'shift': 16,
-	'ctrl': 17,
-	'alt': 18,
-	'pause/break': 19,
-	'caps lock': 20,
-	'esc': 27,
-	'space': 32,
-	'page up': 33,
-	'page down': 34,
-	'end': 35,
-	'home': 36,
-	'left': 37,
-	'up': 38,
-	'right': 39,
-	'down': 40,
-	// 'add': 43,
-	'insert': 45,
-	'delete': 46,
-	'command': 91,
-	'left command': 91,
-	'right command': 93,
-	'numpad *': 106,
-	'numpad plus': [43, 107],
-	'numpad add': 43, // as a trick
-	'numpad -': 109,
-	'numpad .': 110,
-	'numpad /': 111,
-	'num lock': 144,
-	'scroll lock': 145,
-	'my computer': 182,
-	'my calculator': 183,
-	';': 186,
-	'=': 187,
-	',': 188,
-	'-': 189,
-	'.': 190,
-	'/': 191,
-	'`': 192,
-	'[': 219,
-	'\\': 220,
-	']': 221,
-	"'": 222
-};
-
-// Helper aliases
-
 export const aliases = {
-	'windows': 91,
-	'⇧': 16,
-	'⌥': 18,
-	'⌃': 17,
-	'⌘': 91,
-	'ctl': 17,
-	'control': 17,
-	'option': 18,
-	'pause': 19,
-	'break': 19,
-	'caps': 20,
-	'return': 13,
-	'escape': 27,
-	'spc': 32,
-	'pgup': 33,
-	'pgdn': 34,
-	'ins': 45,
-	'del': 46,
-	'cmd': 91
+	'esc': 'Escape',
+	'enter': ['Enter', 'NumpadEnter'],
+	'up': 'ArrowUp',
+	'down': 'ArrowDown',
+	'left': 'ArrowLeft',
+	'right': 'ArrowRight',
+	'plus': ['NumpadAdd', 'Semicolon'],
 };
 
 /*!
@@ -119,15 +23,11 @@ export const aliases = {
 
 // lower case chars
 for (let i = 97; i < 123; i++) {
-	codes[String.fromCharCode(i)] = i - 32;
+	const char = String.fromCharCode(i);
+	aliases[char] = `Key${char.toUpperCase()}`;
 }
 
 // numbers
-for (let i = 48; i < 58; i++) {
-	codes[i - 48] = [i, (i - 48) + 96];
-}
-
-// function keys
-for (let i = 1; i < 13; i++) {
-	codes['f' + i] = i + 111;
+for (let i = 0; i < 10; i++) {
+	aliases[i] = [`Numpad${i}`, `Digit${i}`];
 }
diff --git a/src/client/app/common/views/components/reaction-picker.vue b/src/client/app/common/views/components/reaction-picker.vue
index f415e89d0e..811f82283d 100644
--- a/src/client/app/common/views/components/reaction-picker.vue
+++ b/src/client/app/common/views/components/reaction-picker.vue
@@ -72,7 +72,7 @@ export default Vue.extend({
 				'esc': this.close,
 				'enter': this.choose,
 				'space': this.choose,
-				'numpad plus': this.choose,
+				'plus': this.choose,
 				'up': this.focusUp,
 				'right': this.focusRight,
 				'down': this.focusDown,
diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue
index b8c5f31511..26f6ef4554 100644
--- a/src/client/app/desktop/views/components/notes.note.vue
+++ b/src/client/app/desktop/views/components/notes.note.vue
@@ -115,7 +115,7 @@ export default Vue.extend({
 			return {
 				'r': this.reply,
 				'a': () => this.react(true),
-				'numpad plus': () => this.react(true),
+				'plus': () => this.react(true),
 				'n': this.renote,
 				'up': this.focusBefore,
 				'shift+tab': this.focusBefore,