aboutsummaryrefslogtreecommitdiff
path: root/src/content
diff options
context:
space:
mode:
Diffstat (limited to 'src/content')
-rw-r--r--src/content/Mark.ts6
-rw-r--r--src/content/MessageListener.ts32
-rw-r--r--src/content/actions/addon.js19
-rw-r--r--src/content/actions/addon.ts19
-rw-r--r--src/content/actions/find.js68
-rw-r--r--src/content/actions/find.ts100
-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.js31
-rw-r--r--src/content/actions/index.ts122
-rw-r--r--src/content/actions/input.js16
-rw-r--r--src/content/actions/input.ts17
-rw-r--r--src/content/actions/mark.js46
-rw-r--r--src/content/actions/mark.ts46
-rw-r--r--src/content/actions/operation.ts (renamed from src/content/actions/operation.js)27
-rw-r--r--src/content/actions/setting.js37
-rw-r--r--src/content/actions/setting.ts28
-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.js55
-rw-r--r--src/content/components/common/index.ts61
-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.js74
-rw-r--r--src/content/components/common/mark.ts79
-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.js41
-rw-r--r--src/content/components/top-content/find.ts46
-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.js15
-rw-r--r--src/content/reducers/addon.ts22
-rw-r--r--src/content/reducers/find.js17
-rw-r--r--src/content/reducers/find.ts25
-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.js11
-rw-r--r--src/content/reducers/index.ts21
-rw-r--r--src/content/reducers/input.js18
-rw-r--r--src/content/reducers/input.ts26
-rw-r--r--src/content/reducers/mark.ts (renamed from src/content/reducers/mark.js)16
-rw-r--r--src/content/reducers/setting.js16
-rw-r--r--src/content/reducers/setting.ts40
-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.ts8
-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,