diff options
Diffstat (limited to 'src/content')
-rw-r--r-- | src/content/Mark.ts | 6 | ||||
-rw-r--r-- | src/content/MessageListener.ts | 32 | ||||
-rw-r--r-- | src/content/actions/addon.js | 19 | ||||
-rw-r--r-- | src/content/actions/addon.ts | 19 | ||||
-rw-r--r-- | src/content/actions/find.js | 68 | ||||
-rw-r--r-- | src/content/actions/find.ts | 100 | ||||
-rw-r--r-- | src/content/actions/follow-controller.ts (renamed from src/content/actions/follow-controller.js) | 12 | ||||
-rw-r--r-- | src/content/actions/index.js | 31 | ||||
-rw-r--r-- | src/content/actions/index.ts | 122 | ||||
-rw-r--r-- | src/content/actions/input.js | 16 | ||||
-rw-r--r-- | src/content/actions/input.ts | 17 | ||||
-rw-r--r-- | src/content/actions/mark.js | 46 | ||||
-rw-r--r-- | src/content/actions/mark.ts | 46 | ||||
-rw-r--r-- | src/content/actions/operation.ts (renamed from src/content/actions/operation.js) | 27 | ||||
-rw-r--r-- | src/content/actions/setting.js | 37 | ||||
-rw-r--r-- | src/content/actions/setting.ts | 28 | ||||
-rw-r--r-- | src/content/components/common/follow.ts (renamed from src/content/components/common/follow.js) | 79 | ||||
-rw-r--r-- | src/content/components/common/hint.ts (renamed from src/content/components/common/hint.js) | 33 | ||||
-rw-r--r-- | src/content/components/common/index.js | 55 | ||||
-rw-r--r-- | src/content/components/common/index.ts | 61 | ||||
-rw-r--r-- | src/content/components/common/input.ts (renamed from src/content/components/common/input.js) | 47 | ||||
-rw-r--r-- | src/content/components/common/keymapper.ts (renamed from src/content/components/common/keymapper.js) | 36 | ||||
-rw-r--r-- | src/content/components/common/mark.js | 74 | ||||
-rw-r--r-- | src/content/components/common/mark.ts | 79 | ||||
-rw-r--r-- | src/content/components/frame-content.ts (renamed from src/content/components/frame-content.js) | 0 | ||||
-rw-r--r-- | src/content/components/top-content/find.js | 41 | ||||
-rw-r--r-- | src/content/components/top-content/find.ts | 46 | ||||
-rw-r--r-- | src/content/components/top-content/follow-controller.ts (renamed from src/content/components/top-content/follow-controller.js) | 67 | ||||
-rw-r--r-- | src/content/components/top-content/index.ts (renamed from src/content/components/top-content/index.js) | 28 | ||||
-rw-r--r-- | src/content/console-frames.ts (renamed from src/content/console-frames.js) | 18 | ||||
-rw-r--r-- | src/content/focuses.ts (renamed from src/content/focuses.js) | 8 | ||||
-rw-r--r-- | src/content/hint-key-producer.ts (renamed from src/content/hint-key-producer.js) | 10 | ||||
-rw-r--r-- | src/content/index.ts (renamed from src/content/index.js) | 11 | ||||
-rw-r--r-- | src/content/navigates.ts (renamed from src/content/navigates.js) | 45 | ||||
-rw-r--r-- | src/content/reducers/addon.js | 15 | ||||
-rw-r--r-- | src/content/reducers/addon.ts | 22 | ||||
-rw-r--r-- | src/content/reducers/find.js | 17 | ||||
-rw-r--r-- | src/content/reducers/find.ts | 25 | ||||
-rw-r--r-- | src/content/reducers/follow-controller.ts (renamed from src/content/reducers/follow-controller.js) | 16 | ||||
-rw-r--r-- | src/content/reducers/index.js | 11 | ||||
-rw-r--r-- | src/content/reducers/index.ts | 21 | ||||
-rw-r--r-- | src/content/reducers/input.js | 18 | ||||
-rw-r--r-- | src/content/reducers/input.ts | 26 | ||||
-rw-r--r-- | src/content/reducers/mark.ts (renamed from src/content/reducers/mark.js) | 16 | ||||
-rw-r--r-- | src/content/reducers/setting.js | 16 | ||||
-rw-r--r-- | src/content/reducers/setting.ts | 40 | ||||
-rw-r--r-- | src/content/scrolls.ts (renamed from src/content/scrolls.js) | 48 | ||||
-rw-r--r-- | src/content/site-style.ts (renamed from src/content/site-style.js) | 2 | ||||
-rw-r--r-- | src/content/store/index.ts | 8 | ||||
-rw-r--r-- | src/content/urls.ts (renamed from src/content/urls.js) | 11 |
50 files changed, 1012 insertions, 664 deletions
diff --git a/src/content/Mark.ts b/src/content/Mark.ts new file mode 100644 index 0000000..f1282fc --- /dev/null +++ b/src/content/Mark.ts @@ -0,0 +1,6 @@ +export default interface Mark { + x: number; + y: number; + // eslint-disable-next-line semi +} + diff --git a/src/content/MessageListener.ts b/src/content/MessageListener.ts new file mode 100644 index 0000000..105d028 --- /dev/null +++ b/src/content/MessageListener.ts @@ -0,0 +1,32 @@ +import { Message, valueOf } from '../shared/messages'; + +export type WebMessageSender = Window | MessagePort | ServiceWorker | null; +export type WebExtMessageSender = browser.runtime.MessageSender; + +export default class MessageListener { + onWebMessage( + listener: (msg: Message, sender: WebMessageSender) => void, + ) { + window.addEventListener('message', (event: MessageEvent) => { + let sender = event.source; + let message = null; + try { + message = JSON.parse(event.data); + } catch (e) { + // ignore unexpected message + return; + } + listener(message, sender); + }); + } + + onBackgroundMessage( + listener: (msg: Message, sender: WebExtMessageSender) => any, + ) { + browser.runtime.onMessage.addListener( + (msg: any, sender: WebExtMessageSender) => { + listener(valueOf(msg), sender); + }, + ); + } +} 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..8dedae0 --- /dev/null +++ b/src/content/actions/addon.ts @@ -0,0 +1,19 @@ +import * as messages from '../../shared/messages'; +import * as actions from './index'; + +const enable = (): Promise<actions.AddonAction> => setEnabled(true); + +const disable = (): Promise<actions.AddonAction> => setEnabled(false); + +const setEnabled = async(enabled: boolean): Promise<actions.AddonAction> => { + 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..53e03ae --- /dev/null +++ b/src/content/actions/find.ts @@ -0,0 +1,100 @@ +// +// 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 * as messages from '../../shared/messages'; +import * as actions from './index'; +import * as consoleFrames from '../console-frames'; + +interface MyWindow extends Window { + find( + aString: string, + aCaseSensitive?: boolean, + aBackwards?: boolean, + aWrapAround?: boolean, + aWholeWord?: boolean, + aSearchInFrames?: boolean, + aShowDialog?: boolean): boolean; +} + +// eslint-disable-next-line no-var, vars-on-top, init-declarations +declare var window: MyWindow; + +const find = (str: string, backwards: boolean): boolean => { + let caseSensitive = false; + let wrapScan = true; + + + // NOTE: aWholeWord dows not implemented, and aSearchInFrames does not work + // because of same origin policy + + // eslint-disable-next-line no-extra-parens + let found = window.find(str, caseSensitive, backwards, wrapScan); + if (found) { + return found; + } + let sel = window.getSelection(); + if (sel) { + sel.removeAllRanges(); + } + + // eslint-disable-next-line no-extra-parens + return window.find(str, caseSensitive, backwards, wrapScan); +}; + +// eslint-disable-next-line max-statements +const findNext = async( + currentKeyword: string, reset: boolean, backwards: boolean, +): Promise<actions.FindAction> => { + if (reset) { + let sel = window.getSelection(); + if (sel) { + sel.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) { + await consoleFrames.postError('No previous search keywords'); + return { type: actions.NOOP }; + } + 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: string, reset: boolean, +): Promise<actions.FindAction> => { + return findNext(currentKeyword, reset, false); +}; + +const prev = ( + currentKeyword: string, reset: boolean, +): Promise<actions.FindAction> => { + return findNext(currentKeyword, reset, true); +}; + +export { next, prev }; diff --git a/src/content/actions/follow-controller.js b/src/content/actions/follow-controller.ts index 006b248..115b3b6 100644 --- a/src/content/actions/follow-controller.js +++ b/src/content/actions/follow-controller.ts @@ -1,6 +1,8 @@ -import actions from 'content/actions'; +import * as actions from './index'; -const enable = (newTab, background) => { +const enable = ( + newTab: boolean, background: boolean, +): actions.FollowAction => { return { type: actions.FOLLOW_CONTROLLER_ENABLE, newTab, @@ -8,20 +10,20 @@ const enable = (newTab, background) => { }; }; -const disable = () => { +const disable = (): actions.FollowAction => { return { type: actions.FOLLOW_CONTROLLER_DISABLE, }; }; -const keyPress = (key) => { +const keyPress = (key: string): actions.FollowAction => { return { type: actions.FOLLOW_CONTROLLER_KEY_PRESS, key: key }; }; -const backspace = () => { +const backspace = (): actions.FollowAction => { return { type: actions.FOLLOW_CONTROLLER_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..8aa9c23 --- /dev/null +++ b/src/content/actions/index.ts @@ -0,0 +1,122 @@ +import Redux from 'redux'; +import Settings from '../../shared/Settings'; +import * as keyUtils from '../../shared/utils/keys'; + +// Enable/disable +export const ADDON_SET_ENABLED = 'addon.set.enabled'; + +// Find +export const FIND_SET_KEYWORD = 'find.set.keyword'; + +// Settings +export const SETTING_SET = 'setting.set'; + +// User input +export const INPUT_KEY_PRESS = 'input.key.press'; +export const INPUT_CLEAR_KEYS = 'input.clear.keys'; + +// Completion +export const COMPLETION_SET_ITEMS = 'completion.set.items'; +export const COMPLETION_SELECT_NEXT = 'completions.select.next'; +export const COMPLETION_SELECT_PREV = 'completions.select.prev'; + +// Follow +export const FOLLOW_CONTROLLER_ENABLE = 'follow.controller.enable'; +export const FOLLOW_CONTROLLER_DISABLE = 'follow.controller.disable'; +export const FOLLOW_CONTROLLER_KEY_PRESS = 'follow.controller.key.press'; +export const FOLLOW_CONTROLLER_BACKSPACE = 'follow.controller.backspace'; + +// Mark +export const MARK_START_SET = 'mark.start.set'; +export const MARK_START_JUMP = 'mark.start.jump'; +export const MARK_CANCEL = 'mark.cancel'; +export const MARK_SET_LOCAL = 'mark.set.local'; + +export const NOOP = 'noop'; + +export interface AddonSetEnabledAction extends Redux.Action { + type: typeof ADDON_SET_ENABLED; + enabled: boolean; +} + +export interface FindSetKeywordAction extends Redux.Action { + type: typeof FIND_SET_KEYWORD; + keyword: string; + found: boolean; +} + +export interface SettingSetAction extends Redux.Action { + type: typeof SETTING_SET; + settings: Settings, +} + +export interface InputKeyPressAction extends Redux.Action { + type: typeof INPUT_KEY_PRESS; + key: keyUtils.Key; +} + +export interface InputClearKeysAction extends Redux.Action { + type: typeof INPUT_CLEAR_KEYS; +} + +export interface FollowControllerEnableAction extends Redux.Action { + type: typeof FOLLOW_CONTROLLER_ENABLE; + newTab: boolean; + background: boolean; +} + +export interface FollowControllerDisableAction extends Redux.Action { + type: typeof FOLLOW_CONTROLLER_DISABLE; +} + +export interface FollowControllerKeyPressAction extends Redux.Action { + type: typeof FOLLOW_CONTROLLER_KEY_PRESS; + key: string; +} + +export interface FollowControllerBackspaceAction extends Redux.Action { + type: typeof FOLLOW_CONTROLLER_BACKSPACE; +} + +export interface MarkStartSetAction extends Redux.Action { + type: typeof MARK_START_SET; +} + +export interface MarkStartJumpAction extends Redux.Action { + type: typeof MARK_START_JUMP; +} + +export interface MarkCancelAction extends Redux.Action { + type: typeof MARK_CANCEL; +} + +export interface MarkSetLocalAction extends Redux.Action { + type: typeof MARK_SET_LOCAL; + key: string; + x: number; + y: number; +} + +export interface NoopAction extends Redux.Action { + type: typeof NOOP; +} + +export type AddonAction = AddonSetEnabledAction; +export type FindAction = FindSetKeywordAction | NoopAction; +export type SettingAction = SettingSetAction; +export type InputAction = InputKeyPressAction | InputClearKeysAction; +export type FollowAction = + FollowControllerEnableAction | FollowControllerDisableAction | + FollowControllerKeyPressAction | FollowControllerBackspaceAction; +export type MarkAction = + MarkStartSetAction | MarkStartJumpAction | + MarkCancelAction | MarkSetLocalAction | NoopAction; + +export type Action = + AddonAction | + FindAction | + SettingAction | + InputAction | + FollowAction | + MarkAction | + NoopAction; 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..1df6452 --- /dev/null +++ b/src/content/actions/input.ts @@ -0,0 +1,17 @@ +import * as actions from './index'; +import * as keyUtils from '../../shared/utils/keys'; + +const keyPress = (key: keyUtils.Key): actions.InputAction => { + return { + type: actions.INPUT_KEY_PRESS, + key, + }; +}; + +const clearKeys = (): actions.InputAction => { + 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..5eb9554 --- /dev/null +++ b/src/content/actions/mark.ts @@ -0,0 +1,46 @@ +import * as actions from './index'; +import * as messages from '../../shared/messages'; + +const startSet = (): actions.MarkAction => { + return { type: actions.MARK_START_SET }; +}; + +const startJump = (): actions.MarkAction => { + return { type: actions.MARK_START_JUMP }; +}; + +const cancel = (): actions.MarkAction => { + return { type: actions.MARK_CANCEL }; +}; + +const setLocal = (key: string, x: number, y: number): actions.MarkAction => { + return { + type: actions.MARK_SET_LOCAL, + key, + x, + y, + }; +}; + +const setGlobal = (key: string, x: number, y: number): actions.MarkAction => { + browser.runtime.sendMessage({ + type: messages.MARK_SET_GLOBAL, + key, + x, + y, + }); + return { type: actions.NOOP }; +}; + +const jumpGlobal = (key: string): actions.MarkAction => { + browser.runtime.sendMessage({ + type: messages.MARK_JUMP_GLOBAL, + key, + }); + return { type: actions.NOOP }; +}; + +export { + startSet, startJump, cancel, setLocal, + setGlobal, jumpGlobal, +}; diff --git a/src/content/actions/operation.js b/src/content/actions/operation.ts index ed9b2cf..41e080b 100644 --- a/src/content/actions/operation.js +++ b/src/content/actions/operation.ts @@ -1,18 +1,21 @@ -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 operations from '../../shared/operations'; +import * as actions from './index'; +import * as messages from '../../shared/messages'; +import * as scrolls from '../scrolls'; +import * as navigates from '../navigates'; +import * as focuses from '../focuses'; +import * as urls from '../urls'; +import * as consoleFrames from '../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; +const exec = ( + operation: operations.Operation, + settings: any, + addonEnabled: boolean, +): Promise<actions.Action> | actions.Action => { + let smoothscroll = settings.properties.smoothscroll; switch (operation.type) { case operations.ADDON_ENABLE: return addonActions.enable(); @@ -98,7 +101,7 @@ const exec = (operation, settings, addonEnabled) => { operation, }); } - return { type: '' }; + return { type: actions.NOOP }; }; 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 = { - '<Esc>': { type: operations.CANCEL }, - '<C-[>': { 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..92f8559 --- /dev/null +++ b/src/content/actions/setting.ts @@ -0,0 +1,28 @@ +import * as actions from './index'; +import * as operations from '../../shared/operations'; +import * as messages from '../../shared/messages'; +import Settings, { Keymaps } from '../../shared/Settings'; + +const reservedKeymaps: Keymaps = { + '<Esc>': { type: operations.CANCEL }, + '<C-[>': { type: operations.CANCEL }, +}; + +const set = (settings: Settings): actions.SettingAction => { + return { + type: actions.SETTING_SET, + settings: { + ...settings, + keymaps: { ...settings.keymaps, ...reservedKeymaps }, + } + }; +}; + +const load = async(): Promise<actions.SettingAction> => { + 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.ts index 63ce603..67f2dd9 100644 --- a/src/content/components/common/follow.js +++ b/src/content/components/common/follow.ts @@ -1,6 +1,8 @@ -import messages from 'shared/messages'; +import MessageListener from '../../MessageListener'; import Hint from './hint'; -import * as dom from 'shared/utils/dom'; +import * as dom from '../../../shared/utils/dom'; +import * as messages from '../../../shared/messages'; +import * as keyUtils from '../../../shared/utils/keys'; const TARGET_SELECTOR = [ 'a', 'button', 'input', 'textarea', 'area', @@ -8,8 +10,22 @@ const TARGET_SELECTOR = [ '[role="button"]', 'summary' ].join(','); +interface Size { + width: number; + height: number; +} + +interface Point { + x: number; + y: number; +} -const inViewport = (win, element, viewSize, framePosition) => { +const inViewport = ( + win: Window, + element: Element, + viewSize: Size, + framePosition: Point, +): boolean => { let { top, left, bottom, right } = dom.viewportRect(element); @@ -30,34 +46,44 @@ const inViewport = (win, element, viewSize, framePosition) => { return true; }; -const isAriaHiddenOrAriaDisabled = (win, element) => { +const isAriaHiddenOrAriaDisabled = (win: Window, element: Element): boolean => { 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(); + let value = element.getAttribute(attr); + if (value !== null) { + let hidden = value.toLowerCase(); if (hidden === '' || hidden === 'true') { return true; } } } - return isAriaHiddenOrAriaDisabled(win, element.parentNode); + return isAriaHiddenOrAriaDisabled(win, element.parentElement as Element); }; export default class Follow { - constructor(win, store) { + private win: Window; + + private newTab: boolean; + + private background: boolean; + + private hints: {[key: string]: Hint }; + + private targets: HTMLElement[] = []; + + constructor(win: Window) { this.win = win; - this.store = store; this.newTab = false; this.background = false; this.hints = {}; this.targets = []; - messages.onMessage(this.onMessage.bind(this)); + new MessageListener().onWebMessage(this.onMessage.bind(this)); } - key(key) { + key(key: keyUtils.Key): boolean { if (Object.keys(this.hints).length === 0) { return false; } @@ -69,7 +95,7 @@ export default class Follow { return true; } - openLink(element) { + openLink(element: HTMLAreaElement|HTMLAnchorElement) { // Browser prevent new tab by link with target='_blank' if (!this.newTab && element.getAttribute('target') !== '_blank') { element.click(); @@ -90,7 +116,7 @@ export default class Follow { }); } - countHints(sender, viewSize, framePosition) { + countHints(sender: any, viewSize: Size, framePosition: Point) { this.targets = Follow.getTargetElements(this.win, viewSize, framePosition); sender.postMessage(JSON.stringify({ type: messages.FOLLOW_RESPONSE_COUNT_TARGETS, @@ -98,7 +124,7 @@ export default class Follow { }), '*'); } - createHints(keysArray, newTab, background) { + createHints(keysArray: string[], newTab: boolean, background: boolean) { if (keysArray.length !== this.targets.length) { throw new Error('illegal hint count'); } @@ -113,7 +139,7 @@ export default class Follow { } } - showHints(keys) { + showHints(keys: string) { Object.keys(this.hints).filter(key => key.startsWith(keys)) .forEach(key => this.hints[key].show()); Object.keys(this.hints).filter(key => !key.startsWith(keys)) @@ -128,18 +154,19 @@ export default class Follow { this.targets = []; } - activateHints(keys) { + activateHints(keys: string) { let hint = this.hints[keys]; if (!hint) { return; } - let element = hint.target; + let element = hint.getTarget(); switch (element.tagName.toLowerCase()) { case 'a': + return this.openLink(element as HTMLAnchorElement); case 'area': - return this.openLink(element); + return this.openLink(element as HTMLAreaElement); case 'input': - switch (element.type) { + switch ((element as HTMLInputElement).type) { case 'file': case 'checkbox': case 'radio': @@ -166,7 +193,7 @@ export default class Follow { } } - onMessage(message, sender) { + onMessage(message: messages.Message, sender: any) { switch (message.type) { case messages.FOLLOW_REQUEST_COUNT_TARGETS: return this.countHints(sender, message.viewSize, message.framePosition); @@ -178,19 +205,23 @@ export default class Follow { case messages.FOLLOW_ACTIVATE: return this.activateHints(message.keys); case messages.FOLLOW_REMOVE_HINTS: - return this.removeHints(message.keys); + return this.removeHints(); } } - static getTargetElements(win, viewSize, framePosition) { + static getTargetElements( + win: Window, + viewSize: + Size, framePosition: Point, + ): HTMLElement[] { let all = win.document.querySelectorAll(TARGET_SELECTOR); - let filtered = Array.prototype.filter.call(all, (element) => { + let filtered = Array.prototype.filter.call(all, (element: HTMLElement) => { 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 as HTMLInputElement).type !== 'hidden' && element.offsetHeight > 0 && !isAriaHiddenOrAriaDisabled(win, element) && inViewport(win, element, viewSize, framePosition); diff --git a/src/content/components/common/hint.js b/src/content/components/common/hint.ts index 1472587..2fcbb0f 100644 --- a/src/content/components/common/hint.js +++ b/src/content/components/common/hint.ts @@ -1,6 +1,11 @@ -import * as dom from 'shared/utils/dom'; +import * as dom from '../../../shared/utils/dom'; -const hintPosition = (element) => { +interface Point { + x: number; + y: number; +} + +const hintPosition = (element: Element): Point => { let { left, top, right, bottom } = dom.viewportRect(element); if (element.tagName !== 'AREA') { @@ -14,17 +19,21 @@ const hintPosition = (element) => { }; export default class Hint { - constructor(target, tag) { - if (!(document.body instanceof HTMLElement)) { - throw new TypeError('target is not an HTMLElement'); - } + private target: HTMLElement; - this.target = target; + private element: HTMLElement; + constructor(target: HTMLElement, tag: string) { let doc = target.ownerDocument; + if (doc === null) { + throw new TypeError('ownerDocument is null'); + } + let { x, y } = hintPosition(target); let { scrollX, scrollY } = window; + this.target = target; + this.element = doc.createElement('span'); this.element.className = 'vimvixen-hint'; this.element.textContent = tag; @@ -35,15 +44,19 @@ export default class Hint { doc.body.append(this.element); } - show() { + show(): void { this.element.style.display = 'inline'; } - hide() { + hide(): void { this.element.style.display = 'none'; } - remove() { + remove(): void { this.element.remove(); } + + getTarget(): HTMLElement { + return this.target; + } } 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..5b097b6 --- /dev/null +++ b/src/content/components/common/index.ts @@ -0,0 +1,61 @@ +import InputComponent from './input'; +import FollowComponent from './follow'; +import MarkComponent from './mark'; +import KeymapperComponent from './keymapper'; +import * as settingActions from '../../actions/setting'; +import * as messages from '../../../shared/messages'; +import MessageListener from '../../MessageListener'; +import * as addonActions from '../../actions/addon'; +import * as blacklists from '../../../shared/blacklists'; +import * as keys from '../../../shared/utils/keys'; +import * as actions from '../../actions'; + +export default class Common { + private win: Window; + + private store: any; + + constructor(win: Window, store: any) { + const input = new InputComponent(win.document.body); + const follow = new FollowComponent(win); + const mark = new MarkComponent(store); + const keymapper = new KeymapperComponent(store); + + input.onKey((key: keys.Key) => follow.key(key)); + input.onKey((key: keys.Key) => mark.key(key)); + input.onKey((key: keys.Key) => keymapper.key(key)); + + this.win = win; + this.store = store; + + this.reloadSettings(); + + new MessageListener().onBackgroundMessage(this.onMessage.bind(this)); + } + + onMessage(message: messages.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((action: actions.SettingAction) => { + let enabled = !blacklists.includes( + action.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.ts index eefaf10..1fe34c9 100644 --- a/src/content/components/common/input.js +++ b/src/content/components/common/input.ts @@ -1,12 +1,16 @@ -import * as dom from 'shared/utils/dom'; -import * as keys from 'shared/utils/keys'; +import * as dom from '../../../shared/utils/dom'; +import * as keys from '../../../shared/utils/keys'; -const cancelKey = (e) => { +const cancelKey = (e: KeyboardEvent): boolean => { return e.key === 'Escape' || e.key === '[' && e.ctrlKey; }; export default class InputComponent { - constructor(target) { + private pressed: {[key: string]: string} = {}; + + private onKeyListeners: ((key: keys.Key) => boolean)[] = []; + + constructor(target: HTMLElement) { this.pressed = {}; this.onKeyListeners = []; @@ -15,11 +19,11 @@ export default class InputComponent { target.addEventListener('keyup', this.onKeyUp.bind(this)); } - onKey(cb) { + onKey(cb: (key: keys.Key) => boolean) { this.onKeyListeners.push(cb); } - onKeyPress(e) { + onKeyPress(e: KeyboardEvent) { if (this.pressed[e.key] && this.pressed[e.key] !== 'keypress') { return; } @@ -27,7 +31,7 @@ export default class InputComponent { this.capture(e); } - onKeyDown(e) { + onKeyDown(e: KeyboardEvent) { if (this.pressed[e.key] && this.pressed[e.key] !== 'keydown') { return; } @@ -35,14 +39,19 @@ export default class InputComponent { this.capture(e); } - onKeyUp(e) { + onKeyUp(e: KeyboardEvent) { delete this.pressed[e.key]; } - capture(e) { - if (this.fromInput(e)) { - if (cancelKey(e) && e.target.blur) { - e.target.blur(); + // eslint-disable-next-line max-statements + capture(e: KeyboardEvent) { + let target = e.target; + if (!(target instanceof HTMLElement)) { + return; + } + if (this.fromInput(target)) { + if (cancelKey(e) && target.blur) { + target.blur(); } return; } @@ -52,7 +61,6 @@ export default class InputComponent { } let key = keys.fromKeyboardEvent(e); - for (let listener of this.onKeyListeners) { let stop = listener(key); if (stop) { @@ -63,13 +71,10 @@ export default class InputComponent { } } - fromInput(e) { - if (!e.target) { - return false; - } - return e.target instanceof HTMLInputElement || - e.target instanceof HTMLTextAreaElement || - e.target instanceof HTMLSelectElement || - dom.isContentEditable(e.target); + fromInput(e: Element) { + return e instanceof HTMLInputElement || + e instanceof HTMLTextAreaElement || + e instanceof HTMLSelectElement || + dom.isContentEditable(e); } } diff --git a/src/content/components/common/keymapper.js b/src/content/components/common/keymapper.ts index ec0d093..c94bae0 100644 --- a/src/content/components/common/keymapper.js +++ b/src/content/components/common/keymapper.ts @@ -1,9 +1,12 @@ -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'; +import * as inputActions from '../../actions/input'; +import * as operationActions from '../../actions/operation'; +import * as operations from '../../../shared/operations'; +import * as keyUtils from '../../../shared/utils/keys'; -const mapStartsWith = (mapping, keys) => { +const mapStartsWith = ( + mapping: keyUtils.Key[], + keys: keyUtils.Key[], +): boolean => { if (mapping.length < keys.length) { return false; } @@ -16,26 +19,33 @@ const mapStartsWith = (mapping, keys) => { }; export default class KeymapperComponent { - constructor(store) { + private store: any; + + constructor(store: any) { this.store = store; } // eslint-disable-next-line max-statements - key(key) { + key(key: keyUtils.Key): boolean { this.store.dispatch(inputActions.keyPress(key)); let state = this.store.getState(); let input = state.input; - let keymaps = new Map(state.setting.keymaps); + let keymaps = new Map<keyUtils.Key[], operations.Operation>( + state.setting.keymaps.map( + (e: {key: keyUtils.Key[], op: operations.Operation}) => [e.key, e.op], + ) + ); - let matched = Array.from(keymaps.keys()).filter((mapping) => { - return mapStartsWith(mapping, input.keys); - }); + let matched = Array.from(keymaps.keys()).filter( + (mapping: keyUtils.Key[]) => { + 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; + let type = (keymaps.get(keys) as operations.Operation).type; return type === operations.ADDON_ENABLE || type === operations.ADDON_TOGGLE_ENABLED; }); @@ -47,7 +57,7 @@ export default class KeymapperComponent { matched.length === 1 && input.keys.length < matched[0].length) { return true; } - let operation = keymaps.get(matched[0]); + let operation = keymaps.get(matched[0]) as operations.Operation; let act = operationActions.exec( operation, state.setting, state.addon.enabled ); 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..1237385 --- /dev/null +++ b/src/content/components/common/mark.ts @@ -0,0 +1,79 @@ +import * as markActions from '../../actions/mark'; +import * as scrolls from '../..//scrolls'; +import * as consoleFrames from '../..//console-frames'; +import * as keyUtils from '../../../shared/utils/keys'; +import Mark from '../../Mark'; + +const cancelKey = (key: keyUtils.Key): boolean => { + return key.key === 'Esc' || key.key === '[' && Boolean(key.ctrlKey); +}; + +const globalKey = (key: string): boolean => { + return (/^[A-Z0-9]$/).test(key); +}; + +export default class MarkComponent { + private store: any; + + constructor(store: any) { + this.store = store; + } + + // eslint-disable-next-line max-statements + key(key: keyUtils.Key) { + let { mark: markState, setting } = this.store.getState(); + let smoothscroll = setting.properties.smoothscroll; + + if (!markState.setMode && !markState.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) && markState.setMode) { + this.doSetGlobal(key); + } else if (globalKey(key.key) && markState.jumpMode) { + this.doJumpGlobal(key); + } else if (markState.setMode) { + this.doSet(key); + } else if (markState.jumpMode) { + this.doJump(markState.marks, key, smoothscroll); + } + + this.store.dispatch(markActions.cancel()); + return true; + } + + doSet(key: keyUtils.Key) { + let { x, y } = scrolls.getScroll(); + this.store.dispatch(markActions.setLocal(key.key, x, y)); + } + + doJump( + marks: { [key: string]: Mark }, + key: keyUtils.Key, + smoothscroll: boolean, + ) { + if (!marks[key.key]) { + consoleFrames.postError('Mark is not set'); + return; + } + + let { x, y } = marks[key.key]; + scrolls.scrollTo(x, y, smoothscroll); + } + + doSetGlobal(key: keyUtils.Key) { + let { x, y } = scrolls.getScroll(); + this.store.dispatch(markActions.setGlobal(key.key, x, y)); + } + + doJumpGlobal(key: keyUtils.Key) { + this.store.dispatch(markActions.jumpGlobal(key.key)); + } +} diff --git a/src/content/components/frame-content.js b/src/content/components/frame-content.ts index ca999ba..ca999ba 100644 --- a/src/content/components/frame-content.js +++ b/src/content/components/frame-content.ts 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..74b95bc --- /dev/null +++ b/src/content/components/top-content/find.ts @@ -0,0 +1,46 @@ +import * as findActions from '../../actions/find'; +import * as messages from '../../../shared/messages'; +import MessageListener from '../../MessageListener'; + +export default class FindComponent { + private store: any; + + constructor(store: any) { + this.store = store; + + new MessageListener().onWebMessage(this.onMessage.bind(this)); + } + + onMessage(message: messages.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: string) { + let state = this.store.getState().find; + + if (text.length === 0) { + return this.store.dispatch( + findActions.next(state.keyword as string, true)); + } + return this.store.dispatch(findActions.next(text, true)); + } + + next() { + let state = this.store.getState().find; + return this.store.dispatch( + findActions.next(state.keyword as string, false)); + } + + prev() { + let state = this.store.getState().find; + return this.store.dispatch( + findActions.prev(state.keyword as string, false)); + } +} diff --git a/src/content/components/top-content/follow-controller.js b/src/content/components/top-content/follow-controller.ts index 7f36604..d49b22a 100644 --- a/src/content/components/top-content/follow-controller.js +++ b/src/content/components/top-content/follow-controller.ts @@ -1,30 +1,45 @@ -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'; +import * as followControllerActions from '../../actions/follow-controller'; +import * as messages from '../../../shared/messages'; +import MessageListener, { WebMessageSender } from '../../MessageListener'; +import HintKeyProducer from '../../hint-key-producer'; -const broadcastMessage = (win, message) => { +const broadcastMessage = (win: Window, message: messages.Message): void => { let json = JSON.stringify(message); - let frames = [window.self].concat(Array.from(window.frames)); + let frames = [win.self].concat(Array.from(win.frames as any)); frames.forEach(frame => frame.postMessage(json, '*')); }; export default class FollowController { - constructor(win, store) { + private win: Window; + + private store: any; + + private state: { + enabled?: boolean; + newTab?: boolean; + background?: boolean; + keys?: string, + }; + + private keys: string[]; + + private producer: HintKeyProducer | null; + + constructor(win: Window, store: any) { this.win = win; this.store = store; this.state = {}; this.keys = []; this.producer = null; - messages.onMessage(this.onMessage.bind(this)); + new MessageListener().onWebMessage(this.onMessage.bind(this)); store.subscribe(() => { this.update(); }); } - onMessage(message, sender) { + onMessage(message: messages.Message, sender: WebMessageSender) { switch (message.type) { case messages.FOLLOW_START: return this.store.dispatch( @@ -36,7 +51,7 @@ export default class FollowController { } } - update() { + update(): void { let prevState = this.state; this.state = this.store.getState().followController; @@ -49,8 +64,10 @@ export default class FollowController { } } - updateHints() { - let shown = this.keys.filter(key => key.startsWith(this.state.keys)); + updateHints(): void { + let shown = this.keys.filter((key) => { + return key.startsWith(this.state.keys as string); + }); if (shown.length === 1) { this.activate(); this.store.dispatch(followControllerActions.disable()); @@ -58,18 +75,18 @@ export default class FollowController { broadcastMessage(this.win, { type: messages.FOLLOW_SHOW_HINTS, - keys: this.state.keys, + keys: this.state.keys as string, }); } - activate() { + activate(): void { broadcastMessage(this.win, { type: messages.FOLLOW_ACTIVATE, - keys: this.state.keys, + keys: this.state.keys as string, }); } - keyPress(key, ctrlKey) { + keyPress(key: string, ctrlKey: boolean): boolean { if (key === '[' && ctrlKey) { this.store.dispatch(followControllerActions.disable()); return true; @@ -107,25 +124,28 @@ export default class FollowController { viewSize: { width: viewWidth, height: viewHeight }, framePosition: { x: 0, y: 0 }, }), '*'); - frameElements.forEach((element) => { - let { left: frameX, top: frameY } = element.getBoundingClientRect(); + frameElements.forEach((ele) => { + let { left: frameX, top: frameY } = ele.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, '*'); + if (ele instanceof HTMLFrameElement && ele.contentWindow || + ele instanceof HTMLIFrameElement && ele.contentWindow) { + ele.contentWindow.postMessage(message, '*'); + } }); } - create(count, sender) { + create(count: number, sender: WebMessageSender) { let produced = []; for (let i = 0; i < count; ++i) { - produced.push(this.producer.produce()); + produced.push((this.producer as HintKeyProducer).produce()); } this.keys = this.keys.concat(produced); - sender.postMessage(JSON.stringify({ + (sender as Window).postMessage(JSON.stringify({ type: messages.FOLLOW_CREATE_HINTS, keysArray: produced, newTab: this.state.newTab, @@ -141,7 +161,6 @@ export default class FollowController { } hintchars() { - return this.store.getState().setting.properties.hintchars || - properties.defaults.hintchars; + return this.store.getState().setting.properties.hintchars; } } diff --git a/src/content/components/top-content/index.js b/src/content/components/top-content/index.ts index 1aaef1b..ac95ea9 100644 --- a/src/content/components/top-content/index.js +++ b/src/content/components/top-content/index.ts @@ -2,33 +2,43 @@ 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'; +import * as messages from '../../../shared/messages'; +import MessageListener from '../../MessageListener'; +import * as scrolls from '../../scrolls'; export default class TopContent { + private win: Window; - constructor(win, store) { + private store: any; + + constructor(win: Window, store: any) { 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 + new FindComponent(store); // eslint-disable-line no-new // TODO make component consoleFrames.initialize(this.win.document); - messages.onMessage(this.onMessage.bind(this)); + new MessageListener().onWebMessage(this.onWebMessage.bind(this)); + new MessageListener().onBackgroundMessage( + this.onBackgroundMessage.bind(this)); } - onMessage(message) { - let addonState = this.store.getState().addon; - + onWebMessage(message: messages.Message) { switch (message.type) { case messages.CONSOLE_UNFOCUS: this.win.focus(); consoleFrames.blur(window.document); - return Promise.resolve(); + } + } + + onBackgroundMessage(message: messages.Message) { + let addonState = this.store.getState().addon; + + switch (message.type) { case messages.ADDON_ENABLED_QUERY: return Promise.resolve({ type: messages.ADDON_ENABLED_RESPONSE, diff --git a/src/content/console-frames.js b/src/content/console-frames.ts index ecb5a87..bd6b835 100644 --- a/src/content/console-frames.js +++ b/src/content/console-frames.ts @@ -1,6 +1,6 @@ -import messages from 'shared/messages'; +import * as messages from '../shared/messages'; -const initialize = (doc) => { +const initialize = (doc: Document): HTMLIFrameElement => { let iframe = doc.createElement('iframe'); iframe.src = browser.runtime.getURL('build/console.html'); iframe.id = 'vimvixen-console-frame'; @@ -10,13 +10,13 @@ const initialize = (doc) => { return iframe; }; -const blur = (doc) => { - let iframe = doc.getElementById('vimvixen-console-frame'); - iframe.blur(); +const blur = (doc: Document) => { + let ele = doc.getElementById('vimvixen-console-frame') as HTMLIFrameElement; + ele.blur(); }; -const postError = (text) => { - browser.runtime.sendMessage({ +const postError = (text: string): Promise<any> => { + return browser.runtime.sendMessage({ type: messages.CONSOLE_FRAME_MESSAGE, message: { type: messages.CONSOLE_SHOW_ERROR, @@ -25,8 +25,8 @@ const postError = (text) => { }); }; -const postInfo = (text) => { - browser.runtime.sendMessage({ +const postInfo = (text: string): Promise<any> => { + return browser.runtime.sendMessage({ type: messages.CONSOLE_FRAME_MESSAGE, message: { type: messages.CONSOLE_SHOW_INFO, diff --git a/src/content/focuses.js b/src/content/focuses.ts index a6f6cc8..8f53881 100644 --- a/src/content/focuses.js +++ b/src/content/focuses.ts @@ -1,11 +1,13 @@ -import * as doms from 'shared/utils/dom'; +import * as doms from '../shared/utils/dom'; -const focusInput = () => { +const focusInput = (): void => { 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) { + if (target instanceof HTMLInputElement) { + target.focus(); + } else if (target instanceof HTMLTextAreaElement) { target.focus(); } }; diff --git a/src/content/hint-key-producer.js b/src/content/hint-key-producer.ts index 14b23b6..935394e 100644 --- a/src/content/hint-key-producer.js +++ b/src/content/hint-key-producer.ts @@ -1,5 +1,9 @@ export default class HintKeyProducer { - constructor(charset) { + private charset: string; + + private counter: number[]; + + constructor(charset: string) { if (charset.length === 0) { throw new TypeError('charset is empty'); } @@ -8,13 +12,13 @@ export default class HintKeyProducer { this.counter = []; } - produce() { + produce(): string { this.increment(); return this.counter.map(x => this.charset[x]).join(''); } - increment() { + private increment(): void { let max = this.charset.length - 1; if (this.counter.every(x => x === max)) { this.counter = new Array(this.counter.length + 1).fill(0); diff --git a/src/content/index.js b/src/content/index.ts index 9edb712..9d791fc 100644 --- a/src/content/index.js +++ b/src/content/index.ts @@ -1,14 +1,9 @@ -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'; +import { newStore } from './store'; -const store = createStore( - reducers, - applyMiddleware(promise), -); +const store = newStore(); if (window.self === window.top) { new TopContentComponent(window, store); // eslint-disable-line no-new @@ -17,5 +12,5 @@ if (window.self === window.top) { } let style = window.document.createElement('style'); -style.textContent = consoleFrameStyle.default; +style.textContent = consoleFrameStyle; window.document.head.appendChild(style); diff --git a/src/content/navigates.js b/src/content/navigates.ts index c9baa30..a2007a6 100644 --- a/src/content/navigates.js +++ b/src/content/navigates.ts @@ -1,58 +1,63 @@ -const REL_PATTERN = { +const REL_PATTERN: {[key: string]: RegExp} = { prev: /^(?:prev(?:ious)?|older)\b|\u2039|\u2190|\xab|\u226a|<</i, next: /^(?:next|newer)\b|\u203a|\u2192|\xbb|\u226b|>>/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); +// eslint-disable-next-line func-style +function selectLast<E extends Element>( + win: Window, + selector: string, + filter?: (e: E) => boolean, +): E | null { + let nodes = Array.from( + win.document.querySelectorAll(selector) as NodeListOf<E> + ); if (filter) { - nodes = Array.from(nodes).filter(filter); + nodes = nodes.filter(filter); } - return nodes.length ? nodes[nodes.length - 1] : null; -}; +} -const historyPrev = (win) => { +const historyPrev = (win: Window): void => { win.history.back(); }; -const historyNext = (win) => { +const historyNext = (win: Window): void => { 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]`); - +const linkRel = (win: Window, rel: string): void => { + let link = selectLast<HTMLLinkElement>(win, `link[rel~=${rel}][href]`); if (link) { - win.location = link.href; + win.location.href = link.href; return; } const pattern = REL_PATTERN[rel]; - link = selectLast(win, `a[rel~=${rel}][href]`) || + let a = selectLast<HTMLAnchorElement>(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(); + if (a) { + a.click(); } }; -const linkPrev = (win) => { +const linkPrev = (win: Window): void => { linkRel(win, 'prev'); }; -const linkNext = (win) => { +const linkNext = (win: Window): void => { linkRel(win, 'next'); }; -const parent = (win) => { +const parent = (win: Window): void => { const loc = win.location; if (loc.hash !== '') { loc.hash = ''; @@ -71,8 +76,8 @@ const parent = (win) => { } }; -const root = (win) => { - win.location = win.location.origin; +const root = (win: Window): void => { + win.location.href = 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..2131228 --- /dev/null +++ b/src/content/reducers/addon.ts @@ -0,0 +1,22 @@ +import * as actions from '../actions'; + +export interface State { + enabled: boolean; +} + +const defaultState: State = { + enabled: true, +}; + +export default function reducer( + state: State = defaultState, + action: actions.AddonAction, +): State { + 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..8c3e637 --- /dev/null +++ b/src/content/reducers/find.ts @@ -0,0 +1,25 @@ +import * as actions from '../actions'; + +export interface State { + keyword: string | null; + found: boolean; +} + +const defaultState: State = { + keyword: null, + found: false, +}; + +export default function reducer( + state: State = defaultState, + action: actions.FindAction, +): State { + 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.ts index 5869c47..6965704 100644 --- a/src/content/reducers/follow-controller.js +++ b/src/content/reducers/follow-controller.ts @@ -1,13 +1,23 @@ -import actions from 'content/actions'; +import * as actions from '../actions'; -const defaultState = { +export interface State { + enabled: boolean; + newTab: boolean; + background: boolean; + keys: string, +} + +const defaultState: State = { enabled: false, newTab: false, background: false, keys: '', }; -export default function reducer(state = defaultState, action = {}) { +export default function reducer( + state: State = defaultState, + action: actions.FollowAction, +): State { switch (action.type) { case actions.FOLLOW_CONTROLLER_ENABLE: 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..fb5eb84 --- /dev/null +++ b/src/content/reducers/index.ts @@ -0,0 +1,21 @@ +import { combineReducers } from 'redux'; +import addon, { State as AddonState } from './addon'; +import find, { State as FindState } from './find'; +import setting, { State as SettingState } from './setting'; +import input, { State as InputState } from './input'; +import followController, { State as FollowControllerState } + from './follow-controller'; +import mark, { State as MarkState } from './mark'; + +export interface State { + addon: AddonState; + find: FindState; + setting: SettingState; + input: InputState; + followController: FollowControllerState; + mark: MarkState; +} + +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..35b9075 --- /dev/null +++ b/src/content/reducers/input.ts @@ -0,0 +1,26 @@ +import * as actions from '../actions'; +import * as keyUtils from '../../shared/utils/keys'; + +export interface State { + keys: keyUtils.Key[], +} + +const defaultState: State = { + keys: [] +}; + +export default function reducer( + state: State = defaultState, + action: actions.InputAction, +): State { + 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.ts index 2c96cc5..7409938 100644 --- a/src/content/reducers/mark.js +++ b/src/content/reducers/mark.ts @@ -1,12 +1,22 @@ -import actions from 'content/actions'; +import Mark from '../Mark'; +import * as actions from '../actions'; -const defaultState = { +export interface State { + setMode: boolean; + jumpMode: boolean; + marks: { [key: string]: Mark }; +} + +const defaultState: State = { setMode: false, jumpMode: false, marks: {}, }; -export default function reducer(state = defaultState, action = {}) { +export default function reducer( + state: State = defaultState, + action: actions.MarkAction, +): State { switch (action.type) { case actions.MARK_START_SET: return { ...state, setMode: true }; 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..9ca1380 --- /dev/null +++ b/src/content/reducers/setting.ts @@ -0,0 +1,40 @@ +import * as actions from '../actions'; +import * as keyUtils from '../../shared/utils/keys'; +import * as operations from '../../shared/operations'; +import { Search, Properties, DefaultSetting } from '../../shared/Settings'; + +export interface State { + keymaps: { key: keyUtils.Key[], op: operations.Operation }[]; + search: Search; + properties: Properties; +} + +// defaultState does not refer due to the state is load from +// background on load. +const defaultState: State = { + keymaps: [], + search: DefaultSetting.search, + properties: DefaultSetting.properties, +}; + +export default function reducer( + state: State = defaultState, + action: actions.SettingAction, +): State { + switch (action.type) { + case actions.SETTING_SET: + return { + keymaps: Object.entries(action.settings.keymaps).map((entry) => { + return { + key: keyUtils.fromMapKeys(entry[0]), + op: entry[1], + }; + }), + properties: action.settings.properties, + search: action.settings.search, + }; + default: + return state; + } +} + diff --git a/src/content/scrolls.js b/src/content/scrolls.ts index f3124a1..6a35315 100644 --- a/src/content/scrolls.js +++ b/src/content/scrolls.ts @@ -1,19 +1,19 @@ -import * as doms from 'shared/utils/dom'; +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; +let lastTimeoutId: number | null = null; -const isScrollableStyle = (element) => { +const isScrollableStyle = (element: Element): boolean => { let { overflowX, overflowY } = window.getComputedStyle(element); return !(overflowX !== 'scroll' && overflowX !== 'auto' && overflowY !== 'scroll' && overflowY !== 'auto'); }; -const isOverflowed = (element) => { +const isOverflowed = (element: Element): boolean => { return element.scrollWidth > element.clientWidth || element.scrollHeight > element.clientHeight; }; @@ -22,7 +22,7 @@ const isOverflowed = (element) => { // 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) => { +const findScrollable = (element: Element): Element | null => { if (isScrollableStyle(element) && isOverflowed(element)) { return element; } @@ -56,12 +56,16 @@ const resetScrolling = () => { }; class Scroller { - constructor(element, smooth) { + private element: Element; + + private smooth: boolean; + + constructor(element: Element, smooth: boolean) { this.element = element; this.smooth = smooth; } - scrollTo(x, y) { + scrollTo(x: number, y: number): void { if (!this.smooth) { this.element.scrollTo(x, y); return; @@ -74,13 +78,13 @@ class Scroller { this.prepareReset(); } - scrollBy(x, y) { + scrollBy(x: number, y: number): void { let left = this.element.scrollLeft + x; let top = this.element.scrollTop + y; this.scrollTo(left, top); } - prepareReset() { + prepareReset(): void { scrolling = true; if (lastTimeoutId) { clearTimeout(lastTimeoutId); @@ -90,22 +94,12 @@ class Scroller { } } -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) => { +const scrollVertically = (count: number, smooth: boolean): void => { let target = scrollTarget(); let delta = SCROLL_DELTA_Y * count; if (scrolling) { @@ -114,7 +108,7 @@ const scrollVertically = (count, smooth) => { new Scroller(target, smooth).scrollBy(0, delta); }; -const scrollHorizonally = (count, smooth) => { +const scrollHorizonally = (count: number, smooth: boolean): void => { let target = scrollTarget(); let delta = SCROLL_DELTA_X * count; if (scrolling) { @@ -123,7 +117,7 @@ const scrollHorizonally = (count, smooth) => { new Scroller(target, smooth).scrollBy(delta, 0); }; -const scrollPages = (count, smooth) => { +const scrollPages = (count: number, smooth: boolean): void => { let target = scrollTarget(); let height = target.clientHeight; let delta = height * count; @@ -133,33 +127,33 @@ const scrollPages = (count, smooth) => { new Scroller(target, smooth).scrollBy(0, delta); }; -const scrollTo = (x, y, smooth) => { +const scrollTo = (x: number, y: number, smooth: boolean): void => { let target = scrollTarget(); new Scroller(target, smooth).scrollTo(x, y); }; -const scrollToTop = (smooth) => { +const scrollToTop = (smooth: boolean): void => { let target = scrollTarget(); let x = target.scrollLeft; let y = 0; new Scroller(target, smooth).scrollTo(x, y); }; -const scrollToBottom = (smooth) => { +const scrollToBottom = (smooth: boolean): void => { let target = scrollTarget(); let x = target.scrollLeft; let y = target.scrollHeight; new Scroller(target, smooth).scrollTo(x, y); }; -const scrollToHome = (smooth) => { +const scrollToHome = (smooth: boolean): void => { let target = scrollTarget(); let x = 0; let y = target.scrollTop; new Scroller(target, smooth).scrollTo(x, y); }; -const scrollToEnd = (smooth) => { +const scrollToEnd = (smooth: boolean): void => { let target = scrollTarget(); let x = target.scrollWidth; let y = target.scrollTop; diff --git a/src/content/site-style.js b/src/content/site-style.ts index e7a82a5..0c335fc 100644 --- a/src/content/site-style.js +++ b/src/content/site-style.ts @@ -1,4 +1,4 @@ -exports.default = ` +export default ` .vimvixen-console-frame { margin: 0; padding: 0; diff --git a/src/content/store/index.ts b/src/content/store/index.ts new file mode 100644 index 0000000..5c41744 --- /dev/null +++ b/src/content/store/index.ts @@ -0,0 +1,8 @@ +import promise from 'redux-promise'; +import reducers from '../reducers'; +import { createStore, applyMiddleware } from 'redux'; + +export const newStore = () => createStore( + reducers, + applyMiddleware(promise), +); diff --git a/src/content/urls.js b/src/content/urls.ts index 6e7ea31..035b9bb 100644 --- a/src/content/urls.js +++ b/src/content/urls.ts @@ -1,7 +1,8 @@ -import messages from 'shared/messages'; +import * as messages from '../shared/messages'; import * as urls from '../shared/urls'; +import { Search } from '../shared/Settings'; -const yank = (win) => { +const yank = (win: Window) => { let input = win.document.createElement('input'); win.document.body.append(input); @@ -15,7 +16,7 @@ const yank = (win) => { input.remove(); }; -const paste = (win, newTab, searchSettings) => { +const paste = (win: Window, newTab: boolean, search: Search) => { let textarea = win.document.createElement('textarea'); win.document.body.append(textarea); @@ -25,8 +26,8 @@ const paste = (win, newTab, searchSettings) => { textarea.focus(); if (win.document.execCommand('paste')) { - let value = textarea.textContent; - let url = urls.searchUrl(value, searchSettings); + let value = textarea.textContent as string; + let url = urls.searchUrl(value, search); browser.runtime.sendMessage({ type: messages.OPEN_URL, url, |