diff --git a/src/actions/operation.js b/src/actions/operation.js index 295fd4f..0bb8310 100644 --- a/src/actions/operation.js +++ b/src/actions/operation.js @@ -2,6 +2,9 @@ import operations from 'shared/operations'; import messages from 'content/messages'; import * as tabs from 'background/tabs'; import * as zooms from 'background/zooms'; +import * as scrolls from 'content/scrolls'; +import * as navigates from 'content/navigates'; +import * as followActions from 'actions/follow'; const sendConsoleShowCommand = (tab, command) => { return browser.tabs.sendMessage(tab.id, { @@ -10,7 +13,43 @@ const sendConsoleShowCommand = (tab, command) => { }); }; -const exec = (operation, tab) => { +const exec = (operation) => { + switch (operation.type) { + case operations.SCROLL_LINES: + return scrolls.scrollLines(window, operation.count); + case operations.SCROLL_PAGES: + return scrolls.scrollPages(window, operation.count); + case operations.SCROLL_TOP: + return scrolls.scrollTop(window); + case operations.SCROLL_BOTTOM: + return scrolls.scrollBottom(window); + case operations.SCROLL_HOME: + return scrolls.scrollLeft(window); + case operations.SCROLL_END: + return scrolls.scrollRight(window); + case operations.FOLLOW_START: + return followActions.enable(false); + case operations.NAVIGATE_HISTORY_PREV: + return navigates.historyPrev(window); + case operations.NAVIGATE_HISTORY_NEXT: + return navigates.historyNext(window); + case operations.NAVIGATE_LINK_PREV: + return navigates.linkPrev(window); + case operations.NAVIGATE_LINK_NEXT: + return navigates.linkNext(window); + case operations.NAVIGATE_PARENT: + return navigates.parent(window); + case operations.NAVIGATE_ROOT: + return navigates.root(window); + default: + browser.runtime.sendMessage({ + type: messages.BACKGROUND_OPERATION, + operation, + }); + } +}; + +const execBackground = (operation, tab) => { switch (operation.type) { case operations.TAB_CLOSE: return tabs.closeTab(tab.id); @@ -45,11 +84,8 @@ const exec = (operation, tab) => { case operations.COMMAND_SHOW_BUFFER: return sendConsoleShowCommand(tab, 'buffer '); default: - return browser.tabs.sendMessage(tab.id, { - type: messages.CONTENT_OPERATION, - operation - }); + return Promise.resolve(); } }; -export { exec }; +export { exec, execBackground }; diff --git a/src/background/index.js b/src/background/index.js index b966c13..dbc10fb 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -1,7 +1,6 @@ import * as settingsActions from 'actions/setting'; import messages from 'content/messages'; import BackgroundComponent from 'components/background'; -import BackgroundInputComponent from 'components/background-input'; import reducers from 'reducers'; import { createStore } from 'store'; @@ -15,10 +14,8 @@ const store = createStore(reducers, (e, sender) => { } }); const backgroundComponent = new BackgroundComponent(store); -const backgroundInputComponent = new BackgroundInputComponent(store); store.subscribe((sender) => { backgroundComponent.update(sender); - backgroundInputComponent.update(sender); }); store.dispatch(settingsActions.load()); diff --git a/src/components/background-input.js b/src/components/background-input.js deleted file mode 100644 index bd6ecf9..0000000 --- a/src/components/background-input.js +++ /dev/null @@ -1,53 +0,0 @@ -import * as inputActions from 'actions/input'; -import * as operationActions from 'actions/operation'; - -export default class BackgroundInputComponent { - constructor(store) { - this.store = store; - this.keymaps = {}; - this.prevInputs = []; - } - - update(sender) { - let state = this.store.getState(); - this.reloadSettings(state.setting); - this.handleKeyInputs(sender, state.input); - } - - reloadSettings(setting) { - if (!setting.settings.json) { - return; - } - this.keymaps = JSON.parse(setting.settings.json).keymaps; - } - - handleKeyInputs(sender, input) { - if (JSON.stringify(this.prevInputs) === JSON.stringify(input)) { - return; - } - this.prevInputs = input; - - if (input.keys.length === 0) { - return; - } - if (sender) { - return this.handleKeysChanged(sender, input); - } - } - - handleKeysChanged(sender, input) { - let matched = Object.keys(this.keymaps).filter((keyStr) => { - return keyStr.startsWith(input.keys); - }); - if (matched.length === 0) { - this.store.dispatch(inputActions.clearKeys(), sender); - return Promise.resolve(); - } else if (matched.length > 1 || - matched.length === 1 && input.keys !== matched[0]) { - return Promise.resolve(); - } - let operation = this.keymaps[matched]; - this.store.dispatch(operationActions.exec(operation, sender.tab), sender); - this.store.dispatch(inputActions.clearKeys(), sender); - } -} diff --git a/src/components/background.js b/src/components/background.js index de44dae..79a7a6a 100644 --- a/src/components/background.js +++ b/src/components/background.js @@ -1,5 +1,5 @@ import messages from 'content/messages'; -import * as inputActions from 'actions/input'; +import * as operationActions from 'actions/operation'; import * as settingsActions from 'actions/setting'; import * as tabActions from 'actions/tab'; import * as commands from 'shared/commands'; @@ -37,9 +37,10 @@ export default class BackgroundComponent { onMessage(message, sender) { switch (message.type) { - case messages.KEYDOWN: + case messages.BACKGROUND_OPERATION: return this.store.dispatch( - inputActions.keyPress(message.key, message.ctrl), sender); + operationActions.execBackground(message.operation, sender.tab), + sender); case messages.OPEN_URL: if (message.newTab) { return this.store.dispatch( @@ -70,7 +71,7 @@ export default class BackgroundComponent { } onTabUpdated(id, info) { - if (info.url) { + if (info.status === 'complete') { this.syncSettings(id); } } diff --git a/src/components/content-input.js b/src/components/content-input.js index 38d57fd..1dbf48f 100644 --- a/src/components/content-input.js +++ b/src/components/content-input.js @@ -1,14 +1,43 @@ -import messages from 'content/messages'; +import * as inputActions from 'actions/input'; +import * as operationActions from 'actions/operation'; export default class ContentInputComponent { - constructor(target) { + constructor(target, store) { this.pressed = {}; + this.store = store; target.addEventListener('keypress', this.onKeyPress.bind(this)); target.addEventListener('keydown', this.onKeyDown.bind(this)); target.addEventListener('keyup', this.onKeyUp.bind(this)); } + update() { + let settings = this.store.getState().setting.settings; + if (!settings) { + return; + } + let input = this.store.getState().input; + let keymaps = JSON.parse(settings.json).keymaps; + + let matched = Object.keys(keymaps).filter((keyStr) => { + return keyStr.startsWith(input.keys); + }); + if (matched.length === 0) { + this.store.dispatch(inputActions.clearKeys()); + return Promise.resolve(); + } else if (matched.length > 1 || + matched.length === 1 && input.keys !== matched[0]) { + return Promise.resolve(); + } + let operation = keymaps[matched]; + try { + this.store.dispatch(operationActions.exec(operation)); + } catch (e) { + console.error(e); + } + this.store.dispatch(inputActions.clearKeys()); + } + onKeyPress(e) { if (this.pressed[e.key] && this.pressed[e.key] !== 'keypress') { return; @@ -30,18 +59,21 @@ export default class ContentInputComponent { } capture(e) { - if (e.target instanceof HTMLInputElement || - e.target instanceof HTMLTextAreaElement || - e.target instanceof HTMLSelectElement) { + if (this.fromInput(e)) { if (e.key === 'Escape' && e.target.blur) { e.target.blur(); } return; } - browser.runtime.sendMessage({ - type: messages.KEYDOWN, - key: e.key, - ctrl: e.ctrlKey - }); + if (e.key === 'OS') { + return; + } + this.store.dispatch(inputActions.keyPress(e.key, e.ctrlKey)); + } + + fromInput(e) { + return e.target instanceof HTMLInputElement || + e.target instanceof HTMLTextAreaElement || + e.target instanceof HTMLSelectElement; } } diff --git a/src/content/index.js b/src/content/index.js index cd1a0af..09143b0 100644 --- a/src/content/index.js +++ b/src/content/index.js @@ -1,70 +1,32 @@ import './console-frame.scss'; import * as consoleFrames from './console-frames'; -import * as scrolls from 'content/scrolls'; -import * as navigates from 'content/navigates'; import * as settingActions from 'actions/setting'; -import * as followActions from 'actions/follow'; import { createStore } from 'store'; import ContentInputComponent from 'components/content-input'; import FollowComponent from 'components/follow'; import reducers from 'reducers'; -import operations from 'shared/operations'; import messages from './messages'; const store = createStore(reducers); const followComponent = new FollowComponent(window.document.body, store); +const contentInputComponent = new ContentInputComponent(window, store); store.subscribe(() => { try { followComponent.update(); + contentInputComponent.update(); } catch (e) { console.error(e); } }); -// eslint-disable-next-line no-unused-vars -const contentInputComponent = new ContentInputComponent(window); consoleFrames.initialize(window.document); -const execOperation = (operation) => { - switch (operation.type) { - case operations.SCROLL_LINES: - return scrolls.scrollLines(window, operation.count); - case operations.SCROLL_PAGES: - return scrolls.scrollPages(window, operation.count); - case operations.SCROLL_TOP: - return scrolls.scrollTop(window); - case operations.SCROLL_BOTTOM: - return scrolls.scrollBottom(window); - case operations.SCROLL_HOME: - return scrolls.scrollLeft(window); - case operations.SCROLL_END: - return scrolls.scrollRight(window); - case operations.FOLLOW_START: - return store.dispatch(followActions.enable(false)); - case operations.NAVIGATE_HISTORY_PREV: - return navigates.historyPrev(window); - case operations.NAVIGATE_HISTORY_NEXT: - return navigates.historyNext(window); - case operations.NAVIGATE_LINK_PREV: - return navigates.linkPrev(window); - case operations.NAVIGATE_LINK_NEXT: - return navigates.linkNext(window); - case operations.NAVIGATE_PARENT: - return navigates.parent(window); - case operations.NAVIGATE_ROOT: - return navigates.root(window); - } -}; - browser.runtime.onMessage.addListener((action) => { switch (action.type) { case messages.CONSOLE_HIDE: window.focus(); consoleFrames.blur(window.document); return Promise.resolve(); - case messages.CONTENT_OPERATION: - execOperation(action.operation); - return Promise.resolve(); case messages.CONTENT_SET_SETTINGS: store.dispatch(settingActions.set(action.settings)); return Promise.resolve(); diff --git a/src/content/messages.js b/src/content/messages.js index 8d416c7..eb056a7 100644 --- a/src/content/messages.js +++ b/src/content/messages.js @@ -1,6 +1,6 @@ export default { - CONTENT_OPERATION: 'content.operation', CONTENT_SET_SETTINGS: 'content.set.settings', + BACKGROUND_OPERATION: 'background.operation', CONSOLE_BLURRED: 'console.blured', CONSOLE_ENTERED: 'console.entered', @@ -9,8 +9,6 @@ export default { CONSOLE_SHOW_ERROR: 'console.show.error', CONSOLE_HIDE: 'console.hide', - KEYDOWN: 'keydown', - OPEN_URL: 'open.url', SETTINGS_RELOAD: 'settings.reload',