diff options
author | Shin'ya Ueoka <ueokande@i-beam.org> | 2019-05-02 14:08:51 +0900 |
---|---|---|
committer | Shin'ya Ueoka <ueokande@i-beam.org> | 2019-05-05 23:59:59 +0900 |
commit | d01db82c0dca352de2d7644c383d388fc3ec0366 (patch) | |
tree | ff09ebc545ce00192dff9e1119b0c9e2cf92fdef /src/content | |
parent | 992b3ac65d7fe86ac7bc3b560960c8bcf86bec69 (diff) |
Types src/content
Diffstat (limited to 'src/content')
33 files changed, 605 insertions, 287 deletions
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.ts b/src/content/actions/addon.ts index b30cf16..8dedae0 100644 --- a/src/content/actions/addon.ts +++ b/src/content/actions/addon.ts @@ -1,11 +1,11 @@ -import messages from 'shared/messages'; -import actions from 'content/actions'; +import * as messages from '../../shared/messages'; +import * as actions from './index'; -const enable = () => setEnabled(true); +const enable = (): Promise<actions.AddonAction> => setEnabled(true); -const disable = () => setEnabled(false); +const disable = (): Promise<actions.AddonAction> => setEnabled(false); -const setEnabled = async(enabled) => { +const setEnabled = async(enabled: boolean): Promise<actions.AddonAction> => { await browser.runtime.sendMessage({ type: messages.ADDON_ENABLED_RESPONSE, enabled, diff --git a/src/content/actions/find.ts b/src/content/actions/find.ts index e08d7e5..6dd2ae6 100644 --- a/src/content/actions/find.ts +++ b/src/content/actions/find.ts @@ -5,28 +5,41 @@ // 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 messages from '../../shared/messages'; +import * as actions from './index'; import * as consoleFrames from '../console-frames'; -const find = (string, backwards) => { +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 - let found = window.find(string, caseSensitive, backwards, wrapScan); + + // eslint-disable-next-line no-extra-parens + let found = (<any>window).find(str, caseSensitive, backwards, wrapScan); if (found) { return found; } - window.getSelection().removeAllRanges(); - return window.find(string, caseSensitive, backwards, wrapScan); + let sel = window.getSelection(); + if (sel) { + sel.removeAllRanges(); + } + + // eslint-disable-next-line no-extra-parens + return (<any>window).find(str, caseSensitive, backwards, wrapScan); }; -const findNext = async(currentKeyword, reset, backwards) => { +// eslint-disable-next-line max-statements +const findNext = async( + currentKeyword: string, reset: boolean, backwards: boolean, +): Promise<actions.FindAction> => { if (reset) { - window.getSelection().removeAllRanges(); + let sel = window.getSelection(); + if (sel) { + sel.removeAllRanges(); + } } let keyword = currentKeyword; @@ -41,7 +54,8 @@ const findNext = async(currentKeyword, reset, backwards) => { }); } if (!keyword) { - return consoleFrames.postError('No previous search keywords'); + await consoleFrames.postError('No previous search keywords'); + return { type: actions.NOOP }; } let found = find(keyword, backwards); if (found) { @@ -57,11 +71,15 @@ const findNext = async(currentKeyword, reset, backwards) => { }; }; -const next = (currentKeyword, reset) => { +const next = ( + currentKeyword: string, reset: boolean, +): Promise<actions.FindAction> => { return findNext(currentKeyword, reset, false); }; -const prev = (currentKeyword, reset) => { +const prev = ( + currentKeyword: string, reset: boolean, +): Promise<actions.FindAction> => { return findNext(currentKeyword, reset, true); }; diff --git a/src/content/actions/follow-controller.ts b/src/content/actions/follow-controller.ts index 006b248..115b3b6 100644 --- a/src/content/actions/follow-controller.ts +++ 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.ts b/src/content/actions/index.ts index 0a16fdf..18d0a69 100644 --- a/src/content/actions/index.ts +++ b/src/content/actions/index.ts @@ -1,31 +1,120 @@ -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', -}; +import Redux from 'redux'; + +// 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; + value: any; +} + +export interface InputKeyPressAction extends Redux.Action { + type: typeof INPUT_KEY_PRESS; + key: string; +} + +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.ts b/src/content/actions/input.ts index 465a486..21c912e 100644 --- a/src/content/actions/input.ts +++ b/src/content/actions/input.ts @@ -1,13 +1,13 @@ -import actions from 'content/actions'; +import * as actions from './index'; -const keyPress = (key) => { +const keyPress = (key: string): actions.InputAction => { return { type: actions.INPUT_KEY_PRESS, key, }; }; -const clearKeys = () => { +const clearKeys = (): actions.InputAction => { return { type: actions.INPUT_CLEAR_KEYS }; diff --git a/src/content/actions/mark.ts b/src/content/actions/mark.ts index 712a811..5eb9554 100644 --- a/src/content/actions/mark.ts +++ b/src/content/actions/mark.ts @@ -1,19 +1,19 @@ -import actions from 'content/actions'; -import messages from 'shared/messages'; +import * as actions from './index'; +import * as messages from '../../shared/messages'; -const startSet = () => { +const startSet = (): actions.MarkAction => { return { type: actions.MARK_START_SET }; }; -const startJump = () => { +const startJump = (): actions.MarkAction => { return { type: actions.MARK_START_JUMP }; }; -const cancel = () => { +const cancel = (): actions.MarkAction => { return { type: actions.MARK_CANCEL }; }; -const setLocal = (key, x, y) => { +const setLocal = (key: string, x: number, y: number): actions.MarkAction => { return { type: actions.MARK_SET_LOCAL, key, @@ -22,22 +22,22 @@ const setLocal = (key, x, y) => { }; }; -const setGlobal = (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: '' }; + return { type: actions.NOOP }; }; -const jumpGlobal = (key) => { +const jumpGlobal = (key: string): actions.MarkAction => { browser.runtime.sendMessage({ type: messages.MARK_JUMP_GLOBAL, key, }); - return { type: '' }; + return { type: actions.NOOP }; }; export { diff --git a/src/content/actions/operation.ts b/src/content/actions/operation.ts index ed9b2cf..6acb407 100644 --- a/src/content/actions/operation.ts +++ b/src/content/actions/operation.ts @@ -1,16 +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'; +import * as properties from '../../shared/settings/properties'; // eslint-disable-next-line complexity, max-lines-per-function -const exec = (operation, settings, addonEnabled) => { +const exec = ( + operation: operations.Operation, + settings: any, + addonEnabled: boolean, +): Promise<actions.Action> | actions.Action => { let smoothscroll = settings.properties.smoothscroll || properties.defaults.smoothscroll; switch (operation.type) { @@ -98,7 +103,7 @@ const exec = (operation, settings, addonEnabled) => { operation, }); } - return { type: '' }; + return { type: actions.NOOP }; }; export { exec }; diff --git a/src/content/actions/setting.ts b/src/content/actions/setting.ts index 1c15dd7..a8f049a 100644 --- a/src/content/actions/setting.ts +++ b/src/content/actions/setting.ts @@ -1,15 +1,15 @@ -import actions from 'content/actions'; -import * as keyUtils from 'shared/utils/keys'; -import operations from 'shared/operations'; -import messages from 'shared/messages'; +import * as actions from './index'; +import * as keyUtils from '../../shared/utils/keys'; +import * as operations from '../../shared/operations'; +import * as messages from '../../shared/messages'; const reservedKeymaps = { '<Esc>': { type: operations.CANCEL }, '<C-[>': { type: operations.CANCEL }, }; -const set = (value) => { - let entries = []; +const set = (value: any): actions.SettingAction => { + let entries: any[] = []; if (value.keymaps) { let keymaps = { ...value.keymaps, ...reservedKeymaps }; entries = Object.entries(keymaps).map((entry) => { @@ -27,7 +27,7 @@ const set = (value) => { }; }; -const load = async() => { +const load = async(): Promise<actions.SettingAction> => { let settings = await browser.runtime.sendMessage({ type: messages.SETTINGS_QUERY, }); diff --git a/src/content/components/common/follow.ts b/src/content/components/common/follow.ts index 63ce603..67f2dd9 100644 --- a/src/content/components/common/follow.ts +++ 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.ts b/src/content/components/common/hint.ts index 1472587..2fcbb0f 100644 --- a/src/content/components/common/hint.ts +++ 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.ts b/src/content/components/common/index.ts index bcab4fa..9b5164e 100644 --- a/src/content/components/common/index.ts +++ b/src/content/components/common/index.ts @@ -2,33 +2,37 @@ 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 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 blacklists from '../../../shared/blacklists'; +import * as keys from '../../../shared/utils/keys'; export default class Common { - constructor(win, store) { - const input = new InputComponent(win.document.body, store); - const follow = new FollowComponent(win, store); + 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(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)); + 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.prevEnabled = undefined; - this.prevBlacklist = undefined; this.reloadSettings(); - messages.onMessage(this.onMessage.bind(this)); + new MessageListener().onBackgroundMessage(this.onMessage.bind(this)); } - onMessage(message) { + onMessage(message: messages.Message) { let { enabled } = this.store.getState().addon; switch (message.type) { case messages.SETTINGS_CHANGED: @@ -40,12 +44,13 @@ export default class Common { 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)); - }); + this.store.dispatch(settingActions.load()) + .then(({ value: settings }: any) => { + 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); diff --git a/src/content/components/common/input.ts b/src/content/components/common/input.ts index eefaf10..64eb5f3 100644 --- a/src/content/components/common/input.ts +++ 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; } @@ -63,13 +72,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.ts b/src/content/components/common/keymapper.ts index ec0d093..d9c9834 100644 --- a/src/content/components/common/keymapper.ts +++ b/src/content/components/common/keymapper.ts @@ -1,7 +1,7 @@ -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) => { if (mapping.length < keys.length) { diff --git a/src/content/components/common/mark.ts b/src/content/components/common/mark.ts index 0f838a9..500d03b 100644 --- a/src/content/components/common/mark.ts +++ b/src/content/components/common/mark.ts @@ -3,7 +3,7 @@ import * as scrolls from 'content/scrolls'; import * as consoleFrames from 'content/console-frames'; import * as properties from 'shared/settings/properties'; -const cancelKey = (key) => { +const cancelKey = (key): boolean => { return key.key === 'Esc' || key.key === '[' && key.ctrlKey; }; diff --git a/src/content/components/top-content/find.ts b/src/content/components/top-content/find.ts index 4d46d79..74b95bc 100644 --- a/src/content/components/top-content/find.ts +++ b/src/content/components/top-content/find.ts @@ -1,15 +1,17 @@ -import * as findActions from 'content/actions/find'; -import messages from 'shared/messages'; +import * as findActions from '../../actions/find'; +import * as messages from '../../../shared/messages'; +import MessageListener from '../../MessageListener'; export default class FindComponent { - constructor(win, store) { - this.win = win; + private store: any; + + constructor(store: any) { this.store = store; - messages.onMessage(this.onMessage.bind(this)); + new MessageListener().onWebMessage(this.onMessage.bind(this)); } - onMessage(message) { + onMessage(message: messages.Message) { switch (message.type) { case messages.CONSOLE_ENTER_FIND: return this.start(message.text); @@ -20,22 +22,25 @@ export default class FindComponent { } } - start(text) { + start(text: string) { 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(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, false)); + 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, false)); + return this.store.dispatch( + findActions.prev(state.keyword as string, false)); } } diff --git a/src/content/components/top-content/follow-controller.ts b/src/content/components/top-content/follow-controller.ts index 7f36604..be71f6e 100644 --- a/src/content/components/top-content/follow-controller.ts +++ b/src/content/components/top-content/follow-controller.ts @@ -1,30 +1,46 @@ -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'; +import * as properties from '../../../shared/settings/properties'; -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 +52,7 @@ export default class FollowController { } } - update() { + update(): void { let prevState = this.state; this.state = this.store.getState().followController; @@ -49,8 +65,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 +76,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 +125,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, diff --git a/src/content/components/top-content/index.ts b/src/content/components/top-content/index.ts index 1aaef1b..ac95ea9 100644 --- a/src/content/components/top-content/index.ts +++ 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.ts b/src/content/console-frames.ts index ecb5a87..bd6b835 100644 --- a/src/content/console-frames.ts +++ 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.ts b/src/content/focuses.ts index a6f6cc8..8f53881 100644 --- a/src/content/focuses.ts +++ 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.ts b/src/content/hint-key-producer.ts index 14b23b6..935394e 100644 --- a/src/content/hint-key-producer.ts +++ 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.ts b/src/content/index.ts index 9edb712..309f27f 100644 --- a/src/content/index.ts +++ 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 diff --git a/src/content/navigates.ts b/src/content/navigates.ts index c9baa30..a2007a6 100644 --- a/src/content/navigates.ts +++ 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.ts b/src/content/reducers/addon.ts index 0def55a..2131228 100644 --- a/src/content/reducers/addon.ts +++ b/src/content/reducers/addon.ts @@ -1,10 +1,17 @@ -import actions from 'content/actions'; +import * as actions from '../actions'; -const defaultState = { +export interface State { + enabled: boolean; +} + +const defaultState: State = { enabled: true, }; -export default function reducer(state = defaultState, action = {}) { +export default function reducer( + state: State = defaultState, + action: actions.AddonAction, +): State { switch (action.type) { case actions.ADDON_SET_ENABLED: return { ...state, diff --git a/src/content/reducers/find.ts b/src/content/reducers/find.ts index 4560e2c..8c3e637 100644 --- a/src/content/reducers/find.ts +++ b/src/content/reducers/find.ts @@ -1,11 +1,19 @@ -import actions from 'content/actions'; +import * as actions from '../actions'; -const defaultState = { +export interface State { + keyword: string | null; + found: boolean; +} + +const defaultState: State = { keyword: null, found: false, }; -export default function reducer(state = defaultState, action = {}) { +export default function reducer( + state: State = defaultState, + action: actions.FindAction, +): State { switch (action.type) { case actions.FIND_SET_KEYWORD: return { ...state, diff --git a/src/content/reducers/follow-controller.ts b/src/content/reducers/follow-controller.ts index 5869c47..6965704 100644 --- a/src/content/reducers/follow-controller.ts +++ 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.ts b/src/content/reducers/index.ts index bf612a3..fb5eb84 100644 --- a/src/content/reducers/index.ts +++ b/src/content/reducers/index.ts @@ -1,10 +1,20 @@ 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'; +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.ts b/src/content/reducers/input.ts index 23e7dd2..6257e49 100644 --- a/src/content/reducers/input.ts +++ b/src/content/reducers/input.ts @@ -1,10 +1,17 @@ -import actions from 'content/actions'; +import * as actions from '../actions'; -const defaultState = { +export interface State { + keys: string[]; +} + +const defaultState: State = { keys: [] }; -export default function reducer(state = defaultState, action = {}) { +export default function reducer( + state: State = defaultState, + action: actions.InputAction, +): State { switch (action.type) { case actions.INPUT_KEY_PRESS: return { ...state, diff --git a/src/content/reducers/mark.ts b/src/content/reducers/mark.ts index 2c96cc5..e78b7b9 100644 --- a/src/content/reducers/mark.ts +++ b/src/content/reducers/mark.ts @@ -1,12 +1,26 @@ -import actions from 'content/actions'; +import * as actions from '../actions'; -const defaultState = { +interface Mark { + x: number; + y: number; +} + +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.ts b/src/content/reducers/setting.ts index a49db6d..fa8e8ee 100644 --- a/src/content/reducers/setting.ts +++ b/src/content/reducers/setting.ts @@ -1,11 +1,18 @@ -import actions from 'content/actions'; +import * as actions from '../actions'; + +export interface State { + keymaps: any[]; +} const defaultState = { // keymaps is and arrays of key-binding pairs, which is entries of Map keymaps: [], }; -export default function reducer(state = defaultState, action = {}) { +export default function reducer( + state: State = defaultState, + action: actions.SettingAction, +): State { switch (action.type) { case actions.SETTING_SET: return { ...action.value }; diff --git a/src/content/scrolls.ts b/src/content/scrolls.ts index bbf2491..6a35315 100644 --- a/src/content/scrolls.ts +++ 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); @@ -95,7 +99,7 @@ const getScroll = () => { 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) { @@ -104,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) { @@ -113,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; @@ -123,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/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.ts b/src/content/urls.ts index 6e7ea31..390efde 100644 --- a/src/content/urls.ts +++ b/src/content/urls.ts @@ -1,7 +1,7 @@ -import messages from 'shared/messages'; +import * as messages from '../shared/messages'; import * as urls from '../shared/urls'; -const yank = (win) => { +const yank = (win: Window) => { let input = win.document.createElement('input'); win.document.body.append(input); @@ -15,7 +15,7 @@ const yank = (win) => { input.remove(); }; -const paste = (win, newTab, searchSettings) => { +const paste = (win: Window, newTab: boolean, searchSettings: any) => { let textarea = win.document.createElement('textarea'); win.document.body.append(textarea); @@ -25,7 +25,7 @@ const paste = (win, newTab, searchSettings) => { textarea.focus(); if (win.document.execCommand('paste')) { - let value = textarea.textContent; + let value = textarea.textContent as string; let url = urls.searchUrl(value, searchSettings); browser.runtime.sendMessage({ type: messages.OPEN_URL, |