diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/background/infrastructures/content-message-listener.js | 22 | ||||
-rw-r--r-- | src/console/components/completion.js | 65 | ||||
-rw-r--r-- | src/console/components/console.js | 175 | ||||
-rw-r--r-- | src/console/components/console.jsx | 133 | ||||
-rw-r--r-- | src/console/components/console.scss (renamed from src/console/site.scss) | 10 | ||||
-rw-r--r-- | src/console/components/console/completion.jsx | 56 | ||||
-rw-r--r-- | src/console/components/console/input.jsx | 32 | ||||
-rw-r--r-- | src/console/components/console/message.jsx | 18 | ||||
-rw-r--r-- | src/console/index.html | 12 | ||||
-rw-r--r-- | src/console/index.jsx (renamed from src/console/index.js) | 21 | ||||
-rw-r--r-- | src/content/actions/find.js | 25 | ||||
-rw-r--r-- | src/content/actions/operation.js | 2 | ||||
-rw-r--r-- | src/content/components/common/mark.js | 4 | ||||
-rw-r--r-- | src/content/console-frames.js | 27 | ||||
-rw-r--r-- | src/shared/messages.js | 3 |
15 files changed, 298 insertions, 307 deletions
diff --git a/src/background/infrastructures/content-message-listener.js b/src/background/infrastructures/content-message-listener.js index beb52fe..aae07c0 100644 --- a/src/background/infrastructures/content-message-listener.js +++ b/src/background/infrastructures/content-message-listener.js @@ -16,6 +16,8 @@ export default class ContentMessageListener { this.linkController = new LinkController(); this.backgroundOperationController = new OperationController(); this.markController = new MarkController(); + + this.consolePorts = {}; } run() { @@ -38,6 +40,7 @@ export default class ContentMessageListener { }); } }); + browser.runtime.onConnect.addListener(this.onConnected.bind(this)); } onMessage(message, sender) { @@ -65,6 +68,8 @@ export default class ContentMessageListener { return this.onMarkSetGlobal(message.key, message.x, message.y); case messages.MARK_JUMP_GLOBAL: return this.onMarkJumpGlobal(message.key); + case messages.CONSOLE_FRAME_MESSAGE: + return this.onConsoleFrameMessage(sender.tab.id, message.message); } } @@ -116,4 +121,21 @@ export default class ContentMessageListener { onMarkJumpGlobal(key) { return this.markController.jumpGlobal(key); } + + onConsoleFrameMessage(tabId, message) { + let port = this.consolePorts[tabId]; + if (!port) { + return; + } + port.postMessage(message); + } + + onConnected(port) { + if (port.name !== 'vimvixen-console') { + return; + } + + let id = port.sender.tab.id; + this.consolePorts[id] = port; + } } diff --git a/src/console/components/completion.js b/src/console/components/completion.js deleted file mode 100644 index a49a221..0000000 --- a/src/console/components/completion.js +++ /dev/null @@ -1,65 +0,0 @@ -export default class Completion { - constructor(wrapper, store) { - this.wrapper = wrapper; - this.store = store; - this.prevState = {}; - - store.subscribe(() => { - this.update(); - }); - } - - update() { - let state = this.store.getState(); - if (JSON.stringify(this.prevState) === JSON.stringify(state)) { - return; - } - - this.wrapper.innerHTML = ''; - - for (let i = 0; i < state.completions.length; ++i) { - let group = state.completions[i]; - let title = this.createCompletionTitle(group.name); - this.wrapper.append(title); - - for (let j = 0; j < group.items.length; ++j) { - let item = group.items[j]; - let li = this.createCompletionItem(item.icon, item.caption, item.url); - this.wrapper.append(li); - - if (i === state.groupSelection && j === state.itemSelection) { - li.classList.add('vimvixen-completion-selected'); - } - } - } - - this.prevState = state; - } - - createCompletionTitle(text) { - let doc = this.wrapper.ownerDocument; - let li = doc.createElement('li'); - li.className = 'vimvixen-console-completion-title'; - li.textContent = text; - return li; - } - - createCompletionItem(icon, caption, url) { - let doc = this.wrapper.ownerDocument; - - let captionEle = doc.createElement('span'); - captionEle.className = 'vimvixen-console-completion-item-caption'; - captionEle.textContent = caption; - - let urlEle = doc.createElement('span'); - urlEle.className = 'vimvixen-console-completion-item-url'; - urlEle.textContent = url; - - let li = doc.createElement('li'); - li.style.backgroundImage = 'url(' + icon + ')'; - li.className = 'vimvixen-console-completion-item'; - li.append(captionEle); - li.append(urlEle); - return li; - } -} diff --git a/src/console/components/console.js b/src/console/components/console.js deleted file mode 100644 index 4fc8a53..0000000 --- a/src/console/components/console.js +++ /dev/null @@ -1,175 +0,0 @@ -import * as consoleActions from 'console/actions/console'; - -const inputShownMode = (state) => { - return ['command', 'find'].includes(state.mode); -}; - -export default class ConsoleComponent { - constructor(wrapper, store) { - this.wrapper = wrapper; - this.store = store; - this.prevMode = ''; - - let doc = this.wrapper.ownerDocument; - let input = doc.querySelector('#vimvixen-console-command-input'); - - input.addEventListener('blur', this.onBlur.bind(this)); - input.addEventListener('keydown', this.onKeyDown.bind(this)); - input.addEventListener('input', this.onInput.bind(this)); - - store.subscribe(() => { - this.update(); - }); - this.update(); - } - - onBlur() { - let state = this.store.getState(); - if (state.mode === 'command' || state.mode === 'find') { - return this.store.dispatch(consoleActions.hideCommand()); - } - } - - doEnter(e) { - e.stopPropagation(); - e.preventDefault(); - - let state = this.store.getState(); - let value = e.target.value; - if (state.mode === 'command') { - return this.store.dispatch(consoleActions.enterCommand(value)); - } else if (state.mode === 'find') { - return this.store.dispatch(consoleActions.enterFind(value)); - } - } - - selectNext(e) { - this.store.dispatch(consoleActions.completionNext()); - e.stopPropagation(); - e.preventDefault(); - } - - selectPrev(e) { - this.store.dispatch(consoleActions.completionPrev()); - e.stopPropagation(); - e.preventDefault(); - } - - onKeyDown(e) { - if (e.keyCode === KeyboardEvent.DOM_VK_ESCAPE && e.ctrlKey) { - this.store.dispatch(consoleActions.hideCommand()); - } - switch (e.keyCode) { - case KeyboardEvent.DOM_VK_ESCAPE: - return this.store.dispatch(consoleActions.hideCommand()); - case KeyboardEvent.DOM_VK_RETURN: - return this.doEnter(e); - case KeyboardEvent.DOM_VK_TAB: - if (e.shiftKey) { - this.store.dispatch(consoleActions.completionPrev()); - } else { - this.store.dispatch(consoleActions.completionNext()); - } - e.stopPropagation(); - e.preventDefault(); - break; - case KeyboardEvent.DOM_VK_OPEN_BRACKET: - if (e.ctrlKey) { - return this.store.dispatch(consoleActions.hideCommand()); - } - break; - case KeyboardEvent.DOM_VK_M: - if (e.ctrlKey) { - return this.doEnter(e); - } - break; - case KeyboardEvent.DOM_VK_N: - if (e.ctrlKey) { - this.selectNext(e); - } - break; - case KeyboardEvent.DOM_VK_P: - if (e.ctrlKey) { - this.selectPrev(e); - } - break; - } - } - - onInput(e) { - let state = this.store.getState(); - let text = e.target.value; - this.store.dispatch(consoleActions.setConsoleText(text)); - if (state.mode === 'command') { - this.store.dispatch(consoleActions.getCompletions(text)); - } - } - - onInputShown(state) { - let doc = this.wrapper.ownerDocument; - let input = doc.querySelector('#vimvixen-console-command-input'); - - input.focus(); - window.focus(); - - if (state.mode === 'command') { - let text = state.consoleText; - input.value = text; - this.store.dispatch(consoleActions.getCompletions(text)); - } - } - - update() { - let state = this.store.getState(); - - this.updateMessage(state); - this.updateCommand(state); - this.updatePrompt(state); - - if (this.prevMode !== state.mode && inputShownMode(state)) { - this.onInputShown(state); - } - this.prevMode = state.mode; - } - - updateMessage(state) { - let doc = this.wrapper.ownerDocument; - let box = doc.querySelector('.vimvixen-console-message'); - let display = 'none'; - let classList = ['vimvixen-console-message']; - - if (state.mode === 'error' || state.mode === 'info') { - display = 'block'; - classList.push('vimvixen-console-' + state.mode); - } - - box.className = classList.join(' '); - box.style.display = display; - box.textContent = state.messageText; - } - - updateCommand(state) { - let doc = this.wrapper.ownerDocument; - let command = doc.querySelector('#vimvixen-console-command'); - let input = doc.querySelector('#vimvixen-console-command-input'); - - let display = 'none'; - if (inputShownMode(state)) { - display = 'block'; - } - - command.style.display = display; - input.value = state.consoleText; - } - - updatePrompt(state) { - let classList = ['vimvixen-console-command-prompt']; - if (inputShownMode(state)) { - classList.push('prompt-' + state.mode); - } - - let doc = this.wrapper.ownerDocument; - let ele = doc.querySelector('.vimvixen-console-command-prompt'); - ele.className = classList.join(' '); - } -} diff --git a/src/console/components/console.jsx b/src/console/components/console.jsx new file mode 100644 index 0000000..23c93e3 --- /dev/null +++ b/src/console/components/console.jsx @@ -0,0 +1,133 @@ +import './console.scss'; +import { connect } from 'preact-redux'; +import { Component, h } from 'preact'; +import Input from './console/input'; +import Completion from './console/completion'; +import Message from './console/message'; +import * as consoleActions from '../../console/actions/console'; + +class ConsoleComponent extends Component { + onBlur() { + if (this.props.mode === 'command' || this.props.mode === 'find') { + return this.context.store.dispatch(consoleActions.hideCommand()); + } + } + + doEnter(e) { + e.stopPropagation(); + e.preventDefault(); + + let value = e.target.value; + if (this.props.mode === 'command') { + return this.context.store.dispatch(consoleActions.enterCommand(value)); + } else if (this.props.mode === 'find') { + return this.context.store.dispatch(consoleActions.enterFind(value)); + } + } + + selectNext(e) { + this.context.store.dispatch(consoleActions.completionNext()); + e.stopPropagation(); + e.preventDefault(); + } + + selectPrev(e) { + this.context.store.dispatch(consoleActions.completionPrev()); + e.stopPropagation(); + e.preventDefault(); + } + + onKeyDown(e) { + if (e.keyCode === KeyboardEvent.DOM_VK_ESCAPE && e.ctrlKey) { + this.context.store.dispatch(consoleActions.hideCommand()); + } + switch (e.keyCode) { + case KeyboardEvent.DOM_VK_ESCAPE: + return this.context.store.dispatch(consoleActions.hideCommand()); + case KeyboardEvent.DOM_VK_RETURN: + return this.doEnter(e); + case KeyboardEvent.DOM_VK_TAB: + if (e.shiftKey) { + this.context.store.dispatch(consoleActions.completionPrev()); + } else { + this.context.store.dispatch(consoleActions.completionNext()); + } + e.stopPropagation(); + e.preventDefault(); + break; + case KeyboardEvent.DOM_VK_OPEN_BRACKET: + if (e.ctrlKey) { + return this.context.store.dispatch(consoleActions.hideCommand()); + } + break; + case KeyboardEvent.DOM_VK_M: + if (e.ctrlKey) { + return this.doEnter(e); + } + break; + case KeyboardEvent.DOM_VK_N: + if (e.ctrlKey) { + this.selectNext(e); + } + break; + case KeyboardEvent.DOM_VK_P: + if (e.ctrlKey) { + this.selectPrev(e); + } + break; + } + } + + onInput(e) { + let text = e.target.value; + this.context.store.dispatch(consoleActions.setConsoleText(text)); + if (this.props.mode === 'command') { + this.context.store.dispatch(consoleActions.getCompletions(text)); + } + } + + + componentDidUpdate(prevProps) { + if (!this.input) { + return; + } + if (prevProps.mode !== 'command' && this.props.mode === 'command') { + this.context.store.dispatch( + consoleActions.getCompletions(this.props.consoleText)); + this.focus(); + } else if (prevProps.mode !== 'find' && this.props.mode === 'find') { + this.focus(); + } + } + + render() { + switch (this.props.mode) { + case 'command': + case 'find': + return <div className='vimvixen-console-command-wrapper'> + <Completion /> + <Input + ref={(c) => { this.input = c; }} + mode={this.props.mode} + onBlur={this.onBlur.bind(this)} + onKeyDown={this.onKeyDown.bind(this)} + onInput={this.onInput.bind(this)} + value={this.props.consoleText} + /> + </div>; + case 'info': + case 'error': + return <Message mode={ this.props.mode } > + { this.props.messageText } + </Message>; + } + } + + focus() { + window.focus(); + this.input.focus(); + } +} + +const mapStateToProps = state => state; +export default connect(mapStateToProps)(ConsoleComponent); diff --git a/src/console/site.scss b/src/console/components/console.scss index b9b1016..c0b9b12 100644 --- a/src/console/site.scss +++ b/src/console/components/console.scss @@ -89,18 +89,10 @@ body { background-color: white; display: flex; - &-prompt:before { + &-prompt { @include consoole-font; } - &-prompt.prompt-command:before { - content: ':'; - } - - &-prompt.prompt-find:before { - content: '/'; - } - &-input { border: none; flex-grow: 1; diff --git a/src/console/components/console/completion.jsx b/src/console/components/console/completion.jsx new file mode 100644 index 0000000..c60543b --- /dev/null +++ b/src/console/components/console/completion.jsx @@ -0,0 +1,56 @@ +import { Component, h } from 'preact'; +import { connect } from 'preact-redux'; + +const CompletionTitle = (props) => { + return <li className='vimvixen-console-completion-title' >{props.title}</li>; +}; + +const CompletionItem = (props) => { + let className = 'vimvixen-console-completion-item'; + if (props.highlight) { + className += ' vimvixen-completion-selected'; + } + return <li + className={className} + style={{ backgroundImage: 'url(' + props.icon + ')' }} + > + <span + className='vimvixen-console-completion-item-caption' + >{props.caption}</span> + <span + className='vimvixen-console-completion-item-url' + >{props.url}</span> + </li>; +}; + + +class CompletionComponent extends Component { + render() { + let eles = []; + for (let i = 0; i < this.props.completions.length; ++i) { + let group = this.props.completions[i]; + eles.push(<CompletionTitle title={ group.name }/>); + for (let j = 0; j < group.items.length; ++j) { + let item = group.items[j]; + let selected = + i === this.props.groupSelection && + j === this.props.itemSelection; + eles.push(<CompletionItem + icon={item.icon} + caption={item.caption} + url={item.url} + highlight={selected} + / >); + } + } + + return ( + <ul className='vimvixen-console-completion'> + { eles } + </ul> + ); + } +} + +const mapStateToProps = state => state; +export default connect(mapStateToProps)(CompletionComponent); diff --git a/src/console/components/console/input.jsx b/src/console/components/console/input.jsx new file mode 100644 index 0000000..d59e6e7 --- /dev/null +++ b/src/console/components/console/input.jsx @@ -0,0 +1,32 @@ +import { Component, h } from 'preact'; + +export default class InputComponent extends Component { + focus() { + this.input.focus(); + } + + render() { + let prompt = ''; + if (this.props.mode === 'command') { + prompt = ':'; + } else if (this.props.mode === 'find') { + prompt = '/'; + } + + return ( + <div className='vimvixen-console-command'> + <i className='vimvixen-console-command-prompt'> + { prompt } + </i> + <input + className='vimvixen-console-command-input' + ref={(c) => { this.input = c; }} + onBlur={this.props.onBlur} + onKeyDown={this.props.onKeyDown} + onInput={this.props.onInput} + value={this.props.value} + /> + </div> + ); + } +} diff --git a/src/console/components/console/message.jsx b/src/console/components/console/message.jsx new file mode 100644 index 0000000..5b60af4 --- /dev/null +++ b/src/console/components/console/message.jsx @@ -0,0 +1,18 @@ +import { h } from 'preact'; + +export default function Message(props) { + switch (props.mode) { + case 'error': + return ( + <p className='vimvixen-console-message vimvixen-console-error'> + { props.children } + </p> + ); + case 'info': + return ( + <p className='vimvixen-console-message vimvixen-console-info'> + { props.children } + </p> + ); + } +} diff --git a/src/console/index.html b/src/console/index.html index e049b5e..5c1e99c 100644 --- a/src/console/index.html +++ b/src/console/index.html @@ -5,15 +5,5 @@ <title>VimVixen console</title> <script src='console.js'></script> </head> - <body class='vimvixen-console'> - <p class='vimvixen-console-message'></p> - <div id='vimvixen-console-command' class='vimvixen-console-command-wrapper'> - <ul id='vimvixen-console-completion' class='vimvixen-console-completion'></ul> - <div class='vimvixen-console-command'> - <i class='vimvixen-console-command-prompt'></i><input - id='vimvixen-console-command-input' - class='vimvixen-console-command-input'></input> - </div> - </div> - </body> + <body class='vimvixen-console'></body> </html> diff --git a/src/console/index.js b/src/console/index.jsx index 8724a44..dfd323e 100644 --- a/src/console/index.js +++ b/src/console/index.jsx @@ -1,21 +1,25 @@ -import './site.scss'; import messages from 'shared/messages'; -import CompletionComponent from 'console/components/completion'; -import ConsoleComponent from 'console/components/console'; import reducers from 'console/reducers'; import { createStore, applyMiddleware } from 'redux'; import promise from 'redux-promise'; import * as consoleActions from 'console/actions/console'; +import { Provider } from 'preact-redux'; +import Console from './components/console'; + +import { render, h } from 'preact'; + const store = createStore( reducers, applyMiddleware(promise), ); window.addEventListener('load', () => { - let wrapper = document.querySelector('#vimvixen-console-completion'); - new CompletionComponent(wrapper, store); // eslint-disable-line no-new - new ConsoleComponent(document.body, store); // eslint-disable-line no-new + render( + <Provider store={store} > + <Console></Console> + </Provider>, + document.body); }); const onMessage = (message) => { @@ -34,6 +38,5 @@ const onMessage = (message) => { }; browser.runtime.onMessage.addListener(onMessage); -window.addEventListener('message', (event) => { - onMessage(JSON.parse(event.data)); -}, false); +let port = browser.runtime.connect({ name: 'vimvixen-console' }); +port.onMessage.addListener(onMessage); diff --git a/src/content/actions/find.js b/src/content/actions/find.js index b3d7e30..e08d7e5 100644 --- a/src/content/actions/find.js +++ b/src/content/actions/find.js @@ -9,25 +9,6 @@ import messages from 'shared/messages'; import actions from 'content/actions'; import * as consoleFrames from '../console-frames'; -const postPatternNotFound = (pattern) => { - return consoleFrames.postError( - window.document, - 'Pattern not found: ' + pattern); -}; - -const postPatternFound = (pattern) => { - return consoleFrames.postInfo( - window.document, - 'Pattern found: ' + pattern, - ); -}; - -const postNoPrevious = () => { - return consoleFrames.postError( - window.document, - 'No previous search keywords'); -}; - const find = (string, backwards) => { let caseSensitive = false; let wrapScan = true; @@ -60,13 +41,13 @@ const findNext = async(currentKeyword, reset, backwards) => { }); } if (!keyword) { - return postNoPrevious(); + return consoleFrames.postError('No previous search keywords'); } let found = find(keyword, backwards); if (found) { - postPatternFound(keyword); + consoleFrames.postInfo('Pattern found: ' + keyword); } else { - postPatternNotFound(keyword); + consoleFrames.postError('Pattern not found: ' + keyword); } return { diff --git a/src/content/actions/operation.js b/src/content/actions/operation.js index 1aeb8be..b96c6b9 100644 --- a/src/content/actions/operation.js +++ b/src/content/actions/operation.js @@ -85,7 +85,7 @@ const exec = (operation, repeat, settings, addonEnabled) => { break; case operations.URLS_YANK: urls.yank(window); - consoleFrames.postInfo(window.document, 'Current url yanked'); + consoleFrames.postInfo('Current url yanked'); break; case operations.URLS_PASTE: urls.paste( diff --git a/src/content/components/common/mark.js b/src/content/components/common/mark.js index 1ed636b..0f838a9 100644 --- a/src/content/components/common/mark.js +++ b/src/content/components/common/mark.js @@ -33,7 +33,7 @@ export default class MarkComponent { } if (key.ctrlKey || key.metaKey || key.altKey) { - consoleFrames.postError(window.document, 'Unknown mark'); + consoleFrames.postError('Unknown mark'); } else if (globalKey(key.key) && markStage.setMode) { this.doSetGlobal(key); } else if (globalKey(key.key) && markStage.jumpMode) { @@ -55,7 +55,7 @@ export default class MarkComponent { doJump(marks, key, smoothscroll) { if (!marks[key.key]) { - consoleFrames.postError(window.document, 'Mark is not set'); + consoleFrames.postError('Mark is not set'); return; } diff --git a/src/content/console-frames.js b/src/content/console-frames.js index 0c0ec02..401765c 100644 --- a/src/content/console-frames.js +++ b/src/content/console-frames.js @@ -16,22 +16,23 @@ const blur = (doc) => { iframe.blur(); }; -const postMessage = (doc, message) => { - let iframe = doc.getElementById('vimvixen-console-frame'); - iframe.contentWindow.postMessage(JSON.stringify(message), '*'); -}; - -const postError = (doc, message) => { - return postMessage(doc, { - type: messages.CONSOLE_SHOW_ERROR, - text: message, +const postError = (text) => { + browser.runtime.sendMessage({ + type: messages.CONSOLE_FRAME_MESSAGE, + message: { + type: messages.CONSOLE_SHOW_ERROR, + text, + }, }); }; -const postInfo = (doc, message) => { - return postMessage(doc, { - type: messages.CONSOLE_SHOW_INFO, - text: message, +const postInfo = (text) => { + browser.runtime.sendMessage({ + type: messages.CONSOLE_FRAME_MESSAGE, + message: { + type: messages.CONSOLE_SHOW_INFO, + text, + }, }); }; diff --git a/src/shared/messages.js b/src/shared/messages.js index dad2b7a..e86600e 100644 --- a/src/shared/messages.js +++ b/src/shared/messages.js @@ -63,6 +63,9 @@ export default { SETTINGS_CHANGED: 'settings.changed', SETTINGS_QUERY: 'settings.query', + WINDOW_TOP_MESSAGE: 'window.top.message', + CONSOLE_FRAME_MESSAGE: 'console.frame.message', + onWebMessage, onBackgroundMessage, onMessage, |