From c60d0e7392fc708e961614d6b756a045de74f458 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Tue, 30 Apr 2019 14:00:07 +0900 Subject: Rename .js/.jsx to .ts/.tsx --- src/content/actions/addon.js | 19 -- src/content/actions/addon.ts | 19 ++ src/content/actions/find.js | 68 ------- src/content/actions/find.ts | 68 +++++++ src/content/actions/follow-controller.js | 30 ---- src/content/actions/follow-controller.ts | 30 ++++ src/content/actions/index.js | 31 ---- src/content/actions/index.ts | 31 ++++ src/content/actions/input.js | 16 -- src/content/actions/input.ts | 16 ++ src/content/actions/mark.js | 46 ----- src/content/actions/mark.ts | 46 +++++ src/content/actions/operation.js | 104 ----------- src/content/actions/operation.ts | 104 +++++++++++ src/content/actions/setting.js | 37 ---- src/content/actions/setting.ts | 37 ++++ src/content/components/common/follow.js | 200 --------------------- src/content/components/common/follow.ts | 200 +++++++++++++++++++++ src/content/components/common/hint.js | 49 ----- src/content/components/common/hint.ts | 49 +++++ src/content/components/common/index.js | 55 ------ src/content/components/common/index.ts | 55 ++++++ src/content/components/common/input.js | 75 -------- src/content/components/common/input.ts | 75 ++++++++ src/content/components/common/keymapper.js | 58 ------ src/content/components/common/keymapper.ts | 58 ++++++ src/content/components/common/mark.js | 74 -------- src/content/components/common/mark.ts | 74 ++++++++ src/content/components/frame-content.js | 3 - src/content/components/frame-content.ts | 3 + src/content/components/top-content/find.js | 41 ----- src/content/components/top-content/find.ts | 41 +++++ .../components/top-content/follow-controller.js | 147 --------------- .../components/top-content/follow-controller.ts | 147 +++++++++++++++ src/content/components/top-content/index.js | 41 ----- src/content/components/top-content/index.ts | 41 +++++ src/content/console-frames.js | 38 ---- src/content/console-frames.ts | 38 ++++ src/content/focuses.js | 13 -- src/content/focuses.ts | 13 ++ src/content/hint-key-producer.js | 33 ---- src/content/hint-key-producer.ts | 33 ++++ src/content/index.js | 21 --- src/content/index.ts | 21 +++ src/content/navigates.js | 78 -------- src/content/navigates.ts | 78 ++++++++ src/content/reducers/addon.js | 15 -- src/content/reducers/addon.ts | 15 ++ src/content/reducers/find.js | 17 -- src/content/reducers/find.ts | 17 ++ src/content/reducers/follow-controller.js | 30 ---- src/content/reducers/follow-controller.ts | 30 ++++ src/content/reducers/index.js | 11 -- src/content/reducers/index.ts | 11 ++ src/content/reducers/input.js | 18 -- src/content/reducers/input.ts | 18 ++ src/content/reducers/mark.js | 25 --- src/content/reducers/mark.ts | 25 +++ src/content/reducers/setting.js | 16 -- src/content/reducers/setting.ts | 16 ++ src/content/scrolls.js | 174 ------------------ src/content/scrolls.ts | 174 ++++++++++++++++++ src/content/site-style.js | 26 --- src/content/site-style.ts | 26 +++ src/content/urls.js | 40 ----- src/content/urls.ts | 40 +++++ 66 files changed, 1649 insertions(+), 1649 deletions(-) delete mode 100644 src/content/actions/addon.js create mode 100644 src/content/actions/addon.ts delete mode 100644 src/content/actions/find.js create mode 100644 src/content/actions/find.ts delete mode 100644 src/content/actions/follow-controller.js create mode 100644 src/content/actions/follow-controller.ts delete mode 100644 src/content/actions/index.js create mode 100644 src/content/actions/index.ts delete mode 100644 src/content/actions/input.js create mode 100644 src/content/actions/input.ts delete mode 100644 src/content/actions/mark.js create mode 100644 src/content/actions/mark.ts delete mode 100644 src/content/actions/operation.js create mode 100644 src/content/actions/operation.ts delete mode 100644 src/content/actions/setting.js create mode 100644 src/content/actions/setting.ts delete mode 100644 src/content/components/common/follow.js create mode 100644 src/content/components/common/follow.ts delete mode 100644 src/content/components/common/hint.js create mode 100644 src/content/components/common/hint.ts delete mode 100644 src/content/components/common/index.js create mode 100644 src/content/components/common/index.ts delete mode 100644 src/content/components/common/input.js create mode 100644 src/content/components/common/input.ts delete mode 100644 src/content/components/common/keymapper.js create mode 100644 src/content/components/common/keymapper.ts delete mode 100644 src/content/components/common/mark.js create mode 100644 src/content/components/common/mark.ts delete mode 100644 src/content/components/frame-content.js create mode 100644 src/content/components/frame-content.ts delete mode 100644 src/content/components/top-content/find.js create mode 100644 src/content/components/top-content/find.ts delete mode 100644 src/content/components/top-content/follow-controller.js create mode 100644 src/content/components/top-content/follow-controller.ts delete mode 100644 src/content/components/top-content/index.js create mode 100644 src/content/components/top-content/index.ts delete mode 100644 src/content/console-frames.js create mode 100644 src/content/console-frames.ts delete mode 100644 src/content/focuses.js create mode 100644 src/content/focuses.ts delete mode 100644 src/content/hint-key-producer.js create mode 100644 src/content/hint-key-producer.ts delete mode 100644 src/content/index.js create mode 100644 src/content/index.ts delete mode 100644 src/content/navigates.js create mode 100644 src/content/navigates.ts delete mode 100644 src/content/reducers/addon.js create mode 100644 src/content/reducers/addon.ts delete mode 100644 src/content/reducers/find.js create mode 100644 src/content/reducers/find.ts delete mode 100644 src/content/reducers/follow-controller.js create mode 100644 src/content/reducers/follow-controller.ts delete mode 100644 src/content/reducers/index.js create mode 100644 src/content/reducers/index.ts delete mode 100644 src/content/reducers/input.js create mode 100644 src/content/reducers/input.ts delete mode 100644 src/content/reducers/mark.js create mode 100644 src/content/reducers/mark.ts delete mode 100644 src/content/reducers/setting.js create mode 100644 src/content/reducers/setting.ts delete mode 100644 src/content/scrolls.js create mode 100644 src/content/scrolls.ts delete mode 100644 src/content/site-style.js create mode 100644 src/content/site-style.ts delete mode 100644 src/content/urls.js create mode 100644 src/content/urls.ts (limited to 'src/content') diff --git a/src/content/actions/addon.js b/src/content/actions/addon.js deleted file mode 100644 index b30cf16..0000000 --- a/src/content/actions/addon.js +++ /dev/null @@ -1,19 +0,0 @@ -import messages from 'shared/messages'; -import actions from 'content/actions'; - -const enable = () => setEnabled(true); - -const disable = () => setEnabled(false); - -const setEnabled = async(enabled) => { - await browser.runtime.sendMessage({ - type: messages.ADDON_ENABLED_RESPONSE, - enabled, - }); - return { - type: actions.ADDON_SET_ENABLED, - enabled, - }; -}; - -export { enable, disable, setEnabled }; diff --git a/src/content/actions/addon.ts b/src/content/actions/addon.ts new file mode 100644 index 0000000..b30cf16 --- /dev/null +++ b/src/content/actions/addon.ts @@ -0,0 +1,19 @@ +import messages from 'shared/messages'; +import actions from 'content/actions'; + +const enable = () => setEnabled(true); + +const disable = () => setEnabled(false); + +const setEnabled = async(enabled) => { + await browser.runtime.sendMessage({ + type: messages.ADDON_ENABLED_RESPONSE, + enabled, + }); + return { + type: actions.ADDON_SET_ENABLED, + enabled, + }; +}; + +export { enable, disable, setEnabled }; diff --git a/src/content/actions/find.js b/src/content/actions/find.js deleted file mode 100644 index e08d7e5..0000000 --- a/src/content/actions/find.js +++ /dev/null @@ -1,68 +0,0 @@ -// -// window.find(aString, aCaseSensitive, aBackwards, aWrapAround, -// aWholeWord, aSearchInFrames); -// -// NOTE: window.find is not standard API -// https://developer.mozilla.org/en-US/docs/Web/API/Window/find - -import messages from 'shared/messages'; -import actions from 'content/actions'; -import * as consoleFrames from '../console-frames'; - -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 - let found = window.find(string, caseSensitive, backwards, wrapScan); - if (found) { - return found; - } - window.getSelection().removeAllRanges(); - return window.find(string, caseSensitive, backwards, wrapScan); -}; - -const findNext = async(currentKeyword, reset, backwards) => { - if (reset) { - window.getSelection().removeAllRanges(); - } - - let keyword = currentKeyword; - if (currentKeyword) { - browser.runtime.sendMessage({ - type: messages.FIND_SET_KEYWORD, - keyword: currentKeyword, - }); - } else { - keyword = await browser.runtime.sendMessage({ - type: messages.FIND_GET_KEYWORD, - }); - } - if (!keyword) { - return consoleFrames.postError('No previous search keywords'); - } - let found = find(keyword, backwards); - if (found) { - consoleFrames.postInfo('Pattern found: ' + keyword); - } else { - consoleFrames.postError('Pattern not found: ' + keyword); - } - - return { - type: actions.FIND_SET_KEYWORD, - keyword, - found, - }; -}; - -const next = (currentKeyword, reset) => { - return findNext(currentKeyword, reset, false); -}; - -const prev = (currentKeyword, reset) => { - return findNext(currentKeyword, reset, true); -}; - -export { next, prev }; diff --git a/src/content/actions/find.ts b/src/content/actions/find.ts new file mode 100644 index 0000000..e08d7e5 --- /dev/null +++ b/src/content/actions/find.ts @@ -0,0 +1,68 @@ +// +// window.find(aString, aCaseSensitive, aBackwards, aWrapAround, +// aWholeWord, aSearchInFrames); +// +// NOTE: window.find is not standard API +// https://developer.mozilla.org/en-US/docs/Web/API/Window/find + +import messages from 'shared/messages'; +import actions from 'content/actions'; +import * as consoleFrames from '../console-frames'; + +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 + let found = window.find(string, caseSensitive, backwards, wrapScan); + if (found) { + return found; + } + window.getSelection().removeAllRanges(); + return window.find(string, caseSensitive, backwards, wrapScan); +}; + +const findNext = async(currentKeyword, reset, backwards) => { + if (reset) { + window.getSelection().removeAllRanges(); + } + + let keyword = currentKeyword; + if (currentKeyword) { + browser.runtime.sendMessage({ + type: messages.FIND_SET_KEYWORD, + keyword: currentKeyword, + }); + } else { + keyword = await browser.runtime.sendMessage({ + type: messages.FIND_GET_KEYWORD, + }); + } + if (!keyword) { + return consoleFrames.postError('No previous search keywords'); + } + let found = find(keyword, backwards); + if (found) { + consoleFrames.postInfo('Pattern found: ' + keyword); + } else { + consoleFrames.postError('Pattern not found: ' + keyword); + } + + return { + type: actions.FIND_SET_KEYWORD, + keyword, + found, + }; +}; + +const next = (currentKeyword, reset) => { + return findNext(currentKeyword, reset, false); +}; + +const prev = (currentKeyword, reset) => { + return findNext(currentKeyword, reset, true); +}; + +export { next, prev }; diff --git a/src/content/actions/follow-controller.js b/src/content/actions/follow-controller.js deleted file mode 100644 index 006b248..0000000 --- a/src/content/actions/follow-controller.js +++ /dev/null @@ -1,30 +0,0 @@ -import actions from 'content/actions'; - -const enable = (newTab, background) => { - return { - type: actions.FOLLOW_CONTROLLER_ENABLE, - newTab, - background, - }; -}; - -const disable = () => { - return { - type: actions.FOLLOW_CONTROLLER_DISABLE, - }; -}; - -const keyPress = (key) => { - return { - type: actions.FOLLOW_CONTROLLER_KEY_PRESS, - key: key - }; -}; - -const backspace = () => { - return { - type: actions.FOLLOW_CONTROLLER_BACKSPACE, - }; -}; - -export { enable, disable, keyPress, backspace }; diff --git a/src/content/actions/follow-controller.ts b/src/content/actions/follow-controller.ts new file mode 100644 index 0000000..006b248 --- /dev/null +++ b/src/content/actions/follow-controller.ts @@ -0,0 +1,30 @@ +import actions from 'content/actions'; + +const enable = (newTab, background) => { + return { + type: actions.FOLLOW_CONTROLLER_ENABLE, + newTab, + background, + }; +}; + +const disable = () => { + return { + type: actions.FOLLOW_CONTROLLER_DISABLE, + }; +}; + +const keyPress = (key) => { + return { + type: actions.FOLLOW_CONTROLLER_KEY_PRESS, + key: key + }; +}; + +const backspace = () => { + return { + type: actions.FOLLOW_CONTROLLER_BACKSPACE, + }; +}; + +export { enable, disable, keyPress, backspace }; diff --git a/src/content/actions/index.js b/src/content/actions/index.js deleted file mode 100644 index 0a16fdf..0000000 --- a/src/content/actions/index.js +++ /dev/null @@ -1,31 +0,0 @@ -export default { - // Enable/disable - ADDON_SET_ENABLED: 'addon.set.enabled', - - // Settings - SETTING_SET: 'setting.set', - - // User input - INPUT_KEY_PRESS: 'input.key.press', - INPUT_CLEAR_KEYS: 'input.clear.keys', - - // Completion - COMPLETION_SET_ITEMS: 'completion.set.items', - COMPLETION_SELECT_NEXT: 'completions.select.next', - COMPLETION_SELECT_PREV: 'completions.select.prev', - - // Follow - FOLLOW_CONTROLLER_ENABLE: 'follow.controller.enable', - FOLLOW_CONTROLLER_DISABLE: 'follow.controller.disable', - FOLLOW_CONTROLLER_KEY_PRESS: 'follow.controller.key.press', - FOLLOW_CONTROLLER_BACKSPACE: 'follow.controller.backspace', - - // Find - FIND_SET_KEYWORD: 'find.set.keyword', - - // Mark - MARK_START_SET: 'mark.start.set', - MARK_START_JUMP: 'mark.start.jump', - MARK_CANCEL: 'mark.cancel', - MARK_SET_LOCAL: 'mark.set.local', -}; diff --git a/src/content/actions/index.ts b/src/content/actions/index.ts new file mode 100644 index 0000000..0a16fdf --- /dev/null +++ b/src/content/actions/index.ts @@ -0,0 +1,31 @@ +export default { + // Enable/disable + ADDON_SET_ENABLED: 'addon.set.enabled', + + // Settings + SETTING_SET: 'setting.set', + + // User input + INPUT_KEY_PRESS: 'input.key.press', + INPUT_CLEAR_KEYS: 'input.clear.keys', + + // Completion + COMPLETION_SET_ITEMS: 'completion.set.items', + COMPLETION_SELECT_NEXT: 'completions.select.next', + COMPLETION_SELECT_PREV: 'completions.select.prev', + + // Follow + FOLLOW_CONTROLLER_ENABLE: 'follow.controller.enable', + FOLLOW_CONTROLLER_DISABLE: 'follow.controller.disable', + FOLLOW_CONTROLLER_KEY_PRESS: 'follow.controller.key.press', + FOLLOW_CONTROLLER_BACKSPACE: 'follow.controller.backspace', + + // Find + FIND_SET_KEYWORD: 'find.set.keyword', + + // Mark + MARK_START_SET: 'mark.start.set', + MARK_START_JUMP: 'mark.start.jump', + MARK_CANCEL: 'mark.cancel', + MARK_SET_LOCAL: 'mark.set.local', +}; diff --git a/src/content/actions/input.js b/src/content/actions/input.js deleted file mode 100644 index 465a486..0000000 --- a/src/content/actions/input.js +++ /dev/null @@ -1,16 +0,0 @@ -import actions from 'content/actions'; - -const keyPress = (key) => { - return { - type: actions.INPUT_KEY_PRESS, - key, - }; -}; - -const clearKeys = () => { - return { - type: actions.INPUT_CLEAR_KEYS - }; -}; - -export { keyPress, clearKeys }; diff --git a/src/content/actions/input.ts b/src/content/actions/input.ts new file mode 100644 index 0000000..465a486 --- /dev/null +++ b/src/content/actions/input.ts @@ -0,0 +1,16 @@ +import actions from 'content/actions'; + +const keyPress = (key) => { + return { + type: actions.INPUT_KEY_PRESS, + key, + }; +}; + +const clearKeys = () => { + return { + type: actions.INPUT_CLEAR_KEYS + }; +}; + +export { keyPress, clearKeys }; diff --git a/src/content/actions/mark.js b/src/content/actions/mark.js deleted file mode 100644 index 712a811..0000000 --- a/src/content/actions/mark.js +++ /dev/null @@ -1,46 +0,0 @@ -import actions from 'content/actions'; -import messages from 'shared/messages'; - -const startSet = () => { - return { type: actions.MARK_START_SET }; -}; - -const startJump = () => { - return { type: actions.MARK_START_JUMP }; -}; - -const cancel = () => { - return { type: actions.MARK_CANCEL }; -}; - -const setLocal = (key, x, y) => { - return { - type: actions.MARK_SET_LOCAL, - key, - x, - y, - }; -}; - -const setGlobal = (key, x, y) => { - browser.runtime.sendMessage({ - type: messages.MARK_SET_GLOBAL, - key, - x, - y, - }); - return { type: '' }; -}; - -const jumpGlobal = (key) => { - browser.runtime.sendMessage({ - type: messages.MARK_JUMP_GLOBAL, - key, - }); - return { type: '' }; -}; - -export { - startSet, startJump, cancel, setLocal, - setGlobal, jumpGlobal, -}; diff --git a/src/content/actions/mark.ts b/src/content/actions/mark.ts new file mode 100644 index 0000000..712a811 --- /dev/null +++ b/src/content/actions/mark.ts @@ -0,0 +1,46 @@ +import actions from 'content/actions'; +import messages from 'shared/messages'; + +const startSet = () => { + return { type: actions.MARK_START_SET }; +}; + +const startJump = () => { + return { type: actions.MARK_START_JUMP }; +}; + +const cancel = () => { + return { type: actions.MARK_CANCEL }; +}; + +const setLocal = (key, x, y) => { + return { + type: actions.MARK_SET_LOCAL, + key, + x, + y, + }; +}; + +const setGlobal = (key, x, y) => { + browser.runtime.sendMessage({ + type: messages.MARK_SET_GLOBAL, + key, + x, + y, + }); + return { type: '' }; +}; + +const jumpGlobal = (key) => { + browser.runtime.sendMessage({ + type: messages.MARK_JUMP_GLOBAL, + key, + }); + return { type: '' }; +}; + +export { + startSet, startJump, cancel, setLocal, + setGlobal, jumpGlobal, +}; diff --git a/src/content/actions/operation.js b/src/content/actions/operation.js deleted file mode 100644 index ed9b2cf..0000000 --- a/src/content/actions/operation.js +++ /dev/null @@ -1,104 +0,0 @@ -import operations from 'shared/operations'; -import messages from 'shared/messages'; -import * as scrolls from 'content/scrolls'; -import * as navigates from 'content/navigates'; -import * as focuses from 'content/focuses'; -import * as urls from 'content/urls'; -import * as consoleFrames from 'content/console-frames'; -import * as addonActions from './addon'; -import * as markActions from './mark'; -import * as properties from 'shared/settings/properties'; - -// eslint-disable-next-line complexity, max-lines-per-function -const exec = (operation, settings, addonEnabled) => { - let smoothscroll = settings.properties.smoothscroll || - properties.defaults.smoothscroll; - switch (operation.type) { - case operations.ADDON_ENABLE: - return addonActions.enable(); - case operations.ADDON_DISABLE: - return addonActions.disable(); - case operations.ADDON_TOGGLE_ENABLED: - return addonActions.setEnabled(!addonEnabled); - case operations.FIND_NEXT: - window.top.postMessage(JSON.stringify({ - type: messages.FIND_NEXT, - }), '*'); - break; - case operations.FIND_PREV: - window.top.postMessage(JSON.stringify({ - type: messages.FIND_PREV, - }), '*'); - break; - case operations.SCROLL_VERTICALLY: - scrolls.scrollVertically(operation.count, smoothscroll); - break; - case operations.SCROLL_HORIZONALLY: - scrolls.scrollHorizonally(operation.count, smoothscroll); - break; - case operations.SCROLL_PAGES: - scrolls.scrollPages(operation.count, smoothscroll); - break; - case operations.SCROLL_TOP: - scrolls.scrollToTop(smoothscroll); - break; - case operations.SCROLL_BOTTOM: - scrolls.scrollToBottom(smoothscroll); - break; - case operations.SCROLL_HOME: - scrolls.scrollToHome(smoothscroll); - break; - case operations.SCROLL_END: - scrolls.scrollToEnd(smoothscroll); - break; - case operations.FOLLOW_START: - window.top.postMessage(JSON.stringify({ - type: messages.FOLLOW_START, - newTab: operation.newTab, - background: operation.background, - }), '*'); - break; - case operations.MARK_SET_PREFIX: - return markActions.startSet(); - case operations.MARK_JUMP_PREFIX: - return markActions.startJump(); - case operations.NAVIGATE_HISTORY_PREV: - navigates.historyPrev(window); - break; - case operations.NAVIGATE_HISTORY_NEXT: - navigates.historyNext(window); - break; - case operations.NAVIGATE_LINK_PREV: - navigates.linkPrev(window); - break; - case operations.NAVIGATE_LINK_NEXT: - navigates.linkNext(window); - break; - case operations.NAVIGATE_PARENT: - navigates.parent(window); - break; - case operations.NAVIGATE_ROOT: - navigates.root(window); - break; - case operations.FOCUS_INPUT: - focuses.focusInput(); - break; - case operations.URLS_YANK: - urls.yank(window); - consoleFrames.postInfo('Yanked ' + window.location.href); - break; - case operations.URLS_PASTE: - urls.paste( - window, operation.newTab ? operation.newTab : false, settings.search - ); - break; - default: - browser.runtime.sendMessage({ - type: messages.BACKGROUND_OPERATION, - operation, - }); - } - return { type: '' }; -}; - -export { exec }; diff --git a/src/content/actions/operation.ts b/src/content/actions/operation.ts new file mode 100644 index 0000000..ed9b2cf --- /dev/null +++ b/src/content/actions/operation.ts @@ -0,0 +1,104 @@ +import operations from 'shared/operations'; +import messages from 'shared/messages'; +import * as scrolls from 'content/scrolls'; +import * as navigates from 'content/navigates'; +import * as focuses from 'content/focuses'; +import * as urls from 'content/urls'; +import * as consoleFrames from 'content/console-frames'; +import * as addonActions from './addon'; +import * as markActions from './mark'; +import * as properties from 'shared/settings/properties'; + +// eslint-disable-next-line complexity, max-lines-per-function +const exec = (operation, settings, addonEnabled) => { + let smoothscroll = settings.properties.smoothscroll || + properties.defaults.smoothscroll; + switch (operation.type) { + case operations.ADDON_ENABLE: + return addonActions.enable(); + case operations.ADDON_DISABLE: + return addonActions.disable(); + case operations.ADDON_TOGGLE_ENABLED: + return addonActions.setEnabled(!addonEnabled); + case operations.FIND_NEXT: + window.top.postMessage(JSON.stringify({ + type: messages.FIND_NEXT, + }), '*'); + break; + case operations.FIND_PREV: + window.top.postMessage(JSON.stringify({ + type: messages.FIND_PREV, + }), '*'); + break; + case operations.SCROLL_VERTICALLY: + scrolls.scrollVertically(operation.count, smoothscroll); + break; + case operations.SCROLL_HORIZONALLY: + scrolls.scrollHorizonally(operation.count, smoothscroll); + break; + case operations.SCROLL_PAGES: + scrolls.scrollPages(operation.count, smoothscroll); + break; + case operations.SCROLL_TOP: + scrolls.scrollToTop(smoothscroll); + break; + case operations.SCROLL_BOTTOM: + scrolls.scrollToBottom(smoothscroll); + break; + case operations.SCROLL_HOME: + scrolls.scrollToHome(smoothscroll); + break; + case operations.SCROLL_END: + scrolls.scrollToEnd(smoothscroll); + break; + case operations.FOLLOW_START: + window.top.postMessage(JSON.stringify({ + type: messages.FOLLOW_START, + newTab: operation.newTab, + background: operation.background, + }), '*'); + break; + case operations.MARK_SET_PREFIX: + return markActions.startSet(); + case operations.MARK_JUMP_PREFIX: + return markActions.startJump(); + case operations.NAVIGATE_HISTORY_PREV: + navigates.historyPrev(window); + break; + case operations.NAVIGATE_HISTORY_NEXT: + navigates.historyNext(window); + break; + case operations.NAVIGATE_LINK_PREV: + navigates.linkPrev(window); + break; + case operations.NAVIGATE_LINK_NEXT: + navigates.linkNext(window); + break; + case operations.NAVIGATE_PARENT: + navigates.parent(window); + break; + case operations.NAVIGATE_ROOT: + navigates.root(window); + break; + case operations.FOCUS_INPUT: + focuses.focusInput(); + break; + case operations.URLS_YANK: + urls.yank(window); + consoleFrames.postInfo('Yanked ' + window.location.href); + break; + case operations.URLS_PASTE: + urls.paste( + window, operation.newTab ? operation.newTab : false, settings.search + ); + break; + default: + browser.runtime.sendMessage({ + type: messages.BACKGROUND_OPERATION, + operation, + }); + } + return { type: '' }; +}; + +export { exec }; diff --git a/src/content/actions/setting.js b/src/content/actions/setting.js deleted file mode 100644 index 1c15dd7..0000000 --- a/src/content/actions/setting.js +++ /dev/null @@ -1,37 +0,0 @@ -import actions from 'content/actions'; -import * as keyUtils from 'shared/utils/keys'; -import operations from 'shared/operations'; -import messages from 'shared/messages'; - -const reservedKeymaps = { - '': { type: operations.CANCEL }, - '': { type: operations.CANCEL }, -}; - -const set = (value) => { - let entries = []; - if (value.keymaps) { - let keymaps = { ...value.keymaps, ...reservedKeymaps }; - entries = Object.entries(keymaps).map((entry) => { - return [ - keyUtils.fromMapKeys(entry[0]), - entry[1], - ]; - }); - } - - return { - type: actions.SETTING_SET, - value: { ...value, - keymaps: entries, } - }; -}; - -const load = async() => { - let settings = await browser.runtime.sendMessage({ - type: messages.SETTINGS_QUERY, - }); - return set(settings); -}; - -export { set, load }; diff --git a/src/content/actions/setting.ts b/src/content/actions/setting.ts new file mode 100644 index 0000000..1c15dd7 --- /dev/null +++ b/src/content/actions/setting.ts @@ -0,0 +1,37 @@ +import actions from 'content/actions'; +import * as keyUtils from 'shared/utils/keys'; +import operations from 'shared/operations'; +import messages from 'shared/messages'; + +const reservedKeymaps = { + '': { type: operations.CANCEL }, + '': { type: operations.CANCEL }, +}; + +const set = (value) => { + let entries = []; + if (value.keymaps) { + let keymaps = { ...value.keymaps, ...reservedKeymaps }; + entries = Object.entries(keymaps).map((entry) => { + return [ + keyUtils.fromMapKeys(entry[0]), + entry[1], + ]; + }); + } + + return { + type: actions.SETTING_SET, + value: { ...value, + keymaps: entries, } + }; +}; + +const load = async() => { + let settings = await browser.runtime.sendMessage({ + type: messages.SETTINGS_QUERY, + }); + return set(settings); +}; + +export { set, load }; diff --git a/src/content/components/common/follow.js b/src/content/components/common/follow.js deleted file mode 100644 index 63ce603..0000000 --- a/src/content/components/common/follow.js +++ /dev/null @@ -1,200 +0,0 @@ -import messages from 'shared/messages'; -import Hint from './hint'; -import * as dom from 'shared/utils/dom'; - -const TARGET_SELECTOR = [ - 'a', 'button', 'input', 'textarea', 'area', - '[contenteditable=true]', '[contenteditable=""]', '[tabindex]', - '[role="button"]', 'summary' -].join(','); - - -const inViewport = (win, element, viewSize, framePosition) => { - let { - top, left, bottom, right - } = dom.viewportRect(element); - let doc = win.document; - let frameWidth = doc.documentElement.clientWidth; - let frameHeight = doc.documentElement.clientHeight; - - if (right < 0 || bottom < 0 || top > frameHeight || left > frameWidth) { - // out of frame - return false; - } - if (right + framePosition.x < 0 || bottom + framePosition.y < 0 || - left + framePosition.x > viewSize.width || - top + framePosition.y > viewSize.height) { - // out of viewport - return false; - } - return true; -}; - -const isAriaHiddenOrAriaDisabled = (win, element) => { - if (!element || win.document.documentElement === element) { - return false; - } - for (let attr of ['aria-hidden', 'aria-disabled']) { - if (element.hasAttribute(attr)) { - let hidden = element.getAttribute(attr).toLowerCase(); - if (hidden === '' || hidden === 'true') { - return true; - } - } - } - return isAriaHiddenOrAriaDisabled(win, element.parentNode); -}; - -export default class Follow { - constructor(win, store) { - this.win = win; - this.store = store; - this.newTab = false; - this.background = false; - this.hints = {}; - this.targets = []; - - messages.onMessage(this.onMessage.bind(this)); - } - - key(key) { - if (Object.keys(this.hints).length === 0) { - return false; - } - this.win.parent.postMessage(JSON.stringify({ - type: messages.FOLLOW_KEY_PRESS, - key: key.key, - ctrlKey: key.ctrlKey, - }), '*'); - return true; - } - - openLink(element) { - // Browser prevent new tab by link with target='_blank' - if (!this.newTab && element.getAttribute('target') !== '_blank') { - element.click(); - return; - } - - let href = element.getAttribute('href'); - - // eslint-disable-next-line no-script-url - if (!href || href === '#' || href.toLowerCase().startsWith('javascript:')) { - return; - } - return browser.runtime.sendMessage({ - type: messages.OPEN_URL, - url: element.href, - newTab: true, - background: this.background, - }); - } - - countHints(sender, viewSize, framePosition) { - this.targets = Follow.getTargetElements(this.win, viewSize, framePosition); - sender.postMessage(JSON.stringify({ - type: messages.FOLLOW_RESPONSE_COUNT_TARGETS, - count: this.targets.length, - }), '*'); - } - - createHints(keysArray, newTab, background) { - if (keysArray.length !== this.targets.length) { - throw new Error('illegal hint count'); - } - - this.newTab = newTab; - this.background = background; - this.hints = {}; - for (let i = 0; i < keysArray.length; ++i) { - let keys = keysArray[i]; - let hint = new Hint(this.targets[i], keys); - this.hints[keys] = hint; - } - } - - showHints(keys) { - Object.keys(this.hints).filter(key => key.startsWith(keys)) - .forEach(key => this.hints[key].show()); - Object.keys(this.hints).filter(key => !key.startsWith(keys)) - .forEach(key => this.hints[key].hide()); - } - - removeHints() { - Object.keys(this.hints).forEach((key) => { - this.hints[key].remove(); - }); - this.hints = {}; - this.targets = []; - } - - activateHints(keys) { - let hint = this.hints[keys]; - if (!hint) { - return; - } - let element = hint.target; - switch (element.tagName.toLowerCase()) { - case 'a': - case 'area': - return this.openLink(element); - case 'input': - switch (element.type) { - case 'file': - case 'checkbox': - case 'radio': - case 'submit': - case 'reset': - case 'button': - case 'image': - case 'color': - return element.click(); - default: - return element.focus(); - } - case 'textarea': - return element.focus(); - case 'button': - case 'summary': - return element.click(); - default: - if (dom.isContentEditable(element)) { - return element.focus(); - } else if (element.hasAttribute('tabindex')) { - return element.click(); - } - } - } - - onMessage(message, sender) { - switch (message.type) { - case messages.FOLLOW_REQUEST_COUNT_TARGETS: - return this.countHints(sender, message.viewSize, message.framePosition); - case messages.FOLLOW_CREATE_HINTS: - return this.createHints( - message.keysArray, message.newTab, message.background); - case messages.FOLLOW_SHOW_HINTS: - return this.showHints(message.keys); - case messages.FOLLOW_ACTIVATE: - return this.activateHints(message.keys); - case messages.FOLLOW_REMOVE_HINTS: - return this.removeHints(message.keys); - } - } - - static getTargetElements(win, viewSize, framePosition) { - let all = win.document.querySelectorAll(TARGET_SELECTOR); - let filtered = Array.prototype.filter.call(all, (element) => { - let style = win.getComputedStyle(element); - - // AREA's 'display' in Browser style is 'none' - return (element.tagName === 'AREA' || style.display !== 'none') && - style.visibility !== 'hidden' && - element.type !== 'hidden' && - element.offsetHeight > 0 && - !isAriaHiddenOrAriaDisabled(win, element) && - inViewport(win, element, viewSize, framePosition); - }); - return filtered; - } -} diff --git a/src/content/components/common/follow.ts b/src/content/components/common/follow.ts new file mode 100644 index 0000000..63ce603 --- /dev/null +++ b/src/content/components/common/follow.ts @@ -0,0 +1,200 @@ +import messages from 'shared/messages'; +import Hint from './hint'; +import * as dom from 'shared/utils/dom'; + +const TARGET_SELECTOR = [ + 'a', 'button', 'input', 'textarea', 'area', + '[contenteditable=true]', '[contenteditable=""]', '[tabindex]', + '[role="button"]', 'summary' +].join(','); + + +const inViewport = (win, element, viewSize, framePosition) => { + let { + top, left, bottom, right + } = dom.viewportRect(element); + let doc = win.document; + let frameWidth = doc.documentElement.clientWidth; + let frameHeight = doc.documentElement.clientHeight; + + if (right < 0 || bottom < 0 || top > frameHeight || left > frameWidth) { + // out of frame + return false; + } + if (right + framePosition.x < 0 || bottom + framePosition.y < 0 || + left + framePosition.x > viewSize.width || + top + framePosition.y > viewSize.height) { + // out of viewport + return false; + } + return true; +}; + +const isAriaHiddenOrAriaDisabled = (win, element) => { + if (!element || win.document.documentElement === element) { + return false; + } + for (let attr of ['aria-hidden', 'aria-disabled']) { + if (element.hasAttribute(attr)) { + let hidden = element.getAttribute(attr).toLowerCase(); + if (hidden === '' || hidden === 'true') { + return true; + } + } + } + return isAriaHiddenOrAriaDisabled(win, element.parentNode); +}; + +export default class Follow { + constructor(win, store) { + this.win = win; + this.store = store; + this.newTab = false; + this.background = false; + this.hints = {}; + this.targets = []; + + messages.onMessage(this.onMessage.bind(this)); + } + + key(key) { + if (Object.keys(this.hints).length === 0) { + return false; + } + this.win.parent.postMessage(JSON.stringify({ + type: messages.FOLLOW_KEY_PRESS, + key: key.key, + ctrlKey: key.ctrlKey, + }), '*'); + return true; + } + + openLink(element) { + // Browser prevent new tab by link with target='_blank' + if (!this.newTab && element.getAttribute('target') !== '_blank') { + element.click(); + return; + } + + let href = element.getAttribute('href'); + + // eslint-disable-next-line no-script-url + if (!href || href === '#' || href.toLowerCase().startsWith('javascript:')) { + return; + } + return browser.runtime.sendMessage({ + type: messages.OPEN_URL, + url: element.href, + newTab: true, + background: this.background, + }); + } + + countHints(sender, viewSize, framePosition) { + this.targets = Follow.getTargetElements(this.win, viewSize, framePosition); + sender.postMessage(JSON.stringify({ + type: messages.FOLLOW_RESPONSE_COUNT_TARGETS, + count: this.targets.length, + }), '*'); + } + + createHints(keysArray, newTab, background) { + if (keysArray.length !== this.targets.length) { + throw new Error('illegal hint count'); + } + + this.newTab = newTab; + this.background = background; + this.hints = {}; + for (let i = 0; i < keysArray.length; ++i) { + let keys = keysArray[i]; + let hint = new Hint(this.targets[i], keys); + this.hints[keys] = hint; + } + } + + showHints(keys) { + Object.keys(this.hints).filter(key => key.startsWith(keys)) + .forEach(key => this.hints[key].show()); + Object.keys(this.hints).filter(key => !key.startsWith(keys)) + .forEach(key => this.hints[key].hide()); + } + + removeHints() { + Object.keys(this.hints).forEach((key) => { + this.hints[key].remove(); + }); + this.hints = {}; + this.targets = []; + } + + activateHints(keys) { + let hint = this.hints[keys]; + if (!hint) { + return; + } + let element = hint.target; + switch (element.tagName.toLowerCase()) { + case 'a': + case 'area': + return this.openLink(element); + case 'input': + switch (element.type) { + case 'file': + case 'checkbox': + case 'radio': + case 'submit': + case 'reset': + case 'button': + case 'image': + case 'color': + return element.click(); + default: + return element.focus(); + } + case 'textarea': + return element.focus(); + case 'button': + case 'summary': + return element.click(); + default: + if (dom.isContentEditable(element)) { + return element.focus(); + } else if (element.hasAttribute('tabindex')) { + return element.click(); + } + } + } + + onMessage(message, sender) { + switch (message.type) { + case messages.FOLLOW_REQUEST_COUNT_TARGETS: + return this.countHints(sender, message.viewSize, message.framePosition); + case messages.FOLLOW_CREATE_HINTS: + return this.createHints( + message.keysArray, message.newTab, message.background); + case messages.FOLLOW_SHOW_HINTS: + return this.showHints(message.keys); + case messages.FOLLOW_ACTIVATE: + return this.activateHints(message.keys); + case messages.FOLLOW_REMOVE_HINTS: + return this.removeHints(message.keys); + } + } + + static getTargetElements(win, viewSize, framePosition) { + let all = win.document.querySelectorAll(TARGET_SELECTOR); + let filtered = Array.prototype.filter.call(all, (element) => { + let style = win.getComputedStyle(element); + + // AREA's 'display' in Browser style is 'none' + return (element.tagName === 'AREA' || style.display !== 'none') && + style.visibility !== 'hidden' && + element.type !== 'hidden' && + element.offsetHeight > 0 && + !isAriaHiddenOrAriaDisabled(win, element) && + inViewport(win, element, viewSize, framePosition); + }); + return filtered; + } +} diff --git a/src/content/components/common/hint.js b/src/content/components/common/hint.js deleted file mode 100644 index 1472587..0000000 --- a/src/content/components/common/hint.js +++ /dev/null @@ -1,49 +0,0 @@ -import * as dom from 'shared/utils/dom'; - -const hintPosition = (element) => { - let { left, top, right, bottom } = dom.viewportRect(element); - - if (element.tagName !== 'AREA') { - return { x: left, y: top }; - } - - return { - x: (left + right) / 2, - y: (top + bottom) / 2, - }; -}; - -export default class Hint { - constructor(target, tag) { - if (!(document.body instanceof HTMLElement)) { - throw new TypeError('target is not an HTMLElement'); - } - - this.target = target; - - let doc = target.ownerDocument; - let { x, y } = hintPosition(target); - let { scrollX, scrollY } = window; - - this.element = doc.createElement('span'); - this.element.className = 'vimvixen-hint'; - this.element.textContent = tag; - this.element.style.left = x + scrollX + 'px'; - this.element.style.top = y + scrollY + 'px'; - - this.show(); - doc.body.append(this.element); - } - - show() { - this.element.style.display = 'inline'; - } - - hide() { - this.element.style.display = 'none'; - } - - remove() { - this.element.remove(); - } -} diff --git a/src/content/components/common/hint.ts b/src/content/components/common/hint.ts new file mode 100644 index 0000000..1472587 --- /dev/null +++ b/src/content/components/common/hint.ts @@ -0,0 +1,49 @@ +import * as dom from 'shared/utils/dom'; + +const hintPosition = (element) => { + let { left, top, right, bottom } = dom.viewportRect(element); + + if (element.tagName !== 'AREA') { + return { x: left, y: top }; + } + + return { + x: (left + right) / 2, + y: (top + bottom) / 2, + }; +}; + +export default class Hint { + constructor(target, tag) { + if (!(document.body instanceof HTMLElement)) { + throw new TypeError('target is not an HTMLElement'); + } + + this.target = target; + + let doc = target.ownerDocument; + let { x, y } = hintPosition(target); + let { scrollX, scrollY } = window; + + this.element = doc.createElement('span'); + this.element.className = 'vimvixen-hint'; + this.element.textContent = tag; + this.element.style.left = x + scrollX + 'px'; + this.element.style.top = y + scrollY + 'px'; + + this.show(); + doc.body.append(this.element); + } + + show() { + this.element.style.display = 'inline'; + } + + hide() { + this.element.style.display = 'none'; + } + + remove() { + this.element.remove(); + } +} diff --git a/src/content/components/common/index.js b/src/content/components/common/index.js deleted file mode 100644 index bcab4fa..0000000 --- a/src/content/components/common/index.js +++ /dev/null @@ -1,55 +0,0 @@ -import InputComponent from './input'; -import FollowComponent from './follow'; -import MarkComponent from './mark'; -import KeymapperComponent from './keymapper'; -import * as settingActions from 'content/actions/setting'; -import messages from 'shared/messages'; -import * as addonActions from '../../actions/addon'; -import * as blacklists from 'shared/blacklists'; - -export default class Common { - constructor(win, store) { - const input = new InputComponent(win.document.body, store); - const follow = new FollowComponent(win, store); - const mark = new MarkComponent(win.document.body, store); - const keymapper = new KeymapperComponent(store); - - input.onKey(key => follow.key(key)); - input.onKey(key => mark.key(key)); - input.onKey(key => keymapper.key(key)); - - this.win = win; - this.store = store; - this.prevEnabled = undefined; - this.prevBlacklist = undefined; - - this.reloadSettings(); - - messages.onMessage(this.onMessage.bind(this)); - } - - onMessage(message) { - let { enabled } = this.store.getState().addon; - switch (message.type) { - case messages.SETTINGS_CHANGED: - return this.reloadSettings(); - case messages.ADDON_TOGGLE_ENABLED: - this.store.dispatch(addonActions.setEnabled(!enabled)); - } - } - - reloadSettings() { - try { - this.store.dispatch(settingActions.load()).then(({ value: settings }) => { - let enabled = !blacklists.includes( - settings.blacklist, this.win.location.href - ); - this.store.dispatch(addonActions.setEnabled(enabled)); - }); - } catch (e) { - // Sometime sendMessage fails when background script is not ready. - console.warn(e); - setTimeout(() => this.reloadSettings(), 500); - } - } -} diff --git a/src/content/components/common/index.ts b/src/content/components/common/index.ts new file mode 100644 index 0000000..bcab4fa --- /dev/null +++ b/src/content/components/common/index.ts @@ -0,0 +1,55 @@ +import InputComponent from './input'; +import FollowComponent from './follow'; +import MarkComponent from './mark'; +import KeymapperComponent from './keymapper'; +import * as settingActions from 'content/actions/setting'; +import messages from 'shared/messages'; +import * as addonActions from '../../actions/addon'; +import * as blacklists from 'shared/blacklists'; + +export default class Common { + constructor(win, store) { + const input = new InputComponent(win.document.body, store); + const follow = new FollowComponent(win, store); + const mark = new MarkComponent(win.document.body, store); + const keymapper = new KeymapperComponent(store); + + input.onKey(key => follow.key(key)); + input.onKey(key => mark.key(key)); + input.onKey(key => keymapper.key(key)); + + this.win = win; + this.store = store; + this.prevEnabled = undefined; + this.prevBlacklist = undefined; + + this.reloadSettings(); + + messages.onMessage(this.onMessage.bind(this)); + } + + onMessage(message) { + let { enabled } = this.store.getState().addon; + switch (message.type) { + case messages.SETTINGS_CHANGED: + return this.reloadSettings(); + case messages.ADDON_TOGGLE_ENABLED: + this.store.dispatch(addonActions.setEnabled(!enabled)); + } + } + + reloadSettings() { + try { + this.store.dispatch(settingActions.load()).then(({ value: settings }) => { + let enabled = !blacklists.includes( + settings.blacklist, this.win.location.href + ); + this.store.dispatch(addonActions.setEnabled(enabled)); + }); + } catch (e) { + // Sometime sendMessage fails when background script is not ready. + console.warn(e); + setTimeout(() => this.reloadSettings(), 500); + } + } +} diff --git a/src/content/components/common/input.js b/src/content/components/common/input.js deleted file mode 100644 index eefaf10..0000000 --- a/src/content/components/common/input.js +++ /dev/null @@ -1,75 +0,0 @@ -import * as dom from 'shared/utils/dom'; -import * as keys from 'shared/utils/keys'; - -const cancelKey = (e) => { - return e.key === 'Escape' || e.key === '[' && e.ctrlKey; -}; - -export default class InputComponent { - constructor(target) { - this.pressed = {}; - this.onKeyListeners = []; - - target.addEventListener('keypress', this.onKeyPress.bind(this)); - target.addEventListener('keydown', this.onKeyDown.bind(this)); - target.addEventListener('keyup', this.onKeyUp.bind(this)); - } - - onKey(cb) { - this.onKeyListeners.push(cb); - } - - onKeyPress(e) { - if (this.pressed[e.key] && this.pressed[e.key] !== 'keypress') { - return; - } - this.pressed[e.key] = 'keypress'; - this.capture(e); - } - - onKeyDown(e) { - if (this.pressed[e.key] && this.pressed[e.key] !== 'keydown') { - return; - } - this.pressed[e.key] = 'keydown'; - this.capture(e); - } - - onKeyUp(e) { - delete this.pressed[e.key]; - } - - capture(e) { - if (this.fromInput(e)) { - if (cancelKey(e) && e.target.blur) { - e.target.blur(); - } - return; - } - if (['Shift', 'Control', 'Alt', 'OS'].includes(e.key)) { - // pressing only meta key is ignored - return; - } - - let key = keys.fromKeyboardEvent(e); - - for (let listener of this.onKeyListeners) { - let stop = listener(key); - if (stop) { - e.preventDefault(); - e.stopPropagation(); - break; - } - } - } - - fromInput(e) { - if (!e.target) { - return false; - } - return e.target instanceof HTMLInputElement || - e.target instanceof HTMLTextAreaElement || - e.target instanceof HTMLSelectElement || - dom.isContentEditable(e.target); - } -} diff --git a/src/content/components/common/input.ts b/src/content/components/common/input.ts new file mode 100644 index 0000000..eefaf10 --- /dev/null +++ b/src/content/components/common/input.ts @@ -0,0 +1,75 @@ +import * as dom from 'shared/utils/dom'; +import * as keys from 'shared/utils/keys'; + +const cancelKey = (e) => { + return e.key === 'Escape' || e.key === '[' && e.ctrlKey; +}; + +export default class InputComponent { + constructor(target) { + this.pressed = {}; + this.onKeyListeners = []; + + target.addEventListener('keypress', this.onKeyPress.bind(this)); + target.addEventListener('keydown', this.onKeyDown.bind(this)); + target.addEventListener('keyup', this.onKeyUp.bind(this)); + } + + onKey(cb) { + this.onKeyListeners.push(cb); + } + + onKeyPress(e) { + if (this.pressed[e.key] && this.pressed[e.key] !== 'keypress') { + return; + } + this.pressed[e.key] = 'keypress'; + this.capture(e); + } + + onKeyDown(e) { + if (this.pressed[e.key] && this.pressed[e.key] !== 'keydown') { + return; + } + this.pressed[e.key] = 'keydown'; + this.capture(e); + } + + onKeyUp(e) { + delete this.pressed[e.key]; + } + + capture(e) { + if (this.fromInput(e)) { + if (cancelKey(e) && e.target.blur) { + e.target.blur(); + } + return; + } + if (['Shift', 'Control', 'Alt', 'OS'].includes(e.key)) { + // pressing only meta key is ignored + return; + } + + let key = keys.fromKeyboardEvent(e); + + for (let listener of this.onKeyListeners) { + let stop = listener(key); + if (stop) { + e.preventDefault(); + e.stopPropagation(); + break; + } + } + } + + fromInput(e) { + if (!e.target) { + return false; + } + return e.target instanceof HTMLInputElement || + e.target instanceof HTMLTextAreaElement || + e.target instanceof HTMLSelectElement || + dom.isContentEditable(e.target); + } +} diff --git a/src/content/components/common/keymapper.js b/src/content/components/common/keymapper.js deleted file mode 100644 index ec0d093..0000000 --- a/src/content/components/common/keymapper.js +++ /dev/null @@ -1,58 +0,0 @@ -import * as inputActions from 'content/actions/input'; -import * as operationActions from 'content/actions/operation'; -import operations from 'shared/operations'; -import * as keyUtils from 'shared/utils/keys'; - -const mapStartsWith = (mapping, keys) => { - if (mapping.length < keys.length) { - return false; - } - for (let i = 0; i < keys.length; ++i) { - if (!keyUtils.equals(mapping[i], keys[i])) { - return false; - } - } - return true; -}; - -export default class KeymapperComponent { - constructor(store) { - this.store = store; - } - - // eslint-disable-next-line max-statements - key(key) { - this.store.dispatch(inputActions.keyPress(key)); - - let state = this.store.getState(); - let input = state.input; - let keymaps = new Map(state.setting.keymaps); - - let matched = Array.from(keymaps.keys()).filter((mapping) => { - return mapStartsWith(mapping, input.keys); - }); - if (!state.addon.enabled) { - // available keymaps are only ADDON_ENABLE and ADDON_TOGGLE_ENABLED if - // the addon disabled - matched = matched.filter((keys) => { - let type = keymaps.get(keys).type; - return type === operations.ADDON_ENABLE || - type === operations.ADDON_TOGGLE_ENABLED; - }); - } - if (matched.length === 0) { - this.store.dispatch(inputActions.clearKeys()); - return false; - } else if (matched.length > 1 || - matched.length === 1 && input.keys.length < matched[0].length) { - return true; - } - let operation = keymaps.get(matched[0]); - let act = operationActions.exec( - operation, state.setting, state.addon.enabled - ); - this.store.dispatch(act); - this.store.dispatch(inputActions.clearKeys()); - return true; - } -} diff --git a/src/content/components/common/keymapper.ts b/src/content/components/common/keymapper.ts new file mode 100644 index 0000000..ec0d093 --- /dev/null +++ b/src/content/components/common/keymapper.ts @@ -0,0 +1,58 @@ +import * as inputActions from 'content/actions/input'; +import * as operationActions from 'content/actions/operation'; +import operations from 'shared/operations'; +import * as keyUtils from 'shared/utils/keys'; + +const mapStartsWith = (mapping, keys) => { + if (mapping.length < keys.length) { + return false; + } + for (let i = 0; i < keys.length; ++i) { + if (!keyUtils.equals(mapping[i], keys[i])) { + return false; + } + } + return true; +}; + +export default class KeymapperComponent { + constructor(store) { + this.store = store; + } + + // eslint-disable-next-line max-statements + key(key) { + this.store.dispatch(inputActions.keyPress(key)); + + let state = this.store.getState(); + let input = state.input; + let keymaps = new Map(state.setting.keymaps); + + let matched = Array.from(keymaps.keys()).filter((mapping) => { + return mapStartsWith(mapping, input.keys); + }); + if (!state.addon.enabled) { + // available keymaps are only ADDON_ENABLE and ADDON_TOGGLE_ENABLED if + // the addon disabled + matched = matched.filter((keys) => { + let type = keymaps.get(keys).type; + return type === operations.ADDON_ENABLE || + type === operations.ADDON_TOGGLE_ENABLED; + }); + } + if (matched.length === 0) { + this.store.dispatch(inputActions.clearKeys()); + return false; + } else if (matched.length > 1 || + matched.length === 1 && input.keys.length < matched[0].length) { + return true; + } + let operation = keymaps.get(matched[0]); + let act = operationActions.exec( + operation, state.setting, state.addon.enabled + ); + this.store.dispatch(act); + this.store.dispatch(inputActions.clearKeys()); + return true; + } +} diff --git a/src/content/components/common/mark.js b/src/content/components/common/mark.js deleted file mode 100644 index 0f838a9..0000000 --- a/src/content/components/common/mark.js +++ /dev/null @@ -1,74 +0,0 @@ -import * as markActions from 'content/actions/mark'; -import * as scrolls from 'content/scrolls'; -import * as consoleFrames from 'content/console-frames'; -import * as properties from 'shared/settings/properties'; - -const cancelKey = (key) => { - return key.key === 'Esc' || key.key === '[' && key.ctrlKey; -}; - -const globalKey = (key) => { - return (/^[A-Z0-9]$/).test(key); -}; - -export default class MarkComponent { - constructor(body, store) { - this.body = body; - this.store = store; - } - - // eslint-disable-next-line max-statements - key(key) { - let { mark: markStage, setting } = this.store.getState(); - let smoothscroll = setting.properties.smoothscroll || - properties.defaults.smoothscroll; - - if (!markStage.setMode && !markStage.jumpMode) { - return false; - } - - if (cancelKey(key)) { - this.store.dispatch(markActions.cancel()); - return true; - } - - if (key.ctrlKey || key.metaKey || key.altKey) { - consoleFrames.postError('Unknown mark'); - } else if (globalKey(key.key) && markStage.setMode) { - this.doSetGlobal(key); - } else if (globalKey(key.key) && markStage.jumpMode) { - this.doJumpGlobal(key); - } else if (markStage.setMode) { - this.doSet(key); - } else if (markStage.jumpMode) { - this.doJump(markStage.marks, key, smoothscroll); - } - - this.store.dispatch(markActions.cancel()); - return true; - } - - doSet(key) { - let { x, y } = scrolls.getScroll(); - this.store.dispatch(markActions.setLocal(key.key, x, y)); - } - - doJump(marks, key, smoothscroll) { - if (!marks[key.key]) { - consoleFrames.postError('Mark is not set'); - return; - } - - let { x, y } = marks[key.key]; - scrolls.scrollTo(x, y, smoothscroll); - } - - doSetGlobal(key) { - let { x, y } = scrolls.getScroll(); - this.store.dispatch(markActions.setGlobal(key.key, x, y)); - } - - doJumpGlobal(key) { - this.store.dispatch(markActions.jumpGlobal(key.key)); - } -} diff --git a/src/content/components/common/mark.ts b/src/content/components/common/mark.ts new file mode 100644 index 0000000..0f838a9 --- /dev/null +++ b/src/content/components/common/mark.ts @@ -0,0 +1,74 @@ +import * as markActions from 'content/actions/mark'; +import * as scrolls from 'content/scrolls'; +import * as consoleFrames from 'content/console-frames'; +import * as properties from 'shared/settings/properties'; + +const cancelKey = (key) => { + return key.key === 'Esc' || key.key === '[' && key.ctrlKey; +}; + +const globalKey = (key) => { + return (/^[A-Z0-9]$/).test(key); +}; + +export default class MarkComponent { + constructor(body, store) { + this.body = body; + this.store = store; + } + + // eslint-disable-next-line max-statements + key(key) { + let { mark: markStage, setting } = this.store.getState(); + let smoothscroll = setting.properties.smoothscroll || + properties.defaults.smoothscroll; + + if (!markStage.setMode && !markStage.jumpMode) { + return false; + } + + if (cancelKey(key)) { + this.store.dispatch(markActions.cancel()); + return true; + } + + if (key.ctrlKey || key.metaKey || key.altKey) { + consoleFrames.postError('Unknown mark'); + } else if (globalKey(key.key) && markStage.setMode) { + this.doSetGlobal(key); + } else if (globalKey(key.key) && markStage.jumpMode) { + this.doJumpGlobal(key); + } else if (markStage.setMode) { + this.doSet(key); + } else if (markStage.jumpMode) { + this.doJump(markStage.marks, key, smoothscroll); + } + + this.store.dispatch(markActions.cancel()); + return true; + } + + doSet(key) { + let { x, y } = scrolls.getScroll(); + this.store.dispatch(markActions.setLocal(key.key, x, y)); + } + + doJump(marks, key, smoothscroll) { + if (!marks[key.key]) { + consoleFrames.postError('Mark is not set'); + return; + } + + let { x, y } = marks[key.key]; + scrolls.scrollTo(x, y, smoothscroll); + } + + doSetGlobal(key) { + let { x, y } = scrolls.getScroll(); + this.store.dispatch(markActions.setGlobal(key.key, x, y)); + } + + doJumpGlobal(key) { + this.store.dispatch(markActions.jumpGlobal(key.key)); + } +} diff --git a/src/content/components/frame-content.js b/src/content/components/frame-content.js deleted file mode 100644 index ca999ba..0000000 --- a/src/content/components/frame-content.js +++ /dev/null @@ -1,3 +0,0 @@ -import CommonComponent from './common'; - -export default CommonComponent; diff --git a/src/content/components/frame-content.ts b/src/content/components/frame-content.ts new file mode 100644 index 0000000..ca999ba --- /dev/null +++ b/src/content/components/frame-content.ts @@ -0,0 +1,3 @@ +import CommonComponent from './common'; + +export default CommonComponent; diff --git a/src/content/components/top-content/find.js b/src/content/components/top-content/find.js deleted file mode 100644 index 4d46d79..0000000 --- a/src/content/components/top-content/find.js +++ /dev/null @@ -1,41 +0,0 @@ -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) { - switch (message.type) { - case messages.CONSOLE_ENTER_FIND: - return this.start(message.text); - case messages.FIND_NEXT: - return this.next(); - case messages.FIND_PREV: - 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; - return this.store.dispatch(findActions.next(state.keyword, false)); - } - - prev() { - let state = this.store.getState().find; - return this.store.dispatch(findActions.prev(state.keyword, false)); - } -} diff --git a/src/content/components/top-content/find.ts b/src/content/components/top-content/find.ts new file mode 100644 index 0000000..4d46d79 --- /dev/null +++ b/src/content/components/top-content/find.ts @@ -0,0 +1,41 @@ +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) { + switch (message.type) { + case messages.CONSOLE_ENTER_FIND: + return this.start(message.text); + case messages.FIND_NEXT: + return this.next(); + case messages.FIND_PREV: + 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; + return this.store.dispatch(findActions.next(state.keyword, false)); + } + + prev() { + let state = this.store.getState().find; + return this.store.dispatch(findActions.prev(state.keyword, false)); + } +} diff --git a/src/content/components/top-content/follow-controller.js b/src/content/components/top-content/follow-controller.js deleted file mode 100644 index 7f36604..0000000 --- a/src/content/components/top-content/follow-controller.js +++ /dev/null @@ -1,147 +0,0 @@ -import * as followControllerActions from 'content/actions/follow-controller'; -import messages from 'shared/messages'; -import HintKeyProducer from 'content/hint-key-producer'; -import * as properties from 'shared/settings/properties'; - -const broadcastMessage = (win, message) => { - let json = JSON.stringify(message); - let frames = [window.self].concat(Array.from(window.frames)); - frames.forEach(frame => frame.postMessage(json, '*')); -}; - -export default class FollowController { - constructor(win, store) { - this.win = win; - this.store = store; - this.state = {}; - this.keys = []; - this.producer = null; - - messages.onMessage(this.onMessage.bind(this)); - - store.subscribe(() => { - this.update(); - }); - } - - onMessage(message, sender) { - switch (message.type) { - case messages.FOLLOW_START: - return this.store.dispatch( - followControllerActions.enable(message.newTab, message.background)); - case messages.FOLLOW_RESPONSE_COUNT_TARGETS: - return this.create(message.count, sender); - case messages.FOLLOW_KEY_PRESS: - return this.keyPress(message.key, message.ctrlKey); - } - } - - update() { - let prevState = this.state; - this.state = this.store.getState().followController; - - if (!prevState.enabled && this.state.enabled) { - this.count(); - } else if (prevState.enabled && !this.state.enabled) { - this.remove(); - } else if (prevState.keys !== this.state.keys) { - this.updateHints(); - } - } - - updateHints() { - let shown = this.keys.filter(key => key.startsWith(this.state.keys)); - if (shown.length === 1) { - this.activate(); - this.store.dispatch(followControllerActions.disable()); - } - - broadcastMessage(this.win, { - type: messages.FOLLOW_SHOW_HINTS, - keys: this.state.keys, - }); - } - - activate() { - broadcastMessage(this.win, { - type: messages.FOLLOW_ACTIVATE, - keys: this.state.keys, - }); - } - - keyPress(key, ctrlKey) { - if (key === '[' && ctrlKey) { - this.store.dispatch(followControllerActions.disable()); - return true; - } - switch (key) { - case 'Enter': - this.activate(); - this.store.dispatch(followControllerActions.disable()); - break; - case 'Esc': - this.store.dispatch(followControllerActions.disable()); - break; - case 'Backspace': - case 'Delete': - this.store.dispatch(followControllerActions.backspace()); - break; - default: - if (this.hintchars().includes(key)) { - this.store.dispatch(followControllerActions.keyPress(key)); - } - break; - } - return true; - } - - count() { - this.producer = new HintKeyProducer(this.hintchars()); - let doc = this.win.document; - let viewWidth = this.win.innerWidth || doc.documentElement.clientWidth; - let viewHeight = this.win.innerHeight || doc.documentElement.clientHeight; - let frameElements = this.win.document.querySelectorAll('frame,iframe'); - - this.win.postMessage(JSON.stringify({ - type: messages.FOLLOW_REQUEST_COUNT_TARGETS, - viewSize: { width: viewWidth, height: viewHeight }, - framePosition: { x: 0, y: 0 }, - }), '*'); - frameElements.forEach((element) => { - let { left: frameX, top: frameY } = element.getBoundingClientRect(); - let message = JSON.stringify({ - type: messages.FOLLOW_REQUEST_COUNT_TARGETS, - viewSize: { width: viewWidth, height: viewHeight }, - framePosition: { x: frameX, y: frameY }, - }); - element.contentWindow.postMessage(message, '*'); - }); - } - - create(count, sender) { - let produced = []; - for (let i = 0; i < count; ++i) { - produced.push(this.producer.produce()); - } - this.keys = this.keys.concat(produced); - - sender.postMessage(JSON.stringify({ - type: messages.FOLLOW_CREATE_HINTS, - keysArray: produced, - newTab: this.state.newTab, - background: this.state.background, - }), '*'); - } - - remove() { - this.keys = []; - broadcastMessage(this.win, { - type: messages.FOLLOW_REMOVE_HINTS, - }); - } - - hintchars() { - return this.store.getState().setting.properties.hintchars || - properties.defaults.hintchars; - } -} diff --git a/src/content/components/top-content/follow-controller.ts b/src/content/components/top-content/follow-controller.ts new file mode 100644 index 0000000..7f36604 --- /dev/null +++ b/src/content/components/top-content/follow-controller.ts @@ -0,0 +1,147 @@ +import * as followControllerActions from 'content/actions/follow-controller'; +import messages from 'shared/messages'; +import HintKeyProducer from 'content/hint-key-producer'; +import * as properties from 'shared/settings/properties'; + +const broadcastMessage = (win, message) => { + let json = JSON.stringify(message); + let frames = [window.self].concat(Array.from(window.frames)); + frames.forEach(frame => frame.postMessage(json, '*')); +}; + +export default class FollowController { + constructor(win, store) { + this.win = win; + this.store = store; + this.state = {}; + this.keys = []; + this.producer = null; + + messages.onMessage(this.onMessage.bind(this)); + + store.subscribe(() => { + this.update(); + }); + } + + onMessage(message, sender) { + switch (message.type) { + case messages.FOLLOW_START: + return this.store.dispatch( + followControllerActions.enable(message.newTab, message.background)); + case messages.FOLLOW_RESPONSE_COUNT_TARGETS: + return this.create(message.count, sender); + case messages.FOLLOW_KEY_PRESS: + return this.keyPress(message.key, message.ctrlKey); + } + } + + update() { + let prevState = this.state; + this.state = this.store.getState().followController; + + if (!prevState.enabled && this.state.enabled) { + this.count(); + } else if (prevState.enabled && !this.state.enabled) { + this.remove(); + } else if (prevState.keys !== this.state.keys) { + this.updateHints(); + } + } + + updateHints() { + let shown = this.keys.filter(key => key.startsWith(this.state.keys)); + if (shown.length === 1) { + this.activate(); + this.store.dispatch(followControllerActions.disable()); + } + + broadcastMessage(this.win, { + type: messages.FOLLOW_SHOW_HINTS, + keys: this.state.keys, + }); + } + + activate() { + broadcastMessage(this.win, { + type: messages.FOLLOW_ACTIVATE, + keys: this.state.keys, + }); + } + + keyPress(key, ctrlKey) { + if (key === '[' && ctrlKey) { + this.store.dispatch(followControllerActions.disable()); + return true; + } + switch (key) { + case 'Enter': + this.activate(); + this.store.dispatch(followControllerActions.disable()); + break; + case 'Esc': + this.store.dispatch(followControllerActions.disable()); + break; + case 'Backspace': + case 'Delete': + this.store.dispatch(followControllerActions.backspace()); + break; + default: + if (this.hintchars().includes(key)) { + this.store.dispatch(followControllerActions.keyPress(key)); + } + break; + } + return true; + } + + count() { + this.producer = new HintKeyProducer(this.hintchars()); + let doc = this.win.document; + let viewWidth = this.win.innerWidth || doc.documentElement.clientWidth; + let viewHeight = this.win.innerHeight || doc.documentElement.clientHeight; + let frameElements = this.win.document.querySelectorAll('frame,iframe'); + + this.win.postMessage(JSON.stringify({ + type: messages.FOLLOW_REQUEST_COUNT_TARGETS, + viewSize: { width: viewWidth, height: viewHeight }, + framePosition: { x: 0, y: 0 }, + }), '*'); + frameElements.forEach((element) => { + let { left: frameX, top: frameY } = element.getBoundingClientRect(); + let message = JSON.stringify({ + type: messages.FOLLOW_REQUEST_COUNT_TARGETS, + viewSize: { width: viewWidth, height: viewHeight }, + framePosition: { x: frameX, y: frameY }, + }); + element.contentWindow.postMessage(message, '*'); + }); + } + + create(count, sender) { + let produced = []; + for (let i = 0; i < count; ++i) { + produced.push(this.producer.produce()); + } + this.keys = this.keys.concat(produced); + + sender.postMessage(JSON.stringify({ + type: messages.FOLLOW_CREATE_HINTS, + keysArray: produced, + newTab: this.state.newTab, + background: this.state.background, + }), '*'); + } + + remove() { + this.keys = []; + broadcastMessage(this.win, { + type: messages.FOLLOW_REMOVE_HINTS, + }); + } + + hintchars() { + return this.store.getState().setting.properties.hintchars || + properties.defaults.hintchars; + } +} diff --git a/src/content/components/top-content/index.js b/src/content/components/top-content/index.js deleted file mode 100644 index 1aaef1b..0000000 --- a/src/content/components/top-content/index.js +++ /dev/null @@ -1,41 +0,0 @@ -import CommonComponent from '../common'; -import FollowController from './follow-controller'; -import FindComponent from './find'; -import * as consoleFrames from '../../console-frames'; -import messages from 'shared/messages'; -import * as scrolls from 'content/scrolls'; - -export default class TopContent { - - constructor(win, store) { - this.win = win; - this.store = store; - - 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); - - messages.onMessage(this.onMessage.bind(this)); - } - - onMessage(message) { - let addonState = this.store.getState().addon; - - switch (message.type) { - case messages.CONSOLE_UNFOCUS: - this.win.focus(); - consoleFrames.blur(window.document); - return Promise.resolve(); - case messages.ADDON_ENABLED_QUERY: - return Promise.resolve({ - type: messages.ADDON_ENABLED_RESPONSE, - enabled: addonState.enabled, - }); - case messages.TAB_SCROLL_TO: - return scrolls.scrollTo(message.x, message.y, false); - } - } -} diff --git a/src/content/components/top-content/index.ts b/src/content/components/top-content/index.ts new file mode 100644 index 0000000..1aaef1b --- /dev/null +++ b/src/content/components/top-content/index.ts @@ -0,0 +1,41 @@ +import CommonComponent from '../common'; +import FollowController from './follow-controller'; +import FindComponent from './find'; +import * as consoleFrames from '../../console-frames'; +import messages from 'shared/messages'; +import * as scrolls from 'content/scrolls'; + +export default class TopContent { + + constructor(win, store) { + this.win = win; + this.store = store; + + 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); + + messages.onMessage(this.onMessage.bind(this)); + } + + onMessage(message) { + let addonState = this.store.getState().addon; + + switch (message.type) { + case messages.CONSOLE_UNFOCUS: + this.win.focus(); + consoleFrames.blur(window.document); + return Promise.resolve(); + case messages.ADDON_ENABLED_QUERY: + return Promise.resolve({ + type: messages.ADDON_ENABLED_RESPONSE, + enabled: addonState.enabled, + }); + case messages.TAB_SCROLL_TO: + return scrolls.scrollTo(message.x, message.y, false); + } + } +} diff --git a/src/content/console-frames.js b/src/content/console-frames.js deleted file mode 100644 index ecb5a87..0000000 --- a/src/content/console-frames.js +++ /dev/null @@ -1,38 +0,0 @@ -import messages from 'shared/messages'; - -const initialize = (doc) => { - let iframe = doc.createElement('iframe'); - iframe.src = browser.runtime.getURL('build/console.html'); - iframe.id = 'vimvixen-console-frame'; - iframe.className = 'vimvixen-console-frame'; - doc.body.append(iframe); - - return iframe; -}; - -const blur = (doc) => { - let iframe = doc.getElementById('vimvixen-console-frame'); - iframe.blur(); -}; - -const postError = (text) => { - browser.runtime.sendMessage({ - type: messages.CONSOLE_FRAME_MESSAGE, - message: { - type: messages.CONSOLE_SHOW_ERROR, - text, - }, - }); -}; - -const postInfo = (text) => { - browser.runtime.sendMessage({ - type: messages.CONSOLE_FRAME_MESSAGE, - message: { - type: messages.CONSOLE_SHOW_INFO, - text, - }, - }); -}; - -export { initialize, blur, postError, postInfo }; diff --git a/src/content/console-frames.ts b/src/content/console-frames.ts new file mode 100644 index 0000000..ecb5a87 --- /dev/null +++ b/src/content/console-frames.ts @@ -0,0 +1,38 @@ +import messages from 'shared/messages'; + +const initialize = (doc) => { + let iframe = doc.createElement('iframe'); + iframe.src = browser.runtime.getURL('build/console.html'); + iframe.id = 'vimvixen-console-frame'; + iframe.className = 'vimvixen-console-frame'; + doc.body.append(iframe); + + return iframe; +}; + +const blur = (doc) => { + let iframe = doc.getElementById('vimvixen-console-frame'); + iframe.blur(); +}; + +const postError = (text) => { + browser.runtime.sendMessage({ + type: messages.CONSOLE_FRAME_MESSAGE, + message: { + type: messages.CONSOLE_SHOW_ERROR, + text, + }, + }); +}; + +const postInfo = (text) => { + browser.runtime.sendMessage({ + type: messages.CONSOLE_FRAME_MESSAGE, + message: { + type: messages.CONSOLE_SHOW_INFO, + text, + }, + }); +}; + +export { initialize, blur, postError, postInfo }; diff --git a/src/content/focuses.js b/src/content/focuses.js deleted file mode 100644 index a6f6cc8..0000000 --- a/src/content/focuses.js +++ /dev/null @@ -1,13 +0,0 @@ -import * as doms from 'shared/utils/dom'; - -const focusInput = () => { - let inputTypes = ['email', 'number', 'search', 'tel', 'text', 'url']; - let inputSelector = inputTypes.map(type => `input[type=${type}]`).join(','); - let targets = window.document.querySelectorAll(inputSelector + ',textarea'); - let target = Array.from(targets).find(doms.isVisible); - if (target) { - target.focus(); - } -}; - -export { focusInput }; diff --git a/src/content/focuses.ts b/src/content/focuses.ts new file mode 100644 index 0000000..a6f6cc8 --- /dev/null +++ b/src/content/focuses.ts @@ -0,0 +1,13 @@ +import * as doms from 'shared/utils/dom'; + +const focusInput = () => { + let inputTypes = ['email', 'number', 'search', 'tel', 'text', 'url']; + let inputSelector = inputTypes.map(type => `input[type=${type}]`).join(','); + let targets = window.document.querySelectorAll(inputSelector + ',textarea'); + let target = Array.from(targets).find(doms.isVisible); + if (target) { + target.focus(); + } +}; + +export { focusInput }; diff --git a/src/content/hint-key-producer.js b/src/content/hint-key-producer.js deleted file mode 100644 index 14b23b6..0000000 --- a/src/content/hint-key-producer.js +++ /dev/null @@ -1,33 +0,0 @@ -export default class HintKeyProducer { - constructor(charset) { - if (charset.length === 0) { - throw new TypeError('charset is empty'); - } - - this.charset = charset; - this.counter = []; - } - - produce() { - this.increment(); - - return this.counter.map(x => this.charset[x]).join(''); - } - - increment() { - let max = this.charset.length - 1; - if (this.counter.every(x => x === max)) { - this.counter = new Array(this.counter.length + 1).fill(0); - return; - } - - this.counter.reverse(); - let len = this.charset.length; - let num = this.counter.reduce((x, y, index) => x + y * len ** index) + 1; - for (let i = 0; i < this.counter.length; ++i) { - this.counter[i] = num % len; - num = ~~(num / len); - } - this.counter.reverse(); - } -} diff --git a/src/content/hint-key-producer.ts b/src/content/hint-key-producer.ts new file mode 100644 index 0000000..14b23b6 --- /dev/null +++ b/src/content/hint-key-producer.ts @@ -0,0 +1,33 @@ +export default class HintKeyProducer { + constructor(charset) { + if (charset.length === 0) { + throw new TypeError('charset is empty'); + } + + this.charset = charset; + this.counter = []; + } + + produce() { + this.increment(); + + return this.counter.map(x => this.charset[x]).join(''); + } + + increment() { + let max = this.charset.length - 1; + if (this.counter.every(x => x === max)) { + this.counter = new Array(this.counter.length + 1).fill(0); + return; + } + + this.counter.reverse(); + let len = this.charset.length; + let num = this.counter.reduce((x, y, index) => x + y * len ** index) + 1; + for (let i = 0; i < this.counter.length; ++i) { + this.counter[i] = num % len; + num = ~~(num / len); + } + this.counter.reverse(); + } +} diff --git a/src/content/index.js b/src/content/index.js deleted file mode 100644 index 9edb712..0000000 --- a/src/content/index.js +++ /dev/null @@ -1,21 +0,0 @@ -import { createStore, applyMiddleware } from 'redux'; -import promise from 'redux-promise'; -import reducers from 'content/reducers'; -import TopContentComponent from './components/top-content'; -import FrameContentComponent from './components/frame-content'; -import consoleFrameStyle from './site-style'; - -const store = createStore( - reducers, - applyMiddleware(promise), -); - -if (window.self === window.top) { - new TopContentComponent(window, store); // eslint-disable-line no-new -} else { - new FrameContentComponent(window, store); // eslint-disable-line no-new -} - -let style = window.document.createElement('style'); -style.textContent = consoleFrameStyle.default; -window.document.head.appendChild(style); diff --git a/src/content/index.ts b/src/content/index.ts new file mode 100644 index 0000000..9edb712 --- /dev/null +++ b/src/content/index.ts @@ -0,0 +1,21 @@ +import { createStore, applyMiddleware } from 'redux'; +import promise from 'redux-promise'; +import reducers from 'content/reducers'; +import TopContentComponent from './components/top-content'; +import FrameContentComponent from './components/frame-content'; +import consoleFrameStyle from './site-style'; + +const store = createStore( + reducers, + applyMiddleware(promise), +); + +if (window.self === window.top) { + new TopContentComponent(window, store); // eslint-disable-line no-new +} else { + new FrameContentComponent(window, store); // eslint-disable-line no-new +} + +let style = window.document.createElement('style'); +style.textContent = consoleFrameStyle.default; +window.document.head.appendChild(style); diff --git a/src/content/navigates.js b/src/content/navigates.js deleted file mode 100644 index c9baa30..0000000 --- a/src/content/navigates.js +++ /dev/null @@ -1,78 +0,0 @@ -const REL_PATTERN = { - prev: /^(?:prev(?:ious)?|older)\b|\u2039|\u2190|\xab|\u226a|<>/i, -}; - -// Return the last element in the document matching the supplied selector -// and the optional filter, or null if there are no matches. -const selectLast = (win, selector, filter) => { - let nodes = win.document.querySelectorAll(selector); - - if (filter) { - nodes = Array.from(nodes).filter(filter); - } - - return nodes.length ? nodes[nodes.length - 1] : null; -}; - -const historyPrev = (win) => { - win.history.back(); -}; - -const historyNext = (win) => { - win.history.forward(); -}; - -// Code common to linkPrev and linkNext which navigates to the specified page. -const linkRel = (win, rel) => { - let link = selectLast(win, `link[rel~=${rel}][href]`); - - if (link) { - win.location = link.href; - return; - } - - const pattern = REL_PATTERN[rel]; - - link = selectLast(win, `a[rel~=${rel}][href]`) || - // `innerText` is much slower than `textContent`, but produces much better - // (i.e. less unexpected) results - selectLast(win, 'a[href]', lnk => pattern.test(lnk.innerText)); - - if (link) { - link.click(); - } -}; - -const linkPrev = (win) => { - linkRel(win, 'prev'); -}; - -const linkNext = (win) => { - linkRel(win, 'next'); -}; - -const parent = (win) => { - const loc = win.location; - if (loc.hash !== '') { - loc.hash = ''; - return; - } else if (loc.search !== '') { - loc.search = ''; - return; - } - - const basenamePattern = /\/[^/]+$/; - const lastDirPattern = /\/[^/]+\/$/; - if (basenamePattern.test(loc.pathname)) { - loc.pathname = loc.pathname.replace(basenamePattern, '/'); - } else if (lastDirPattern.test(loc.pathname)) { - loc.pathname = loc.pathname.replace(lastDirPattern, '/'); - } -}; - -const root = (win) => { - win.location = win.location.origin; -}; - -export { historyPrev, historyNext, linkPrev, linkNext, parent, root }; diff --git a/src/content/navigates.ts b/src/content/navigates.ts new file mode 100644 index 0000000..c9baa30 --- /dev/null +++ b/src/content/navigates.ts @@ -0,0 +1,78 @@ +const REL_PATTERN = { + prev: /^(?:prev(?:ious)?|older)\b|\u2039|\u2190|\xab|\u226a|<>/i, +}; + +// Return the last element in the document matching the supplied selector +// and the optional filter, or null if there are no matches. +const selectLast = (win, selector, filter) => { + let nodes = win.document.querySelectorAll(selector); + + if (filter) { + nodes = Array.from(nodes).filter(filter); + } + + return nodes.length ? nodes[nodes.length - 1] : null; +}; + +const historyPrev = (win) => { + win.history.back(); +}; + +const historyNext = (win) => { + win.history.forward(); +}; + +// Code common to linkPrev and linkNext which navigates to the specified page. +const linkRel = (win, rel) => { + let link = selectLast(win, `link[rel~=${rel}][href]`); + + if (link) { + win.location = link.href; + return; + } + + const pattern = REL_PATTERN[rel]; + + link = selectLast(win, `a[rel~=${rel}][href]`) || + // `innerText` is much slower than `textContent`, but produces much better + // (i.e. less unexpected) results + selectLast(win, 'a[href]', lnk => pattern.test(lnk.innerText)); + + if (link) { + link.click(); + } +}; + +const linkPrev = (win) => { + linkRel(win, 'prev'); +}; + +const linkNext = (win) => { + linkRel(win, 'next'); +}; + +const parent = (win) => { + const loc = win.location; + if (loc.hash !== '') { + loc.hash = ''; + return; + } else if (loc.search !== '') { + loc.search = ''; + return; + } + + const basenamePattern = /\/[^/]+$/; + const lastDirPattern = /\/[^/]+\/$/; + if (basenamePattern.test(loc.pathname)) { + loc.pathname = loc.pathname.replace(basenamePattern, '/'); + } else if (lastDirPattern.test(loc.pathname)) { + loc.pathname = loc.pathname.replace(lastDirPattern, '/'); + } +}; + +const root = (win) => { + win.location = win.location.origin; +}; + +export { historyPrev, historyNext, linkPrev, linkNext, parent, root }; diff --git a/src/content/reducers/addon.js b/src/content/reducers/addon.js deleted file mode 100644 index 0def55a..0000000 --- a/src/content/reducers/addon.js +++ /dev/null @@ -1,15 +0,0 @@ -import actions from 'content/actions'; - -const defaultState = { - enabled: true, -}; - -export default function reducer(state = defaultState, action = {}) { - switch (action.type) { - case actions.ADDON_SET_ENABLED: - return { ...state, - enabled: action.enabled, }; - default: - return state; - } -} diff --git a/src/content/reducers/addon.ts b/src/content/reducers/addon.ts new file mode 100644 index 0000000..0def55a --- /dev/null +++ b/src/content/reducers/addon.ts @@ -0,0 +1,15 @@ +import actions from 'content/actions'; + +const defaultState = { + enabled: true, +}; + +export default function reducer(state = defaultState, action = {}) { + switch (action.type) { + case actions.ADDON_SET_ENABLED: + return { ...state, + enabled: action.enabled, }; + default: + return state; + } +} diff --git a/src/content/reducers/find.js b/src/content/reducers/find.js deleted file mode 100644 index 4560e2c..0000000 --- a/src/content/reducers/find.js +++ /dev/null @@ -1,17 +0,0 @@ -import actions from 'content/actions'; - -const defaultState = { - keyword: null, - found: false, -}; - -export default function reducer(state = defaultState, action = {}) { - switch (action.type) { - case actions.FIND_SET_KEYWORD: - return { ...state, - keyword: action.keyword, - found: action.found, }; - default: - return state; - } -} diff --git a/src/content/reducers/find.ts b/src/content/reducers/find.ts new file mode 100644 index 0000000..4560e2c --- /dev/null +++ b/src/content/reducers/find.ts @@ -0,0 +1,17 @@ +import actions from 'content/actions'; + +const defaultState = { + keyword: null, + found: false, +}; + +export default function reducer(state = defaultState, action = {}) { + switch (action.type) { + case actions.FIND_SET_KEYWORD: + return { ...state, + keyword: action.keyword, + found: action.found, }; + default: + return state; + } +} diff --git a/src/content/reducers/follow-controller.js b/src/content/reducers/follow-controller.js deleted file mode 100644 index 5869c47..0000000 --- a/src/content/reducers/follow-controller.js +++ /dev/null @@ -1,30 +0,0 @@ -import actions from 'content/actions'; - -const defaultState = { - enabled: false, - newTab: false, - background: false, - keys: '', -}; - -export default function reducer(state = defaultState, action = {}) { - switch (action.type) { - case actions.FOLLOW_CONTROLLER_ENABLE: - return { ...state, - enabled: true, - newTab: action.newTab, - background: action.background, - keys: '', }; - case actions.FOLLOW_CONTROLLER_DISABLE: - return { ...state, - enabled: false, }; - case actions.FOLLOW_CONTROLLER_KEY_PRESS: - return { ...state, - keys: state.keys + action.key, }; - case actions.FOLLOW_CONTROLLER_BACKSPACE: - return { ...state, - keys: state.keys.slice(0, -1), }; - default: - return state; - } -} diff --git a/src/content/reducers/follow-controller.ts b/src/content/reducers/follow-controller.ts new file mode 100644 index 0000000..5869c47 --- /dev/null +++ b/src/content/reducers/follow-controller.ts @@ -0,0 +1,30 @@ +import actions from 'content/actions'; + +const defaultState = { + enabled: false, + newTab: false, + background: false, + keys: '', +}; + +export default function reducer(state = defaultState, action = {}) { + switch (action.type) { + case actions.FOLLOW_CONTROLLER_ENABLE: + return { ...state, + enabled: true, + newTab: action.newTab, + background: action.background, + keys: '', }; + case actions.FOLLOW_CONTROLLER_DISABLE: + return { ...state, + enabled: false, }; + case actions.FOLLOW_CONTROLLER_KEY_PRESS: + return { ...state, + keys: state.keys + action.key, }; + case actions.FOLLOW_CONTROLLER_BACKSPACE: + return { ...state, + keys: state.keys.slice(0, -1), }; + default: + return state; + } +} diff --git a/src/content/reducers/index.js b/src/content/reducers/index.js deleted file mode 100644 index bf612a3..0000000 --- a/src/content/reducers/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import { combineReducers } from 'redux'; -import addon from './addon'; -import find from './find'; -import setting from './setting'; -import input from './input'; -import followController from './follow-controller'; -import mark from './mark'; - -export default combineReducers({ - addon, find, setting, input, followController, mark, -}); diff --git a/src/content/reducers/index.ts b/src/content/reducers/index.ts new file mode 100644 index 0000000..bf612a3 --- /dev/null +++ b/src/content/reducers/index.ts @@ -0,0 +1,11 @@ +import { combineReducers } from 'redux'; +import addon from './addon'; +import find from './find'; +import setting from './setting'; +import input from './input'; +import followController from './follow-controller'; +import mark from './mark'; + +export default combineReducers({ + addon, find, setting, input, followController, mark, +}); diff --git a/src/content/reducers/input.js b/src/content/reducers/input.js deleted file mode 100644 index 23e7dd2..0000000 --- a/src/content/reducers/input.js +++ /dev/null @@ -1,18 +0,0 @@ -import actions from 'content/actions'; - -const defaultState = { - keys: [] -}; - -export default function reducer(state = defaultState, action = {}) { - switch (action.type) { - case actions.INPUT_KEY_PRESS: - return { ...state, - keys: state.keys.concat([action.key]), }; - case actions.INPUT_CLEAR_KEYS: - return { ...state, - keys: [], }; - default: - return state; - } -} diff --git a/src/content/reducers/input.ts b/src/content/reducers/input.ts new file mode 100644 index 0000000..23e7dd2 --- /dev/null +++ b/src/content/reducers/input.ts @@ -0,0 +1,18 @@ +import actions from 'content/actions'; + +const defaultState = { + keys: [] +}; + +export default function reducer(state = defaultState, action = {}) { + switch (action.type) { + case actions.INPUT_KEY_PRESS: + return { ...state, + keys: state.keys.concat([action.key]), }; + case actions.INPUT_CLEAR_KEYS: + return { ...state, + keys: [], }; + default: + return state; + } +} diff --git a/src/content/reducers/mark.js b/src/content/reducers/mark.js deleted file mode 100644 index 2c96cc5..0000000 --- a/src/content/reducers/mark.js +++ /dev/null @@ -1,25 +0,0 @@ -import actions from 'content/actions'; - -const defaultState = { - setMode: false, - jumpMode: false, - marks: {}, -}; - -export default function reducer(state = defaultState, action = {}) { - switch (action.type) { - case actions.MARK_START_SET: - return { ...state, setMode: true }; - case actions.MARK_START_JUMP: - return { ...state, jumpMode: true }; - case actions.MARK_CANCEL: - return { ...state, setMode: false, jumpMode: false }; - case actions.MARK_SET_LOCAL: { - let marks = { ...state.marks }; - marks[action.key] = { x: action.x, y: action.y }; - return { ...state, setMode: false, marks }; - } - default: - return state; - } -} diff --git a/src/content/reducers/mark.ts b/src/content/reducers/mark.ts new file mode 100644 index 0000000..2c96cc5 --- /dev/null +++ b/src/content/reducers/mark.ts @@ -0,0 +1,25 @@ +import actions from 'content/actions'; + +const defaultState = { + setMode: false, + jumpMode: false, + marks: {}, +}; + +export default function reducer(state = defaultState, action = {}) { + switch (action.type) { + case actions.MARK_START_SET: + return { ...state, setMode: true }; + case actions.MARK_START_JUMP: + return { ...state, jumpMode: true }; + case actions.MARK_CANCEL: + return { ...state, setMode: false, jumpMode: false }; + case actions.MARK_SET_LOCAL: { + let marks = { ...state.marks }; + marks[action.key] = { x: action.x, y: action.y }; + return { ...state, setMode: false, marks }; + } + default: + return state; + } +} diff --git a/src/content/reducers/setting.js b/src/content/reducers/setting.js deleted file mode 100644 index a49db6d..0000000 --- a/src/content/reducers/setting.js +++ /dev/null @@ -1,16 +0,0 @@ -import actions from 'content/actions'; - -const defaultState = { - // keymaps is and arrays of key-binding pairs, which is entries of Map - keymaps: [], -}; - -export default function reducer(state = defaultState, action = {}) { - switch (action.type) { - case actions.SETTING_SET: - return { ...action.value }; - default: - return state; - } -} - diff --git a/src/content/reducers/setting.ts b/src/content/reducers/setting.ts new file mode 100644 index 0000000..a49db6d --- /dev/null +++ b/src/content/reducers/setting.ts @@ -0,0 +1,16 @@ +import actions from 'content/actions'; + +const defaultState = { + // keymaps is and arrays of key-binding pairs, which is entries of Map + keymaps: [], +}; + +export default function reducer(state = defaultState, action = {}) { + switch (action.type) { + case actions.SETTING_SET: + return { ...action.value }; + default: + return state; + } +} + diff --git a/src/content/scrolls.js b/src/content/scrolls.js deleted file mode 100644 index f3124a1..0000000 --- a/src/content/scrolls.js +++ /dev/null @@ -1,174 +0,0 @@ -import * as doms from 'shared/utils/dom'; - -const SCROLL_DELTA_X = 64; -const SCROLL_DELTA_Y = 64; - -// dirty way to store scrolling state on globally -let scrolling = false; -let lastTimeoutId = null; - -const isScrollableStyle = (element) => { - let { overflowX, overflowY } = window.getComputedStyle(element); - return !(overflowX !== 'scroll' && overflowX !== 'auto' && - overflowY !== 'scroll' && overflowY !== 'auto'); -}; - -const isOverflowed = (element) => { - return element.scrollWidth > element.clientWidth || - element.scrollHeight > element.clientHeight; -}; - -// Find a visiable and scrollable element by depth-first search. Currently -// this method is called by each scrolling, and the returned value of this -// method is not cached. That does not cause performance issue because in the -// most pages, the window is root element i,e, documentElement. -const findScrollable = (element) => { - if (isScrollableStyle(element) && isOverflowed(element)) { - return element; - } - - let children = Array.from(element.children).filter(doms.isVisible); - for (let child of children) { - let scrollable = findScrollable(child); - if (scrollable) { - return scrollable; - } - } - return null; -}; - -const scrollTarget = () => { - if (isOverflowed(window.document.documentElement)) { - return window.document.documentElement; - } - if (isOverflowed(window.document.body)) { - return window.document.body; - } - let target = findScrollable(window.document.documentElement); - if (target) { - return target; - } - return window.document.documentElement; -}; - -const resetScrolling = () => { - scrolling = false; -}; - -class Scroller { - constructor(element, smooth) { - this.element = element; - this.smooth = smooth; - } - - scrollTo(x, y) { - if (!this.smooth) { - this.element.scrollTo(x, y); - return; - } - this.element.scrollTo({ - left: x, - top: y, - behavior: 'smooth', - }); - this.prepareReset(); - } - - scrollBy(x, y) { - let left = this.element.scrollLeft + x; - let top = this.element.scrollTop + y; - this.scrollTo(left, top); - } - - prepareReset() { - scrolling = true; - if (lastTimeoutId) { - clearTimeout(lastTimeoutId); - lastTimeoutId = null; - } - lastTimeoutId = setTimeout(resetScrolling, 100); - } -} - -class RoughtScroller { - constructor(element) { - this.element = element; - } - - scroll(x, y) { - this.element.scrollTo(x, y); - } -} - -const getScroll = () => { - let target = scrollTarget(); - return { x: target.scrollLeft, y: target.scrollTop }; -}; - -const scrollVertically = (count, smooth) => { - let target = scrollTarget(); - let delta = SCROLL_DELTA_Y * count; - if (scrolling) { - delta = SCROLL_DELTA_Y * count * 4; - } - new Scroller(target, smooth).scrollBy(0, delta); -}; - -const scrollHorizonally = (count, smooth) => { - let target = scrollTarget(); - let delta = SCROLL_DELTA_X * count; - if (scrolling) { - delta = SCROLL_DELTA_X * count * 4; - } - new Scroller(target, smooth).scrollBy(delta, 0); -}; - -const scrollPages = (count, smooth) => { - let target = scrollTarget(); - let height = target.clientHeight; - let delta = height * count; - if (scrolling) { - delta = height * count; - } - new Scroller(target, smooth).scrollBy(0, delta); -}; - -const scrollTo = (x, y, smooth) => { - let target = scrollTarget(); - new Scroller(target, smooth).scrollTo(x, y); -}; - -const scrollToTop = (smooth) => { - let target = scrollTarget(); - let x = target.scrollLeft; - let y = 0; - new Scroller(target, smooth).scrollTo(x, y); -}; - -const scrollToBottom = (smooth) => { - let target = scrollTarget(); - let x = target.scrollLeft; - let y = target.scrollHeight; - new Scroller(target, smooth).scrollTo(x, y); -}; - -const scrollToHome = (smooth) => { - let target = scrollTarget(); - let x = 0; - let y = target.scrollTop; - new Scroller(target, smooth).scrollTo(x, y); -}; - -const scrollToEnd = (smooth) => { - let target = scrollTarget(); - let x = target.scrollWidth; - let y = target.scrollTop; - new Scroller(target, smooth).scrollTo(x, y); -}; - -export { - getScroll, - scrollVertically, scrollHorizonally, scrollPages, - scrollTo, - scrollToTop, scrollToBottom, scrollToHome, scrollToEnd -}; diff --git a/src/content/scrolls.ts b/src/content/scrolls.ts new file mode 100644 index 0000000..f3124a1 --- /dev/null +++ b/src/content/scrolls.ts @@ -0,0 +1,174 @@ +import * as doms from 'shared/utils/dom'; + +const SCROLL_DELTA_X = 64; +const SCROLL_DELTA_Y = 64; + +// dirty way to store scrolling state on globally +let scrolling = false; +let lastTimeoutId = null; + +const isScrollableStyle = (element) => { + let { overflowX, overflowY } = window.getComputedStyle(element); + return !(overflowX !== 'scroll' && overflowX !== 'auto' && + overflowY !== 'scroll' && overflowY !== 'auto'); +}; + +const isOverflowed = (element) => { + return element.scrollWidth > element.clientWidth || + element.scrollHeight > element.clientHeight; +}; + +// Find a visiable and scrollable element by depth-first search. Currently +// this method is called by each scrolling, and the returned value of this +// method is not cached. That does not cause performance issue because in the +// most pages, the window is root element i,e, documentElement. +const findScrollable = (element) => { + if (isScrollableStyle(element) && isOverflowed(element)) { + return element; + } + + let children = Array.from(element.children).filter(doms.isVisible); + for (let child of children) { + let scrollable = findScrollable(child); + if (scrollable) { + return scrollable; + } + } + return null; +}; + +const scrollTarget = () => { + if (isOverflowed(window.document.documentElement)) { + return window.document.documentElement; + } + if (isOverflowed(window.document.body)) { + return window.document.body; + } + let target = findScrollable(window.document.documentElement); + if (target) { + return target; + } + return window.document.documentElement; +}; + +const resetScrolling = () => { + scrolling = false; +}; + +class Scroller { + constructor(element, smooth) { + this.element = element; + this.smooth = smooth; + } + + scrollTo(x, y) { + if (!this.smooth) { + this.element.scrollTo(x, y); + return; + } + this.element.scrollTo({ + left: x, + top: y, + behavior: 'smooth', + }); + this.prepareReset(); + } + + scrollBy(x, y) { + let left = this.element.scrollLeft + x; + let top = this.element.scrollTop + y; + this.scrollTo(left, top); + } + + prepareReset() { + scrolling = true; + if (lastTimeoutId) { + clearTimeout(lastTimeoutId); + lastTimeoutId = null; + } + lastTimeoutId = setTimeout(resetScrolling, 100); + } +} + +class RoughtScroller { + constructor(element) { + this.element = element; + } + + scroll(x, y) { + this.element.scrollTo(x, y); + } +} + +const getScroll = () => { + let target = scrollTarget(); + return { x: target.scrollLeft, y: target.scrollTop }; +}; + +const scrollVertically = (count, smooth) => { + let target = scrollTarget(); + let delta = SCROLL_DELTA_Y * count; + if (scrolling) { + delta = SCROLL_DELTA_Y * count * 4; + } + new Scroller(target, smooth).scrollBy(0, delta); +}; + +const scrollHorizonally = (count, smooth) => { + let target = scrollTarget(); + let delta = SCROLL_DELTA_X * count; + if (scrolling) { + delta = SCROLL_DELTA_X * count * 4; + } + new Scroller(target, smooth).scrollBy(delta, 0); +}; + +const scrollPages = (count, smooth) => { + let target = scrollTarget(); + let height = target.clientHeight; + let delta = height * count; + if (scrolling) { + delta = height * count; + } + new Scroller(target, smooth).scrollBy(0, delta); +}; + +const scrollTo = (x, y, smooth) => { + let target = scrollTarget(); + new Scroller(target, smooth).scrollTo(x, y); +}; + +const scrollToTop = (smooth) => { + let target = scrollTarget(); + let x = target.scrollLeft; + let y = 0; + new Scroller(target, smooth).scrollTo(x, y); +}; + +const scrollToBottom = (smooth) => { + let target = scrollTarget(); + let x = target.scrollLeft; + let y = target.scrollHeight; + new Scroller(target, smooth).scrollTo(x, y); +}; + +const scrollToHome = (smooth) => { + let target = scrollTarget(); + let x = 0; + let y = target.scrollTop; + new Scroller(target, smooth).scrollTo(x, y); +}; + +const scrollToEnd = (smooth) => { + let target = scrollTarget(); + let x = target.scrollWidth; + let y = target.scrollTop; + new Scroller(target, smooth).scrollTo(x, y); +}; + +export { + getScroll, + scrollVertically, scrollHorizonally, scrollPages, + scrollTo, + scrollToTop, scrollToBottom, scrollToHome, scrollToEnd +}; diff --git a/src/content/site-style.js b/src/content/site-style.js deleted file mode 100644 index e7a82a5..0000000 --- a/src/content/site-style.js +++ /dev/null @@ -1,26 +0,0 @@ -exports.default = ` -.vimvixen-console-frame { - margin: 0; - padding: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - position: fixed; - z-index: 2147483647; - border: none; - background-color: unset; - pointer-events:none; -} - -.vimvixen-hint { - background-color: yellow; - border: 1px solid gold; - font-weight: bold; - position: absolute; - text-transform: uppercase; - z-index: 2147483647; - font-size: 12px; - color: black; -} -`; diff --git a/src/content/site-style.ts b/src/content/site-style.ts new file mode 100644 index 0000000..e7a82a5 --- /dev/null +++ b/src/content/site-style.ts @@ -0,0 +1,26 @@ +exports.default = ` +.vimvixen-console-frame { + margin: 0; + padding: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + position: fixed; + z-index: 2147483647; + border: none; + background-color: unset; + pointer-events:none; +} + +.vimvixen-hint { + background-color: yellow; + border: 1px solid gold; + font-weight: bold; + position: absolute; + text-transform: uppercase; + z-index: 2147483647; + font-size: 12px; + color: black; +} +`; diff --git a/src/content/urls.js b/src/content/urls.js deleted file mode 100644 index 6e7ea31..0000000 --- a/src/content/urls.js +++ /dev/null @@ -1,40 +0,0 @@ -import messages from 'shared/messages'; -import * as urls from '../shared/urls'; - -const yank = (win) => { - let input = win.document.createElement('input'); - win.document.body.append(input); - - input.style.position = 'fixed'; - input.style.top = '-100px'; - input.value = win.location.href; - input.select(); - - win.document.execCommand('copy'); - - input.remove(); -}; - -const paste = (win, newTab, searchSettings) => { - let textarea = win.document.createElement('textarea'); - win.document.body.append(textarea); - - textarea.style.position = 'fixed'; - textarea.style.top = '-100px'; - textarea.contentEditable = 'true'; - textarea.focus(); - - if (win.document.execCommand('paste')) { - let value = textarea.textContent; - let url = urls.searchUrl(value, searchSettings); - browser.runtime.sendMessage({ - type: messages.OPEN_URL, - url, - newTab, - }); - } - - textarea.remove(); -}; - -export { yank, paste }; diff --git a/src/content/urls.ts b/src/content/urls.ts new file mode 100644 index 0000000..6e7ea31 --- /dev/null +++ b/src/content/urls.ts @@ -0,0 +1,40 @@ +import messages from 'shared/messages'; +import * as urls from '../shared/urls'; + +const yank = (win) => { + let input = win.document.createElement('input'); + win.document.body.append(input); + + input.style.position = 'fixed'; + input.style.top = '-100px'; + input.value = win.location.href; + input.select(); + + win.document.execCommand('copy'); + + input.remove(); +}; + +const paste = (win, newTab, searchSettings) => { + let textarea = win.document.createElement('textarea'); + win.document.body.append(textarea); + + textarea.style.position = 'fixed'; + textarea.style.top = '-100px'; + textarea.contentEditable = 'true'; + textarea.focus(); + + if (win.document.execCommand('paste')) { + let value = textarea.textContent; + let url = urls.searchUrl(value, searchSettings); + browser.runtime.sendMessage({ + type: messages.OPEN_URL, + url, + newTab, + }); + } + + textarea.remove(); +}; + +export { yank, paste }; -- cgit v1.2.3