diff --git a/src/content/InputDriver.ts b/src/content/InputDriver.ts index 0472088..e77d857 100644 --- a/src/content/InputDriver.ts +++ b/src/content/InputDriver.ts @@ -1,5 +1,5 @@ import * as dom from '../shared/utils/dom'; -import Key, * as keys from './domains/Key'; +import Key from './domains/Key'; const cancelKey = (e: KeyboardEvent): boolean => { if (e.key === 'Escape') { @@ -66,7 +66,7 @@ export default class InputDriver { return; } - let key = keys.fromKeyboardEvent(e); + let key = Key.fromKeyboardEvent(e); for (let listener of this.onKeyListeners) { let stop = listener(key); if (stop) { diff --git a/src/content/client/FollowMasterClient.ts b/src/content/client/FollowMasterClient.ts index da75308..f79c8b0 100644 --- a/src/content/client/FollowMasterClient.ts +++ b/src/content/client/FollowMasterClient.ts @@ -35,7 +35,7 @@ export class FollowMasterClientImpl implements FollowMasterClient { this.postMessage({ type: messages.FOLLOW_KEY_PRESS, key: key.key, - ctrlKey: key.ctrlKey || false, + ctrlKey: key.ctrl || false, }); } diff --git a/src/content/domains/Key.ts b/src/content/domains/Key.ts index b25616e..669edfc 100644 --- a/src/content/domains/Key.ts +++ b/src/content/domains/Key.ts @@ -1,11 +1,3 @@ -export default interface Key { - key: string; - shiftKey?: boolean; - ctrlKey?: boolean; - altKey?: boolean; - metaKey?: boolean; -} - const modifiedKeyName = (name: string): string => { if (name === ' ') { return 'Space'; @@ -18,55 +10,84 @@ const modifiedKeyName = (name: string): string => { return name; }; -export const fromKeyboardEvent = (e: KeyboardEvent): Key => { - let key = modifiedKeyName(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; +export default class Key { + public readonly key: string; + + public readonly shift: boolean; + + public readonly ctrl: boolean; + + public readonly alt: boolean; + + public readonly meta: boolean; + + constructor({ key, shift, ctrl, alt, meta }: { + key: string; + shift: boolean; + ctrl: boolean; + alt: boolean; + meta: boolean; + }) { + this.key = key; + this.shift = shift; + this.ctrl = ctrl; + this.alt = alt; + this.meta = meta; } - return { - key: modifiedKeyName(e.key), - shiftKey: shift, - ctrlKey: e.ctrlKey, - altKey: e.altKey, - metaKey: e.metaKey, - }; -}; + static fromMapKey(str: string): Key { + if (str.startsWith('<') && str.endsWith('>')) { + let inner = str.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 new Key({ + key: base, + shift: shift, + ctrl: inner.includes('C-'), + alt: inner.includes('A-'), + meta: inner.includes('M-'), + }); + } -export const fromMapKey = (key: string): 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 new Key({ + key: str, + shift: str.toLowerCase() !== str, + ctrl: false, + alt: false, + meta: false, + }); + } + + static fromKeyboardEvent(e: KeyboardEvent): Key { + let key = modifiedKeyName(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: base, - shiftKey: inner.includes('S-'), - ctrlKey: inner.includes('C-'), - altKey: inner.includes('A-'), - metaKey: inner.includes('M-'), - }; + + return new Key({ + key: modifiedKeyName(e.key), + shift: shift, + ctrl: e.ctrlKey, + alt: e.altKey, + meta: e.metaKey, + }); } - return { - key: key, - shiftKey: key.toLowerCase() !== key, - ctrlKey: false, - altKey: false, - metaKey: false, - }; -}; -export const equals = (e1: Key, e2: Key): boolean => { - return e1.key === e2.key && - e1.ctrlKey === e2.ctrlKey && - e1.metaKey === e2.metaKey && - e1.altKey === e2.altKey && - e1.shiftKey === e2.shiftKey; -}; + equals(key: Key) { + return this.key === key.key && + this.ctrl === key.ctrl && + this.meta === key.meta && + this.alt === key.alt && + this.shift === key.shift; + } +} + diff --git a/src/content/domains/KeySequence.ts b/src/content/domains/KeySequence.ts index 6a05c2f..61ceab1 100644 --- a/src/content/domains/KeySequence.ts +++ b/src/content/domains/KeySequence.ts @@ -1,4 +1,4 @@ -import Key, * as keyUtils from './Key'; +import Key from './Key'; export default class KeySequence { private keys: Key[]; @@ -24,7 +24,7 @@ export default class KeySequence { return false; } for (let i = 0; i < o.keys.length; ++i) { - if (!keyUtils.equals(this.keys[i], o.keys[i])) { + if (!this.keys[i].equals(o.keys[i])) { return false; } } @@ -54,7 +54,7 @@ export const fromMapKeys = (keys: string): KeySequence => { return fromMapKeysRecursive( remainings.slice(nextPos), - mappedKeys.concat([keyUtils.fromMapKey(remainings.slice(0, nextPos))]) + mappedKeys.concat([Key.fromMapKey(remainings.slice(0, nextPos))]) ); }; diff --git a/test/content/InputDriver.test.ts b/test/content/InputDriver.test.ts index b9f2c28..b39312c 100644 --- a/test/content/InputDriver.test.ts +++ b/test/content/InputDriver.test.ts @@ -21,10 +21,10 @@ describe('InputDriver', () => { it('register callbacks', (done) => { driver.onKey((key: Key): boolean => { expect(key.key).to.equal('a'); - expect(key.ctrlKey).to.be.true; - expect(key.shiftKey).to.be.false; - expect(key.altKey).to.be.false; - expect(key.metaKey).to.be.false; + expect(key.ctrl).to.be.true; + expect(key.shift).to.be.false; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; done(); return true; }); @@ -68,15 +68,15 @@ describe('InputDriver', () => { it('propagates and stop handler chain', () => { let a = 0, b = 0, c = 0; - driver.onKey((key: Key): boolean => { + driver.onKey((_key: Key): boolean => { a++; return false; }); - driver.onKey((key: Key): boolean => { + driver.onKey((_key: Key): boolean => { b++; return true; }); - driver.onKey((key: Key): boolean => { + driver.onKey((_key: Key): boolean => { c++; return true; }); @@ -89,7 +89,7 @@ describe('InputDriver', () => { }) it('does not invoke only meta keys', () => { - driver.onKey((key: Key): boolean=> { + driver.onKey((_key: Key): boolean=> { expect.fail(); return false; }); @@ -115,7 +115,7 @@ describe('InputDriver', () => { it('ignores events from contenteditable elements', () => { let div = window.document.createElement('div'); let driver = new InputDriver(div); - driver.onKey((key: Key): boolean => { + driver.onKey((_key: Key): boolean => { expect.fail(); return false; }); diff --git a/test/content/domains/Key.test.ts b/test/content/domains/Key.test.ts index b3f9fb6..8e62f80 100644 --- a/test/content/domains/Key.test.ts +++ b/test/content/domains/Key.test.ts @@ -1,137 +1,139 @@ -import Key, * as keys from '../../../src/content/domains/Key'; +import Key from '../../../src/content/domains/Key'; import { expect } from 'chai' describe("Key", () => { describe('fromKeyboardEvent', () => { it('returns from keyboard input Ctrl+X', () => { - let k = keys.fromKeyboardEvent(new KeyboardEvent('keydown', { + let k = Key.fromKeyboardEvent(new KeyboardEvent('keydown', { key: 'x', shiftKey: false, ctrlKey: true, altKey: false, metaKey: true, })); expect(k.key).to.equal('x'); - expect(k.shiftKey).to.be.false; - expect(k.ctrlKey).to.be.true; - expect(k.altKey).to.be.false; - expect(k.metaKey).to.be.true; + expect(k.shift).to.be.false; + expect(k.ctrl).to.be.true; + expect(k.alt).to.be.false; + expect(k.meta).to.be.true; }); it('returns from keyboard input Shift+Esc', () => { - let k = keys.fromKeyboardEvent(new KeyboardEvent('keydown', { + let k = Key.fromKeyboardEvent(new KeyboardEvent('keydown', { key: 'Escape', shiftKey: true, ctrlKey: false, altKey: false, metaKey: true })); expect(k.key).to.equal('Esc'); - expect(k.shiftKey).to.be.true; - expect(k.ctrlKey).to.be.false; - expect(k.altKey).to.be.false; - expect(k.metaKey).to.be.true; + expect(k.shift).to.be.true; + expect(k.ctrl).to.be.false; + expect(k.alt).to.be.false; + expect(k.meta).to.be.true; }); it('returns from keyboard input Ctrl+$', () => { // $ required shift pressing on most keyboards - let k = keys.fromKeyboardEvent(new KeyboardEvent('keydown', { + let k = Key.fromKeyboardEvent(new KeyboardEvent('keydown', { key: '$', shiftKey: true, ctrlKey: true, altKey: false, metaKey: false })); expect(k.key).to.equal('$'); - expect(k.shiftKey).to.be.false; - expect(k.ctrlKey).to.be.true; - expect(k.altKey).to.be.false; - expect(k.metaKey).to.be.false; + expect(k.shift).to.be.false; + expect(k.ctrl).to.be.true; + expect(k.alt).to.be.false; + expect(k.meta).to.be.false; }); it('returns from keyboard input Crtl+Space', () => { - let k = keys.fromKeyboardEvent(new KeyboardEvent('keydown', { + let k = Key.fromKeyboardEvent(new KeyboardEvent('keydown', { key: ' ', shiftKey: false, ctrlKey: true, altKey: false, metaKey: false })); expect(k.key).to.equal('Space'); - expect(k.shiftKey).to.be.false; - expect(k.ctrlKey).to.be.true; - expect(k.altKey).to.be.false; - expect(k.metaKey).to.be.false; + expect(k.shift).to.be.false; + expect(k.ctrl).to.be.true; + expect(k.alt).to.be.false; + expect(k.meta).to.be.false; }); }); describe('fromMapKey', () => { it('return for X', () => { - let key = keys.fromMapKey('x'); + let key = Key.fromMapKey('x'); expect(key.key).to.equal('x'); - expect(key.shiftKey).to.be.false; - expect(key.ctrlKey).to.be.false; - expect(key.altKey).to.be.false; - expect(key.metaKey).to.be.false; + expect(key.shift).to.be.false; + expect(key.ctrl).to.be.false; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; }); it('return for Shift+X', () => { - let key = keys.fromMapKey('X'); + let key = Key.fromMapKey('X'); expect(key.key).to.equal('X'); - expect(key.shiftKey).to.be.true; - expect(key.ctrlKey).to.be.false; - expect(key.altKey).to.be.false; - expect(key.metaKey).to.be.false; + expect(key.shift).to.be.true; + expect(key.ctrl).to.be.false; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; }); it('return for Ctrl+X', () => { - let key = keys.fromMapKey(''); + let key = Key.fromMapKey(''); expect(key.key).to.equal('x'); - expect(key.shiftKey).to.be.false; - expect(key.ctrlKey).to.be.true; - expect(key.altKey).to.be.false; - expect(key.metaKey).to.be.false; + expect(key.shift).to.be.false; + expect(key.ctrl).to.be.true; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; }); it('returns for Ctrl+Meta+X', () => { - let key = keys.fromMapKey(''); + let key = Key.fromMapKey(''); expect(key.key).to.equal('x'); - expect(key.shiftKey).to.be.false; - expect(key.ctrlKey).to.be.true; - expect(key.altKey).to.be.false; - expect(key.metaKey).to.be.true; + expect(key.shift).to.be.false; + expect(key.ctrl).to.be.true; + expect(key.alt).to.be.false; + expect(key.meta).to.be.true; }); it('returns for Ctrl+Shift+x', () => { - let key = keys.fromMapKey(''); + let key = Key.fromMapKey(''); expect(key.key).to.equal('X'); - expect(key.shiftKey).to.be.true; - expect(key.ctrlKey).to.be.true; - expect(key.altKey).to.be.false; - expect(key.metaKey).to.be.false; + expect(key.shift).to.be.true; + expect(key.ctrl).to.be.true; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; }); it('returns for Shift+Esc', () => { - let key = keys.fromMapKey(''); + let key = Key.fromMapKey(''); expect(key.key).to.equal('Esc'); - expect(key.shiftKey).to.be.true; - expect(key.ctrlKey).to.be.false; - expect(key.altKey).to.be.false; - expect(key.metaKey).to.be.false; + expect(key.shift).to.be.true; + expect(key.ctrl).to.be.false; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; }); it('returns for Ctrl+Esc', () => { - let key = keys.fromMapKey(''); + let key = Key.fromMapKey(''); expect(key.key).to.equal('Esc'); - expect(key.shiftKey).to.be.false; - expect(key.ctrlKey).to.be.true; - expect(key.altKey).to.be.false; - expect(key.metaKey).to.be.false; + expect(key.shift).to.be.false; + expect(key.ctrl).to.be.true; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; }); it('returns for Ctrl+Esc', () => { - let key = keys.fromMapKey(''); + let key = Key.fromMapKey(''); expect(key.key).to.equal('Space'); - expect(key.shiftKey).to.be.false; - expect(key.ctrlKey).to.be.true; - expect(key.altKey).to.be.false; - expect(key.metaKey).to.be.false; + expect(key.shift).to.be.false; + expect(key.ctrl).to.be.true; + expect(key.alt).to.be.false; + expect(key.meta).to.be.false; }); }); describe('equals', () => { - expect(keys.equals( - { key: 'x', ctrlKey: true, }, - { key: 'x', ctrlKey: true, }, - )).to.be.true; + expect(new Key({ + key: 'x', shift: false, ctrl: true, alt: false, meta: false, + }).equals(new Key({ + key: 'x', shift: false, ctrl: true, alt: false, meta: false, + }))).to.be.true; - expect(keys.equals( - { key: 'X', shiftKey: true, }, - { key: 'x', ctrlKey: true, }, - )).to.be.false; + expect(new Key({ + key: 'x', shift: false, ctrl: false, alt: false, meta: false, + }).equals(new Key({ + key: 'X', shift: true, ctrl: false, alt: false, meta: false, + }))).to.be.false; }); }); diff --git a/test/content/domains/KeySequence.test.ts b/test/content/domains/KeySequence.test.ts index 7387c06..9afc360 100644 --- a/test/content/domains/KeySequence.test.ts +++ b/test/content/domains/KeySequence.test.ts @@ -1,48 +1,50 @@ import KeySequence, * as utils from '../../../src/content/domains/KeySequence'; +import Key from '../../../src/content/domains/Key'; import { expect } from 'chai' describe("KeySequence", () => { describe('#push', () => { it('append a key to the sequence', () => { let seq = KeySequence.from([]); - seq.push({ key: 'g' }); - seq.push({ key: 'u', shiftKey: true }); + seq.push(Key.fromMapKey('g')); + seq.push(Key.fromMapKey('')); let array = seq.getKeyArray(); - expect(array[0]).to.deep.equal({ key: 'g' }); - expect(array[1]).to.deep.equal({ key: 'u', shiftKey: true }); + expect(array[0].key).to.equal('g'); + expect(array[1].key).to.equal('U'); + expect(array[1].shift).to.be.true; }) }); describe('#startsWith', () => { it('returns true if the key sequence starts with param', () => { let seq = KeySequence.from([ - { key: 'g' }, - { key: 'u', shiftKey: true }, + Key.fromMapKey('g'), + Key.fromMapKey(''), ]); expect(seq.startsWith(KeySequence.from([ ]))).to.be.true; expect(seq.startsWith(KeySequence.from([ - { key: 'g' }, + Key.fromMapKey('g'), ]))).to.be.true; expect(seq.startsWith(KeySequence.from([ - { key: 'g' }, { key: 'u', shiftKey: true }, + Key.fromMapKey('g'), Key.fromMapKey(''), ]))).to.be.true; expect(seq.startsWith(KeySequence.from([ - { key: 'g' }, { key: 'u', shiftKey: true }, { key: 'x' }, + Key.fromMapKey('g'), Key.fromMapKey(''), Key.fromMapKey('x'), ]))).to.be.false; expect(seq.startsWith(KeySequence.from([ - { key: 'h' }, + Key.fromMapKey('h'), ]))).to.be.false; - }) + }); it('returns true if the empty sequence starts with an empty sequence', () => { let seq = KeySequence.from([]); expect(seq.startsWith(KeySequence.from([]))).to.be.true; expect(seq.startsWith(KeySequence.from([ - { key: 'h' }, + Key.fromMapKey('h'), ]))).to.be.false; }) }); @@ -52,21 +54,20 @@ describe("KeySequence", () => { let keyArray = utils.fromMapKeys('').getKeyArray(); expect(keyArray).to.have.lengthOf(1); expect(keyArray[0].key).to.equal('Esc'); - expect(keyArray[0].shiftKey).to.be.true; + expect(keyArray[0].shift).to.be.true; }); it('returns mapped keys for ad', () => { let keyArray = utils.fromMapKeys('ad').getKeyArray(); expect(keyArray).to.have.lengthOf(5); expect(keyArray[0].key).to.equal('a'); - expect(keyArray[1].ctrlKey).to.be.true; + expect(keyArray[1].ctrl).to.be.true; expect(keyArray[1].key).to.equal('b'); - expect(keyArray[2].altKey).to.be.true; + expect(keyArray[2].alt).to.be.true; expect(keyArray[2].key).to.equal('c'); expect(keyArray[3].key).to.equal('d'); - expect(keyArray[4].metaKey).to.be.true; + expect(keyArray[4].meta).to.be.true; expect(keyArray[4].key).to.equal('e'); }); }) - }); diff --git a/test/content/repositories/KeymapRepository.test.ts b/test/content/repositories/KeymapRepository.test.ts index 34704d9..8f0be67 100644 --- a/test/content/repositories/KeymapRepository.test.ts +++ b/test/content/repositories/KeymapRepository.test.ts @@ -1,5 +1,6 @@ import KeymapRepository, { KeymapRepositoryImpl } from '../../../src/content/repositories/KeymapRepository'; +import Key from '../../../src/content/domains/Key' import { expect } from 'chai'; describe('KeymapRepositoryImpl', () => { @@ -11,24 +12,25 @@ describe('KeymapRepositoryImpl', () => { describe('#enqueueKey()', () => { it('enqueues keys', () => { - sut.enqueueKey({ key: 'a' }); - sut.enqueueKey({ key: 'b' }); - let sequence = sut.enqueueKey({ key: 'c' }); - - expect(sequence.getKeyArray()).deep.equals([ - { key: 'a' }, { key: 'b' }, { key: 'c' }, - ]); + sut.enqueueKey(Key.fromMapKey('a'); + sut.enqueueKey(Key.fromMapKey('b'); + let sequence = sut.enqueueKey(Key.fromMapKey('c')); + + let keys = sequence.getKeyArray(); + expect(keys[0].equals(Key.fromMapKey('a'))).to.be.true; + expect(keys[1].equals(Key.fromMapKey('b'))).to.be.true; + expect(keys[2].equals(Key.fromMapKey('c'))).to.be.true; }); }); describe('#clear()', () => { it('clears keys', () => { - sut.enqueueKey({ key: 'a' }); - sut.enqueueKey({ key: 'b' }); - sut.enqueueKey({ key: 'c' }); + sut.enqueueKey(Key.fromMapKey('a'); + sut.enqueueKey(Key.fromMapKey('b'); + sut.enqueueKey(Key.fromMapKey('c'); sut.clear(); - let sequence = sut.enqueueKey({ key: 'a' }); + let sequence = sut.enqueueKey(Key.fromMapKey('a')); expect(sequence.length()).to.equal(1); }); });