From e1c70769ea235a78da463ab21de40582381bec78 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Mon, 6 Nov 2017 22:03:53 +0900 Subject: add find action and reducer --- src/content/actions/find.js | 36 ++++++++++++++++++++++++++++++++++++ src/content/actions/index.js | 5 +++++ src/content/reducers/find.js | 25 +++++++++++++++++++++++++ src/content/reducers/index.js | 3 +++ 4 files changed, 69 insertions(+) create mode 100644 src/content/actions/find.js create mode 100644 src/content/reducers/find.js (limited to 'src/content') diff --git a/src/content/actions/find.js b/src/content/actions/find.js new file mode 100644 index 0000000..90c9de9 --- /dev/null +++ b/src/content/actions/find.js @@ -0,0 +1,36 @@ +// +// window.find(aString, aCaseSensitive, aBackwards, aWrapAround, +// aWholeWord, aSearchInFrames, aShowDialog); +// +// NOTE: window.find is not standard API +// https://developer.mozilla.org/en-US/docs/Web/API/Window/find + +import actions from 'content/actions'; + +const show = () => { + return { type: actions.FIND_SHOW }; +}; + +const hide = () => { + return { type: actions.FIND_HIDE }; +}; + +const next = (keyword) => { + // TODO Error on no matched + window.find(keyword, false, false, true, false, true, false); + return { + type: actions.FIND_SET_KEYWORD, + keyword, + }; +}; + +const prev = (keyword) => { + // TODO Error on no matched + window.find(keyword, false, true, true, false, true, false); + return { + type: actions.FIND_SET_KEYWORD, + keyword, + }; +}; + +export { show, hide, next, prev }; diff --git a/src/content/actions/index.js b/src/content/actions/index.js index 8cc2303..a727e13 100644 --- a/src/content/actions/index.js +++ b/src/content/actions/index.js @@ -21,4 +21,9 @@ export default { FOLLOW_CONTROLLER_DISABLE: 'follow.controller.disable', FOLLOW_CONTROLLER_KEY_PRESS: 'follow.controller.key.press', FOLLOW_CONTROLLER_BACKSPACE: 'follow.controller.backspace', + + // Find + FIND_SHOW: 'find.show', + FIND_HIDE: 'find.hide', + FIND_SET_KEYWORD: 'find.set.keyword', }; diff --git a/src/content/reducers/find.js b/src/content/reducers/find.js new file mode 100644 index 0000000..6042f50 --- /dev/null +++ b/src/content/reducers/find.js @@ -0,0 +1,25 @@ +import actions from 'content/actions'; + +const defaultState = { + enabled: false, + keyword: '', +}; + +export default function reducer(state = defaultState, action = {}) { + switch (action.type) { + case actions.FIND_SHOW: + return Object.assign({}, state, { + enabled: true, + }); + case actions.FIND_HIDE: + return Object.assign({}, state, { + enabled: false, + }); + case actions.FIND_SET_KEYWORD: + return Object.assign({}, state, { + keyword: action.keyword, + }); + default: + return state; + } +} diff --git a/src/content/reducers/index.js b/src/content/reducers/index.js index 17c0429..2487d85 100644 --- a/src/content/reducers/index.js +++ b/src/content/reducers/index.js @@ -1,4 +1,5 @@ import addonReducer from './addon'; +import findReducer from './find'; import settingReducer from './setting'; import inputReducer from './input'; import followControllerReducer from './follow-controller'; @@ -6,6 +7,7 @@ import followControllerReducer from './follow-controller'; // Make setting reducer instead of re-use const defaultState = { addon: addonReducer(undefined, {}), + find: findReducer(undefined, {}), setting: settingReducer(undefined, {}), input: inputReducer(undefined, {}), followController: followControllerReducer(undefined, {}), @@ -14,6 +16,7 @@ const defaultState = { export default function reducer(state = defaultState, action = {}) { return Object.assign({}, state, { addon: addonReducer(state.addon, action), + find: findReducer(state.find, action), setting: settingReducer(state.setting, action), input: inputReducer(state.input, action), followController: followControllerReducer(state.followController, action), -- cgit v1.2.3 From e021504356016dc4cdb89356cae542c31486fe6a Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Thu, 9 Nov 2017 21:05:02 +0900 Subject: first find implementation --- src/background/components/background.js | 6 +---- src/console/components/console.js | 40 +++++++++++++++++++++-------- src/console/index.js | 2 -- src/content/actions/find.js | 6 ++--- src/content/actions/operation.js | 9 +++++++ src/content/components/top-content/find.js | 23 +++++++++++++++++ src/content/components/top-content/index.js | 4 ++- src/shared/messages.js | 9 ++++--- 8 files changed, 74 insertions(+), 25 deletions(-) create mode 100644 src/content/components/top-content/find.js (limited to 'src/content') diff --git a/src/background/components/background.js b/src/background/components/background.js index a5f4f5f..2d94310 100644 --- a/src/background/components/background.js +++ b/src/background/components/background.js @@ -34,11 +34,7 @@ export default class BackgroundComponent { } return this.store.dispatch( tabActions.openToTab(message.url, sender.tab), sender); - case messages.CONSOLE_BLURRED: - return browser.tabs.sendMessage(sender.tab.id, { - type: messages.CONSOLE_HIDE_COMMAND, - }); - case messages.CONSOLE_ENTERED: + case messages.CONSOLE_ENTER_COMMAND: return commands.exec(message.text, settings.value).catch((e) => { return browser.tabs.sendMessage(sender.tab.id, { type: messages.CONSOLE_SHOW_ERROR, diff --git a/src/console/components/console.js b/src/console/components/console.js index 605feb2..e83a1c9 100644 --- a/src/console/components/console.js +++ b/src/console/components/console.js @@ -25,23 +25,18 @@ export default class ConsoleComponent { } onBlur() { - return browser.runtime.sendMessage({ - type: messages.CONSOLE_BLURRED, - }); + let state = this.store.getState(); + if (state.mode === 'command') { + this.hideCommand(); + } } onKeyDown(e) { - let doc = this.wrapper.ownerDocument; - let input = doc.querySelector('#vimvixen-console-command-input'); - switch (e.keyCode) { case KeyboardEvent.DOM_VK_ESCAPE: - return input.blur(); + return this.hideCommand(); case KeyboardEvent.DOM_VK_RETURN: - return browser.runtime.sendMessage({ - type: messages.CONSOLE_ENTERED, - text: e.target.value - }).then(this.onBlur); + return this.onEntered(e.target.value); case KeyboardEvent.DOM_VK_TAB: if (e.shiftKey) { this.store.dispatch(consoleActions.completionPrev()); @@ -54,6 +49,22 @@ export default class ConsoleComponent { } } + onEntered(value) { + let state = this.store.getState(); + if (state.mode === 'command') { + browser.runtime.sendMessage({ + type: messages.CONSOLE_ENTER_COMMAND, + text: value, + }).then(this.hideCommand); + } else if (state.mode === 'find') { + this.hideCommand(); + window.top.postMessage(JSON.stringify({ + type: messages.CONSOLE_ENTER_FIND, + text: value, + }), '*'); + } + } + onInput(e) { this.store.dispatch(consoleActions.setConsoleText(e.target.value)); @@ -78,6 +89,13 @@ export default class ConsoleComponent { } } + hideCommand() { + this.store.dispatch(consoleActions.hideCommand()); + window.top.postMessage(JSON.stringify({ + type: messages.CONSOLE_UNFOCUS, + }), '*'); + } + update() { let state = this.store.getState(); diff --git a/src/console/index.js b/src/console/index.js index f886520..86edd9a 100644 --- a/src/console/index.js +++ b/src/console/index.js @@ -24,8 +24,6 @@ const onMessage = (message) => { return store.dispatch(consoleActions.showError(message.text)); case messages.CONSOLE_SHOW_INFO: return store.dispatch(consoleActions.showInfo(message.text)); - case messages.CONSOLE_HIDE_COMMAND: - return store.dispatch(consoleActions.hideCommand()); } }; diff --git a/src/content/actions/find.js b/src/content/actions/find.js index 90c9de9..2d301fb 100644 --- a/src/content/actions/find.js +++ b/src/content/actions/find.js @@ -1,6 +1,6 @@ // // window.find(aString, aCaseSensitive, aBackwards, aWrapAround, -// aWholeWord, aSearchInFrames, aShowDialog); +// aWholeWord, aSearchInFrames); // // NOTE: window.find is not standard API // https://developer.mozilla.org/en-US/docs/Web/API/Window/find @@ -17,7 +17,7 @@ const hide = () => { const next = (keyword) => { // TODO Error on no matched - window.find(keyword, false, false, true, false, true, false); + window.find(keyword, false, false, true, false, true); return { type: actions.FIND_SET_KEYWORD, keyword, @@ -26,7 +26,7 @@ const next = (keyword) => { const prev = (keyword) => { // TODO Error on no matched - window.find(keyword, false, true, true, false, true, false); + window.find(keyword, false, true, true, false, true); return { type: actions.FIND_SET_KEYWORD, keyword, diff --git a/src/content/actions/operation.js b/src/content/actions/operation.js index 897f361..767f14b 100644 --- a/src/content/actions/operation.js +++ b/src/content/actions/operation.js @@ -6,6 +6,7 @@ import * as urls from 'content/urls'; import * as consoleFrames from 'content/console-frames'; import * as addonActions from './addon'; +// eslint-disable-next-line complexity const exec = (operation) => { switch (operation.type) { case operations.ADDON_ENABLE: @@ -14,6 +15,14 @@ const exec = (operation) => { return addonActions.disable(); case operations.ADDON_TOGGLE_ENABLED: return addonActions.toggleEnabled(); + case operations.FIND_NEXT: + return window.top.postMessage(JSON.stringify({ + type: messages.FIND_NEXT, + }), '*'); + case operations.FIND_PREV: + return window.top.postMessage(JSON.stringify({ + type: messages.FIND_PREV, + }), '*'); case operations.SCROLL_VERTICALLY: return scrolls.scrollVertically(window, operation.count); case operations.SCROLL_HORIZONALLY: diff --git a/src/content/components/top-content/find.js b/src/content/components/top-content/find.js new file mode 100644 index 0000000..6696f00 --- /dev/null +++ b/src/content/components/top-content/find.js @@ -0,0 +1,23 @@ +import * as findActions from 'content/actions/find'; +import messages from 'shared/messages'; + +export default class FindComponent { + constructor(win, store) { + this.win = win; + this.store = store; + + messages.onMessage(this.onMessage.bind(this)); + } + + onMessage(message) { + let state = this.store.getState().find; + switch (message.type) { + case messages.CONSOLE_ENTER_FIND: + return this.store.dispatch(findActions.next(message.text)); + case messages.FIND_NEXT: + return this.store.dispatch(findActions.next(state.keyword)); + case messages.FIND_PREV: + return this.store.dispatch(findActions.prev(state.keyword)); + } + } +} diff --git a/src/content/components/top-content/index.js b/src/content/components/top-content/index.js index 5124f83..cf21ec4 100644 --- a/src/content/components/top-content/index.js +++ b/src/content/components/top-content/index.js @@ -1,5 +1,6 @@ import CommonComponent from '../common'; import FollowController from './follow-controller'; +import FindComponent from './find'; import * as consoleFrames from '../../console-frames'; import * as addonActions from '../../actions/addon'; import messages from 'shared/messages'; @@ -14,6 +15,7 @@ export default class TopContent { new CommonComponent(win, store); // eslint-disable-line no-new new FollowController(win, store); // eslint-disable-line no-new + new FindComponent(win, store); // eslint-disable-line no-new // TODO make component consoleFrames.initialize(this.win.document); @@ -47,7 +49,7 @@ export default class TopContent { onMessage(message) { switch (message.type) { - case messages.CONSOLE_HIDE_COMMAND: + case messages.CONSOLE_UNFOCUS: this.win.focus(); consoleFrames.blur(window.document); return Promise.resolve(); diff --git a/src/shared/messages.js b/src/shared/messages.js index f859e93..de00a3f 100644 --- a/src/shared/messages.js +++ b/src/shared/messages.js @@ -24,13 +24,13 @@ const onMessage = (listener) => { export default { BACKGROUND_OPERATION: 'background.operation', - CONSOLE_BLURRED: 'console.blured', - CONSOLE_ENTERED: 'console.entered', + CONSOLE_UNFOCUS: 'console.unfocus', + CONSOLE_ENTER_COMMAND: 'console.enter.command', + CONSOLE_ENTER_FIND: 'console.enter.find', CONSOLE_QUERY_COMPLETIONS: 'console.query.completions', CONSOLE_SHOW_COMMAND: 'console.show.command', CONSOLE_SHOW_ERROR: 'console.show.error', CONSOLE_SHOW_INFO: 'console.show.info', - CONSOLE_HIDE_COMMAND: 'console.hide.command', CONSOLE_SHOW_FIND: 'console.show.find', FOLLOW_START: 'follow.start', @@ -42,6 +42,9 @@ export default { FOLLOW_ACTIVATE: 'follow.activate', FOLLOW_KEY_PRESS: 'follow.key.press', + FIND_NEXT: 'find.next', + FIND_PREV: 'find.prev', + OPEN_URL: 'open.url', SETTINGS_RELOAD: 'settings.reload', -- cgit v1.2.3 From 12db4943ee54e1b0b48c806cde589254cb6fef51 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sat, 11 Nov 2017 15:53:46 +0900 Subject: show error on find and wrap search --- src/content/actions/find.js | 47 +++++++++++++++++++++++------- src/content/components/top-content/find.js | 39 ++++++++++++++++++++++--- src/content/console-frames.js | 10 ++++++- src/content/reducers/find.js | 2 ++ 4 files changed, 83 insertions(+), 15 deletions(-) (limited to 'src/content') diff --git a/src/content/actions/find.js b/src/content/actions/find.js index 2d301fb..ac1b842 100644 --- a/src/content/actions/find.js +++ b/src/content/actions/find.js @@ -6,6 +6,13 @@ // https://developer.mozilla.org/en-US/docs/Web/API/Window/find import actions from 'content/actions'; +import * as consoleFrames from '../console-frames'; + +const postPatternNotFound = (pattern) => { + return consoleFrames.postError( + window.document, + 'Pattern not found: ' + pattern); +}; const show = () => { return { type: actions.FIND_SHOW }; @@ -15,22 +22,42 @@ const hide = () => { return { type: actions.FIND_HIDE }; }; -const next = (keyword) => { - // TODO Error on no matched - window.find(keyword, false, false, true, false, true); - return { - type: actions.FIND_SET_KEYWORD, - keyword, - }; +const find = (string, backwards) => { + let caseSensitive = false; + let wrapScan = true; + + + // NOTE: aWholeWord dows not implemented, and aSearchInFrames does not work + // because of same origin policy + return window.find(string, caseSensitive, backwards, wrapScan); }; -const prev = (keyword) => { - // TODO Error on no matched - window.find(keyword, false, true, true, false, true); +const findNext = (keyword, reset, backwards) => { + if (reset) { + window.getSelection().removeAllRanges(); + } + + let found = find(keyword, backwards); + if (!found) { + window.getSelection().removeAllRanges(); + found = find(keyword, backwards); + } + if (!found) { + postPatternNotFound(keyword); + } return { type: actions.FIND_SET_KEYWORD, keyword, + found, }; }; +const next = (keyword, reset) => { + return findNext(keyword, reset, false); +}; + +const prev = (keyword, reset) => { + return findNext(keyword, reset, true); +}; + export { show, hide, next, prev }; diff --git a/src/content/components/top-content/find.js b/src/content/components/top-content/find.js index 6696f00..bccf040 100644 --- a/src/content/components/top-content/find.js +++ b/src/content/components/top-content/find.js @@ -1,5 +1,6 @@ import * as findActions from 'content/actions/find'; import messages from 'shared/messages'; +import * as consoleFrames from '../../console-frames'; export default class FindComponent { constructor(win, store) { @@ -10,14 +11,44 @@ export default class FindComponent { } onMessage(message) { - let state = this.store.getState().find; switch (message.type) { case messages.CONSOLE_ENTER_FIND: - return this.store.dispatch(findActions.next(message.text)); + return this.start(message.text); case messages.FIND_NEXT: - return this.store.dispatch(findActions.next(state.keyword)); + return this.next(); case messages.FIND_PREV: - return this.store.dispatch(findActions.prev(state.keyword)); + return this.prev(); + } + } + + start(text) { + let state = this.store.getState().find; + + if (text.length === 0) { + return this.store.dispatch(findActions.next(state.keyword, true)); + } + return this.store.dispatch(findActions.next(text, true)); + } + + next() { + let state = this.store.getState().find; + + if (!state.found) { + return consoleFrames.postError( + window.document, + 'Pattern not found: ' + state.keyword); + } + return this.store.dispatch(findActions.next(state.keyword, false)); + } + + prev() { + let state = this.store.getState().find; + + if (!state.found) { + return consoleFrames.postError( + window.document, + 'Pattern not found: ' + state.keyword); } + return this.store.dispatch(findActions.prev(state.keyword, false)); } } diff --git a/src/content/console-frames.js b/src/content/console-frames.js index 35b975f..515ae09 100644 --- a/src/content/console-frames.js +++ b/src/content/console-frames.js @@ -1,4 +1,5 @@ import './console-frame.scss'; +import messages from 'shared/messages'; const initialize = (doc) => { let iframe = doc.createElement('iframe'); @@ -20,4 +21,11 @@ const postMessage = (doc, message) => { iframe.contentWindow.postMessage(JSON.stringify(message), '*'); }; -export { initialize, blur, postMessage }; +const postError = (doc, message) => { + return postMessage(doc, { + type: messages.CONSOLE_SHOW_ERROR, + text: message, + }); +}; + +export { initialize, blur, postMessage, postError }; diff --git a/src/content/reducers/find.js b/src/content/reducers/find.js index 6042f50..f0bfbeb 100644 --- a/src/content/reducers/find.js +++ b/src/content/reducers/find.js @@ -3,6 +3,7 @@ import actions from 'content/actions'; const defaultState = { enabled: false, keyword: '', + found: false, }; export default function reducer(state = defaultState, action = {}) { @@ -18,6 +19,7 @@ export default function reducer(state = defaultState, action = {}) { case actions.FIND_SET_KEYWORD: return Object.assign({}, state, { keyword: action.keyword, + found: action.found, }); default: return state; -- cgit v1.2.3 From fe8a9283172e43e29480c4293c34565859c04c32 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sat, 11 Nov 2017 18:45:10 +0900 Subject: remove unused actions and fix test --- src/content/actions/find.js | 10 +--------- src/content/actions/index.js | 2 -- src/content/reducers/find.js | 9 --------- test/content/actions/find.test.js | 19 ------------------- test/content/reducers/find.test.js | 33 ++++++++++----------------------- 5 files changed, 11 insertions(+), 62 deletions(-) delete mode 100644 test/content/actions/find.test.js (limited to 'src/content') diff --git a/src/content/actions/find.js b/src/content/actions/find.js index ac1b842..80d6210 100644 --- a/src/content/actions/find.js +++ b/src/content/actions/find.js @@ -14,14 +14,6 @@ const postPatternNotFound = (pattern) => { 'Pattern not found: ' + pattern); }; -const show = () => { - return { type: actions.FIND_SHOW }; -}; - -const hide = () => { - return { type: actions.FIND_HIDE }; -}; - const find = (string, backwards) => { let caseSensitive = false; let wrapScan = true; @@ -60,4 +52,4 @@ const prev = (keyword, reset) => { return findNext(keyword, reset, true); }; -export { show, hide, next, prev }; +export { next, prev }; diff --git a/src/content/actions/index.js b/src/content/actions/index.js index a727e13..7e32e12 100644 --- a/src/content/actions/index.js +++ b/src/content/actions/index.js @@ -23,7 +23,5 @@ export default { FOLLOW_CONTROLLER_BACKSPACE: 'follow.controller.backspace', // Find - FIND_SHOW: 'find.show', - FIND_HIDE: 'find.hide', FIND_SET_KEYWORD: 'find.set.keyword', }; diff --git a/src/content/reducers/find.js b/src/content/reducers/find.js index f0bfbeb..eb43c37 100644 --- a/src/content/reducers/find.js +++ b/src/content/reducers/find.js @@ -1,21 +1,12 @@ import actions from 'content/actions'; const defaultState = { - enabled: false, keyword: '', found: false, }; export default function reducer(state = defaultState, action = {}) { switch (action.type) { - case actions.FIND_SHOW: - return Object.assign({}, state, { - enabled: true, - }); - case actions.FIND_HIDE: - return Object.assign({}, state, { - enabled: false, - }); case actions.FIND_SET_KEYWORD: return Object.assign({}, state, { keyword: action.keyword, diff --git a/test/content/actions/find.test.js b/test/content/actions/find.test.js deleted file mode 100644 index 676e105..0000000 --- a/test/content/actions/find.test.js +++ /dev/null @@ -1,19 +0,0 @@ -import { expect } from "chai"; -import actions from 'content/actions'; -import * as findActions from 'content/actions/find'; - -describe("find actions", () => { - describe("show", () => { - it('create FIND_SHOW action', () => { - let action = findActions.show(); - expect(action.type).to.equal(actions.FIND_SHOW); - }); - }); - - describe("hide", () => { - it('create FIND_HIDE action', () => { - let action = findActions.hide(); - expect(action.type).to.equal(actions.FIND_HIDE); - }); - }); -}); diff --git a/test/content/reducers/find.test.js b/test/content/reducers/find.test.js index 3aacbd9..93625da 100644 --- a/test/content/reducers/find.test.js +++ b/test/content/reducers/find.test.js @@ -5,32 +5,19 @@ import findReducer from 'content/reducers/find'; describe("find reducer", () => { it('return the initial state', () => { let state = findReducer(undefined, {}); - expect(state).to.have.property('enabled', false); expect(state).to.have.property('keyword', ''); - }); - - it('return next state for FIND_SHOW', () => { - let action = { type: actions.FIND_SHOW }; - let prev = { enabled: false }; - let state = findReducer(prev, action); - - expect(state.enabled).is.equal(true); - }); - - it('return next state for FIND_HIDE', () => { - let action = { type: actions.FIND_HIDE }; - let prev = { enabled: true }; - let state = findReducer(prev, action); - - expect(state.enabled).is.equal(false); + expect(state).to.have.property('found', false); }); it('return next state for FIND_SET_KEYWORD', () => { - let action = { type: actions.FIND_SET_KEYWORD, keyword: 'my-search' }; - let state = { enabled: true, keyword: '' }; - - state = findReducer(state, action); - - expect(state.keyword).is.equal('my-search'); + let action = { + type: actions.FIND_SET_KEYWORD, + keyword: 'xyz', + found: true, + }; + let state = findReducer({}, action); + + expect(state.keyword).is.equal('xyz'); + expect(state.found).to.be.true; }); }); -- cgit v1.2.3