diff --git a/manifest.json b/manifest.json index dfb4ae9..7c60cdd 100644 --- a/manifest.json +++ b/manifest.json @@ -15,11 +15,15 @@ ] }, "permissions": [ + "history", "sessions", - "tabs", - "history" + "storage", + "tabs" ], "web_accessible_resources": [ "build/console.html" - ] + ], + "options_ui": { + "page": "build/settings.html" + } } diff --git a/src/actions/index.js b/src/actions/index.js index 977b3c2..7b79864 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -8,4 +8,5 @@ export default { // User input INPUT_KEY_PRESS: 'input.key,press', INPUT_CLEAR_KEYS: 'input.clear.keys', + INPUT_SET_KEYMAPS: 'input.set,keymaps', }; diff --git a/src/actions/input.js b/src/actions/input.js index 07948a1..de6de4e 100644 --- a/src/actions/input.js +++ b/src/actions/input.js @@ -14,4 +14,11 @@ const clearKeys = () => { }; }; -export { keyPress, clearKeys }; +const setKeymaps = (keymaps) => { + return { + type: actions.INPUT_SET_KEYMAPS, + keymaps: keymaps + }; +}; + +export { keyPress, clearKeys, setKeymaps }; diff --git a/src/actions/operation.js b/src/actions/operation.js index 50329f8..5646c1c 100644 --- a/src/actions/operation.js +++ b/src/actions/operation.js @@ -6,15 +6,15 @@ import * as zooms from '../background/zooms'; const exec = (operation, tab) => { switch (operation.type) { - case operations.TABS_CLOSE: + case operations.TAB_CLOSE: return tabs.closeTab(tab.id); - case operations.TABS_REOPEN: + case operations.TAB_REOPEN: return tabs.reopenTab(); - case operations.TABS_PREV: + case operations.TAB_PREV: return tabs.selectPrevTab(tab.index, operation.count); - case operations.TABS_NEXT: + case operations.TAB_NEXT: return tabs.selectNextTab(tab.index, operation.count); - case operations.TABS_RELOAD: + case operations.TAB_RELOAD: return tabs.reload(tab, operation.cache); case operations.ZOOM_IN: return zooms.zoomIn(); @@ -22,21 +22,21 @@ const exec = (operation, tab) => { return zooms.zoomOut(); case operations.ZOOM_NEUTRAL: return zooms.neutral(); - case operations.COMMAND_OPEN: + case operations.COMMAND_SHOW: return consoleActions.showCommand(''); - case operations.COMMAND_TABS_OPEN: + case operations.COMMAND_SHOW_OPEN: if (operation.alter) { // alter url return consoleActions.showCommand('open ' + tab.url); } return consoleActions.showCommand('open '); - case operations.COMMAND_TABS_NEW: + case operations.COMMAND_SHOW_TABOPEN: if (operation.alter) { // alter url return consoleActions.showCommand('tabopen ' + tab.url); } return consoleActions.showCommand('tabopen '); - case operations.COMMAND_BUFFER: + case operations.COMMAND_SHOW_BUFFER: return consoleActions.showCommand('buffer '); default: return browser.tabs.sendMessage(tab.id, { diff --git a/src/background/default-settings.js b/src/background/default-settings.js new file mode 100644 index 0000000..ea6318b --- /dev/null +++ b/src/background/default-settings.js @@ -0,0 +1,39 @@ +export default `{ + "keymaps": { + "0": { "type": "scroll.home" }, + ":": { "type": "command.show" }, + "o": { "type": "command.show.open", "alter": false }, + "O": { "type": "command.show.open", "alter": true }, + "t": { "type": "command.show.tabopen", "alter": false }, + "T": { "type": "command.show.tabopen", "alter": true }, + "b": { "type": "command.show.buffer" }, + "k": { "type": "scroll.lines", "count": -1 }, + "j": { "type": "scroll.lines", "count": 1 }, + "": { "type": "scroll.lines", "count": -1 }, + "": { "type": "scroll.lines", "count": 1 }, + "": { "type": "scroll.pages", "count": -0.5 }, + "": { "type": "scroll.pages", "count": 0.5 }, + "": { "type": "scroll.pages", "count": -1 }, + "": { "type": "scroll.pages", "count": 1 }, + "gg": { "type": "scroll.top" }, + "G": { "type": "scroll.bottom" }, + "$": { "type": "scroll.end" }, + "d": { "type": "tabs.close" }, + "u": { "type": "tabs.reopen" }, + "h": { "type": "tabs.prev", "count": 1 }, + "l": { "type": "tabs.next", "count": 1 }, + "r": { "type": "tabs.reload", "cache": false }, + "R": { "type": "tabs.reload", "cache": true }, + "zi": { "type": "zoom.in" }, + "zo": { "type": "zoom.out" }, + "zz": { "type": "zoom.neutral" }, + "f": { "type": "follow.start", "newTab": false }, + "F": { "type": "follow.start", "newTab": true }, + "H": { "type": "navigate.history.prev" }, + "L": { "type": "navigate.history.next" }, + "[[": { "type": "navigate.link.prev" }, + "]]": { "type": "navigate.link.next" }, + "gu": { "type": "navigate.parent" }, + "gU": { "type": "navigate.root" } + } +}`; diff --git a/src/background/index.js b/src/background/index.js index 9df22fd..5ae967c 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -41,7 +41,7 @@ backgroundStore.subscribe((sender) => { const keyQueueChanged = (state, sender) => { let prefix = keys.asKeymapChars(state.input.keys); - let matched = Object.keys(keys.defaultKeymap).filter((keyStr) => { + let matched = Object.keys(state.input.keymaps).filter((keyStr) => { return keyStr.startsWith(prefix); }); if (matched.length === 0) { @@ -51,11 +51,19 @@ const keyQueueChanged = (state, sender) => { matched.length === 1 && prefix !== matched[0]) { return Promise.resolve(); } - let action = keys.defaultKeymap[matched]; + let action = state.input.keymaps[matched]; backgroundStore.dispatch(operationActions.exec(action, sender.tab), sender); backgroundStore.dispatch(inputActions.clearKeys(), sender); }; +const reloadSettings = () => { + browser.storage.local.get('settings').then((value) => { + let settings = JSON.parse(value.settings.json); + let action = inputActions.setKeymaps(settings.keymaps); + backgroundStore.dispatch(action); + }, console.error); +}; + const handleMessage = (message, sender) => { switch (message.type) { case messages.KEYDOWN: @@ -77,6 +85,8 @@ const handleMessage = (message, sender) => { case messages.CONSOLE_CHANGEED: return backgroundStore.dispatch( commandActions.complete(message.text), sender); + case messages.SETTINGS_RELOAD: + return reloadSettings(); } }; @@ -87,3 +97,9 @@ browser.runtime.onMessage.addListener((message, sender) => { backgroundStore.dispatch(consoleActions.showError(e.message), sender); } }); + +const initializeSettings = () => { + reloadSettings(); +}; + +initializeSettings(); diff --git a/src/background/keys.js b/src/background/keys.js index e4a8b19..aca050e 100644 --- a/src/background/keys.js +++ b/src/background/keys.js @@ -1,43 +1,3 @@ -import operations from '../operations'; - -const defaultKeymap = { - ':': { type: operations.COMMAND_OPEN }, - 'o': { type: operations.COMMAND_TABS_OPEN, alter: false }, - 'O': { type: operations.COMMAND_TABS_OPEN, alter: true }, - 't': { type: operations.COMMAND_TABS_NEW, alter: false }, - 'T': { type: operations.COMMAND_TABS_NEW, alter: true }, - 'b': { type: operations.COMMAND_BUFFER }, - 'k': { type: operations.SCROLL_LINES, count: -1 }, - 'j': { type: operations.SCROLL_LINES, count: 1 }, - '': { type: operations.SCROLL_LINES, count: -1 }, - '': { type: operations.SCROLL_LINES, count: 1 }, - '': { type: operations.SCROLL_PAGES, count: -0.5 }, - '': { type: operations.SCROLL_PAGES, count: 0.5 }, - '': { type: operations.SCROLL_PAGES, count: -1 }, - '': { type: operations.SCROLL_PAGES, count: 1 }, - 'gg': { type: operations.SCROLL_TOP }, - 'G': { type: operations.SCROLL_BOTTOM }, - '0': { type: operations.SCROLL_LEFT }, - '$': { type: operations.SCROLL_RIGHT }, - 'd': { type: operations.TABS_CLOSE }, - 'u': { type: operations.TABS_REOPEN }, - 'h': { type: operations.TABS_PREV, count: 1 }, - 'l': { type: operations.TABS_NEXT, count: 1 }, - 'r': { type: operations.TABS_RELOAD, cache: false }, - 'R': { type: operations.TABS_RELOAD, cache: true }, - 'zi': { type: operations.ZOOM_IN }, - 'zo': { type: operations.ZOOM_OUT }, - 'zz': { type: operations.ZOOM_NEUTRAL }, - 'f': { type: operations.FOLLOW_START, newTab: false }, - 'F': { type: operations.FOLLOW_START, newTab: true }, - 'H': { type: operations.NAVIGATE_HISTORY_PREV }, - 'L': { type: operations.NAVIGATE_HISTORY_NEXT }, - '[[': { type: operations.NAVIGATE_LINK_PREV }, - ']]': { type: operations.NAVIGATE_LINK_NEXT }, - 'gu': { type: operations.NAVIGATE_PARENT }, - 'gU': { type: operations.NAVIGATE_ROOT }, -}; - const asKeymapChars = (keys) => { return keys.map((k) => { let c = String.fromCharCode(k.code); @@ -58,4 +18,4 @@ const asCaretChars = (keys) => { }).join(''); }; -export { defaultKeymap, asKeymapChars, asCaretChars }; +export { asKeymapChars, asCaretChars }; diff --git a/src/content/index.js b/src/content/index.js index 80acd2d..812fbc5 100644 --- a/src/content/index.js +++ b/src/content/index.js @@ -85,9 +85,9 @@ const execOperation = (operation) => { return scrolls.scrollTop(window); case operations.SCROLL_BOTTOM: return scrolls.scrollBottom(window); - case operations.SCROLL_LEFT: + case operations.SCROLL_HOME: return scrolls.scrollLeft(window); - case operations.SCROLL_RIGHT: + case operations.SCROLL_END: return scrolls.scrollRight(window); case operations.FOLLOW_START: return startFollows(operation.newTab); diff --git a/src/messages/index.js b/src/messages/index.js index 4e34436..df9fba2 100644 --- a/src/messages/index.js +++ b/src/messages/index.js @@ -8,5 +8,7 @@ export default { KEYDOWN: 'keydown', - OPEN_URL: 'open.url' + OPEN_URL: 'open.url', + + SETTINGS_RELOAD: 'settings.reload', }; diff --git a/src/operations/index.js b/src/operations/index.js index d6ffc42..b68f59d 100644 --- a/src/operations/index.js +++ b/src/operations/index.js @@ -1,17 +1,22 @@ export default { // Command - COMMAND_OPEN: 'cmd.open', - COMMAND_TABS_OPEN: 'cmd.tabs.open', - COMMAND_TABS_NEW: 'cmd.tabs.new', - COMMAND_BUFFER: 'cmd.buffer', + COMMAND_SHOW: 'command.show', + COMMAND_SHOW_OPEN: 'command.show.open', + COMMAND_SHOW_TABOPEN: 'command.show.tabopen', + COMMAND_SHOW_BUFFER: 'command.show.buffer', + // Scrolls SCROLL_LINES: 'scroll.lines', SCROLL_PAGES: 'scroll.pages', SCROLL_TOP: 'scroll.top', SCROLL_BOTTOM: 'scroll.bottom', - SCROLL_LEFT: 'scroll.left', - SCROLL_RIGHT: 'scroll.right', + SCROLL_HOME: 'scroll.home', + SCROLL_END: 'scroll.end', + + // Follows FOLLOW_START: 'follow.start', + + // Navigations NAVIGATE_HISTORY_PREV: 'navigate.history.prev', NAVIGATE_HISTORY_NEXT: 'navigate.history.next', NAVIGATE_LINK_PREV: 'navigate.link.prev', @@ -19,12 +24,14 @@ export default { NAVIGATE_PARENT: 'navigate.parent', NAVIGATE_ROOT: 'navigate.root', - // Background - TABS_CLOSE: 'tabs.close', - TABS_REOPEN: 'tabs.reopen', - TABS_PREV: 'tabs.prev', - TABS_NEXT: 'tabs.next', - TABS_RELOAD: 'tabs.reload', + // Tabs + TAB_CLOSE: 'tabs.close', + TAB_REOPEN: 'tabs.reopen', + TAB_PREV: 'tabs.prev', + TAB_NEXT: 'tabs.next', + TAB_RELOAD: 'tabs.reload', + + // Zooms ZOOM_IN: 'zoom.in', ZOOM_OUT: 'zoom.out', ZOOM_NEUTRAL: 'zoom.neutral', diff --git a/src/reducers/input.js b/src/reducers/input.js index eb7ff24..dca26e2 100644 --- a/src/reducers/input.js +++ b/src/reducers/input.js @@ -2,6 +2,7 @@ import actions from '../actions'; const defaultState = { keys: [], + keymaps: {} }; export default function reducer(state = defaultState, action = {}) { @@ -19,6 +20,11 @@ export default function reducer(state = defaultState, action = {}) { return Object.assign({}, state, { keys: [], }); + case actions.INPUT_SET_KEYMAPS: + return Object.assign({}, state, { + keymaps: action.keymaps, + keys: [], + }); default: return state; } diff --git a/src/settings/index.js b/src/settings/index.js new file mode 100644 index 0000000..2ed060c --- /dev/null +++ b/src/settings/index.js @@ -0,0 +1,22 @@ +import './settings.scss'; +import messages from '../messages'; + +document.addEventListener('DOMContentLoaded', () => { + let form = document.getElementById('vimvixen-settings-form'); + form.addEventListener('submit', (e) => { + e.preventDefault(); + browser.storage.local.set({ + settings: { + json: e.target.elements['plain-json'].value + } + }).then(() => { + return browser.runtime.sendMessage({ + type: messages.SETTINGS_RELOAD + }); + }); + }); + + browser.storage.local.get('settings').then((value) => { + form.elements['plain-json'].value = value.settings.json; + }, console.error); +}); diff --git a/src/settings/settings.html b/src/settings/settings.html new file mode 100644 index 0000000..99d6c6b --- /dev/null +++ b/src/settings/settings.html @@ -0,0 +1,18 @@ + + + + + + +

Configure

+ +

Home page

+
+ + + + +
+ + + diff --git a/src/settings/settings.scss b/src/settings/settings.scss new file mode 100644 index 0000000..5707c8a --- /dev/null +++ b/src/settings/settings.scss @@ -0,0 +1,8 @@ +.vimvixen-settings-form { + textarea[name=plain-json] { + font-family: monospace; + width: 100%; + min-height: 64ex; + resize: vertical; + } +} diff --git a/webpack.config.js b/webpack.config.js index ba08975..bf121d1 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -7,6 +7,7 @@ const dist = path.resolve(__dirname, 'build'); module.exports = { entry: { index: path.join(src, 'content'), + settings: path.join(src, 'settings'), background: path.join(src, 'background'), console: path.join(src, 'console', 'console.js') }, @@ -46,6 +47,11 @@ module.exports = { template: path.join(src, 'console', 'console.html'), filename: path.join(dist, 'console.html'), inject: false + }), + new HtmlWebpackPlugin({ + template: path.join(src, 'settings', 'settings.html'), + filename: path.join(dist, 'settings.html'), + inject: false }) ] };