From c60d0e7392fc708e961614d6b756a045de74f458 Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka
Date: Tue, 30 Apr 2019 14:00:07 +0900
Subject: Rename .js/.jsx to .ts/.tsx
---
.../controllers/AddonEnabledController.js | 11 --
.../controllers/AddonEnabledController.ts | 11 ++
src/background/controllers/CommandController.js | 99 ----------
src/background/controllers/CommandController.ts | 99 ++++++++++
src/background/controllers/FindController.js | 15 --
src/background/controllers/FindController.ts | 15 ++
src/background/controllers/LinkController.js | 15 --
src/background/controllers/LinkController.ts | 15 ++
src/background/controllers/MarkController.js | 15 --
src/background/controllers/MarkController.ts | 15 ++
src/background/controllers/OperationController.js | 77 --------
src/background/controllers/OperationController.ts | 77 ++++++++
src/background/controllers/SettingController.js | 18 --
src/background/controllers/SettingController.ts | 18 ++
src/background/controllers/VersionController.js | 11 --
src/background/controllers/VersionController.ts | 11 ++
src/background/controllers/version.js | 13 --
src/background/controllers/version.ts | 13 ++
src/background/domains/CommandDocs.js | 12 --
src/background/domains/CommandDocs.ts | 12 ++
src/background/domains/CompletionGroup.js | 14 --
src/background/domains/CompletionGroup.ts | 14 ++
src/background/domains/CompletionItem.js | 24 ---
src/background/domains/CompletionItem.ts | 24 +++
src/background/domains/Completions.js | 27 ---
src/background/domains/Completions.ts | 27 +++
src/background/domains/GlobalMark.js | 24 ---
src/background/domains/GlobalMark.ts | 24 +++
src/background/domains/Setting.js | 51 -----
src/background/domains/Setting.ts | 51 +++++
src/background/index.js | 23 ---
src/background/index.ts | 23 +++
src/background/infrastructures/ConsoleClient.js | 37 ----
src/background/infrastructures/ConsoleClient.ts | 37 ++++
.../infrastructures/ContentMessageClient.js | 36 ----
.../infrastructures/ContentMessageClient.ts | 36 ++++
.../infrastructures/ContentMessageListener.js | 135 --------------
.../infrastructures/ContentMessageListener.ts | 135 ++++++++++++++
src/background/infrastructures/MemoryStorage.js | 19 --
src/background/infrastructures/MemoryStorage.ts | 19 ++
src/background/presenters/IndicatorPresenter.js | 12 --
src/background/presenters/IndicatorPresenter.ts | 12 ++
src/background/presenters/NotifyPresenter.js | 23 ---
src/background/presenters/NotifyPresenter.ts | 23 +++
src/background/presenters/TabPresenter.js | 102 ----------
src/background/presenters/TabPresenter.ts | 102 ++++++++++
src/background/presenters/WindowPresenter.js | 5 -
src/background/presenters/WindowPresenter.ts | 5 +
src/background/repositories/BookmarkRepository.js | 13 --
src/background/repositories/BookmarkRepository.ts | 13 ++
.../repositories/BrowserSettingRepository.js | 8 -
.../repositories/BrowserSettingRepository.ts | 8 +
.../repositories/CompletionsRepository.js | 31 ----
.../repositories/CompletionsRepository.ts | 31 ++++
src/background/repositories/FindRepository.js | 19 --
src/background/repositories/FindRepository.ts | 19 ++
src/background/repositories/MarkRepository.js | 33 ----
src/background/repositories/MarkRepository.ts | 33 ++++
.../repositories/PersistentSettingRepository.js | 12 --
.../repositories/PersistentSettingRepository.ts | 12 ++
src/background/repositories/SettingRepository.js | 23 ---
src/background/repositories/SettingRepository.ts | 23 +++
src/background/repositories/VersionRepository.js | 10 -
src/background/repositories/VersionRepository.ts | 10 +
src/background/usecases/AddonEnabledUseCase.js | 29 ---
src/background/usecases/AddonEnabledUseCase.ts | 29 +++
src/background/usecases/CommandUseCase.js | 125 -------------
src/background/usecases/CommandUseCase.ts | 125 +++++++++++++
src/background/usecases/CompletionsUseCase.js | 205 ---------------------
src/background/usecases/CompletionsUseCase.ts | 205 +++++++++++++++++++++
src/background/usecases/ConsoleUseCase.js | 61 ------
src/background/usecases/ConsoleUseCase.ts | 61 ++++++
src/background/usecases/FindUseCase.js | 24 ---
src/background/usecases/FindUseCase.ts | 24 +++
src/background/usecases/LinkUseCase.js | 19 --
src/background/usecases/LinkUseCase.ts | 19 ++
src/background/usecases/MarkUseCase.js | 39 ----
src/background/usecases/MarkUseCase.ts | 39 ++++
src/background/usecases/SettingUseCase.js | 28 ---
src/background/usecases/SettingUseCase.ts | 28 +++
src/background/usecases/TabSelectUseCase.js | 51 -----
src/background/usecases/TabSelectUseCase.ts | 51 +++++
src/background/usecases/TabUseCase.js | 77 --------
src/background/usecases/TabUseCase.ts | 77 ++++++++
src/background/usecases/VersionUseCase.js | 26 ---
src/background/usecases/VersionUseCase.ts | 26 +++
src/background/usecases/ZoomUseCase.js | 35 ----
src/background/usecases/ZoomUseCase.ts | 35 ++++
src/background/usecases/filters.js | 72 --------
src/background/usecases/filters.ts | 72 ++++++++
src/background/usecases/parsers.js | 31 ----
src/background/usecases/parsers.ts | 31 ++++
src/console/actions/console.js | 96 ----------
src/console/actions/console.ts | 96 ++++++++++
src/console/actions/index.js | 13 --
src/console/actions/index.ts | 13 ++
src/console/components/Console.jsx | 149 ---------------
src/console/components/Console.tsx | 149 +++++++++++++++
src/console/components/console/Completion.jsx | 86 ---------
src/console/components/console/Completion.tsx | 86 +++++++++
src/console/components/console/CompletionItem.jsx | 28 ---
src/console/components/console/CompletionItem.tsx | 28 +++
src/console/components/console/CompletionTitle.jsx | 14 --
src/console/components/console/CompletionTitle.tsx | 14 ++
src/console/components/console/Input.jsx | 43 -----
src/console/components/console/Input.tsx | 43 +++++
src/console/components/console/Message.jsx | 25 ---
src/console/components/console/Message.tsx | 25 +++
src/console/index.jsx | 42 -----
src/console/index.tsx | 42 +++++
src/console/reducers/index.js | 102 ----------
src/console/reducers/index.ts | 102 ++++++++++
src/content/actions/addon.js | 19 --
src/content/actions/addon.ts | 19 ++
src/content/actions/find.js | 68 -------
src/content/actions/find.ts | 68 +++++++
src/content/actions/follow-controller.js | 30 ---
src/content/actions/follow-controller.ts | 30 +++
src/content/actions/index.js | 31 ----
src/content/actions/index.ts | 31 ++++
src/content/actions/input.js | 16 --
src/content/actions/input.ts | 16 ++
src/content/actions/mark.js | 46 -----
src/content/actions/mark.ts | 46 +++++
src/content/actions/operation.js | 104 -----------
src/content/actions/operation.ts | 104 +++++++++++
src/content/actions/setting.js | 37 ----
src/content/actions/setting.ts | 37 ++++
src/content/components/common/follow.js | 200 --------------------
src/content/components/common/follow.ts | 200 ++++++++++++++++++++
src/content/components/common/hint.js | 49 -----
src/content/components/common/hint.ts | 49 +++++
src/content/components/common/index.js | 55 ------
src/content/components/common/index.ts | 55 ++++++
src/content/components/common/input.js | 75 --------
src/content/components/common/input.ts | 75 ++++++++
src/content/components/common/keymapper.js | 58 ------
src/content/components/common/keymapper.ts | 58 ++++++
src/content/components/common/mark.js | 74 --------
src/content/components/common/mark.ts | 74 ++++++++
src/content/components/frame-content.js | 3 -
src/content/components/frame-content.ts | 3 +
src/content/components/top-content/find.js | 41 -----
src/content/components/top-content/find.ts | 41 +++++
.../components/top-content/follow-controller.js | 147 ---------------
.../components/top-content/follow-controller.ts | 147 +++++++++++++++
src/content/components/top-content/index.js | 41 -----
src/content/components/top-content/index.ts | 41 +++++
src/content/console-frames.js | 38 ----
src/content/console-frames.ts | 38 ++++
src/content/focuses.js | 13 --
src/content/focuses.ts | 13 ++
src/content/hint-key-producer.js | 33 ----
src/content/hint-key-producer.ts | 33 ++++
src/content/index.js | 21 ---
src/content/index.ts | 21 +++
src/content/navigates.js | 78 --------
src/content/navigates.ts | 78 ++++++++
src/content/reducers/addon.js | 15 --
src/content/reducers/addon.ts | 15 ++
src/content/reducers/find.js | 17 --
src/content/reducers/find.ts | 17 ++
src/content/reducers/follow-controller.js | 30 ---
src/content/reducers/follow-controller.ts | 30 +++
src/content/reducers/index.js | 11 --
src/content/reducers/index.ts | 11 ++
src/content/reducers/input.js | 18 --
src/content/reducers/input.ts | 18 ++
src/content/reducers/mark.js | 25 ---
src/content/reducers/mark.ts | 25 +++
src/content/reducers/setting.js | 16 --
src/content/reducers/setting.ts | 16 ++
src/content/scrolls.js | 174 -----------------
src/content/scrolls.ts | 174 +++++++++++++++++
src/content/site-style.js | 26 ---
src/content/site-style.ts | 26 +++
src/content/urls.js | 40 ----
src/content/urls.ts | 40 ++++
src/settings/actions/index.js | 7 -
src/settings/actions/index.ts | 7 +
src/settings/actions/setting.js | 63 -------
src/settings/actions/setting.ts | 63 +++++++
src/settings/components/form/BlacklistForm.jsx | 63 -------
src/settings/components/form/BlacklistForm.tsx | 63 +++++++
src/settings/components/form/KeymapsForm.jsx | 51 -----
src/settings/components/form/KeymapsForm.tsx | 51 +++++
src/settings/components/form/PropertiesForm.jsx | 65 -------
src/settings/components/form/PropertiesForm.tsx | 65 +++++++
src/settings/components/form/SearchForm.jsx | 92 ---------
src/settings/components/form/SearchForm.tsx | 92 +++++++++
src/settings/components/index.jsx | 153 ---------------
src/settings/components/index.tsx | 153 +++++++++++++++
src/settings/components/ui/AddButton.jsx | 12 --
src/settings/components/ui/AddButton.tsx | 12 ++
src/settings/components/ui/DeleteButton.jsx | 12 --
src/settings/components/ui/DeleteButton.tsx | 12 ++
src/settings/components/ui/Input.jsx | 60 ------
src/settings/components/ui/Input.tsx | 60 ++++++
src/settings/index.jsx | 22 ---
src/settings/index.tsx | 22 +++
src/settings/keymaps.js | 74 --------
src/settings/keymaps.ts | 74 ++++++++
src/settings/reducers/setting.js | 35 ----
src/settings/reducers/setting.ts | 35 ++++
src/shared/blacklists.js | 13 --
src/shared/blacklists.ts | 13 ++
src/shared/messages.js | 71 -------
src/shared/messages.ts | 71 +++++++
src/shared/operations.js | 78 --------
src/shared/operations.ts | 78 ++++++++
src/shared/settings/default.js | 85 ---------
src/shared/settings/default.ts | 85 +++++++++
src/shared/settings/properties.js | 24 ---
src/shared/settings/properties.ts | 24 +++
src/shared/settings/storage.js | 32 ----
src/shared/settings/storage.ts | 32 ++++
src/shared/settings/validator.js | 76 --------
src/shared/settings/validator.ts | 76 ++++++++
src/shared/settings/values.js | 108 -----------
src/shared/settings/values.ts | 108 +++++++++++
src/shared/urls.js | 43 -----
src/shared/urls.ts | 43 +++++
src/shared/utils/dom.js | 108 -----------
src/shared/utils/dom.ts | 108 +++++++++++
src/shared/utils/keys.js | 89 ---------
src/shared/utils/keys.ts | 89 +++++++++
src/shared/utils/re.js | 6 -
src/shared/utils/re.ts | 6 +
228 files changed, 5478 insertions(+), 5478 deletions(-)
delete mode 100644 src/background/controllers/AddonEnabledController.js
create mode 100644 src/background/controllers/AddonEnabledController.ts
delete mode 100644 src/background/controllers/CommandController.js
create mode 100644 src/background/controllers/CommandController.ts
delete mode 100644 src/background/controllers/FindController.js
create mode 100644 src/background/controllers/FindController.ts
delete mode 100644 src/background/controllers/LinkController.js
create mode 100644 src/background/controllers/LinkController.ts
delete mode 100644 src/background/controllers/MarkController.js
create mode 100644 src/background/controllers/MarkController.ts
delete mode 100644 src/background/controllers/OperationController.js
create mode 100644 src/background/controllers/OperationController.ts
delete mode 100644 src/background/controllers/SettingController.js
create mode 100644 src/background/controllers/SettingController.ts
delete mode 100644 src/background/controllers/VersionController.js
create mode 100644 src/background/controllers/VersionController.ts
delete mode 100644 src/background/controllers/version.js
create mode 100644 src/background/controllers/version.ts
delete mode 100644 src/background/domains/CommandDocs.js
create mode 100644 src/background/domains/CommandDocs.ts
delete mode 100644 src/background/domains/CompletionGroup.js
create mode 100644 src/background/domains/CompletionGroup.ts
delete mode 100644 src/background/domains/CompletionItem.js
create mode 100644 src/background/domains/CompletionItem.ts
delete mode 100644 src/background/domains/Completions.js
create mode 100644 src/background/domains/Completions.ts
delete mode 100644 src/background/domains/GlobalMark.js
create mode 100644 src/background/domains/GlobalMark.ts
delete mode 100644 src/background/domains/Setting.js
create mode 100644 src/background/domains/Setting.ts
delete mode 100644 src/background/index.js
create mode 100644 src/background/index.ts
delete mode 100644 src/background/infrastructures/ConsoleClient.js
create mode 100644 src/background/infrastructures/ConsoleClient.ts
delete mode 100644 src/background/infrastructures/ContentMessageClient.js
create mode 100644 src/background/infrastructures/ContentMessageClient.ts
delete mode 100644 src/background/infrastructures/ContentMessageListener.js
create mode 100644 src/background/infrastructures/ContentMessageListener.ts
delete mode 100644 src/background/infrastructures/MemoryStorage.js
create mode 100644 src/background/infrastructures/MemoryStorage.ts
delete mode 100644 src/background/presenters/IndicatorPresenter.js
create mode 100644 src/background/presenters/IndicatorPresenter.ts
delete mode 100644 src/background/presenters/NotifyPresenter.js
create mode 100644 src/background/presenters/NotifyPresenter.ts
delete mode 100644 src/background/presenters/TabPresenter.js
create mode 100644 src/background/presenters/TabPresenter.ts
delete mode 100644 src/background/presenters/WindowPresenter.js
create mode 100644 src/background/presenters/WindowPresenter.ts
delete mode 100644 src/background/repositories/BookmarkRepository.js
create mode 100644 src/background/repositories/BookmarkRepository.ts
delete mode 100644 src/background/repositories/BrowserSettingRepository.js
create mode 100644 src/background/repositories/BrowserSettingRepository.ts
delete mode 100644 src/background/repositories/CompletionsRepository.js
create mode 100644 src/background/repositories/CompletionsRepository.ts
delete mode 100644 src/background/repositories/FindRepository.js
create mode 100644 src/background/repositories/FindRepository.ts
delete mode 100644 src/background/repositories/MarkRepository.js
create mode 100644 src/background/repositories/MarkRepository.ts
delete mode 100644 src/background/repositories/PersistentSettingRepository.js
create mode 100644 src/background/repositories/PersistentSettingRepository.ts
delete mode 100644 src/background/repositories/SettingRepository.js
create mode 100644 src/background/repositories/SettingRepository.ts
delete mode 100644 src/background/repositories/VersionRepository.js
create mode 100644 src/background/repositories/VersionRepository.ts
delete mode 100644 src/background/usecases/AddonEnabledUseCase.js
create mode 100644 src/background/usecases/AddonEnabledUseCase.ts
delete mode 100644 src/background/usecases/CommandUseCase.js
create mode 100644 src/background/usecases/CommandUseCase.ts
delete mode 100644 src/background/usecases/CompletionsUseCase.js
create mode 100644 src/background/usecases/CompletionsUseCase.ts
delete mode 100644 src/background/usecases/ConsoleUseCase.js
create mode 100644 src/background/usecases/ConsoleUseCase.ts
delete mode 100644 src/background/usecases/FindUseCase.js
create mode 100644 src/background/usecases/FindUseCase.ts
delete mode 100644 src/background/usecases/LinkUseCase.js
create mode 100644 src/background/usecases/LinkUseCase.ts
delete mode 100644 src/background/usecases/MarkUseCase.js
create mode 100644 src/background/usecases/MarkUseCase.ts
delete mode 100644 src/background/usecases/SettingUseCase.js
create mode 100644 src/background/usecases/SettingUseCase.ts
delete mode 100644 src/background/usecases/TabSelectUseCase.js
create mode 100644 src/background/usecases/TabSelectUseCase.ts
delete mode 100644 src/background/usecases/TabUseCase.js
create mode 100644 src/background/usecases/TabUseCase.ts
delete mode 100644 src/background/usecases/VersionUseCase.js
create mode 100644 src/background/usecases/VersionUseCase.ts
delete mode 100644 src/background/usecases/ZoomUseCase.js
create mode 100644 src/background/usecases/ZoomUseCase.ts
delete mode 100644 src/background/usecases/filters.js
create mode 100644 src/background/usecases/filters.ts
delete mode 100644 src/background/usecases/parsers.js
create mode 100644 src/background/usecases/parsers.ts
delete mode 100644 src/console/actions/console.js
create mode 100644 src/console/actions/console.ts
delete mode 100644 src/console/actions/index.js
create mode 100644 src/console/actions/index.ts
delete mode 100644 src/console/components/Console.jsx
create mode 100644 src/console/components/Console.tsx
delete mode 100644 src/console/components/console/Completion.jsx
create mode 100644 src/console/components/console/Completion.tsx
delete mode 100644 src/console/components/console/CompletionItem.jsx
create mode 100644 src/console/components/console/CompletionItem.tsx
delete mode 100644 src/console/components/console/CompletionTitle.jsx
create mode 100644 src/console/components/console/CompletionTitle.tsx
delete mode 100644 src/console/components/console/Input.jsx
create mode 100644 src/console/components/console/Input.tsx
delete mode 100644 src/console/components/console/Message.jsx
create mode 100644 src/console/components/console/Message.tsx
delete mode 100644 src/console/index.jsx
create mode 100644 src/console/index.tsx
delete mode 100644 src/console/reducers/index.js
create mode 100644 src/console/reducers/index.ts
delete mode 100644 src/content/actions/addon.js
create mode 100644 src/content/actions/addon.ts
delete mode 100644 src/content/actions/find.js
create mode 100644 src/content/actions/find.ts
delete mode 100644 src/content/actions/follow-controller.js
create mode 100644 src/content/actions/follow-controller.ts
delete mode 100644 src/content/actions/index.js
create mode 100644 src/content/actions/index.ts
delete mode 100644 src/content/actions/input.js
create mode 100644 src/content/actions/input.ts
delete mode 100644 src/content/actions/mark.js
create mode 100644 src/content/actions/mark.ts
delete mode 100644 src/content/actions/operation.js
create mode 100644 src/content/actions/operation.ts
delete mode 100644 src/content/actions/setting.js
create mode 100644 src/content/actions/setting.ts
delete mode 100644 src/content/components/common/follow.js
create mode 100644 src/content/components/common/follow.ts
delete mode 100644 src/content/components/common/hint.js
create mode 100644 src/content/components/common/hint.ts
delete mode 100644 src/content/components/common/index.js
create mode 100644 src/content/components/common/index.ts
delete mode 100644 src/content/components/common/input.js
create mode 100644 src/content/components/common/input.ts
delete mode 100644 src/content/components/common/keymapper.js
create mode 100644 src/content/components/common/keymapper.ts
delete mode 100644 src/content/components/common/mark.js
create mode 100644 src/content/components/common/mark.ts
delete mode 100644 src/content/components/frame-content.js
create mode 100644 src/content/components/frame-content.ts
delete mode 100644 src/content/components/top-content/find.js
create mode 100644 src/content/components/top-content/find.ts
delete mode 100644 src/content/components/top-content/follow-controller.js
create mode 100644 src/content/components/top-content/follow-controller.ts
delete mode 100644 src/content/components/top-content/index.js
create mode 100644 src/content/components/top-content/index.ts
delete mode 100644 src/content/console-frames.js
create mode 100644 src/content/console-frames.ts
delete mode 100644 src/content/focuses.js
create mode 100644 src/content/focuses.ts
delete mode 100644 src/content/hint-key-producer.js
create mode 100644 src/content/hint-key-producer.ts
delete mode 100644 src/content/index.js
create mode 100644 src/content/index.ts
delete mode 100644 src/content/navigates.js
create mode 100644 src/content/navigates.ts
delete mode 100644 src/content/reducers/addon.js
create mode 100644 src/content/reducers/addon.ts
delete mode 100644 src/content/reducers/find.js
create mode 100644 src/content/reducers/find.ts
delete mode 100644 src/content/reducers/follow-controller.js
create mode 100644 src/content/reducers/follow-controller.ts
delete mode 100644 src/content/reducers/index.js
create mode 100644 src/content/reducers/index.ts
delete mode 100644 src/content/reducers/input.js
create mode 100644 src/content/reducers/input.ts
delete mode 100644 src/content/reducers/mark.js
create mode 100644 src/content/reducers/mark.ts
delete mode 100644 src/content/reducers/setting.js
create mode 100644 src/content/reducers/setting.ts
delete mode 100644 src/content/scrolls.js
create mode 100644 src/content/scrolls.ts
delete mode 100644 src/content/site-style.js
create mode 100644 src/content/site-style.ts
delete mode 100644 src/content/urls.js
create mode 100644 src/content/urls.ts
delete mode 100644 src/settings/actions/index.js
create mode 100644 src/settings/actions/index.ts
delete mode 100644 src/settings/actions/setting.js
create mode 100644 src/settings/actions/setting.ts
delete mode 100644 src/settings/components/form/BlacklistForm.jsx
create mode 100644 src/settings/components/form/BlacklistForm.tsx
delete mode 100644 src/settings/components/form/KeymapsForm.jsx
create mode 100644 src/settings/components/form/KeymapsForm.tsx
delete mode 100644 src/settings/components/form/PropertiesForm.jsx
create mode 100644 src/settings/components/form/PropertiesForm.tsx
delete mode 100644 src/settings/components/form/SearchForm.jsx
create mode 100644 src/settings/components/form/SearchForm.tsx
delete mode 100644 src/settings/components/index.jsx
create mode 100644 src/settings/components/index.tsx
delete mode 100644 src/settings/components/ui/AddButton.jsx
create mode 100644 src/settings/components/ui/AddButton.tsx
delete mode 100644 src/settings/components/ui/DeleteButton.jsx
create mode 100644 src/settings/components/ui/DeleteButton.tsx
delete mode 100644 src/settings/components/ui/Input.jsx
create mode 100644 src/settings/components/ui/Input.tsx
delete mode 100644 src/settings/index.jsx
create mode 100644 src/settings/index.tsx
delete mode 100644 src/settings/keymaps.js
create mode 100644 src/settings/keymaps.ts
delete mode 100644 src/settings/reducers/setting.js
create mode 100644 src/settings/reducers/setting.ts
delete mode 100644 src/shared/blacklists.js
create mode 100644 src/shared/blacklists.ts
delete mode 100644 src/shared/messages.js
create mode 100644 src/shared/messages.ts
delete mode 100644 src/shared/operations.js
create mode 100644 src/shared/operations.ts
delete mode 100644 src/shared/settings/default.js
create mode 100644 src/shared/settings/default.ts
delete mode 100644 src/shared/settings/properties.js
create mode 100644 src/shared/settings/properties.ts
delete mode 100644 src/shared/settings/storage.js
create mode 100644 src/shared/settings/storage.ts
delete mode 100644 src/shared/settings/validator.js
create mode 100644 src/shared/settings/validator.ts
delete mode 100644 src/shared/settings/values.js
create mode 100644 src/shared/settings/values.ts
delete mode 100644 src/shared/urls.js
create mode 100644 src/shared/urls.ts
delete mode 100644 src/shared/utils/dom.js
create mode 100644 src/shared/utils/dom.ts
delete mode 100644 src/shared/utils/keys.js
create mode 100644 src/shared/utils/keys.ts
delete mode 100644 src/shared/utils/re.js
create mode 100644 src/shared/utils/re.ts
(limited to 'src')
diff --git a/src/background/controllers/AddonEnabledController.js b/src/background/controllers/AddonEnabledController.js
deleted file mode 100644
index 9a3a521..0000000
--- a/src/background/controllers/AddonEnabledController.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import AddonEnabledUseCase from '../usecases/AddonEnabledUseCase';
-
-export default class AddonEnabledController {
- constructor() {
- this.addonEnabledUseCase = new AddonEnabledUseCase();
- }
-
- indicate(enabled) {
- return this.addonEnabledUseCase.indicate(enabled);
- }
-}
diff --git a/src/background/controllers/AddonEnabledController.ts b/src/background/controllers/AddonEnabledController.ts
new file mode 100644
index 0000000..9a3a521
--- /dev/null
+++ b/src/background/controllers/AddonEnabledController.ts
@@ -0,0 +1,11 @@
+import AddonEnabledUseCase from '../usecases/AddonEnabledUseCase';
+
+export default class AddonEnabledController {
+ constructor() {
+ this.addonEnabledUseCase = new AddonEnabledUseCase();
+ }
+
+ indicate(enabled) {
+ return this.addonEnabledUseCase.indicate(enabled);
+ }
+}
diff --git a/src/background/controllers/CommandController.js b/src/background/controllers/CommandController.js
deleted file mode 100644
index b113709..0000000
--- a/src/background/controllers/CommandController.js
+++ /dev/null
@@ -1,99 +0,0 @@
-import CompletionsUseCase from '../usecases/CompletionsUseCase';
-import CommandUseCase from '../usecases/CommandUseCase';
-import Completions from '../domains/Completions';
-
-const trimStart = (str) => {
- // NOTE String.trimStart is available on Firefox 61
- return str.replace(/^\s+/, '');
-};
-
-export default class CommandController {
- constructor() {
- this.completionsUseCase = new CompletionsUseCase();
- this.commandIndicator = new CommandUseCase();
- }
-
- getCompletions(line) {
- let trimmed = trimStart(line);
- let words = trimmed.split(/ +/);
- let name = words[0];
- if (words.length === 1) {
- return this.completionsUseCase.queryConsoleCommand(name);
- }
- let keywords = trimStart(trimmed.slice(name.length));
- switch (words[0]) {
- case 'o':
- case 'open':
- case 't':
- case 'tabopen':
- case 'w':
- case 'winopen':
- return this.completionsUseCase.queryOpen(name, keywords);
- case 'b':
- case 'buffer':
- return this.completionsUseCase.queryBuffer(name, keywords);
- case 'bd':
- case 'bdel':
- case 'bdelete':
- case 'bdeletes':
- return this.completionsUseCase.queryBdelete(name, keywords);
- case 'bd!':
- case 'bdel!':
- case 'bdelete!':
- case 'bdeletes!':
- return this.completionsUseCase.queryBdeleteForce(name, keywords);
- case 'set':
- return this.completionsUseCase.querySet(name, keywords);
- }
- return Promise.resolve(Completions.empty());
- }
-
- // eslint-disable-next-line complexity
- exec(line) {
- let trimmed = trimStart(line);
- let words = trimmed.split(/ +/);
- let name = words[0];
- if (words[0].length === 0) {
- return Promise.resolve();
- }
-
- let keywords = trimStart(trimmed.slice(name.length));
- switch (words[0]) {
- case 'o':
- case 'open':
- return this.commandIndicator.open(keywords);
- case 't':
- case 'tabopen':
- return this.commandIndicator.tabopen(keywords);
- case 'w':
- case 'winopen':
- return this.commandIndicator.winopen(keywords);
- case 'b':
- case 'buffer':
- return this.commandIndicator.buffer(keywords);
- case 'bd':
- case 'bdel':
- case 'bdelete':
- return this.commandIndicator.bdelete(false, keywords);
- case 'bd!':
- case 'bdel!':
- case 'bdelete!':
- return this.commandIndicator.bdelete(true, keywords);
- case 'bdeletes':
- return this.commandIndicator.bdeletes(false, keywords);
- case 'bdeletes!':
- return this.commandIndicator.bdeletes(true, keywords);
- case 'addbookmark':
- return this.commandIndicator.addbookmark(keywords);
- case 'q':
- case 'quit':
- return this.commandIndicator.quit();
- case 'qa':
- case 'quitall':
- return this.commandIndicator.quitAll();
- case 'set':
- return this.commandIndicator.set(keywords);
- }
- throw new Error(words[0] + ' command is not defined');
- }
-}
diff --git a/src/background/controllers/CommandController.ts b/src/background/controllers/CommandController.ts
new file mode 100644
index 0000000..b113709
--- /dev/null
+++ b/src/background/controllers/CommandController.ts
@@ -0,0 +1,99 @@
+import CompletionsUseCase from '../usecases/CompletionsUseCase';
+import CommandUseCase from '../usecases/CommandUseCase';
+import Completions from '../domains/Completions';
+
+const trimStart = (str) => {
+ // NOTE String.trimStart is available on Firefox 61
+ return str.replace(/^\s+/, '');
+};
+
+export default class CommandController {
+ constructor() {
+ this.completionsUseCase = new CompletionsUseCase();
+ this.commandIndicator = new CommandUseCase();
+ }
+
+ getCompletions(line) {
+ let trimmed = trimStart(line);
+ let words = trimmed.split(/ +/);
+ let name = words[0];
+ if (words.length === 1) {
+ return this.completionsUseCase.queryConsoleCommand(name);
+ }
+ let keywords = trimStart(trimmed.slice(name.length));
+ switch (words[0]) {
+ case 'o':
+ case 'open':
+ case 't':
+ case 'tabopen':
+ case 'w':
+ case 'winopen':
+ return this.completionsUseCase.queryOpen(name, keywords);
+ case 'b':
+ case 'buffer':
+ return this.completionsUseCase.queryBuffer(name, keywords);
+ case 'bd':
+ case 'bdel':
+ case 'bdelete':
+ case 'bdeletes':
+ return this.completionsUseCase.queryBdelete(name, keywords);
+ case 'bd!':
+ case 'bdel!':
+ case 'bdelete!':
+ case 'bdeletes!':
+ return this.completionsUseCase.queryBdeleteForce(name, keywords);
+ case 'set':
+ return this.completionsUseCase.querySet(name, keywords);
+ }
+ return Promise.resolve(Completions.empty());
+ }
+
+ // eslint-disable-next-line complexity
+ exec(line) {
+ let trimmed = trimStart(line);
+ let words = trimmed.split(/ +/);
+ let name = words[0];
+ if (words[0].length === 0) {
+ return Promise.resolve();
+ }
+
+ let keywords = trimStart(trimmed.slice(name.length));
+ switch (words[0]) {
+ case 'o':
+ case 'open':
+ return this.commandIndicator.open(keywords);
+ case 't':
+ case 'tabopen':
+ return this.commandIndicator.tabopen(keywords);
+ case 'w':
+ case 'winopen':
+ return this.commandIndicator.winopen(keywords);
+ case 'b':
+ case 'buffer':
+ return this.commandIndicator.buffer(keywords);
+ case 'bd':
+ case 'bdel':
+ case 'bdelete':
+ return this.commandIndicator.bdelete(false, keywords);
+ case 'bd!':
+ case 'bdel!':
+ case 'bdelete!':
+ return this.commandIndicator.bdelete(true, keywords);
+ case 'bdeletes':
+ return this.commandIndicator.bdeletes(false, keywords);
+ case 'bdeletes!':
+ return this.commandIndicator.bdeletes(true, keywords);
+ case 'addbookmark':
+ return this.commandIndicator.addbookmark(keywords);
+ case 'q':
+ case 'quit':
+ return this.commandIndicator.quit();
+ case 'qa':
+ case 'quitall':
+ return this.commandIndicator.quitAll();
+ case 'set':
+ return this.commandIndicator.set(keywords);
+ }
+ throw new Error(words[0] + ' command is not defined');
+ }
+}
diff --git a/src/background/controllers/FindController.js b/src/background/controllers/FindController.js
deleted file mode 100644
index caeff98..0000000
--- a/src/background/controllers/FindController.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import FindUseCase from '../usecases/FindUseCase';
-
-export default class FindController {
- constructor() {
- this.findUseCase = new FindUseCase();
- }
-
- getKeyword() {
- return this.findUseCase.getKeyword();
- }
-
- setKeyword(keyword) {
- return this.findUseCase.setKeyword(keyword);
- }
-}
diff --git a/src/background/controllers/FindController.ts b/src/background/controllers/FindController.ts
new file mode 100644
index 0000000..caeff98
--- /dev/null
+++ b/src/background/controllers/FindController.ts
@@ -0,0 +1,15 @@
+import FindUseCase from '../usecases/FindUseCase';
+
+export default class FindController {
+ constructor() {
+ this.findUseCase = new FindUseCase();
+ }
+
+ getKeyword() {
+ return this.findUseCase.getKeyword();
+ }
+
+ setKeyword(keyword) {
+ return this.findUseCase.setKeyword(keyword);
+ }
+}
diff --git a/src/background/controllers/LinkController.js b/src/background/controllers/LinkController.js
deleted file mode 100644
index 7e395b1..0000000
--- a/src/background/controllers/LinkController.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import LinkUseCase from '../usecases/LinkUseCase';
-
-export default class LinkController {
- constructor() {
- this.linkUseCase = new LinkUseCase();
- }
-
- openToTab(url, tabId) {
- this.linkUseCase.openToTab(url, tabId);
- }
-
- openNewTab(url, openerId, background) {
- this.linkUseCase.openNewTab(url, openerId, background);
- }
-}
diff --git a/src/background/controllers/LinkController.ts b/src/background/controllers/LinkController.ts
new file mode 100644
index 0000000..7e395b1
--- /dev/null
+++ b/src/background/controllers/LinkController.ts
@@ -0,0 +1,15 @@
+import LinkUseCase from '../usecases/LinkUseCase';
+
+export default class LinkController {
+ constructor() {
+ this.linkUseCase = new LinkUseCase();
+ }
+
+ openToTab(url, tabId) {
+ this.linkUseCase.openToTab(url, tabId);
+ }
+
+ openNewTab(url, openerId, background) {
+ this.linkUseCase.openNewTab(url, openerId, background);
+ }
+}
diff --git a/src/background/controllers/MarkController.js b/src/background/controllers/MarkController.js
deleted file mode 100644
index 0478369..0000000
--- a/src/background/controllers/MarkController.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import MarkUseCase from '../usecases/MarkUseCase';
-
-export default class MarkController {
- constructor() {
- this.markUseCase = new MarkUseCase();
- }
-
- setGlobal(key, x, y) {
- this.markUseCase.setGlobal(key, x, y);
- }
-
- jumpGlobal(key) {
- this.markUseCase.jumpGlobal(key);
- }
-}
diff --git a/src/background/controllers/MarkController.ts b/src/background/controllers/MarkController.ts
new file mode 100644
index 0000000..0478369
--- /dev/null
+++ b/src/background/controllers/MarkController.ts
@@ -0,0 +1,15 @@
+import MarkUseCase from '../usecases/MarkUseCase';
+
+export default class MarkController {
+ constructor() {
+ this.markUseCase = new MarkUseCase();
+ }
+
+ setGlobal(key, x, y) {
+ this.markUseCase.setGlobal(key, x, y);
+ }
+
+ jumpGlobal(key) {
+ this.markUseCase.jumpGlobal(key);
+ }
+}
diff --git a/src/background/controllers/OperationController.js b/src/background/controllers/OperationController.js
deleted file mode 100644
index 416aa9c..0000000
--- a/src/background/controllers/OperationController.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import operations from '../../shared/operations';
-import FindUseCase from '../usecases/FindUseCase';
-import ConsoleUseCase from '../usecases/ConsoleUseCase';
-import TabUseCase from '../usecases/TabUseCase';
-import TabSelectUseCase from '../usecases/TabSelectUseCase';
-import ZoomUseCase from '../usecases/ZoomUseCase';
-
-export default class OperationController {
- constructor() {
- this.findUseCase = new FindUseCase();
- this.consoleUseCase = new ConsoleUseCase();
- this.tabUseCase = new TabUseCase();
- this.tabSelectUseCase = new TabSelectUseCase();
- this.zoomUseCase = new ZoomUseCase();
- }
-
- // eslint-disable-next-line complexity, max-lines-per-function
- exec(operation) {
- switch (operation.type) {
- case operations.TAB_CLOSE:
- return this.tabUseCase.close(false);
- case operations.TAB_CLOSE_RIGHT:
- return this.tabUseCase.closeRight();
- case operations.TAB_CLOSE_FORCE:
- return this.tabUseCase.close(true);
- case operations.TAB_REOPEN:
- return this.tabUseCase.reopen();
- case operations.TAB_PREV:
- return this.tabSelectUseCase.selectPrev(1);
- case operations.TAB_NEXT:
- return this.tabSelectUseCase.selectNext(1);
- case operations.TAB_FIRST:
- return this.tabSelectUseCase.selectFirst();
- case operations.TAB_LAST:
- return this.tabSelectUseCase.selectLast();
- case operations.TAB_PREV_SEL:
- return this.tabSelectUseCase.selectPrevSelected();
- case operations.TAB_RELOAD:
- return this.tabUseCase.reload(operation.cache);
- case operations.TAB_PIN:
- return this.tabUseCase.setPinned(true);
- case operations.TAB_UNPIN:
- return this.tabUseCase.setPinned(false);
- case operations.TAB_TOGGLE_PINNED:
- return this.tabUseCase.togglePinned();
- case operations.TAB_DUPLICATE:
- return this.tabUseCase.duplicate();
- case operations.PAGE_SOURCE:
- return this.tabUseCase.openPageSource();
- case operations.PAGE_HOME:
- return this.tabUseCase.openHome(operation.newTab);
- case operations.ZOOM_IN:
- return this.zoomUseCase.zoomIn();
- case operations.ZOOM_OUT:
- return this.zoomUseCase.zoomOut();
- case operations.ZOOM_NEUTRAL:
- return this.zoomUseCase.zoomNutoral();
- case operations.COMMAND_SHOW:
- return this.consoleUseCase.showCommand();
- case operations.COMMAND_SHOW_OPEN:
- return this.consoleUseCase.showOpenCommand(operation.alter);
- case operations.COMMAND_SHOW_TABOPEN:
- return this.consoleUseCase.showTabopenCommand(operation.alter);
- case operations.COMMAND_SHOW_WINOPEN:
- return this.consoleUseCase.showWinopenCommand(operation.alter);
- case operations.COMMAND_SHOW_BUFFER:
- return this.consoleUseCase.showBufferCommand();
- case operations.COMMAND_SHOW_ADDBOOKMARK:
- return this.consoleUseCase.showAddbookmarkCommand(operation.alter);
- case operations.FIND_START:
- return this.findUseCase.findStart();
- case operations.CANCEL:
- return this.consoleUseCase.hideConsole();
- }
- }
-}
-
diff --git a/src/background/controllers/OperationController.ts b/src/background/controllers/OperationController.ts
new file mode 100644
index 0000000..416aa9c
--- /dev/null
+++ b/src/background/controllers/OperationController.ts
@@ -0,0 +1,77 @@
+import operations from '../../shared/operations';
+import FindUseCase from '../usecases/FindUseCase';
+import ConsoleUseCase from '../usecases/ConsoleUseCase';
+import TabUseCase from '../usecases/TabUseCase';
+import TabSelectUseCase from '../usecases/TabSelectUseCase';
+import ZoomUseCase from '../usecases/ZoomUseCase';
+
+export default class OperationController {
+ constructor() {
+ this.findUseCase = new FindUseCase();
+ this.consoleUseCase = new ConsoleUseCase();
+ this.tabUseCase = new TabUseCase();
+ this.tabSelectUseCase = new TabSelectUseCase();
+ this.zoomUseCase = new ZoomUseCase();
+ }
+
+ // eslint-disable-next-line complexity, max-lines-per-function
+ exec(operation) {
+ switch (operation.type) {
+ case operations.TAB_CLOSE:
+ return this.tabUseCase.close(false);
+ case operations.TAB_CLOSE_RIGHT:
+ return this.tabUseCase.closeRight();
+ case operations.TAB_CLOSE_FORCE:
+ return this.tabUseCase.close(true);
+ case operations.TAB_REOPEN:
+ return this.tabUseCase.reopen();
+ case operations.TAB_PREV:
+ return this.tabSelectUseCase.selectPrev(1);
+ case operations.TAB_NEXT:
+ return this.tabSelectUseCase.selectNext(1);
+ case operations.TAB_FIRST:
+ return this.tabSelectUseCase.selectFirst();
+ case operations.TAB_LAST:
+ return this.tabSelectUseCase.selectLast();
+ case operations.TAB_PREV_SEL:
+ return this.tabSelectUseCase.selectPrevSelected();
+ case operations.TAB_RELOAD:
+ return this.tabUseCase.reload(operation.cache);
+ case operations.TAB_PIN:
+ return this.tabUseCase.setPinned(true);
+ case operations.TAB_UNPIN:
+ return this.tabUseCase.setPinned(false);
+ case operations.TAB_TOGGLE_PINNED:
+ return this.tabUseCase.togglePinned();
+ case operations.TAB_DUPLICATE:
+ return this.tabUseCase.duplicate();
+ case operations.PAGE_SOURCE:
+ return this.tabUseCase.openPageSource();
+ case operations.PAGE_HOME:
+ return this.tabUseCase.openHome(operation.newTab);
+ case operations.ZOOM_IN:
+ return this.zoomUseCase.zoomIn();
+ case operations.ZOOM_OUT:
+ return this.zoomUseCase.zoomOut();
+ case operations.ZOOM_NEUTRAL:
+ return this.zoomUseCase.zoomNutoral();
+ case operations.COMMAND_SHOW:
+ return this.consoleUseCase.showCommand();
+ case operations.COMMAND_SHOW_OPEN:
+ return this.consoleUseCase.showOpenCommand(operation.alter);
+ case operations.COMMAND_SHOW_TABOPEN:
+ return this.consoleUseCase.showTabopenCommand(operation.alter);
+ case operations.COMMAND_SHOW_WINOPEN:
+ return this.consoleUseCase.showWinopenCommand(operation.alter);
+ case operations.COMMAND_SHOW_BUFFER:
+ return this.consoleUseCase.showBufferCommand();
+ case operations.COMMAND_SHOW_ADDBOOKMARK:
+ return this.consoleUseCase.showAddbookmarkCommand(operation.alter);
+ case operations.FIND_START:
+ return this.findUseCase.findStart();
+ case operations.CANCEL:
+ return this.consoleUseCase.hideConsole();
+ }
+ }
+}
+
diff --git a/src/background/controllers/SettingController.js b/src/background/controllers/SettingController.js
deleted file mode 100644
index e895d72..0000000
--- a/src/background/controllers/SettingController.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import SettingUseCase from '../usecases/SettingUseCase';
-import ContentMessageClient from '../infrastructures/ContentMessageClient';
-
-export default class SettingController {
- constructor() {
- this.settingUseCase = new SettingUseCase();
- this.contentMessageClient = new ContentMessageClient();
- }
-
- getSetting() {
- return this.settingUseCase.get();
- }
-
- async reload() {
- await this.settingUseCase.reload();
- this.contentMessageClient.broadcastSettingsChanged();
- }
-}
diff --git a/src/background/controllers/SettingController.ts b/src/background/controllers/SettingController.ts
new file mode 100644
index 0000000..e895d72
--- /dev/null
+++ b/src/background/controllers/SettingController.ts
@@ -0,0 +1,18 @@
+import SettingUseCase from '../usecases/SettingUseCase';
+import ContentMessageClient from '../infrastructures/ContentMessageClient';
+
+export default class SettingController {
+ constructor() {
+ this.settingUseCase = new SettingUseCase();
+ this.contentMessageClient = new ContentMessageClient();
+ }
+
+ getSetting() {
+ return this.settingUseCase.get();
+ }
+
+ async reload() {
+ await this.settingUseCase.reload();
+ this.contentMessageClient.broadcastSettingsChanged();
+ }
+}
diff --git a/src/background/controllers/VersionController.js b/src/background/controllers/VersionController.js
deleted file mode 100644
index c596f9b..0000000
--- a/src/background/controllers/VersionController.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import VersionUseCase from '../usecases/VersionUseCase';
-
-export default class VersionController {
- constructor() {
- this.versionUseCase = new VersionUseCase();
- }
-
- notify() {
- this.versionUseCase.notify();
- }
-}
diff --git a/src/background/controllers/VersionController.ts b/src/background/controllers/VersionController.ts
new file mode 100644
index 0000000..c596f9b
--- /dev/null
+++ b/src/background/controllers/VersionController.ts
@@ -0,0 +1,11 @@
+import VersionUseCase from '../usecases/VersionUseCase';
+
+export default class VersionController {
+ constructor() {
+ this.versionUseCase = new VersionUseCase();
+ }
+
+ notify() {
+ this.versionUseCase.notify();
+ }
+}
diff --git a/src/background/controllers/version.js b/src/background/controllers/version.js
deleted file mode 100644
index ec0f634..0000000
--- a/src/background/controllers/version.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import VersionInteractor from '../usecases/version';
-
-export default class VersionController {
- constructor() {
- this.versionInteractor = new VersionInteractor();
- }
-
- notifyIfUpdated() {
- browser.runtime.onInstalled.addListener(() => {
- return this.versionInteractor.notify();
- });
- }
-}
diff --git a/src/background/controllers/version.ts b/src/background/controllers/version.ts
new file mode 100644
index 0000000..ec0f634
--- /dev/null
+++ b/src/background/controllers/version.ts
@@ -0,0 +1,13 @@
+import VersionInteractor from '../usecases/version';
+
+export default class VersionController {
+ constructor() {
+ this.versionInteractor = new VersionInteractor();
+ }
+
+ notifyIfUpdated() {
+ browser.runtime.onInstalled.addListener(() => {
+ return this.versionInteractor.notify();
+ });
+ }
+}
diff --git a/src/background/domains/CommandDocs.js b/src/background/domains/CommandDocs.js
deleted file mode 100644
index 734c68e..0000000
--- a/src/background/domains/CommandDocs.js
+++ /dev/null
@@ -1,12 +0,0 @@
-export default {
- set: 'Set a value of the property',
- open: 'Open a URL or search by keywords in current tab',
- tabopen: 'Open a URL or search by keywords in new tab',
- winopen: 'Open a URL or search by keywords in new window',
- buffer: 'Select tabs by matched keywords',
- bdelete: 'Close a certain tab matched by keywords',
- bdeletes: 'Close all tabs matched by keywords',
- quit: 'Close the current tab',
- quitall: 'Close all tabs',
-};
-
diff --git a/src/background/domains/CommandDocs.ts b/src/background/domains/CommandDocs.ts
new file mode 100644
index 0000000..734c68e
--- /dev/null
+++ b/src/background/domains/CommandDocs.ts
@@ -0,0 +1,12 @@
+export default {
+ set: 'Set a value of the property',
+ open: 'Open a URL or search by keywords in current tab',
+ tabopen: 'Open a URL or search by keywords in new tab',
+ winopen: 'Open a URL or search by keywords in new window',
+ buffer: 'Select tabs by matched keywords',
+ bdelete: 'Close a certain tab matched by keywords',
+ bdeletes: 'Close all tabs matched by keywords',
+ quit: 'Close the current tab',
+ quitall: 'Close all tabs',
+};
+
diff --git a/src/background/domains/CompletionGroup.js b/src/background/domains/CompletionGroup.js
deleted file mode 100644
index 1749d72..0000000
--- a/src/background/domains/CompletionGroup.js
+++ /dev/null
@@ -1,14 +0,0 @@
-export default class CompletionGroup {
- constructor(name, items) {
- this.name0 = name;
- this.items0 = items;
- }
-
- get name() {
- return this.name0;
- }
-
- get items() {
- return this.items0;
- }
-}
diff --git a/src/background/domains/CompletionGroup.ts b/src/background/domains/CompletionGroup.ts
new file mode 100644
index 0000000..1749d72
--- /dev/null
+++ b/src/background/domains/CompletionGroup.ts
@@ -0,0 +1,14 @@
+export default class CompletionGroup {
+ constructor(name, items) {
+ this.name0 = name;
+ this.items0 = items;
+ }
+
+ get name() {
+ return this.name0;
+ }
+
+ get items() {
+ return this.items0;
+ }
+}
diff --git a/src/background/domains/CompletionItem.js b/src/background/domains/CompletionItem.js
deleted file mode 100644
index c7ad8a1..0000000
--- a/src/background/domains/CompletionItem.js
+++ /dev/null
@@ -1,24 +0,0 @@
-export default class CompletionItem {
- constructor({ caption, content, url, icon }) {
- this.caption0 = caption;
- this.content0 = content;
- this.url0 = url;
- this.icon0 = icon;
- }
-
- get caption() {
- return this.caption0;
- }
-
- get content() {
- return this.content0;
- }
-
- get url() {
- return this.url0;
- }
-
- get icon() {
- return this.icon0;
- }
-}
diff --git a/src/background/domains/CompletionItem.ts b/src/background/domains/CompletionItem.ts
new file mode 100644
index 0000000..c7ad8a1
--- /dev/null
+++ b/src/background/domains/CompletionItem.ts
@@ -0,0 +1,24 @@
+export default class CompletionItem {
+ constructor({ caption, content, url, icon }) {
+ this.caption0 = caption;
+ this.content0 = content;
+ this.url0 = url;
+ this.icon0 = icon;
+ }
+
+ get caption() {
+ return this.caption0;
+ }
+
+ get content() {
+ return this.content0;
+ }
+
+ get url() {
+ return this.url0;
+ }
+
+ get icon() {
+ return this.icon0;
+ }
+}
diff --git a/src/background/domains/Completions.js b/src/background/domains/Completions.js
deleted file mode 100644
index f399743..0000000
--- a/src/background/domains/Completions.js
+++ /dev/null
@@ -1,27 +0,0 @@
-export default class Completions {
- constructor(groups) {
- this.g = groups;
- }
-
- get groups() {
- return this.g;
- }
-
- serialize() {
- return this.groups.map(group => ({
- name: group.name,
- items: group.items.map(item => ({
- caption: item.caption,
- content: item.content,
- url: item.url,
- icon: item.icon,
- })),
- }));
- }
-
- static empty() {
- return EMPTY_COMPLETIONS;
- }
-}
-
-let EMPTY_COMPLETIONS = new Completions([]);
diff --git a/src/background/domains/Completions.ts b/src/background/domains/Completions.ts
new file mode 100644
index 0000000..f399743
--- /dev/null
+++ b/src/background/domains/Completions.ts
@@ -0,0 +1,27 @@
+export default class Completions {
+ constructor(groups) {
+ this.g = groups;
+ }
+
+ get groups() {
+ return this.g;
+ }
+
+ serialize() {
+ return this.groups.map(group => ({
+ name: group.name,
+ items: group.items.map(item => ({
+ caption: item.caption,
+ content: item.content,
+ url: item.url,
+ icon: item.icon,
+ })),
+ }));
+ }
+
+ static empty() {
+ return EMPTY_COMPLETIONS;
+ }
+}
+
+let EMPTY_COMPLETIONS = new Completions([]);
diff --git a/src/background/domains/GlobalMark.js b/src/background/domains/GlobalMark.js
deleted file mode 100644
index f0586f1..0000000
--- a/src/background/domains/GlobalMark.js
+++ /dev/null
@@ -1,24 +0,0 @@
-export default class GlobalMark {
- constructor(tabId, url, x, y) {
- this.tabId0 = tabId;
- this.url0 = url;
- this.x0 = x;
- this.y0 = y;
- }
-
- get tabId() {
- return this.tabId0;
- }
-
- get url() {
- return this.url0;
- }
-
- get x() {
- return this.x0;
- }
-
- get y() {
- return this.y0;
- }
-}
diff --git a/src/background/domains/GlobalMark.ts b/src/background/domains/GlobalMark.ts
new file mode 100644
index 0000000..f0586f1
--- /dev/null
+++ b/src/background/domains/GlobalMark.ts
@@ -0,0 +1,24 @@
+export default class GlobalMark {
+ constructor(tabId, url, x, y) {
+ this.tabId0 = tabId;
+ this.url0 = url;
+ this.x0 = x;
+ this.y0 = y;
+ }
+
+ get tabId() {
+ return this.tabId0;
+ }
+
+ get url() {
+ return this.url0;
+ }
+
+ get x() {
+ return this.x0;
+ }
+
+ get y() {
+ return this.y0;
+ }
+}
diff --git a/src/background/domains/Setting.js b/src/background/domains/Setting.js
deleted file mode 100644
index 106ec0f..0000000
--- a/src/background/domains/Setting.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import DefaultSettings from '../../shared/settings/default';
-import * as settingsValues from '../../shared/settings/values';
-
-export default class Setting {
- constructor({ source, json, form }) {
- this.obj = {
- source, json, form
- };
- }
-
- get source() {
- return this.obj.source;
- }
-
- get json() {
- return this.obj.json;
- }
-
- get form() {
- return this.obj.form;
- }
-
- value() {
- let value = JSON.parse(DefaultSettings.json);
- if (this.obj.source === 'json') {
- value = settingsValues.valueFromJson(this.obj.json);
- } else if (this.obj.source === 'form') {
- value = settingsValues.valueFromForm(this.obj.form);
- }
- if (!value.properties) {
- value.properties = {};
- }
- return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value };
- }
-
- serialize() {
- return this.obj;
- }
-
- static deserialize(obj) {
- return new Setting({ source: obj.source, json: obj.json, form: obj.form });
- }
-
- static defaultSettings() {
- return new Setting({
- source: DefaultSettings.source,
- json: DefaultSettings.json,
- form: {},
- });
- }
-}
diff --git a/src/background/domains/Setting.ts b/src/background/domains/Setting.ts
new file mode 100644
index 0000000..106ec0f
--- /dev/null
+++ b/src/background/domains/Setting.ts
@@ -0,0 +1,51 @@
+import DefaultSettings from '../../shared/settings/default';
+import * as settingsValues from '../../shared/settings/values';
+
+export default class Setting {
+ constructor({ source, json, form }) {
+ this.obj = {
+ source, json, form
+ };
+ }
+
+ get source() {
+ return this.obj.source;
+ }
+
+ get json() {
+ return this.obj.json;
+ }
+
+ get form() {
+ return this.obj.form;
+ }
+
+ value() {
+ let value = JSON.parse(DefaultSettings.json);
+ if (this.obj.source === 'json') {
+ value = settingsValues.valueFromJson(this.obj.json);
+ } else if (this.obj.source === 'form') {
+ value = settingsValues.valueFromForm(this.obj.form);
+ }
+ if (!value.properties) {
+ value.properties = {};
+ }
+ return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value };
+ }
+
+ serialize() {
+ return this.obj;
+ }
+
+ static deserialize(obj) {
+ return new Setting({ source: obj.source, json: obj.json, form: obj.form });
+ }
+
+ static defaultSettings() {
+ return new Setting({
+ source: DefaultSettings.source,
+ json: DefaultSettings.json,
+ form: {},
+ });
+ }
+}
diff --git a/src/background/index.js b/src/background/index.js
deleted file mode 100644
index f9efd4d..0000000
--- a/src/background/index.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import ContentMessageListener from './infrastructures/ContentMessageListener';
-import SettingController from './controllers/SettingController';
-import VersionController from './controllers/VersionController';
-
-let settingController = new SettingController();
-settingController.reload();
-
-browser.runtime.onInstalled.addListener((details) => {
- if (details.reason !== 'install' && details.reason !== 'update') {
- return;
- }
- new VersionController().notify();
-});
-
-new ContentMessageListener().run();
-browser.storage.onChanged.addListener((changes, area) => {
- if (area !== 'local') {
- return;
- }
- if (changes.settings) {
- settingController.reload();
- }
-});
diff --git a/src/background/index.ts b/src/background/index.ts
new file mode 100644
index 0000000..f9efd4d
--- /dev/null
+++ b/src/background/index.ts
@@ -0,0 +1,23 @@
+import ContentMessageListener from './infrastructures/ContentMessageListener';
+import SettingController from './controllers/SettingController';
+import VersionController from './controllers/VersionController';
+
+let settingController = new SettingController();
+settingController.reload();
+
+browser.runtime.onInstalled.addListener((details) => {
+ if (details.reason !== 'install' && details.reason !== 'update') {
+ return;
+ }
+ new VersionController().notify();
+});
+
+new ContentMessageListener().run();
+browser.storage.onChanged.addListener((changes, area) => {
+ if (area !== 'local') {
+ return;
+ }
+ if (changes.settings) {
+ settingController.reload();
+ }
+});
diff --git a/src/background/infrastructures/ConsoleClient.js b/src/background/infrastructures/ConsoleClient.js
deleted file mode 100644
index f691515..0000000
--- a/src/background/infrastructures/ConsoleClient.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import messages from '../../shared/messages';
-
-export default class ConsoleClient {
- showCommand(tabId, command) {
- return browser.tabs.sendMessage(tabId, {
- type: messages.CONSOLE_SHOW_COMMAND,
- command,
- });
- }
-
- showFind(tabId) {
- return browser.tabs.sendMessage(tabId, {
- type: messages.CONSOLE_SHOW_FIND
- });
- }
-
- showInfo(tabId, message) {
- return browser.tabs.sendMessage(tabId, {
- type: messages.CONSOLE_SHOW_INFO,
- text: message,
- });
- }
-
- showError(tabId, message) {
- return browser.tabs.sendMessage(tabId, {
- type: messages.CONSOLE_SHOW_ERROR,
- text: message,
- });
- }
-
- hide(tabId) {
- return browser.tabs.sendMessage(tabId, {
- type: messages.CONSOLE_HIDE,
- });
- }
-}
-
diff --git a/src/background/infrastructures/ConsoleClient.ts b/src/background/infrastructures/ConsoleClient.ts
new file mode 100644
index 0000000..f691515
--- /dev/null
+++ b/src/background/infrastructures/ConsoleClient.ts
@@ -0,0 +1,37 @@
+import messages from '../../shared/messages';
+
+export default class ConsoleClient {
+ showCommand(tabId, command) {
+ return browser.tabs.sendMessage(tabId, {
+ type: messages.CONSOLE_SHOW_COMMAND,
+ command,
+ });
+ }
+
+ showFind(tabId) {
+ return browser.tabs.sendMessage(tabId, {
+ type: messages.CONSOLE_SHOW_FIND
+ });
+ }
+
+ showInfo(tabId, message) {
+ return browser.tabs.sendMessage(tabId, {
+ type: messages.CONSOLE_SHOW_INFO,
+ text: message,
+ });
+ }
+
+ showError(tabId, message) {
+ return browser.tabs.sendMessage(tabId, {
+ type: messages.CONSOLE_SHOW_ERROR,
+ text: message,
+ });
+ }
+
+ hide(tabId) {
+ return browser.tabs.sendMessage(tabId, {
+ type: messages.CONSOLE_HIDE,
+ });
+ }
+}
+
diff --git a/src/background/infrastructures/ContentMessageClient.js b/src/background/infrastructures/ContentMessageClient.js
deleted file mode 100644
index 0fab5a3..0000000
--- a/src/background/infrastructures/ContentMessageClient.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import messages from '../../shared/messages';
-
-export default class ContentMessageClient {
- async broadcastSettingsChanged() {
- let tabs = await browser.tabs.query({});
- for (let tab of tabs) {
- if (tab.url.startsWith('about:')) {
- continue;
- }
- browser.tabs.sendMessage(tab.id, {
- type: messages.SETTINGS_CHANGED,
- });
- }
- }
-
- async getAddonEnabled(tabId) {
- let { enabled } = await browser.tabs.sendMessage(tabId, {
- type: messages.ADDON_ENABLED_QUERY,
- });
- return enabled;
- }
-
- toggleAddonEnabled(tabId) {
- return browser.tabs.sendMessage(tabId, {
- type: messages.ADDON_TOGGLE_ENABLED,
- });
- }
-
- scrollTo(tabId, x, y) {
- return browser.tabs.sendMessage(tabId, {
- type: messages.TAB_SCROLL_TO,
- x,
- y,
- });
- }
-}
diff --git a/src/background/infrastructures/ContentMessageClient.ts b/src/background/infrastructures/ContentMessageClient.ts
new file mode 100644
index 0000000..0fab5a3
--- /dev/null
+++ b/src/background/infrastructures/ContentMessageClient.ts
@@ -0,0 +1,36 @@
+import messages from '../../shared/messages';
+
+export default class ContentMessageClient {
+ async broadcastSettingsChanged() {
+ let tabs = await browser.tabs.query({});
+ for (let tab of tabs) {
+ if (tab.url.startsWith('about:')) {
+ continue;
+ }
+ browser.tabs.sendMessage(tab.id, {
+ type: messages.SETTINGS_CHANGED,
+ });
+ }
+ }
+
+ async getAddonEnabled(tabId) {
+ let { enabled } = await browser.tabs.sendMessage(tabId, {
+ type: messages.ADDON_ENABLED_QUERY,
+ });
+ return enabled;
+ }
+
+ toggleAddonEnabled(tabId) {
+ return browser.tabs.sendMessage(tabId, {
+ type: messages.ADDON_TOGGLE_ENABLED,
+ });
+ }
+
+ scrollTo(tabId, x, y) {
+ return browser.tabs.sendMessage(tabId, {
+ type: messages.TAB_SCROLL_TO,
+ x,
+ y,
+ });
+ }
+}
diff --git a/src/background/infrastructures/ContentMessageListener.js b/src/background/infrastructures/ContentMessageListener.js
deleted file mode 100644
index 5b0f62e..0000000
--- a/src/background/infrastructures/ContentMessageListener.js
+++ /dev/null
@@ -1,135 +0,0 @@
-import messages from '../../shared/messages';
-import CommandController from '../controllers/CommandController';
-import SettingController from '../controllers/SettingController';
-import FindController from '../controllers/FindController';
-import AddonEnabledController from '../controllers/AddonEnabledController';
-import LinkController from '../controllers/LinkController';
-import OperationController from '../controllers/OperationController';
-import MarkController from '../controllers/MarkController';
-
-export default class ContentMessageListener {
- constructor() {
- this.settingController = new SettingController();
- this.commandController = new CommandController();
- this.findController = new FindController();
- this.addonEnabledController = new AddonEnabledController();
- this.linkController = new LinkController();
- this.backgroundOperationController = new OperationController();
- this.markController = new MarkController();
-
- this.consolePorts = {};
- }
-
- run() {
- browser.runtime.onMessage.addListener((message, sender) => {
- try {
- let ret = this.onMessage(message, sender);
- if (!(ret instanceof Promise)) {
- return {};
- }
- return ret.catch((e) => {
- return browser.tabs.sendMessage(sender.tab.id, {
- type: messages.CONSOLE_SHOW_ERROR,
- text: e.message,
- });
- });
- } catch (e) {
- return browser.tabs.sendMessage(sender.tab.id, {
- type: messages.CONSOLE_SHOW_ERROR,
- text: e.message,
- });
- }
- });
- browser.runtime.onConnect.addListener(this.onConnected.bind(this));
- }
-
- onMessage(message, sender) {
- switch (message.type) {
- case messages.CONSOLE_QUERY_COMPLETIONS:
- return this.onConsoleQueryCompletions(message.text);
- case messages.CONSOLE_ENTER_COMMAND:
- return this.onConsoleEnterCommand(message.text);
- case messages.SETTINGS_QUERY:
- return this.onSettingsQuery();
- case messages.FIND_GET_KEYWORD:
- return this.onFindGetKeyword();
- case messages.FIND_SET_KEYWORD:
- return this.onFindSetKeyword(message.keyword);
- case messages.ADDON_ENABLED_RESPONSE:
- return this.onAddonEnabledResponse(message.enabled);
- case messages.OPEN_URL:
- return this.onOpenUrl(
- message.newTab, message.url, sender.tab.id, message.background);
- case messages.BACKGROUND_OPERATION:
- return this.onBackgroundOperation(message.operation);
- case messages.MARK_SET_GLOBAL:
- return this.onMarkSetGlobal(message.key, message.x, message.y);
- case messages.MARK_JUMP_GLOBAL:
- return this.onMarkJumpGlobal(message.key);
- case messages.CONSOLE_FRAME_MESSAGE:
- return this.onConsoleFrameMessage(sender.tab.id, message.message);
- }
- }
-
- async onConsoleQueryCompletions(line) {
- let completions = await this.commandController.getCompletions(line);
- return Promise.resolve(completions.serialize());
- }
-
- onConsoleEnterCommand(text) {
- return this.commandController.exec(text);
- }
-
-
- onSettingsQuery() {
- return this.settingController.getSetting();
- }
-
- onFindGetKeyword() {
- return this.findController.getKeyword();
- }
-
- onFindSetKeyword(keyword) {
- return this.findController.setKeyword(keyword);
- }
-
- onAddonEnabledResponse(enabled) {
- return this.addonEnabledController.indicate(enabled);
- }
-
- onOpenUrl(newTab, url, openerId, background) {
- if (newTab) {
- return this.linkController.openNewTab(url, openerId, background);
- }
- return this.linkController.openToTab(url, openerId);
- }
-
- onBackgroundOperation(operation) {
- return this.backgroundOperationController.exec(operation);
- }
-
- onMarkSetGlobal(key, x, y) {
- return this.markController.setGlobal(key, x, y);
- }
-
- onMarkJumpGlobal(key) {
- return this.markController.jumpGlobal(key);
- }
-
- onConsoleFrameMessage(tabId, message) {
- let port = this.consolePorts[tabId];
- if (!port) {
- return;
- }
- port.postMessage(message);
- }
-
- onConnected(port) {
- if (port.name !== 'vimvixen-console') {
- return;
- }
-
- let id = port.sender.tab.id;
- this.consolePorts[id] = port;
- }
-}
diff --git a/src/background/infrastructures/ContentMessageListener.ts b/src/background/infrastructures/ContentMessageListener.ts
new file mode 100644
index 0000000..5b0f62e
--- /dev/null
+++ b/src/background/infrastructures/ContentMessageListener.ts
@@ -0,0 +1,135 @@
+import messages from '../../shared/messages';
+import CommandController from '../controllers/CommandController';
+import SettingController from '../controllers/SettingController';
+import FindController from '../controllers/FindController';
+import AddonEnabledController from '../controllers/AddonEnabledController';
+import LinkController from '../controllers/LinkController';
+import OperationController from '../controllers/OperationController';
+import MarkController from '../controllers/MarkController';
+
+export default class ContentMessageListener {
+ constructor() {
+ this.settingController = new SettingController();
+ this.commandController = new CommandController();
+ this.findController = new FindController();
+ this.addonEnabledController = new AddonEnabledController();
+ this.linkController = new LinkController();
+ this.backgroundOperationController = new OperationController();
+ this.markController = new MarkController();
+
+ this.consolePorts = {};
+ }
+
+ run() {
+ browser.runtime.onMessage.addListener((message, sender) => {
+ try {
+ let ret = this.onMessage(message, sender);
+ if (!(ret instanceof Promise)) {
+ return {};
+ }
+ return ret.catch((e) => {
+ return browser.tabs.sendMessage(sender.tab.id, {
+ type: messages.CONSOLE_SHOW_ERROR,
+ text: e.message,
+ });
+ });
+ } catch (e) {
+ return browser.tabs.sendMessage(sender.tab.id, {
+ type: messages.CONSOLE_SHOW_ERROR,
+ text: e.message,
+ });
+ }
+ });
+ browser.runtime.onConnect.addListener(this.onConnected.bind(this));
+ }
+
+ onMessage(message, sender) {
+ switch (message.type) {
+ case messages.CONSOLE_QUERY_COMPLETIONS:
+ return this.onConsoleQueryCompletions(message.text);
+ case messages.CONSOLE_ENTER_COMMAND:
+ return this.onConsoleEnterCommand(message.text);
+ case messages.SETTINGS_QUERY:
+ return this.onSettingsQuery();
+ case messages.FIND_GET_KEYWORD:
+ return this.onFindGetKeyword();
+ case messages.FIND_SET_KEYWORD:
+ return this.onFindSetKeyword(message.keyword);
+ case messages.ADDON_ENABLED_RESPONSE:
+ return this.onAddonEnabledResponse(message.enabled);
+ case messages.OPEN_URL:
+ return this.onOpenUrl(
+ message.newTab, message.url, sender.tab.id, message.background);
+ case messages.BACKGROUND_OPERATION:
+ return this.onBackgroundOperation(message.operation);
+ case messages.MARK_SET_GLOBAL:
+ return this.onMarkSetGlobal(message.key, message.x, message.y);
+ case messages.MARK_JUMP_GLOBAL:
+ return this.onMarkJumpGlobal(message.key);
+ case messages.CONSOLE_FRAME_MESSAGE:
+ return this.onConsoleFrameMessage(sender.tab.id, message.message);
+ }
+ }
+
+ async onConsoleQueryCompletions(line) {
+ let completions = await this.commandController.getCompletions(line);
+ return Promise.resolve(completions.serialize());
+ }
+
+ onConsoleEnterCommand(text) {
+ return this.commandController.exec(text);
+ }
+
+
+ onSettingsQuery() {
+ return this.settingController.getSetting();
+ }
+
+ onFindGetKeyword() {
+ return this.findController.getKeyword();
+ }
+
+ onFindSetKeyword(keyword) {
+ return this.findController.setKeyword(keyword);
+ }
+
+ onAddonEnabledResponse(enabled) {
+ return this.addonEnabledController.indicate(enabled);
+ }
+
+ onOpenUrl(newTab, url, openerId, background) {
+ if (newTab) {
+ return this.linkController.openNewTab(url, openerId, background);
+ }
+ return this.linkController.openToTab(url, openerId);
+ }
+
+ onBackgroundOperation(operation) {
+ return this.backgroundOperationController.exec(operation);
+ }
+
+ onMarkSetGlobal(key, x, y) {
+ return this.markController.setGlobal(key, x, y);
+ }
+
+ onMarkJumpGlobal(key) {
+ return this.markController.jumpGlobal(key);
+ }
+
+ onConsoleFrameMessage(tabId, message) {
+ let port = this.consolePorts[tabId];
+ if (!port) {
+ return;
+ }
+ port.postMessage(message);
+ }
+
+ onConnected(port) {
+ if (port.name !== 'vimvixen-console') {
+ return;
+ }
+
+ let id = port.sender.tab.id;
+ this.consolePorts[id] = port;
+ }
+}
diff --git a/src/background/infrastructures/MemoryStorage.js b/src/background/infrastructures/MemoryStorage.js
deleted file mode 100644
index 3a7e4f2..0000000
--- a/src/background/infrastructures/MemoryStorage.js
+++ /dev/null
@@ -1,19 +0,0 @@
-const db = {};
-
-export default class MemoryStorage {
- set(name, value) {
- let data = JSON.stringify(value);
- if (typeof data === 'undefined') {
- throw new Error('value is not serializable');
- }
- db[name] = data;
- }
-
- get(name) {
- let data = db[name];
- if (!data) {
- return undefined;
- }
- return JSON.parse(data);
- }
-}
diff --git a/src/background/infrastructures/MemoryStorage.ts b/src/background/infrastructures/MemoryStorage.ts
new file mode 100644
index 0000000..3a7e4f2
--- /dev/null
+++ b/src/background/infrastructures/MemoryStorage.ts
@@ -0,0 +1,19 @@
+const db = {};
+
+export default class MemoryStorage {
+ set(name, value) {
+ let data = JSON.stringify(value);
+ if (typeof data === 'undefined') {
+ throw new Error('value is not serializable');
+ }
+ db[name] = data;
+ }
+
+ get(name) {
+ let data = db[name];
+ if (!data) {
+ return undefined;
+ }
+ return JSON.parse(data);
+ }
+}
diff --git a/src/background/presenters/IndicatorPresenter.js b/src/background/presenters/IndicatorPresenter.js
deleted file mode 100644
index 5737519..0000000
--- a/src/background/presenters/IndicatorPresenter.js
+++ /dev/null
@@ -1,12 +0,0 @@
-export default class IndicatorPresenter {
- indicate(enabled) {
- let path = enabled
- ? 'resources/enabled_32x32.png'
- : 'resources/disabled_32x32.png';
- return browser.browserAction.setIcon({ path });
- }
-
- onClick(listener) {
- browser.browserAction.onClicked.addListener(listener);
- }
-}
diff --git a/src/background/presenters/IndicatorPresenter.ts b/src/background/presenters/IndicatorPresenter.ts
new file mode 100644
index 0000000..5737519
--- /dev/null
+++ b/src/background/presenters/IndicatorPresenter.ts
@@ -0,0 +1,12 @@
+export default class IndicatorPresenter {
+ indicate(enabled) {
+ let path = enabled
+ ? 'resources/enabled_32x32.png'
+ : 'resources/disabled_32x32.png';
+ return browser.browserAction.setIcon({ path });
+ }
+
+ onClick(listener) {
+ browser.browserAction.onClicked.addListener(listener);
+ }
+}
diff --git a/src/background/presenters/NotifyPresenter.js b/src/background/presenters/NotifyPresenter.js
deleted file mode 100644
index a81f227..0000000
--- a/src/background/presenters/NotifyPresenter.js
+++ /dev/null
@@ -1,23 +0,0 @@
-const NOTIFICATION_ID = 'vimvixen-update';
-
-export default class NotifyPresenter {
- notify(title, message, onclick) {
- const listener = (id) => {
- if (id !== NOTIFICATION_ID) {
- return;
- }
-
- onclick();
-
- browser.notifications.onClicked.removeListener(listener);
- };
- browser.notifications.onClicked.addListener(listener);
-
- return browser.notifications.create(NOTIFICATION_ID, {
- 'type': 'basic',
- 'iconUrl': browser.extension.getURL('resources/icon_48x48.png'),
- title,
- message,
- });
- }
-}
diff --git a/src/background/presenters/NotifyPresenter.ts b/src/background/presenters/NotifyPresenter.ts
new file mode 100644
index 0000000..a81f227
--- /dev/null
+++ b/src/background/presenters/NotifyPresenter.ts
@@ -0,0 +1,23 @@
+const NOTIFICATION_ID = 'vimvixen-update';
+
+export default class NotifyPresenter {
+ notify(title, message, onclick) {
+ const listener = (id) => {
+ if (id !== NOTIFICATION_ID) {
+ return;
+ }
+
+ onclick();
+
+ browser.notifications.onClicked.removeListener(listener);
+ };
+ browser.notifications.onClicked.addListener(listener);
+
+ return browser.notifications.create(NOTIFICATION_ID, {
+ 'type': 'basic',
+ 'iconUrl': browser.extension.getURL('resources/icon_48x48.png'),
+ title,
+ message,
+ });
+ }
+}
diff --git a/src/background/presenters/TabPresenter.js b/src/background/presenters/TabPresenter.js
deleted file mode 100644
index 744be39..0000000
--- a/src/background/presenters/TabPresenter.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import MemoryStorage from '../infrastructures/MemoryStorage';
-
-const CURRENT_SELECTED_KEY = 'tabs.current.selected';
-const LAST_SELECTED_KEY = 'tabs.last.selected';
-
-export default class TabPresenter {
- open(url, tabId) {
- return browser.tabs.update(tabId, { url });
- }
-
- create(url, opts) {
- return browser.tabs.create({ url, ...opts });
- }
-
- async getCurrent() {
- let tabs = await browser.tabs.query({
- active: true, currentWindow: true
- });
- return tabs[0];
- }
-
- getAll() {
- return browser.tabs.query({ currentWindow: true });
- }
-
- async getLastSelectedId() {
- let cache = new MemoryStorage();
- let tabId = await cache.get(LAST_SELECTED_KEY);
- if (tabId === null || typeof tabId === 'undefined') {
- return;
- }
- return tabId;
- }
-
- async getByKeyword(keyword, excludePinned = false) {
- let tabs = await browser.tabs.query({ currentWindow: true });
- return tabs.filter((t) => {
- return t.url.toLowerCase().includes(keyword.toLowerCase()) ||
- t.title && t.title.toLowerCase().includes(keyword.toLowerCase());
- }).filter((t) => {
- return !(excludePinned && t.pinned);
- });
- }
-
- select(tabId) {
- return browser.tabs.update(tabId, { active: true });
- }
-
- remove(ids) {
- return browser.tabs.remove(ids);
- }
-
- async reopen() {
- let window = await browser.windows.getCurrent();
- let sessions = await browser.sessions.getRecentlyClosed();
- let session = sessions.find((s) => {
- return s.tab && s.tab.windowId === window.id;
- });
- if (!session) {
- return;
- }
- if (session.tab) {
- return browser.sessions.restore(session.tab.sessionId);
- }
- return browser.sessions.restore(session.window.sessionId);
- }
-
- reload(tabId, cache) {
- return browser.tabs.reload(tabId, { bypassCache: cache });
- }
-
- setPinned(tabId, pinned) {
- return browser.tabs.update(tabId, { pinned });
- }
-
- duplicate(id) {
- return browser.tabs.duplicate(id);
- }
-
- getZoom(tabId) {
- return browser.tabs.getZoom(tabId);
- }
-
- setZoom(tabId, factor) {
- return browser.tabs.setZoom(tabId, factor);
- }
-
- onSelected(listener) {
- browser.tabs.onActivated.addListener(listener);
- }
-}
-
-let tabPresenter = new TabPresenter();
-tabPresenter.onSelected((tab) => {
- let cache = new MemoryStorage();
-
- let lastId = cache.get(CURRENT_SELECTED_KEY);
- if (lastId) {
- cache.set(LAST_SELECTED_KEY, lastId);
- }
- cache.set(CURRENT_SELECTED_KEY, tab.tabId);
-});
diff --git a/src/background/presenters/TabPresenter.ts b/src/background/presenters/TabPresenter.ts
new file mode 100644
index 0000000..744be39
--- /dev/null
+++ b/src/background/presenters/TabPresenter.ts
@@ -0,0 +1,102 @@
+import MemoryStorage from '../infrastructures/MemoryStorage';
+
+const CURRENT_SELECTED_KEY = 'tabs.current.selected';
+const LAST_SELECTED_KEY = 'tabs.last.selected';
+
+export default class TabPresenter {
+ open(url, tabId) {
+ return browser.tabs.update(tabId, { url });
+ }
+
+ create(url, opts) {
+ return browser.tabs.create({ url, ...opts });
+ }
+
+ async getCurrent() {
+ let tabs = await browser.tabs.query({
+ active: true, currentWindow: true
+ });
+ return tabs[0];
+ }
+
+ getAll() {
+ return browser.tabs.query({ currentWindow: true });
+ }
+
+ async getLastSelectedId() {
+ let cache = new MemoryStorage();
+ let tabId = await cache.get(LAST_SELECTED_KEY);
+ if (tabId === null || typeof tabId === 'undefined') {
+ return;
+ }
+ return tabId;
+ }
+
+ async getByKeyword(keyword, excludePinned = false) {
+ let tabs = await browser.tabs.query({ currentWindow: true });
+ return tabs.filter((t) => {
+ return t.url.toLowerCase().includes(keyword.toLowerCase()) ||
+ t.title && t.title.toLowerCase().includes(keyword.toLowerCase());
+ }).filter((t) => {
+ return !(excludePinned && t.pinned);
+ });
+ }
+
+ select(tabId) {
+ return browser.tabs.update(tabId, { active: true });
+ }
+
+ remove(ids) {
+ return browser.tabs.remove(ids);
+ }
+
+ async reopen() {
+ let window = await browser.windows.getCurrent();
+ let sessions = await browser.sessions.getRecentlyClosed();
+ let session = sessions.find((s) => {
+ return s.tab && s.tab.windowId === window.id;
+ });
+ if (!session) {
+ return;
+ }
+ if (session.tab) {
+ return browser.sessions.restore(session.tab.sessionId);
+ }
+ return browser.sessions.restore(session.window.sessionId);
+ }
+
+ reload(tabId, cache) {
+ return browser.tabs.reload(tabId, { bypassCache: cache });
+ }
+
+ setPinned(tabId, pinned) {
+ return browser.tabs.update(tabId, { pinned });
+ }
+
+ duplicate(id) {
+ return browser.tabs.duplicate(id);
+ }
+
+ getZoom(tabId) {
+ return browser.tabs.getZoom(tabId);
+ }
+
+ setZoom(tabId, factor) {
+ return browser.tabs.setZoom(tabId, factor);
+ }
+
+ onSelected(listener) {
+ browser.tabs.onActivated.addListener(listener);
+ }
+}
+
+let tabPresenter = new TabPresenter();
+tabPresenter.onSelected((tab) => {
+ let cache = new MemoryStorage();
+
+ let lastId = cache.get(CURRENT_SELECTED_KEY);
+ if (lastId) {
+ cache.set(LAST_SELECTED_KEY, lastId);
+ }
+ cache.set(CURRENT_SELECTED_KEY, tab.tabId);
+});
diff --git a/src/background/presenters/WindowPresenter.js b/src/background/presenters/WindowPresenter.js
deleted file mode 100644
index a82c4a2..0000000
--- a/src/background/presenters/WindowPresenter.js
+++ /dev/null
@@ -1,5 +0,0 @@
-export default class WindowPresenter {
- create(url) {
- return browser.windows.create({ url });
- }
-}
diff --git a/src/background/presenters/WindowPresenter.ts b/src/background/presenters/WindowPresenter.ts
new file mode 100644
index 0000000..a82c4a2
--- /dev/null
+++ b/src/background/presenters/WindowPresenter.ts
@@ -0,0 +1,5 @@
+export default class WindowPresenter {
+ create(url) {
+ return browser.windows.create({ url });
+ }
+}
diff --git a/src/background/repositories/BookmarkRepository.js b/src/background/repositories/BookmarkRepository.js
deleted file mode 100644
index 99f7ec4..0000000
--- a/src/background/repositories/BookmarkRepository.js
+++ /dev/null
@@ -1,13 +0,0 @@
-export default class BookmarkRepository {
- async create(title, url) {
- let item = await browser.bookmarks.create({
- type: 'bookmark',
- title,
- url,
- });
- if (!item) {
- throw new Error('Could not create a bookmark');
- }
- return item;
- }
-}
diff --git a/src/background/repositories/BookmarkRepository.ts b/src/background/repositories/BookmarkRepository.ts
new file mode 100644
index 0000000..99f7ec4
--- /dev/null
+++ b/src/background/repositories/BookmarkRepository.ts
@@ -0,0 +1,13 @@
+export default class BookmarkRepository {
+ async create(title, url) {
+ let item = await browser.bookmarks.create({
+ type: 'bookmark',
+ title,
+ url,
+ });
+ if (!item) {
+ throw new Error('Could not create a bookmark');
+ }
+ return item;
+ }
+}
diff --git a/src/background/repositories/BrowserSettingRepository.js b/src/background/repositories/BrowserSettingRepository.js
deleted file mode 100644
index a9d2c06..0000000
--- a/src/background/repositories/BrowserSettingRepository.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import * as urls from '../../shared/urls';
-
-export default class BrowserSettingRepository {
- async getHomepageUrls() {
- let { value } = await browser.browserSettings.homepageOverride.get({});
- return value.split('|').map(urls.normalizeUrl);
- }
-}
diff --git a/src/background/repositories/BrowserSettingRepository.ts b/src/background/repositories/BrowserSettingRepository.ts
new file mode 100644
index 0000000..a9d2c06
--- /dev/null
+++ b/src/background/repositories/BrowserSettingRepository.ts
@@ -0,0 +1,8 @@
+import * as urls from '../../shared/urls';
+
+export default class BrowserSettingRepository {
+ async getHomepageUrls() {
+ let { value } = await browser.browserSettings.homepageOverride.get({});
+ return value.split('|').map(urls.normalizeUrl);
+ }
+}
diff --git a/src/background/repositories/CompletionsRepository.js b/src/background/repositories/CompletionsRepository.js
deleted file mode 100644
index 1318d36..0000000
--- a/src/background/repositories/CompletionsRepository.js
+++ /dev/null
@@ -1,31 +0,0 @@
-export default class CompletionsRepository {
- async queryBookmarks(keywords) {
- let items = await browser.bookmarks.search({ query: keywords });
- return items.filter((item) => {
- let url = undefined;
- try {
- url = new URL(item.url);
- } catch (e) {
- return false;
- }
- return item.type === 'bookmark' && url.protocol !== 'place:';
- });
- }
-
- queryHistories(keywords) {
- return browser.history.search({
- text: keywords,
- startTime: 0,
- });
- }
-
- async queryTabs(keywords, excludePinned) {
- let tabs = await browser.tabs.query({ currentWindow: true });
- return tabs.filter((t) => {
- return t.url.toLowerCase().includes(keywords.toLowerCase()) ||
- t.title && t.title.toLowerCase().includes(keywords.toLowerCase());
- }).filter((t) => {
- return !(excludePinned && t.pinned);
- });
- }
-}
diff --git a/src/background/repositories/CompletionsRepository.ts b/src/background/repositories/CompletionsRepository.ts
new file mode 100644
index 0000000..1318d36
--- /dev/null
+++ b/src/background/repositories/CompletionsRepository.ts
@@ -0,0 +1,31 @@
+export default class CompletionsRepository {
+ async queryBookmarks(keywords) {
+ let items = await browser.bookmarks.search({ query: keywords });
+ return items.filter((item) => {
+ let url = undefined;
+ try {
+ url = new URL(item.url);
+ } catch (e) {
+ return false;
+ }
+ return item.type === 'bookmark' && url.protocol !== 'place:';
+ });
+ }
+
+ queryHistories(keywords) {
+ return browser.history.search({
+ text: keywords,
+ startTime: 0,
+ });
+ }
+
+ async queryTabs(keywords, excludePinned) {
+ let tabs = await browser.tabs.query({ currentWindow: true });
+ return tabs.filter((t) => {
+ return t.url.toLowerCase().includes(keywords.toLowerCase()) ||
+ t.title && t.title.toLowerCase().includes(keywords.toLowerCase());
+ }).filter((t) => {
+ return !(excludePinned && t.pinned);
+ });
+ }
+}
diff --git a/src/background/repositories/FindRepository.js b/src/background/repositories/FindRepository.js
deleted file mode 100644
index 74ec914..0000000
--- a/src/background/repositories/FindRepository.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import MemoryStorage from '../infrastructures/MemoryStorage';
-
-const FIND_KEYWORD_KEY = 'find-keyword';
-
-export default class FindRepository {
- constructor() {
- this.cache = new MemoryStorage();
- }
-
- getKeyword() {
- return Promise.resolve(this.cache.get(FIND_KEYWORD_KEY));
- }
-
- setKeyword(keyword) {
- this.cache.set(FIND_KEYWORD_KEY, keyword);
- return Promise.resolve();
- }
-}
-
diff --git a/src/background/repositories/FindRepository.ts b/src/background/repositories/FindRepository.ts
new file mode 100644
index 0000000..74ec914
--- /dev/null
+++ b/src/background/repositories/FindRepository.ts
@@ -0,0 +1,19 @@
+import MemoryStorage from '../infrastructures/MemoryStorage';
+
+const FIND_KEYWORD_KEY = 'find-keyword';
+
+export default class FindRepository {
+ constructor() {
+ this.cache = new MemoryStorage();
+ }
+
+ getKeyword() {
+ return Promise.resolve(this.cache.get(FIND_KEYWORD_KEY));
+ }
+
+ setKeyword(keyword) {
+ this.cache.set(FIND_KEYWORD_KEY, keyword);
+ return Promise.resolve();
+ }
+}
+
diff --git a/src/background/repositories/MarkRepository.js b/src/background/repositories/MarkRepository.js
deleted file mode 100644
index 282c712..0000000
--- a/src/background/repositories/MarkRepository.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import MemoryStorage from '../infrastructures/MemoryStorage';
-import GlobalMark from '../domains/GlobalMark';
-
-const MARK_KEY = 'mark';
-
-export default class MarkRepository {
- constructor() {
- this.cache = new MemoryStorage();
- }
-
- getMark(key) {
- let marks = this.getOrEmptyMarks();
- let data = marks[key];
- if (!data) {
- return Promise.resolve(undefined);
- }
- let mark = new GlobalMark(data.tabId, data.url, data.x, data.y);
- return Promise.resolve(mark);
- }
-
- setMark(key, mark) {
- let marks = this.getOrEmptyMarks();
- marks[key] = { tabId: mark.tabId, url: mark.url, x: mark.x, y: mark.y };
- this.cache.set(MARK_KEY, marks);
-
- return Promise.resolve();
- }
-
- getOrEmptyMarks() {
- return this.cache.get(MARK_KEY) || {};
- }
-}
-
diff --git a/src/background/repositories/MarkRepository.ts b/src/background/repositories/MarkRepository.ts
new file mode 100644
index 0000000..282c712
--- /dev/null
+++ b/src/background/repositories/MarkRepository.ts
@@ -0,0 +1,33 @@
+import MemoryStorage from '../infrastructures/MemoryStorage';
+import GlobalMark from '../domains/GlobalMark';
+
+const MARK_KEY = 'mark';
+
+export default class MarkRepository {
+ constructor() {
+ this.cache = new MemoryStorage();
+ }
+
+ getMark(key) {
+ let marks = this.getOrEmptyMarks();
+ let data = marks[key];
+ if (!data) {
+ return Promise.resolve(undefined);
+ }
+ let mark = new GlobalMark(data.tabId, data.url, data.x, data.y);
+ return Promise.resolve(mark);
+ }
+
+ setMark(key, mark) {
+ let marks = this.getOrEmptyMarks();
+ marks[key] = { tabId: mark.tabId, url: mark.url, x: mark.x, y: mark.y };
+ this.cache.set(MARK_KEY, marks);
+
+ return Promise.resolve();
+ }
+
+ getOrEmptyMarks() {
+ return this.cache.get(MARK_KEY) || {};
+ }
+}
+
diff --git a/src/background/repositories/PersistentSettingRepository.js b/src/background/repositories/PersistentSettingRepository.js
deleted file mode 100644
index 4cab107..0000000
--- a/src/background/repositories/PersistentSettingRepository.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import Setting from '../domains/Setting';
-
-export default class SettingRepository {
- async load() {
- let { settings } = await browser.storage.local.get('settings');
- if (!settings) {
- return null;
- }
- return Setting.deserialize(settings);
- }
-}
-
diff --git a/src/background/repositories/PersistentSettingRepository.ts b/src/background/repositories/PersistentSettingRepository.ts
new file mode 100644
index 0000000..4cab107
--- /dev/null
+++ b/src/background/repositories/PersistentSettingRepository.ts
@@ -0,0 +1,12 @@
+import Setting from '../domains/Setting';
+
+export default class SettingRepository {
+ async load() {
+ let { settings } = await browser.storage.local.get('settings');
+ if (!settings) {
+ return null;
+ }
+ return Setting.deserialize(settings);
+ }
+}
+
diff --git a/src/background/repositories/SettingRepository.js b/src/background/repositories/SettingRepository.js
deleted file mode 100644
index c4667a9..0000000
--- a/src/background/repositories/SettingRepository.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import MemoryStorage from '../infrastructures/MemoryStorage';
-
-const CACHED_SETTING_KEY = 'setting';
-
-export default class SettingRepository {
- constructor() {
- this.cache = new MemoryStorage();
- }
-
- get() {
- return Promise.resolve(this.cache.get(CACHED_SETTING_KEY));
- }
-
- update(value) {
- return this.cache.set(CACHED_SETTING_KEY, value);
- }
-
- async setProperty(name, value) {
- let current = await this.get();
- current.properties[name] = value;
- return this.update(current);
- }
-}
diff --git a/src/background/repositories/SettingRepository.ts b/src/background/repositories/SettingRepository.ts
new file mode 100644
index 0000000..c4667a9
--- /dev/null
+++ b/src/background/repositories/SettingRepository.ts
@@ -0,0 +1,23 @@
+import MemoryStorage from '../infrastructures/MemoryStorage';
+
+const CACHED_SETTING_KEY = 'setting';
+
+export default class SettingRepository {
+ constructor() {
+ this.cache = new MemoryStorage();
+ }
+
+ get() {
+ return Promise.resolve(this.cache.get(CACHED_SETTING_KEY));
+ }
+
+ update(value) {
+ return this.cache.set(CACHED_SETTING_KEY, value);
+ }
+
+ async setProperty(name, value) {
+ let current = await this.get();
+ current.properties[name] = value;
+ return this.update(current);
+ }
+}
diff --git a/src/background/repositories/VersionRepository.js b/src/background/repositories/VersionRepository.js
deleted file mode 100644
index 4c71d05..0000000
--- a/src/background/repositories/VersionRepository.js
+++ /dev/null
@@ -1,10 +0,0 @@
-export default class VersionRepository {
- async get() {
- let { version } = await browser.storage.local.get('version');
- return version;
- }
-
- update(version) {
- return browser.storage.local.set({ version });
- }
-}
diff --git a/src/background/repositories/VersionRepository.ts b/src/background/repositories/VersionRepository.ts
new file mode 100644
index 0000000..4c71d05
--- /dev/null
+++ b/src/background/repositories/VersionRepository.ts
@@ -0,0 +1,10 @@
+export default class VersionRepository {
+ async get() {
+ let { version } = await browser.storage.local.get('version');
+ return version;
+ }
+
+ update(version) {
+ return browser.storage.local.set({ version });
+ }
+}
diff --git a/src/background/usecases/AddonEnabledUseCase.js b/src/background/usecases/AddonEnabledUseCase.js
deleted file mode 100644
index bb2c347..0000000
--- a/src/background/usecases/AddonEnabledUseCase.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import IndicatorPresenter from '../presenters/IndicatorPresenter';
-import TabPresenter from '../presenters/TabPresenter';
-import ContentMessageClient from '../infrastructures/ContentMessageClient';
-
-export default class AddonEnabledUseCase {
- constructor() {
- this.indicatorPresentor = new IndicatorPresenter();
-
- this.indicatorPresentor.onClick(tab => this.onIndicatorClick(tab.id));
-
- this.tabPresenter = new TabPresenter();
- this.tabPresenter.onSelected(info => this.onTabSelected(info.tabId));
-
- this.contentMessageClient = new ContentMessageClient();
- }
-
- indicate(enabled) {
- return this.indicatorPresentor.indicate(enabled);
- }
-
- onIndicatorClick(tabId) {
- return this.contentMessageClient.toggleAddonEnabled(tabId);
- }
-
- async onTabSelected(tabId) {
- let enabled = await this.contentMessageClient.getAddonEnabled(tabId);
- return this.indicatorPresentor.indicate(enabled);
- }
-}
diff --git a/src/background/usecases/AddonEnabledUseCase.ts b/src/background/usecases/AddonEnabledUseCase.ts
new file mode 100644
index 0000000..bb2c347
--- /dev/null
+++ b/src/background/usecases/AddonEnabledUseCase.ts
@@ -0,0 +1,29 @@
+import IndicatorPresenter from '../presenters/IndicatorPresenter';
+import TabPresenter from '../presenters/TabPresenter';
+import ContentMessageClient from '../infrastructures/ContentMessageClient';
+
+export default class AddonEnabledUseCase {
+ constructor() {
+ this.indicatorPresentor = new IndicatorPresenter();
+
+ this.indicatorPresentor.onClick(tab => this.onIndicatorClick(tab.id));
+
+ this.tabPresenter = new TabPresenter();
+ this.tabPresenter.onSelected(info => this.onTabSelected(info.tabId));
+
+ this.contentMessageClient = new ContentMessageClient();
+ }
+
+ indicate(enabled) {
+ return this.indicatorPresentor.indicate(enabled);
+ }
+
+ onIndicatorClick(tabId) {
+ return this.contentMessageClient.toggleAddonEnabled(tabId);
+ }
+
+ async onTabSelected(tabId) {
+ let enabled = await this.contentMessageClient.getAddonEnabled(tabId);
+ return this.indicatorPresentor.indicate(enabled);
+ }
+}
diff --git a/src/background/usecases/CommandUseCase.js b/src/background/usecases/CommandUseCase.js
deleted file mode 100644
index 9ec46fe..0000000
--- a/src/background/usecases/CommandUseCase.js
+++ /dev/null
@@ -1,125 +0,0 @@
-import * as parsers from './parsers';
-import * as urls from '../../shared/urls';
-import TabPresenter from '../presenters/TabPresenter';
-import WindowPresenter from '../presenters/WindowPresenter';
-import SettingRepository from '../repositories/SettingRepository';
-import BookmarkRepository from '../repositories/BookmarkRepository';
-import ConsoleClient from '../infrastructures/ConsoleClient';
-import ContentMessageClient from '../infrastructures/ContentMessageClient';
-import * as properties from 'shared/settings/properties';
-
-export default class CommandIndicator {
- constructor() {
- this.tabPresenter = new TabPresenter();
- this.windowPresenter = new WindowPresenter();
- this.settingRepository = new SettingRepository();
- this.bookmarkRepository = new BookmarkRepository();
- this.consoleClient = new ConsoleClient();
-
- this.contentMessageClient = new ContentMessageClient();
- }
-
- async open(keywords) {
- let url = await this.urlOrSearch(keywords);
- return this.tabPresenter.open(url);
- }
-
- async tabopen(keywords) {
- let url = await this.urlOrSearch(keywords);
- return this.tabPresenter.create(url);
- }
-
- async winopen(keywords) {
- let url = await this.urlOrSearch(keywords);
- return this.windowPresenter.create(url);
- }
-
- // eslint-disable-next-line max-statements
- async buffer(keywords) {
- if (keywords.length === 0) {
- return;
- }
-
- if (!isNaN(keywords)) {
- let tabs = await this.tabPresenter.getAll();
- let index = parseInt(keywords, 10) - 1;
- if (index < 0 || tabs.length <= index) {
- throw new RangeError(`tab ${index + 1} does not exist`);
- }
- return this.tabPresenter.select(tabs[index].id);
- } else if (keywords.trim() === '%') {
- // Select current window
- return;
- } else if (keywords.trim() === '#') {
- // Select last selected window
- let lastId = await this.tabPresenter.getLastSelectedId();
- if (typeof lastId === 'undefined' || lastId === null) {
- throw new Error('No last selected tab');
- }
- return this.tabPresenter.select(lastId);
- }
-
- let current = await this.tabPresenter.getCurrent();
- let tabs = await this.tabPresenter.getByKeyword(keywords);
- if (tabs.length === 0) {
- throw new RangeError('No matching buffer for ' + keywords);
- }
- for (let tab of tabs) {
- if (tab.index > current.index) {
- return this.tabPresenter.select(tab.id);
- }
- }
- return this.tabPresenter.select(tabs[0].id);
- }
-
- async bdelete(force, keywords) {
- let excludePinned = !force;
- let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned);
- if (tabs.length === 0) {
- throw new Error('No matching buffer for ' + keywords);
- } else if (tabs.length > 1) {
- throw new Error('More than one match for ' + keywords);
- }
- return this.tabPresenter.remove([tabs[0].id]);
- }
-
- async bdeletes(force, keywords) {
- let excludePinned = !force;
- let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned);
- let ids = tabs.map(tab => tab.id);
- return this.tabPresenter.remove(ids);
- }
-
- async quit() {
- let tab = await this.tabPresenter.getCurrent();
- return this.tabPresenter.remove([tab.id]);
- }
-
- async quitAll() {
- let tabs = await this.tabPresenter.getAll();
- let ids = tabs.map(tab => tab.id);
- this.tabPresenter.remove(ids);
- }
-
- async addbookmark(title) {
- let tab = await this.tabPresenter.getCurrent();
- let item = await this.bookmarkRepository.create(title, tab.url);
- let message = 'Saved current page: ' + item.url;
- return this.consoleClient.showInfo(tab.id, message);
- }
-
- async set(keywords) {
- if (keywords.length === 0) {
- return;
- }
- let [name, value] = parsers.parseSetOption(keywords, properties.types);
- await this.settingRepository.setProperty(name, value);
-
- return this.contentMessageClient.broadcastSettingsChanged();
- }
-
- async urlOrSearch(keywords) {
- let settings = await this.settingRepository.get();
- return urls.searchUrl(keywords, settings.search);
- }
-}
diff --git a/src/background/usecases/CommandUseCase.ts b/src/background/usecases/CommandUseCase.ts
new file mode 100644
index 0000000..9ec46fe
--- /dev/null
+++ b/src/background/usecases/CommandUseCase.ts
@@ -0,0 +1,125 @@
+import * as parsers from './parsers';
+import * as urls from '../../shared/urls';
+import TabPresenter from '../presenters/TabPresenter';
+import WindowPresenter from '../presenters/WindowPresenter';
+import SettingRepository from '../repositories/SettingRepository';
+import BookmarkRepository from '../repositories/BookmarkRepository';
+import ConsoleClient from '../infrastructures/ConsoleClient';
+import ContentMessageClient from '../infrastructures/ContentMessageClient';
+import * as properties from 'shared/settings/properties';
+
+export default class CommandIndicator {
+ constructor() {
+ this.tabPresenter = new TabPresenter();
+ this.windowPresenter = new WindowPresenter();
+ this.settingRepository = new SettingRepository();
+ this.bookmarkRepository = new BookmarkRepository();
+ this.consoleClient = new ConsoleClient();
+
+ this.contentMessageClient = new ContentMessageClient();
+ }
+
+ async open(keywords) {
+ let url = await this.urlOrSearch(keywords);
+ return this.tabPresenter.open(url);
+ }
+
+ async tabopen(keywords) {
+ let url = await this.urlOrSearch(keywords);
+ return this.tabPresenter.create(url);
+ }
+
+ async winopen(keywords) {
+ let url = await this.urlOrSearch(keywords);
+ return this.windowPresenter.create(url);
+ }
+
+ // eslint-disable-next-line max-statements
+ async buffer(keywords) {
+ if (keywords.length === 0) {
+ return;
+ }
+
+ if (!isNaN(keywords)) {
+ let tabs = await this.tabPresenter.getAll();
+ let index = parseInt(keywords, 10) - 1;
+ if (index < 0 || tabs.length <= index) {
+ throw new RangeError(`tab ${index + 1} does not exist`);
+ }
+ return this.tabPresenter.select(tabs[index].id);
+ } else if (keywords.trim() === '%') {
+ // Select current window
+ return;
+ } else if (keywords.trim() === '#') {
+ // Select last selected window
+ let lastId = await this.tabPresenter.getLastSelectedId();
+ if (typeof lastId === 'undefined' || lastId === null) {
+ throw new Error('No last selected tab');
+ }
+ return this.tabPresenter.select(lastId);
+ }
+
+ let current = await this.tabPresenter.getCurrent();
+ let tabs = await this.tabPresenter.getByKeyword(keywords);
+ if (tabs.length === 0) {
+ throw new RangeError('No matching buffer for ' + keywords);
+ }
+ for (let tab of tabs) {
+ if (tab.index > current.index) {
+ return this.tabPresenter.select(tab.id);
+ }
+ }
+ return this.tabPresenter.select(tabs[0].id);
+ }
+
+ async bdelete(force, keywords) {
+ let excludePinned = !force;
+ let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned);
+ if (tabs.length === 0) {
+ throw new Error('No matching buffer for ' + keywords);
+ } else if (tabs.length > 1) {
+ throw new Error('More than one match for ' + keywords);
+ }
+ return this.tabPresenter.remove([tabs[0].id]);
+ }
+
+ async bdeletes(force, keywords) {
+ let excludePinned = !force;
+ let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned);
+ let ids = tabs.map(tab => tab.id);
+ return this.tabPresenter.remove(ids);
+ }
+
+ async quit() {
+ let tab = await this.tabPresenter.getCurrent();
+ return this.tabPresenter.remove([tab.id]);
+ }
+
+ async quitAll() {
+ let tabs = await this.tabPresenter.getAll();
+ let ids = tabs.map(tab => tab.id);
+ this.tabPresenter.remove(ids);
+ }
+
+ async addbookmark(title) {
+ let tab = await this.tabPresenter.getCurrent();
+ let item = await this.bookmarkRepository.create(title, tab.url);
+ let message = 'Saved current page: ' + item.url;
+ return this.consoleClient.showInfo(tab.id, message);
+ }
+
+ async set(keywords) {
+ if (keywords.length === 0) {
+ return;
+ }
+ let [name, value] = parsers.parseSetOption(keywords, properties.types);
+ await this.settingRepository.setProperty(name, value);
+
+ return this.contentMessageClient.broadcastSettingsChanged();
+ }
+
+ async urlOrSearch(keywords) {
+ let settings = await this.settingRepository.get();
+ return urls.searchUrl(keywords, settings.search);
+ }
+}
diff --git a/src/background/usecases/CompletionsUseCase.js b/src/background/usecases/CompletionsUseCase.js
deleted file mode 100644
index 7dc30ac..0000000
--- a/src/background/usecases/CompletionsUseCase.js
+++ /dev/null
@@ -1,205 +0,0 @@
-import CompletionItem from '../domains/CompletionItem';
-import CompletionGroup from '../domains/CompletionGroup';
-import Completions from '../domains/Completions';
-import CommandDocs from '../domains/CommandDocs';
-import CompletionsRepository from '../repositories/CompletionsRepository';
-import * as filters from './filters';
-import SettingRepository from '../repositories/SettingRepository';
-import TabPresenter from '../presenters/TabPresenter';
-import * as properties from '../../shared/settings/properties';
-
-const COMPLETION_ITEM_LIMIT = 10;
-
-export default class CompletionsUseCase {
- constructor() {
- this.tabPresenter = new TabPresenter();
- this.completionsRepository = new CompletionsRepository();
- this.settingRepository = new SettingRepository();
- }
-
- queryConsoleCommand(prefix) {
- let keys = Object.keys(CommandDocs);
- let items = keys
- .filter(name => name.startsWith(prefix))
- .map(name => ({
- caption: name,
- content: name,
- url: CommandDocs[name],
- }));
-
- if (items.length === 0) {
- return Promise.resolve(Completions.empty());
- }
- return Promise.resolve(
- new Completions([new CompletionGroup('Console Command', items)])
- );
- }
-
- async queryOpen(name, keywords) {
- let settings = await this.settingRepository.get();
- let groups = [];
-
- let complete = settings.properties.complete || properties.defaults.complete;
- for (let c of complete) {
- if (c === 's') {
- // eslint-disable-next-line no-await-in-loop
- let engines = await this.querySearchEngineItems(name, keywords);
- if (engines.length > 0) {
- groups.push(new CompletionGroup('Search Engines', engines));
- }
- } else if (c === 'h') {
- // eslint-disable-next-line no-await-in-loop
- let histories = await this.queryHistoryItems(name, keywords);
- if (histories.length > 0) {
- groups.push(new CompletionGroup('History', histories));
- }
- } else if (c === 'b') {
- // eslint-disable-next-line no-await-in-loop
- let bookmarks = await this.queryBookmarkItems(name, keywords);
- if (bookmarks.length > 0) {
- groups.push(new CompletionGroup('Bookmarks', bookmarks));
- }
- }
- }
- return new Completions(groups);
- }
-
- // eslint-disable-next-line max-statements
- async queryBuffer(name, keywords) {
- let lastId = await this.tabPresenter.getLastSelectedId();
- let trimmed = keywords.trim();
- let tabs = [];
- if (trimmed.length > 0 && !isNaN(trimmed)) {
- let all = await this.tabPresenter.getAll();
- let index = parseInt(trimmed, 10) - 1;
- if (index >= 0 && index < all.length) {
- tabs = [all[index]];
- }
- } else if (trimmed === '%') {
- let all = await this.tabPresenter.getAll();
- let tab = all.find(t => t.active);
- tabs = [tab];
- } else if (trimmed === '#') {
- if (typeof lastId !== 'undefined' && lastId !== null) {
- let all = await this.tabPresenter.getAll();
- let tab = all.find(t => t.id === lastId);
- tabs = [tab];
- }
- } else {
- tabs = await this.completionsRepository.queryTabs(keywords, false);
- }
- const flag = (tab) => {
- if (tab.active) {
- return '%';
- } else if (tab.id === lastId) {
- return '#';
- }
- return ' ';
- };
- let items = tabs.map(tab => new CompletionItem({
- caption: tab.index + 1 + ': ' + flag(tab) + ' ' + tab.title,
- content: name + ' ' + tab.title,
- url: tab.url,
- icon: tab.favIconUrl
- }));
- if (items.length === 0) {
- return Promise.resolve(Completions.empty());
- }
- return new Completions([new CompletionGroup('Buffers', items)]);
- }
-
- queryBdelete(name, keywords) {
- return this.queryTabs(name, true, keywords);
- }
-
- queryBdeleteForce(name, keywords) {
- return this.queryTabs(name, false, keywords);
- }
-
- querySet(name, keywords) {
- let items = Object.keys(properties.docs).map((key) => {
- if (properties.types[key] === 'boolean') {
- return [
- new CompletionItem({
- caption: key,
- content: name + ' ' + key,
- url: 'Enable ' + properties.docs[key],
- }),
- new CompletionItem({
- caption: 'no' + key,
- content: name + ' no' + key,
- url: 'Disable ' + properties.docs[key],
- }),
- ];
- }
- return [
- new CompletionItem({
- caption: key,
- content: name + ' ' + key,
- url: 'Set ' + properties.docs[key],
- })
- ];
- });
- items = items.reduce((acc, val) => acc.concat(val), []);
- items = items.filter((item) => {
- return item.caption.startsWith(keywords);
- });
- if (items.length === 0) {
- return Promise.resolve(Completions.empty());
- }
- return Promise.resolve(
- new Completions([new CompletionGroup('Properties', items)])
- );
- }
-
- async queryTabs(name, excludePinned, args) {
- let tabs = await this.completionsRepository.queryTabs(args, excludePinned);
- let items = tabs.map(tab => new CompletionItem({
- caption: tab.title,
- content: name + ' ' + tab.title,
- url: tab.url,
- icon: tab.favIconUrl
- }));
- if (items.length === 0) {
- return Promise.resolve(Completions.empty());
- }
- return new Completions([new CompletionGroup('Buffers', items)]);
- }
-
- async querySearchEngineItems(name, keywords) {
- let settings = await this.settingRepository.get();
- let engines = Object.keys(settings.search.engines)
- .filter(key => key.startsWith(keywords));
- return engines.map(key => new CompletionItem({
- caption: key,
- content: name + ' ' + key,
- }));
- }
-
- async queryHistoryItems(name, keywords) {
- let histories = await this.completionsRepository.queryHistories(keywords);
- histories = [histories]
- .map(filters.filterBlankTitle)
- .map(filters.filterHttp)
- .map(filters.filterByTailingSlash)
- .map(pages => filters.filterByPathname(pages, COMPLETION_ITEM_LIMIT))
- .map(pages => filters.filterByOrigin(pages, COMPLETION_ITEM_LIMIT))[0]
- .sort((x, y) => x.visitCount < y.visitCount)
- .slice(0, COMPLETION_ITEM_LIMIT);
- return histories.map(page => new CompletionItem({
- caption: page.title,
- content: name + ' ' + page.url,
- url: page.url
- }));
- }
-
- async queryBookmarkItems(name, keywords) {
- let bookmarks = await this.completionsRepository.queryBookmarks(keywords);
- return bookmarks.slice(0, COMPLETION_ITEM_LIMIT)
- .map(page => new CompletionItem({
- caption: page.title,
- content: name + ' ' + page.url,
- url: page.url
- }));
- }
-}
diff --git a/src/background/usecases/CompletionsUseCase.ts b/src/background/usecases/CompletionsUseCase.ts
new file mode 100644
index 0000000..7dc30ac
--- /dev/null
+++ b/src/background/usecases/CompletionsUseCase.ts
@@ -0,0 +1,205 @@
+import CompletionItem from '../domains/CompletionItem';
+import CompletionGroup from '../domains/CompletionGroup';
+import Completions from '../domains/Completions';
+import CommandDocs from '../domains/CommandDocs';
+import CompletionsRepository from '../repositories/CompletionsRepository';
+import * as filters from './filters';
+import SettingRepository from '../repositories/SettingRepository';
+import TabPresenter from '../presenters/TabPresenter';
+import * as properties from '../../shared/settings/properties';
+
+const COMPLETION_ITEM_LIMIT = 10;
+
+export default class CompletionsUseCase {
+ constructor() {
+ this.tabPresenter = new TabPresenter();
+ this.completionsRepository = new CompletionsRepository();
+ this.settingRepository = new SettingRepository();
+ }
+
+ queryConsoleCommand(prefix) {
+ let keys = Object.keys(CommandDocs);
+ let items = keys
+ .filter(name => name.startsWith(prefix))
+ .map(name => ({
+ caption: name,
+ content: name,
+ url: CommandDocs[name],
+ }));
+
+ if (items.length === 0) {
+ return Promise.resolve(Completions.empty());
+ }
+ return Promise.resolve(
+ new Completions([new CompletionGroup('Console Command', items)])
+ );
+ }
+
+ async queryOpen(name, keywords) {
+ let settings = await this.settingRepository.get();
+ let groups = [];
+
+ let complete = settings.properties.complete || properties.defaults.complete;
+ for (let c of complete) {
+ if (c === 's') {
+ // eslint-disable-next-line no-await-in-loop
+ let engines = await this.querySearchEngineItems(name, keywords);
+ if (engines.length > 0) {
+ groups.push(new CompletionGroup('Search Engines', engines));
+ }
+ } else if (c === 'h') {
+ // eslint-disable-next-line no-await-in-loop
+ let histories = await this.queryHistoryItems(name, keywords);
+ if (histories.length > 0) {
+ groups.push(new CompletionGroup('History', histories));
+ }
+ } else if (c === 'b') {
+ // eslint-disable-next-line no-await-in-loop
+ let bookmarks = await this.queryBookmarkItems(name, keywords);
+ if (bookmarks.length > 0) {
+ groups.push(new CompletionGroup('Bookmarks', bookmarks));
+ }
+ }
+ }
+ return new Completions(groups);
+ }
+
+ // eslint-disable-next-line max-statements
+ async queryBuffer(name, keywords) {
+ let lastId = await this.tabPresenter.getLastSelectedId();
+ let trimmed = keywords.trim();
+ let tabs = [];
+ if (trimmed.length > 0 && !isNaN(trimmed)) {
+ let all = await this.tabPresenter.getAll();
+ let index = parseInt(trimmed, 10) - 1;
+ if (index >= 0 && index < all.length) {
+ tabs = [all[index]];
+ }
+ } else if (trimmed === '%') {
+ let all = await this.tabPresenter.getAll();
+ let tab = all.find(t => t.active);
+ tabs = [tab];
+ } else if (trimmed === '#') {
+ if (typeof lastId !== 'undefined' && lastId !== null) {
+ let all = await this.tabPresenter.getAll();
+ let tab = all.find(t => t.id === lastId);
+ tabs = [tab];
+ }
+ } else {
+ tabs = await this.completionsRepository.queryTabs(keywords, false);
+ }
+ const flag = (tab) => {
+ if (tab.active) {
+ return '%';
+ } else if (tab.id === lastId) {
+ return '#';
+ }
+ return ' ';
+ };
+ let items = tabs.map(tab => new CompletionItem({
+ caption: tab.index + 1 + ': ' + flag(tab) + ' ' + tab.title,
+ content: name + ' ' + tab.title,
+ url: tab.url,
+ icon: tab.favIconUrl
+ }));
+ if (items.length === 0) {
+ return Promise.resolve(Completions.empty());
+ }
+ return new Completions([new CompletionGroup('Buffers', items)]);
+ }
+
+ queryBdelete(name, keywords) {
+ return this.queryTabs(name, true, keywords);
+ }
+
+ queryBdeleteForce(name, keywords) {
+ return this.queryTabs(name, false, keywords);
+ }
+
+ querySet(name, keywords) {
+ let items = Object.keys(properties.docs).map((key) => {
+ if (properties.types[key] === 'boolean') {
+ return [
+ new CompletionItem({
+ caption: key,
+ content: name + ' ' + key,
+ url: 'Enable ' + properties.docs[key],
+ }),
+ new CompletionItem({
+ caption: 'no' + key,
+ content: name + ' no' + key,
+ url: 'Disable ' + properties.docs[key],
+ }),
+ ];
+ }
+ return [
+ new CompletionItem({
+ caption: key,
+ content: name + ' ' + key,
+ url: 'Set ' + properties.docs[key],
+ })
+ ];
+ });
+ items = items.reduce((acc, val) => acc.concat(val), []);
+ items = items.filter((item) => {
+ return item.caption.startsWith(keywords);
+ });
+ if (items.length === 0) {
+ return Promise.resolve(Completions.empty());
+ }
+ return Promise.resolve(
+ new Completions([new CompletionGroup('Properties', items)])
+ );
+ }
+
+ async queryTabs(name, excludePinned, args) {
+ let tabs = await this.completionsRepository.queryTabs(args, excludePinned);
+ let items = tabs.map(tab => new CompletionItem({
+ caption: tab.title,
+ content: name + ' ' + tab.title,
+ url: tab.url,
+ icon: tab.favIconUrl
+ }));
+ if (items.length === 0) {
+ return Promise.resolve(Completions.empty());
+ }
+ return new Completions([new CompletionGroup('Buffers', items)]);
+ }
+
+ async querySearchEngineItems(name, keywords) {
+ let settings = await this.settingRepository.get();
+ let engines = Object.keys(settings.search.engines)
+ .filter(key => key.startsWith(keywords));
+ return engines.map(key => new CompletionItem({
+ caption: key,
+ content: name + ' ' + key,
+ }));
+ }
+
+ async queryHistoryItems(name, keywords) {
+ let histories = await this.completionsRepository.queryHistories(keywords);
+ histories = [histories]
+ .map(filters.filterBlankTitle)
+ .map(filters.filterHttp)
+ .map(filters.filterByTailingSlash)
+ .map(pages => filters.filterByPathname(pages, COMPLETION_ITEM_LIMIT))
+ .map(pages => filters.filterByOrigin(pages, COMPLETION_ITEM_LIMIT))[0]
+ .sort((x, y) => x.visitCount < y.visitCount)
+ .slice(0, COMPLETION_ITEM_LIMIT);
+ return histories.map(page => new CompletionItem({
+ caption: page.title,
+ content: name + ' ' + page.url,
+ url: page.url
+ }));
+ }
+
+ async queryBookmarkItems(name, keywords) {
+ let bookmarks = await this.completionsRepository.queryBookmarks(keywords);
+ return bookmarks.slice(0, COMPLETION_ITEM_LIMIT)
+ .map(page => new CompletionItem({
+ caption: page.title,
+ content: name + ' ' + page.url,
+ url: page.url
+ }));
+ }
+}
diff --git a/src/background/usecases/ConsoleUseCase.js b/src/background/usecases/ConsoleUseCase.js
deleted file mode 100644
index e8e5d4a..0000000
--- a/src/background/usecases/ConsoleUseCase.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import TabPresenter from '../presenters/TabPresenter';
-import ConsoleClient from '../infrastructures/ConsoleClient';
-
-export default class ConsoleUseCase {
- constructor() {
- this.tabPresenter = new TabPresenter();
- this.consoleClient = new ConsoleClient();
- }
-
- async showCommand() {
- let tab = await this.tabPresenter.getCurrent();
- return this.consoleClient.showCommand(tab.id, '');
- }
-
- async showOpenCommand(alter) {
- let tab = await this.tabPresenter.getCurrent();
- let command = 'open ';
- if (alter) {
- command += tab.url;
- }
- return this.consoleClient.showCommand(tab.id, command);
- }
-
- async showTabopenCommand(alter) {
- let tab = await this.tabPresenter.getCurrent();
- let command = 'tabopen ';
- if (alter) {
- command += tab.url;
- }
- return this.consoleClient.showCommand(tab.id, command);
- }
-
- async showWinopenCommand(alter) {
- let tab = await this.tabPresenter.getCurrent();
- let command = 'winopen ';
- if (alter) {
- command += tab.url;
- }
- return this.consoleClient.showCommand(tab.id, command);
- }
-
- async showBufferCommand() {
- let tab = await this.tabPresenter.getCurrent();
- let command = 'buffer ';
- return this.consoleClient.showCommand(tab.id, command);
- }
-
- async showAddbookmarkCommand(alter) {
- let tab = await this.tabPresenter.getCurrent();
- let command = 'addbookmark ';
- if (alter) {
- command += tab.title;
- }
- return this.consoleClient.showCommand(tab.id, command);
- }
-
- async hideConsole() {
- let tab = await this.tabPresenter.getCurrent();
- return this.consoleClient.hide(tab.id);
- }
-}
diff --git a/src/background/usecases/ConsoleUseCase.ts b/src/background/usecases/ConsoleUseCase.ts
new file mode 100644
index 0000000..e8e5d4a
--- /dev/null
+++ b/src/background/usecases/ConsoleUseCase.ts
@@ -0,0 +1,61 @@
+import TabPresenter from '../presenters/TabPresenter';
+import ConsoleClient from '../infrastructures/ConsoleClient';
+
+export default class ConsoleUseCase {
+ constructor() {
+ this.tabPresenter = new TabPresenter();
+ this.consoleClient = new ConsoleClient();
+ }
+
+ async showCommand() {
+ let tab = await this.tabPresenter.getCurrent();
+ return this.consoleClient.showCommand(tab.id, '');
+ }
+
+ async showOpenCommand(alter) {
+ let tab = await this.tabPresenter.getCurrent();
+ let command = 'open ';
+ if (alter) {
+ command += tab.url;
+ }
+ return this.consoleClient.showCommand(tab.id, command);
+ }
+
+ async showTabopenCommand(alter) {
+ let tab = await this.tabPresenter.getCurrent();
+ let command = 'tabopen ';
+ if (alter) {
+ command += tab.url;
+ }
+ return this.consoleClient.showCommand(tab.id, command);
+ }
+
+ async showWinopenCommand(alter) {
+ let tab = await this.tabPresenter.getCurrent();
+ let command = 'winopen ';
+ if (alter) {
+ command += tab.url;
+ }
+ return this.consoleClient.showCommand(tab.id, command);
+ }
+
+ async showBufferCommand() {
+ let tab = await this.tabPresenter.getCurrent();
+ let command = 'buffer ';
+ return this.consoleClient.showCommand(tab.id, command);
+ }
+
+ async showAddbookmarkCommand(alter) {
+ let tab = await this.tabPresenter.getCurrent();
+ let command = 'addbookmark ';
+ if (alter) {
+ command += tab.title;
+ }
+ return this.consoleClient.showCommand(tab.id, command);
+ }
+
+ async hideConsole() {
+ let tab = await this.tabPresenter.getCurrent();
+ return this.consoleClient.hide(tab.id);
+ }
+}
diff --git a/src/background/usecases/FindUseCase.js b/src/background/usecases/FindUseCase.js
deleted file mode 100644
index 224e4a9..0000000
--- a/src/background/usecases/FindUseCase.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import FindRepository from '../repositories/FindRepository';
-import TabPresenter from '../presenters/TabPresenter';
-import ConsoleClient from '../infrastructures/ConsoleClient';
-
-export default class FindUseCase {
- constructor() {
- this.tabPresenter = new TabPresenter();
- this.findRepository = new FindRepository();
- this.consoleClient = new ConsoleClient();
- }
-
- getKeyword() {
- return this.findRepository.getKeyword();
- }
-
- setKeyword(keyword) {
- return this.findRepository.setKeyword(keyword);
- }
-
- async findStart() {
- let tab = await this.tabPresenter.getCurrent();
- return this.consoleClient.showFind(tab.id);
- }
-}
diff --git a/src/background/usecases/FindUseCase.ts b/src/background/usecases/FindUseCase.ts
new file mode 100644
index 0000000..224e4a9
--- /dev/null
+++ b/src/background/usecases/FindUseCase.ts
@@ -0,0 +1,24 @@
+import FindRepository from '../repositories/FindRepository';
+import TabPresenter from '../presenters/TabPresenter';
+import ConsoleClient from '../infrastructures/ConsoleClient';
+
+export default class FindUseCase {
+ constructor() {
+ this.tabPresenter = new TabPresenter();
+ this.findRepository = new FindRepository();
+ this.consoleClient = new ConsoleClient();
+ }
+
+ getKeyword() {
+ return this.findRepository.getKeyword();
+ }
+
+ setKeyword(keyword) {
+ return this.findRepository.setKeyword(keyword);
+ }
+
+ async findStart() {
+ let tab = await this.tabPresenter.getCurrent();
+ return this.consoleClient.showFind(tab.id);
+ }
+}
diff --git a/src/background/usecases/LinkUseCase.js b/src/background/usecases/LinkUseCase.js
deleted file mode 100644
index 89412c5..0000000
--- a/src/background/usecases/LinkUseCase.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import SettingRepository from '../repositories/SettingRepository';
-import TabPresenter from '../presenters/TabPresenter';
-
-export default class LinkUseCase {
- constructor() {
- this.settingRepository = new SettingRepository();
- this.tabPresenter = new TabPresenter();
- }
-
- openToTab(url, tabId) {
- return this.tabPresenter.open(url, tabId);
- }
-
- openNewTab(url, openerId, background) {
- return this.tabPresenter.create(url, {
- openerTabId: openerId, active: !background
- });
- }
-}
diff --git a/src/background/usecases/LinkUseCase.ts b/src/background/usecases/LinkUseCase.ts
new file mode 100644
index 0000000..89412c5
--- /dev/null
+++ b/src/background/usecases/LinkUseCase.ts
@@ -0,0 +1,19 @@
+import SettingRepository from '../repositories/SettingRepository';
+import TabPresenter from '../presenters/TabPresenter';
+
+export default class LinkUseCase {
+ constructor() {
+ this.settingRepository = new SettingRepository();
+ this.tabPresenter = new TabPresenter();
+ }
+
+ openToTab(url, tabId) {
+ return this.tabPresenter.open(url, tabId);
+ }
+
+ openNewTab(url, openerId, background) {
+ return this.tabPresenter.create(url, {
+ openerTabId: openerId, active: !background
+ });
+ }
+}
diff --git a/src/background/usecases/MarkUseCase.js b/src/background/usecases/MarkUseCase.js
deleted file mode 100644
index 39c796b..0000000
--- a/src/background/usecases/MarkUseCase.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import GlobalMark from '../domains/GlobalMark';
-import TabPresenter from '../presenters/TabPresenter';
-import MarkRepository from '../repositories/MarkRepository';
-import ConsoleClient from '../infrastructures/ConsoleClient';
-import ContentMessageClient from '../infrastructures/ContentMessageClient';
-
-export default class MarkUseCase {
- constructor() {
- this.tabPresenter = new TabPresenter();
- this.markRepository = new MarkRepository();
- this.consoleClient = new ConsoleClient();
- this.contentMessageClient = new ContentMessageClient();
- }
-
- async setGlobal(key, x, y) {
- let tab = await this.tabPresenter.getCurrent();
- let mark = new GlobalMark(tab.id, tab.url, x, y);
- return this.markRepository.setMark(key, mark);
- }
-
- async jumpGlobal(key) {
- let current = await this.tabPresenter.getCurrent();
-
- let mark = await this.markRepository.getMark(key);
- if (!mark) {
- return this.consoleClient.showError(current.id, 'Mark is not set');
- }
-
- return this.contentMessageClient.scrollTo(
- mark.tabId, mark.x, mark.y
- ).then(() => {
- return this.tabPresenter.select(mark.tabId);
- }).catch(async() => {
- let tab = await this.tabPresenter.create(mark.url);
- let mark2 = new GlobalMark(tab.id, mark.url, mark.x, mark.y);
- return this.markRepository.setMark(key, mark2);
- });
- }
-}
diff --git a/src/background/usecases/MarkUseCase.ts b/src/background/usecases/MarkUseCase.ts
new file mode 100644
index 0000000..39c796b
--- /dev/null
+++ b/src/background/usecases/MarkUseCase.ts
@@ -0,0 +1,39 @@
+import GlobalMark from '../domains/GlobalMark';
+import TabPresenter from '../presenters/TabPresenter';
+import MarkRepository from '../repositories/MarkRepository';
+import ConsoleClient from '../infrastructures/ConsoleClient';
+import ContentMessageClient from '../infrastructures/ContentMessageClient';
+
+export default class MarkUseCase {
+ constructor() {
+ this.tabPresenter = new TabPresenter();
+ this.markRepository = new MarkRepository();
+ this.consoleClient = new ConsoleClient();
+ this.contentMessageClient = new ContentMessageClient();
+ }
+
+ async setGlobal(key, x, y) {
+ let tab = await this.tabPresenter.getCurrent();
+ let mark = new GlobalMark(tab.id, tab.url, x, y);
+ return this.markRepository.setMark(key, mark);
+ }
+
+ async jumpGlobal(key) {
+ let current = await this.tabPresenter.getCurrent();
+
+ let mark = await this.markRepository.getMark(key);
+ if (!mark) {
+ return this.consoleClient.showError(current.id, 'Mark is not set');
+ }
+
+ return this.contentMessageClient.scrollTo(
+ mark.tabId, mark.x, mark.y
+ ).then(() => {
+ return this.tabPresenter.select(mark.tabId);
+ }).catch(async() => {
+ let tab = await this.tabPresenter.create(mark.url);
+ let mark2 = new GlobalMark(tab.id, mark.url, mark.x, mark.y);
+ return this.markRepository.setMark(key, mark2);
+ });
+ }
+}
diff --git a/src/background/usecases/SettingUseCase.js b/src/background/usecases/SettingUseCase.js
deleted file mode 100644
index 9e17408..0000000
--- a/src/background/usecases/SettingUseCase.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import Setting from '../domains/Setting';
-// eslint-disable-next-line max-len
-import PersistentSettingRepository from '../repositories/PersistentSettingRepository';
-import SettingRepository from '../repositories/SettingRepository';
-
-export default class SettingUseCase {
- constructor() {
- this.persistentSettingRepository = new PersistentSettingRepository();
- this.settingRepository = new SettingRepository();
- }
-
- get() {
- return this.settingRepository.get();
- }
-
- async reload() {
- let settings = await this.persistentSettingRepository.load();
- if (!settings) {
- settings = Setting.defaultSettings();
- }
-
- let value = settings.value();
-
- this.settingRepository.update(value);
-
- return value;
- }
-}
diff --git a/src/background/usecases/SettingUseCase.ts b/src/background/usecases/SettingUseCase.ts
new file mode 100644
index 0000000..9e17408
--- /dev/null
+++ b/src/background/usecases/SettingUseCase.ts
@@ -0,0 +1,28 @@
+import Setting from '../domains/Setting';
+// eslint-disable-next-line max-len
+import PersistentSettingRepository from '../repositories/PersistentSettingRepository';
+import SettingRepository from '../repositories/SettingRepository';
+
+export default class SettingUseCase {
+ constructor() {
+ this.persistentSettingRepository = new PersistentSettingRepository();
+ this.settingRepository = new SettingRepository();
+ }
+
+ get() {
+ return this.settingRepository.get();
+ }
+
+ async reload() {
+ let settings = await this.persistentSettingRepository.load();
+ if (!settings) {
+ settings = Setting.defaultSettings();
+ }
+
+ let value = settings.value();
+
+ this.settingRepository.update(value);
+
+ return value;
+ }
+}
diff --git a/src/background/usecases/TabSelectUseCase.js b/src/background/usecases/TabSelectUseCase.js
deleted file mode 100644
index 16b3e14..0000000
--- a/src/background/usecases/TabSelectUseCase.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import TabPresenter from '../presenters/TabPresenter';
-
-export default class TabSelectUseCase {
- constructor() {
- this.tabPresenter = new TabPresenter();
- }
-
- async selectPrev(count) {
- let tabs = await this.tabPresenter.getAll();
- if (tabs.length < 2) {
- return;
- }
- let tab = tabs.find(t => t.active);
- if (!tab) {
- return;
- }
- let select = (tab.index - count + tabs.length) % tabs.length;
- return this.tabPresenter.select(tabs[select].id);
- }
-
- async selectNext(count) {
- let tabs = await this.tabPresenter.getAll();
- if (tabs.length < 2) {
- return;
- }
- let tab = tabs.find(t => t.active);
- if (!tab) {
- return;
- }
- let select = (tab.index + count) % tabs.length;
- return this.tabPresenter.select(tabs[select].id);
- }
-
- async selectFirst() {
- let tabs = await this.tabPresenter.getAll();
- return this.tabPresenter.select(tabs[0].id);
- }
-
- async selectLast() {
- let tabs = await this.tabPresenter.getAll();
- return this.tabPresenter.select(tabs[tabs.length - 1].id);
- }
-
- async selectPrevSelected() {
- let tabId = await this.tabPresenter.getLastSelectedId();
- if (tabId === null || typeof tabId === 'undefined') {
- return;
- }
- this.tabPresenter.select(tabId);
- }
-}
diff --git a/src/background/usecases/TabSelectUseCase.ts b/src/background/usecases/TabSelectUseCase.ts
new file mode 100644
index 0000000..16b3e14
--- /dev/null
+++ b/src/background/usecases/TabSelectUseCase.ts
@@ -0,0 +1,51 @@
+import TabPresenter from '../presenters/TabPresenter';
+
+export default class TabSelectUseCase {
+ constructor() {
+ this.tabPresenter = new TabPresenter();
+ }
+
+ async selectPrev(count) {
+ let tabs = await this.tabPresenter.getAll();
+ if (tabs.length < 2) {
+ return;
+ }
+ let tab = tabs.find(t => t.active);
+ if (!tab) {
+ return;
+ }
+ let select = (tab.index - count + tabs.length) % tabs.length;
+ return this.tabPresenter.select(tabs[select].id);
+ }
+
+ async selectNext(count) {
+ let tabs = await this.tabPresenter.getAll();
+ if (tabs.length < 2) {
+ return;
+ }
+ let tab = tabs.find(t => t.active);
+ if (!tab) {
+ return;
+ }
+ let select = (tab.index + count) % tabs.length;
+ return this.tabPresenter.select(tabs[select].id);
+ }
+
+ async selectFirst() {
+ let tabs = await this.tabPresenter.getAll();
+ return this.tabPresenter.select(tabs[0].id);
+ }
+
+ async selectLast() {
+ let tabs = await this.tabPresenter.getAll();
+ return this.tabPresenter.select(tabs[tabs.length - 1].id);
+ }
+
+ async selectPrevSelected() {
+ let tabId = await this.tabPresenter.getLastSelectedId();
+ if (tabId === null || typeof tabId === 'undefined') {
+ return;
+ }
+ this.tabPresenter.select(tabId);
+ }
+}
diff --git a/src/background/usecases/TabUseCase.js b/src/background/usecases/TabUseCase.js
deleted file mode 100644
index d930842..0000000
--- a/src/background/usecases/TabUseCase.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import TabPresenter from '../presenters/TabPresenter';
-import BrowserSettingRepository from '../repositories/BrowserSettingRepository';
-
-export default class TabUseCase {
- constructor() {
- this.tabPresenter = new TabPresenter();
- this.browserSettingRepository = new BrowserSettingRepository();
- }
-
- async close(force) {
- let tab = await this.tabPresenter.getCurrent();
- if (!force && tab.pinned) {
- return;
- }
- return this.tabPresenter.remove([tab.id]);
- }
-
- async closeRight() {
- let tabs = await this.tabPresenter.getAll();
- tabs.sort((t1, t2) => t1.index - t2.index);
- let index = tabs.findIndex(t => t.active);
- if (index < 0) {
- return;
- }
- for (let i = index + 1; i < tabs.length; ++i) {
- let tab = tabs[i];
- if (!tab.pinned) {
- this.tabPresenter.remove(tab.id);
- }
- }
- }
-
- reopen() {
- return this.tabPresenter.reopen();
- }
-
- async reload(cache) {
- let tab = await this.tabPresenter.getCurrent();
- return this.tabPresenter.reload(tab.id, cache);
- }
-
- async setPinned(pinned) {
- let tab = await this.tabPresenter.getCurrent();
- return this.tabPresenter.setPinned(tab.id, pinned);
- }
-
- async togglePinned() {
- let tab = await this.tabPresenter.getCurrent();
- return this.tabPresenter.setPinned(tab.id, !tab.pinned);
- }
-
- async duplicate() {
- let tab = await this.tabPresenter.getCurrent();
- return this.tabPresenter.duplicate(tab.id);
- }
-
- async openPageSource() {
- let tab = await this.tabPresenter.getCurrent();
- let url = 'view-source:' + tab.url;
- return this.tabPresenter.create(url);
- }
-
- async openHome(newTab) {
- let tab = await this.tabPresenter.getCurrent();
- let urls = await this.browserSettingRepository.getHomepageUrls();
- if (urls.length === 1 && urls[0] === 'about:home') {
- // eslint-disable-next-line max-len
- throw new Error('Cannot open Firefox Home (about:home) by WebExtensions, set your custom URLs');
- }
- if (urls.length === 1 && !newTab) {
- return this.tabPresenter.open(urls[0], tab.id);
- }
- for (let url of urls) {
- this.tabPresenter.create(url);
- }
- }
-}
diff --git a/src/background/usecases/TabUseCase.ts b/src/background/usecases/TabUseCase.ts
new file mode 100644
index 0000000..d930842
--- /dev/null
+++ b/src/background/usecases/TabUseCase.ts
@@ -0,0 +1,77 @@
+import TabPresenter from '../presenters/TabPresenter';
+import BrowserSettingRepository from '../repositories/BrowserSettingRepository';
+
+export default class TabUseCase {
+ constructor() {
+ this.tabPresenter = new TabPresenter();
+ this.browserSettingRepository = new BrowserSettingRepository();
+ }
+
+ async close(force) {
+ let tab = await this.tabPresenter.getCurrent();
+ if (!force && tab.pinned) {
+ return;
+ }
+ return this.tabPresenter.remove([tab.id]);
+ }
+
+ async closeRight() {
+ let tabs = await this.tabPresenter.getAll();
+ tabs.sort((t1, t2) => t1.index - t2.index);
+ let index = tabs.findIndex(t => t.active);
+ if (index < 0) {
+ return;
+ }
+ for (let i = index + 1; i < tabs.length; ++i) {
+ let tab = tabs[i];
+ if (!tab.pinned) {
+ this.tabPresenter.remove(tab.id);
+ }
+ }
+ }
+
+ reopen() {
+ return this.tabPresenter.reopen();
+ }
+
+ async reload(cache) {
+ let tab = await this.tabPresenter.getCurrent();
+ return this.tabPresenter.reload(tab.id, cache);
+ }
+
+ async setPinned(pinned) {
+ let tab = await this.tabPresenter.getCurrent();
+ return this.tabPresenter.setPinned(tab.id, pinned);
+ }
+
+ async togglePinned() {
+ let tab = await this.tabPresenter.getCurrent();
+ return this.tabPresenter.setPinned(tab.id, !tab.pinned);
+ }
+
+ async duplicate() {
+ let tab = await this.tabPresenter.getCurrent();
+ return this.tabPresenter.duplicate(tab.id);
+ }
+
+ async openPageSource() {
+ let tab = await this.tabPresenter.getCurrent();
+ let url = 'view-source:' + tab.url;
+ return this.tabPresenter.create(url);
+ }
+
+ async openHome(newTab) {
+ let tab = await this.tabPresenter.getCurrent();
+ let urls = await this.browserSettingRepository.getHomepageUrls();
+ if (urls.length === 1 && urls[0] === 'about:home') {
+ // eslint-disable-next-line max-len
+ throw new Error('Cannot open Firefox Home (about:home) by WebExtensions, set your custom URLs');
+ }
+ if (urls.length === 1 && !newTab) {
+ return this.tabPresenter.open(urls[0], tab.id);
+ }
+ for (let url of urls) {
+ this.tabPresenter.create(url);
+ }
+ }
+}
diff --git a/src/background/usecases/VersionUseCase.js b/src/background/usecases/VersionUseCase.js
deleted file mode 100644
index ed5112b..0000000
--- a/src/background/usecases/VersionUseCase.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import manifest from '../../../manifest.json';
-import TabPresenter from '../presenters/TabPresenter';
-import NotifyPresenter from '../presenters/NotifyPresenter';
-
-export default class VersionUseCase {
- constructor() {
- this.tabPresenter = new TabPresenter();
- this.notifyPresenter = new NotifyPresenter();
- }
-
- notify() {
- let title = `Vim Vixen ${manifest.version} has been installed`;
- let message = 'Click here to see release notes';
- let url = this.releaseNoteUrl(manifest.version);
- this.notifyPresenter.notify(title, message, () => {
- this.tabPresenter.create(url);
- });
- }
-
- releaseNoteUrl(version) {
- if (version) {
- return `https://github.com/ueokande/vim-vixen/releases/tag/${version}`;
- }
- return 'https://github.com/ueokande/vim-vixen/releases/';
- }
-}
diff --git a/src/background/usecases/VersionUseCase.ts b/src/background/usecases/VersionUseCase.ts
new file mode 100644
index 0000000..ed5112b
--- /dev/null
+++ b/src/background/usecases/VersionUseCase.ts
@@ -0,0 +1,26 @@
+import manifest from '../../../manifest.json';
+import TabPresenter from '../presenters/TabPresenter';
+import NotifyPresenter from '../presenters/NotifyPresenter';
+
+export default class VersionUseCase {
+ constructor() {
+ this.tabPresenter = new TabPresenter();
+ this.notifyPresenter = new NotifyPresenter();
+ }
+
+ notify() {
+ let title = `Vim Vixen ${manifest.version} has been installed`;
+ let message = 'Click here to see release notes';
+ let url = this.releaseNoteUrl(manifest.version);
+ this.notifyPresenter.notify(title, message, () => {
+ this.tabPresenter.create(url);
+ });
+ }
+
+ releaseNoteUrl(version) {
+ if (version) {
+ return `https://github.com/ueokande/vim-vixen/releases/tag/${version}`;
+ }
+ return 'https://github.com/ueokande/vim-vixen/releases/';
+ }
+}
diff --git a/src/background/usecases/ZoomUseCase.js b/src/background/usecases/ZoomUseCase.js
deleted file mode 100644
index 692d6d9..0000000
--- a/src/background/usecases/ZoomUseCase.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import TabPresenter from '../presenters/TabPresenter';
-
-const ZOOM_SETTINGS = [
- 0.33, 0.50, 0.66, 0.75, 0.80, 0.90, 1.00,
- 1.10, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00
-];
-
-export default class ZoomUseCase {
- constructor() {
- this.tabPresenter = new TabPresenter();
- }
-
- async zoomIn(tabId) {
- let tab = await this.tabPresenter.getCurrent();
- let current = await this.tabPresenter.getZoom(tab.id);
- let factor = ZOOM_SETTINGS.find(f => f > current);
- if (factor) {
- return this.tabPresenter.setZoom(tabId, factor);
- }
- }
-
- async zoomOut(tabId) {
- let tab = await this.tabPresenter.getCurrent();
- let current = await this.tabPresenter.getZoom(tab.id);
- let factor = [].concat(ZOOM_SETTINGS).reverse().find(f => f < current);
- if (factor) {
- return this.tabPresenter.setZoom(tabId, factor);
- }
- }
-
- zoomNutoral(tabId) {
- return this.tabPresenter.setZoom(tabId, 1);
- }
-
-}
diff --git a/src/background/usecases/ZoomUseCase.ts b/src/background/usecases/ZoomUseCase.ts
new file mode 100644
index 0000000..692d6d9
--- /dev/null
+++ b/src/background/usecases/ZoomUseCase.ts
@@ -0,0 +1,35 @@
+import TabPresenter from '../presenters/TabPresenter';
+
+const ZOOM_SETTINGS = [
+ 0.33, 0.50, 0.66, 0.75, 0.80, 0.90, 1.00,
+ 1.10, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00
+];
+
+export default class ZoomUseCase {
+ constructor() {
+ this.tabPresenter = new TabPresenter();
+ }
+
+ async zoomIn(tabId) {
+ let tab = await this.tabPresenter.getCurrent();
+ let current = await this.tabPresenter.getZoom(tab.id);
+ let factor = ZOOM_SETTINGS.find(f => f > current);
+ if (factor) {
+ return this.tabPresenter.setZoom(tabId, factor);
+ }
+ }
+
+ async zoomOut(tabId) {
+ let tab = await this.tabPresenter.getCurrent();
+ let current = await this.tabPresenter.getZoom(tab.id);
+ let factor = [].concat(ZOOM_SETTINGS).reverse().find(f => f < current);
+ if (factor) {
+ return this.tabPresenter.setZoom(tabId, factor);
+ }
+ }
+
+ zoomNutoral(tabId) {
+ return this.tabPresenter.setZoom(tabId, 1);
+ }
+
+}
diff --git a/src/background/usecases/filters.js b/src/background/usecases/filters.js
deleted file mode 100644
index d057dca..0000000
--- a/src/background/usecases/filters.js
+++ /dev/null
@@ -1,72 +0,0 @@
-const filterHttp = (items) => {
- let httpsHosts = items.map(x => new URL(x.url))
- .filter(x => x.protocol === 'https:')
- .map(x => x.host);
- httpsHosts = new Set(httpsHosts);
-
- return items.filter((item) => {
- let url = new URL(item.url);
- return url.protocol === 'https:' || !httpsHosts.has(url.host);
- });
-};
-
-const filterBlankTitle = (items) => {
- return items.filter(item => item.title && item.title !== '');
-};
-
-const filterByTailingSlash = (items) => {
- let urls = items.map(item => new URL(item.url));
- let simplePaths = urls
- .filter(url => url.hash === '' && url.search === '')
- .map(url => url.origin + url.pathname);
- simplePaths = new Set(simplePaths);
-
- return items.filter((item) => {
- let url = new URL(item.url);
- if (url.hash !== '' || url.search !== '' ||
- url.pathname.slice(-1) !== '/') {
- return true;
- }
- return !simplePaths.has(url.origin + url.pathname.slice(0, -1));
- });
-};
-
-const filterByPathname = (items, min) => {
- let hash = {};
- for (let item of items) {
- let url = new URL(item.url);
- let pathname = url.origin + url.pathname;
- if (!hash[pathname]) {
- hash[pathname] = item;
- } else if (hash[pathname].url.length > item.url.length) {
- hash[pathname] = item;
- }
- }
- let filtered = Object.values(hash);
- if (filtered.length < min) {
- return items;
- }
- return filtered;
-};
-
-const filterByOrigin = (items, min) => {
- let hash = {};
- for (let item of items) {
- let origin = new URL(item.url).origin;
- if (!hash[origin]) {
- hash[origin] = item;
- } else if (hash[origin].url.length > item.url.length) {
- hash[origin] = item;
- }
- }
- let filtered = Object.values(hash);
- if (filtered.length < min) {
- return items;
- }
- return filtered;
-};
-
-export {
- filterHttp, filterBlankTitle, filterByTailingSlash,
- filterByPathname, filterByOrigin
-};
diff --git a/src/background/usecases/filters.ts b/src/background/usecases/filters.ts
new file mode 100644
index 0000000..d057dca
--- /dev/null
+++ b/src/background/usecases/filters.ts
@@ -0,0 +1,72 @@
+const filterHttp = (items) => {
+ let httpsHosts = items.map(x => new URL(x.url))
+ .filter(x => x.protocol === 'https:')
+ .map(x => x.host);
+ httpsHosts = new Set(httpsHosts);
+
+ return items.filter((item) => {
+ let url = new URL(item.url);
+ return url.protocol === 'https:' || !httpsHosts.has(url.host);
+ });
+};
+
+const filterBlankTitle = (items) => {
+ return items.filter(item => item.title && item.title !== '');
+};
+
+const filterByTailingSlash = (items) => {
+ let urls = items.map(item => new URL(item.url));
+ let simplePaths = urls
+ .filter(url => url.hash === '' && url.search === '')
+ .map(url => url.origin + url.pathname);
+ simplePaths = new Set(simplePaths);
+
+ return items.filter((item) => {
+ let url = new URL(item.url);
+ if (url.hash !== '' || url.search !== '' ||
+ url.pathname.slice(-1) !== '/') {
+ return true;
+ }
+ return !simplePaths.has(url.origin + url.pathname.slice(0, -1));
+ });
+};
+
+const filterByPathname = (items, min) => {
+ let hash = {};
+ for (let item of items) {
+ let url = new URL(item.url);
+ let pathname = url.origin + url.pathname;
+ if (!hash[pathname]) {
+ hash[pathname] = item;
+ } else if (hash[pathname].url.length > item.url.length) {
+ hash[pathname] = item;
+ }
+ }
+ let filtered = Object.values(hash);
+ if (filtered.length < min) {
+ return items;
+ }
+ return filtered;
+};
+
+const filterByOrigin = (items, min) => {
+ let hash = {};
+ for (let item of items) {
+ let origin = new URL(item.url).origin;
+ if (!hash[origin]) {
+ hash[origin] = item;
+ } else if (hash[origin].url.length > item.url.length) {
+ hash[origin] = item;
+ }
+ }
+ let filtered = Object.values(hash);
+ if (filtered.length < min) {
+ return items;
+ }
+ return filtered;
+};
+
+export {
+ filterHttp, filterBlankTitle, filterByTailingSlash,
+ filterByPathname, filterByOrigin
+};
diff --git a/src/background/usecases/parsers.js b/src/background/usecases/parsers.js
deleted file mode 100644
index 43c8177..0000000
--- a/src/background/usecases/parsers.js
+++ /dev/null
@@ -1,31 +0,0 @@
-const mustNumber = (v) => {
- let num = Number(v);
- if (isNaN(num)) {
- throw new Error('Not number: ' + v);
- }
- return num;
-};
-
-const parseSetOption = (word, types) => {
- let [key, value] = word.split('=');
- if (value === undefined) {
- value = !key.startsWith('no');
- key = value ? key : key.slice(2);
- }
- let type = types[key];
- if (!type) {
- throw new Error('Unknown property: ' + key);
- }
- if (type === 'boolean' && typeof value !== 'boolean' ||
- type !== 'boolean' && typeof value === 'boolean') {
- throw new Error('Invalid argument: ' + word);
- }
-
- switch (type) {
- case 'string': return [key, value];
- case 'number': return [key, mustNumber(value)];
- case 'boolean': return [key, value];
- }
-};
-
-export { parseSetOption };
diff --git a/src/background/usecases/parsers.ts b/src/background/usecases/parsers.ts
new file mode 100644
index 0000000..43c8177
--- /dev/null
+++ b/src/background/usecases/parsers.ts
@@ -0,0 +1,31 @@
+const mustNumber = (v) => {
+ let num = Number(v);
+ if (isNaN(num)) {
+ throw new Error('Not number: ' + v);
+ }
+ return num;
+};
+
+const parseSetOption = (word, types) => {
+ let [key, value] = word.split('=');
+ if (value === undefined) {
+ value = !key.startsWith('no');
+ key = value ? key : key.slice(2);
+ }
+ let type = types[key];
+ if (!type) {
+ throw new Error('Unknown property: ' + key);
+ }
+ if (type === 'boolean' && typeof value !== 'boolean' ||
+ type !== 'boolean' && typeof value === 'boolean') {
+ throw new Error('Invalid argument: ' + word);
+ }
+
+ switch (type) {
+ case 'string': return [key, value];
+ case 'number': return [key, mustNumber(value)];
+ case 'boolean': return [key, value];
+ }
+};
+
+export { parseSetOption };
diff --git a/src/console/actions/console.js b/src/console/actions/console.js
deleted file mode 100644
index 3713a76..0000000
--- a/src/console/actions/console.js
+++ /dev/null
@@ -1,96 +0,0 @@
-import messages from 'shared/messages';
-import actions from 'console/actions';
-
-const hide = () => {
- return {
- type: actions.CONSOLE_HIDE,
- };
-};
-
-const showCommand = (text) => {
- return {
- type: actions.CONSOLE_SHOW_COMMAND,
- text: text
- };
-};
-
-const showFind = () => {
- return {
- type: actions.CONSOLE_SHOW_FIND,
- };
-};
-
-const showError = (text) => {
- return {
- type: actions.CONSOLE_SHOW_ERROR,
- text: text
- };
-};
-
-const showInfo = (text) => {
- return {
- type: actions.CONSOLE_SHOW_INFO,
- text: text
- };
-};
-
-const hideCommand = () => {
- window.top.postMessage(JSON.stringify({
- type: messages.CONSOLE_UNFOCUS,
- }), '*');
- return {
- type: actions.CONSOLE_HIDE_COMMAND,
- };
-};
-
-const enterCommand = async(text) => {
- await browser.runtime.sendMessage({
- type: messages.CONSOLE_ENTER_COMMAND,
- text,
- });
- return hideCommand(text);
-};
-
-const enterFind = (text) => {
- window.top.postMessage(JSON.stringify({
- type: messages.CONSOLE_ENTER_FIND,
- text,
- }), '*');
- return hideCommand();
-};
-
-const setConsoleText = (consoleText) => {
- return {
- type: actions.CONSOLE_SET_CONSOLE_TEXT,
- consoleText,
- };
-};
-
-const getCompletions = async(text) => {
- let completions = await browser.runtime.sendMessage({
- type: messages.CONSOLE_QUERY_COMPLETIONS,
- text,
- });
- return {
- type: actions.CONSOLE_SET_COMPLETIONS,
- completions,
- completionSource: text,
- };
-};
-
-const completionNext = () => {
- return {
- type: actions.CONSOLE_COMPLETION_NEXT,
- };
-};
-
-const completionPrev = () => {
- return {
- type: actions.CONSOLE_COMPLETION_PREV,
- };
-};
-
-export {
- hide, showCommand, showFind, showError, showInfo, hideCommand, setConsoleText,
- enterCommand, enterFind, getCompletions, completionNext, completionPrev
-};
diff --git a/src/console/actions/console.ts b/src/console/actions/console.ts
new file mode 100644
index 0000000..3713a76
--- /dev/null
+++ b/src/console/actions/console.ts
@@ -0,0 +1,96 @@
+import messages from 'shared/messages';
+import actions from 'console/actions';
+
+const hide = () => {
+ return {
+ type: actions.CONSOLE_HIDE,
+ };
+};
+
+const showCommand = (text) => {
+ return {
+ type: actions.CONSOLE_SHOW_COMMAND,
+ text: text
+ };
+};
+
+const showFind = () => {
+ return {
+ type: actions.CONSOLE_SHOW_FIND,
+ };
+};
+
+const showError = (text) => {
+ return {
+ type: actions.CONSOLE_SHOW_ERROR,
+ text: text
+ };
+};
+
+const showInfo = (text) => {
+ return {
+ type: actions.CONSOLE_SHOW_INFO,
+ text: text
+ };
+};
+
+const hideCommand = () => {
+ window.top.postMessage(JSON.stringify({
+ type: messages.CONSOLE_UNFOCUS,
+ }), '*');
+ return {
+ type: actions.CONSOLE_HIDE_COMMAND,
+ };
+};
+
+const enterCommand = async(text) => {
+ await browser.runtime.sendMessage({
+ type: messages.CONSOLE_ENTER_COMMAND,
+ text,
+ });
+ return hideCommand(text);
+};
+
+const enterFind = (text) => {
+ window.top.postMessage(JSON.stringify({
+ type: messages.CONSOLE_ENTER_FIND,
+ text,
+ }), '*');
+ return hideCommand();
+};
+
+const setConsoleText = (consoleText) => {
+ return {
+ type: actions.CONSOLE_SET_CONSOLE_TEXT,
+ consoleText,
+ };
+};
+
+const getCompletions = async(text) => {
+ let completions = await browser.runtime.sendMessage({
+ type: messages.CONSOLE_QUERY_COMPLETIONS,
+ text,
+ });
+ return {
+ type: actions.CONSOLE_SET_COMPLETIONS,
+ completions,
+ completionSource: text,
+ };
+};
+
+const completionNext = () => {
+ return {
+ type: actions.CONSOLE_COMPLETION_NEXT,
+ };
+};
+
+const completionPrev = () => {
+ return {
+ type: actions.CONSOLE_COMPLETION_PREV,
+ };
+};
+
+export {
+ hide, showCommand, showFind, showError, showInfo, hideCommand, setConsoleText,
+ enterCommand, enterFind, getCompletions, completionNext, completionPrev
+};
diff --git a/src/console/actions/index.js b/src/console/actions/index.js
deleted file mode 100644
index b394179..0000000
--- a/src/console/actions/index.js
+++ /dev/null
@@ -1,13 +0,0 @@
-export default {
- // console commands
- CONSOLE_HIDE: 'console.hide',
- CONSOLE_SHOW_COMMAND: 'console.show.command',
- CONSOLE_SHOW_ERROR: 'console.show.error',
- CONSOLE_SHOW_INFO: 'console.show.info',
- CONSOLE_HIDE_COMMAND: 'console.hide.command',
- CONSOLE_SET_CONSOLE_TEXT: 'console.set.command',
- CONSOLE_SET_COMPLETIONS: 'console.set.completions',
- CONSOLE_COMPLETION_NEXT: 'console.completion.next',
- CONSOLE_COMPLETION_PREV: 'console.completion.prev',
- CONSOLE_SHOW_FIND: 'console.show.find',
-};
diff --git a/src/console/actions/index.ts b/src/console/actions/index.ts
new file mode 100644
index 0000000..b394179
--- /dev/null
+++ b/src/console/actions/index.ts
@@ -0,0 +1,13 @@
+export default {
+ // console commands
+ CONSOLE_HIDE: 'console.hide',
+ CONSOLE_SHOW_COMMAND: 'console.show.command',
+ CONSOLE_SHOW_ERROR: 'console.show.error',
+ CONSOLE_SHOW_INFO: 'console.show.info',
+ CONSOLE_HIDE_COMMAND: 'console.hide.command',
+ CONSOLE_SET_CONSOLE_TEXT: 'console.set.command',
+ CONSOLE_SET_COMPLETIONS: 'console.set.completions',
+ CONSOLE_COMPLETION_NEXT: 'console.completion.next',
+ CONSOLE_COMPLETION_PREV: 'console.completion.prev',
+ CONSOLE_SHOW_FIND: 'console.show.find',
+};
diff --git a/src/console/components/Console.jsx b/src/console/components/Console.jsx
deleted file mode 100644
index 5427e43..0000000
--- a/src/console/components/Console.jsx
+++ /dev/null
@@ -1,149 +0,0 @@
-import './console.scss';
-import { connect } from 'react-redux';
-import React from 'react';
-import PropTypes from 'prop-types';
-import Input from './console/Input';
-import Completion from './console/Completion';
-import Message from './console/Message';
-import * as consoleActions from '../../console/actions/console';
-
-const COMPLETION_MAX_ITEMS = 33;
-
-class Console extends React.Component {
- onBlur() {
- if (this.props.mode === 'command' || this.props.mode === 'find') {
- return this.props.dispatch(consoleActions.hideCommand());
- }
- }
-
- doEnter(e) {
- e.stopPropagation();
- e.preventDefault();
-
- let value = e.target.value;
- if (this.props.mode === 'command') {
- return this.props.dispatch(consoleActions.enterCommand(value));
- } else if (this.props.mode === 'find') {
- return this.props.dispatch(consoleActions.enterFind(value));
- }
- }
-
- selectNext(e) {
- this.props.dispatch(consoleActions.completionNext());
- e.stopPropagation();
- e.preventDefault();
- }
-
- selectPrev(e) {
- this.props.dispatch(consoleActions.completionPrev());
- e.stopPropagation();
- e.preventDefault();
- }
-
- onKeyDown(e) {
- if (e.keyCode === KeyboardEvent.DOM_VK_ESCAPE && e.ctrlKey) {
- this.props.dispatch(consoleActions.hideCommand());
- }
- switch (e.keyCode) {
- case KeyboardEvent.DOM_VK_ESCAPE:
- return this.props.dispatch(consoleActions.hideCommand());
- case KeyboardEvent.DOM_VK_RETURN:
- return this.doEnter(e);
- case KeyboardEvent.DOM_VK_TAB:
- if (e.shiftKey) {
- this.props.dispatch(consoleActions.completionPrev());
- } else {
- this.props.dispatch(consoleActions.completionNext());
- }
- e.stopPropagation();
- e.preventDefault();
- break;
- case KeyboardEvent.DOM_VK_OPEN_BRACKET:
- if (e.ctrlKey) {
- return this.props.dispatch(consoleActions.hideCommand());
- }
- break;
- case KeyboardEvent.DOM_VK_M:
- if (e.ctrlKey) {
- return this.doEnter(e);
- }
- break;
- case KeyboardEvent.DOM_VK_N:
- if (e.ctrlKey) {
- this.selectNext(e);
- }
- break;
- case KeyboardEvent.DOM_VK_P:
- if (e.ctrlKey) {
- this.selectPrev(e);
- }
- break;
- }
- }
-
- onChange(e) {
- let text = e.target.value;
- this.props.dispatch(consoleActions.setConsoleText(text));
- if (this.props.mode === 'command') {
- this.props.dispatch(consoleActions.getCompletions(text));
- }
- }
-
-
- componentDidUpdate(prevProps) {
- if (!this.input) {
- return;
- }
- if (prevProps.mode !== 'command' && this.props.mode === 'command') {
- this.props.dispatch(
- consoleActions.getCompletions(this.props.consoleText));
- this.focus();
- } else if (prevProps.mode !== 'find' && this.props.mode === 'find') {
- this.focus();
- }
- }
-
- render() {
- switch (this.props.mode) {
- case 'command':
- case 'find':
- return
-
- { this.input = c; }}
- mode={this.props.mode}
- onBlur={this.onBlur.bind(this)}
- onKeyDown={this.onKeyDown.bind(this)}
- onChange={this.onChange.bind(this)}
- value={this.props.consoleText}
- />
-
;
- case 'info':
- case 'error':
- return
- { this.props.messageText }
- ;
- default:
- return null;
- }
- }
-
- focus() {
- window.focus();
- this.input.focus();
- }
-}
-
-Console.propTypes = {
- mode: PropTypes.string,
- consoleText: PropTypes.string,
- messageText: PropTypes.string,
- children: PropTypes.string,
-};
-
-const mapStateToProps = state => state;
-export default connect(mapStateToProps)(Console);
diff --git a/src/console/components/Console.tsx b/src/console/components/Console.tsx
new file mode 100644
index 0000000..5427e43
--- /dev/null
+++ b/src/console/components/Console.tsx
@@ -0,0 +1,149 @@
+import './console.scss';
+import { connect } from 'react-redux';
+import React from 'react';
+import PropTypes from 'prop-types';
+import Input from './console/Input';
+import Completion from './console/Completion';
+import Message from './console/Message';
+import * as consoleActions from '../../console/actions/console';
+
+const COMPLETION_MAX_ITEMS = 33;
+
+class Console extends React.Component {
+ onBlur() {
+ if (this.props.mode === 'command' || this.props.mode === 'find') {
+ return this.props.dispatch(consoleActions.hideCommand());
+ }
+ }
+
+ doEnter(e) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ let value = e.target.value;
+ if (this.props.mode === 'command') {
+ return this.props.dispatch(consoleActions.enterCommand(value));
+ } else if (this.props.mode === 'find') {
+ return this.props.dispatch(consoleActions.enterFind(value));
+ }
+ }
+
+ selectNext(e) {
+ this.props.dispatch(consoleActions.completionNext());
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ selectPrev(e) {
+ this.props.dispatch(consoleActions.completionPrev());
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ onKeyDown(e) {
+ if (e.keyCode === KeyboardEvent.DOM_VK_ESCAPE && e.ctrlKey) {
+ this.props.dispatch(consoleActions.hideCommand());
+ }
+ switch (e.keyCode) {
+ case KeyboardEvent.DOM_VK_ESCAPE:
+ return this.props.dispatch(consoleActions.hideCommand());
+ case KeyboardEvent.DOM_VK_RETURN:
+ return this.doEnter(e);
+ case KeyboardEvent.DOM_VK_TAB:
+ if (e.shiftKey) {
+ this.props.dispatch(consoleActions.completionPrev());
+ } else {
+ this.props.dispatch(consoleActions.completionNext());
+ }
+ e.stopPropagation();
+ e.preventDefault();
+ break;
+ case KeyboardEvent.DOM_VK_OPEN_BRACKET:
+ if (e.ctrlKey) {
+ return this.props.dispatch(consoleActions.hideCommand());
+ }
+ break;
+ case KeyboardEvent.DOM_VK_M:
+ if (e.ctrlKey) {
+ return this.doEnter(e);
+ }
+ break;
+ case KeyboardEvent.DOM_VK_N:
+ if (e.ctrlKey) {
+ this.selectNext(e);
+ }
+ break;
+ case KeyboardEvent.DOM_VK_P:
+ if (e.ctrlKey) {
+ this.selectPrev(e);
+ }
+ break;
+ }
+ }
+
+ onChange(e) {
+ let text = e.target.value;
+ this.props.dispatch(consoleActions.setConsoleText(text));
+ if (this.props.mode === 'command') {
+ this.props.dispatch(consoleActions.getCompletions(text));
+ }
+ }
+
+
+ componentDidUpdate(prevProps) {
+ if (!this.input) {
+ return;
+ }
+ if (prevProps.mode !== 'command' && this.props.mode === 'command') {
+ this.props.dispatch(
+ consoleActions.getCompletions(this.props.consoleText));
+ this.focus();
+ } else if (prevProps.mode !== 'find' && this.props.mode === 'find') {
+ this.focus();
+ }
+ }
+
+ render() {
+ switch (this.props.mode) {
+ case 'command':
+ case 'find':
+ return
+
+ { this.input = c; }}
+ mode={this.props.mode}
+ onBlur={this.onBlur.bind(this)}
+ onKeyDown={this.onKeyDown.bind(this)}
+ onChange={this.onChange.bind(this)}
+ value={this.props.consoleText}
+ />
+
;
+ case 'info':
+ case 'error':
+ return
+ { this.props.messageText }
+ ;
+ default:
+ return null;
+ }
+ }
+
+ focus() {
+ window.focus();
+ this.input.focus();
+ }
+}
+
+Console.propTypes = {
+ mode: PropTypes.string,
+ consoleText: PropTypes.string,
+ messageText: PropTypes.string,
+ children: PropTypes.string,
+};
+
+const mapStateToProps = state => state;
+export default connect(mapStateToProps)(Console);
diff --git a/src/console/components/console/Completion.jsx b/src/console/components/console/Completion.jsx
deleted file mode 100644
index 5477cb6..0000000
--- a/src/console/components/console/Completion.jsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import CompletionItem from './CompletionItem';
-import CompletionTitle from './CompletionTitle';
-
-class Completion extends React.Component {
- constructor() {
- super();
- this.state = { viewOffset: 0, select: -1 };
- }
-
- static getDerivedStateFromProps(nextProps, prevState) {
- if (prevState.select === nextProps.select) {
- return null;
- }
-
- let viewSelect = (() => {
- let index = 0;
- for (let i = 0; i < nextProps.completions.length; ++i) {
- ++index;
- let g = nextProps.completions[i];
- if (nextProps.select + i + 1 < index + g.items.length) {
- return nextProps.select + i + 1;
- }
- index += g.items.length;
- }
- })();
-
- let viewOffset = 0;
- if (nextProps.select < 0) {
- viewOffset = 0;
- } else if (prevState.select < nextProps.select) {
- viewOffset = Math.max(prevState.viewOffset,
- viewSelect - nextProps.size + 1);
- } else if (prevState.select > nextProps.select) {
- viewOffset = Math.min(prevState.viewOffset, viewSelect);
- }
- return { viewOffset, select: nextProps.select };
- }
-
- render() {
- let eles = [];
- let index = 0;
-
- for (let group of this.props.completions) {
- eles.push();
- for (let item of group.items) {
- eles.push();
- ++index;
- }
- }
-
- let viewOffset = this.state.viewOffset;
- eles = eles.slice(viewOffset, viewOffset + this.props.size);
-
- return (
-
- );
- }
-}
-
-Completion.propTypes = {
- select: PropTypes.number,
- size: PropTypes.number,
- completions: PropTypes.arrayOf(PropTypes.shape({
- name: PropTypes.string,
- items: PropTypes.arrayOf(PropTypes.shape({
- icon: PropTypes.string,
- caption: PropTypes.string,
- url: PropTypes.string,
- })),
- })),
-};
-
-export default Completion;
diff --git a/src/console/components/console/Completion.tsx b/src/console/components/console/Completion.tsx
new file mode 100644
index 0000000..5477cb6
--- /dev/null
+++ b/src/console/components/console/Completion.tsx
@@ -0,0 +1,86 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import CompletionItem from './CompletionItem';
+import CompletionTitle from './CompletionTitle';
+
+class Completion extends React.Component {
+ constructor() {
+ super();
+ this.state = { viewOffset: 0, select: -1 };
+ }
+
+ static getDerivedStateFromProps(nextProps, prevState) {
+ if (prevState.select === nextProps.select) {
+ return null;
+ }
+
+ let viewSelect = (() => {
+ let index = 0;
+ for (let i = 0; i < nextProps.completions.length; ++i) {
+ ++index;
+ let g = nextProps.completions[i];
+ if (nextProps.select + i + 1 < index + g.items.length) {
+ return nextProps.select + i + 1;
+ }
+ index += g.items.length;
+ }
+ })();
+
+ let viewOffset = 0;
+ if (nextProps.select < 0) {
+ viewOffset = 0;
+ } else if (prevState.select < nextProps.select) {
+ viewOffset = Math.max(prevState.viewOffset,
+ viewSelect - nextProps.size + 1);
+ } else if (prevState.select > nextProps.select) {
+ viewOffset = Math.min(prevState.viewOffset, viewSelect);
+ }
+ return { viewOffset, select: nextProps.select };
+ }
+
+ render() {
+ let eles = [];
+ let index = 0;
+
+ for (let group of this.props.completions) {
+ eles.push();
+ for (let item of group.items) {
+ eles.push();
+ ++index;
+ }
+ }
+
+ let viewOffset = this.state.viewOffset;
+ eles = eles.slice(viewOffset, viewOffset + this.props.size);
+
+ return (
+
+ );
+ }
+}
+
+Completion.propTypes = {
+ select: PropTypes.number,
+ size: PropTypes.number,
+ completions: PropTypes.arrayOf(PropTypes.shape({
+ name: PropTypes.string,
+ items: PropTypes.arrayOf(PropTypes.shape({
+ icon: PropTypes.string,
+ caption: PropTypes.string,
+ url: PropTypes.string,
+ })),
+ })),
+};
+
+export default Completion;
diff --git a/src/console/components/console/CompletionItem.jsx b/src/console/components/console/CompletionItem.jsx
deleted file mode 100644
index 3dc552b..0000000
--- a/src/console/components/console/CompletionItem.jsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-const CompletionItem = (props) => {
- let className = 'vimvixen-console-completion-item';
- if (props.highlight) {
- className += ' vimvixen-completion-selected';
- }
- return
- {props.caption}
- {props.url}
- ;
-};
-
-CompletionItem.propTypes = {
- highlight: PropTypes.bool,
- caption: PropTypes.string,
- url: PropTypes.string,
-};
-
-export default CompletionItem;
diff --git a/src/console/components/console/CompletionItem.tsx b/src/console/components/console/CompletionItem.tsx
new file mode 100644
index 0000000..3dc552b
--- /dev/null
+++ b/src/console/components/console/CompletionItem.tsx
@@ -0,0 +1,28 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const CompletionItem = (props) => {
+ let className = 'vimvixen-console-completion-item';
+ if (props.highlight) {
+ className += ' vimvixen-completion-selected';
+ }
+ return
+ {props.caption}
+ {props.url}
+ ;
+};
+
+CompletionItem.propTypes = {
+ highlight: PropTypes.bool,
+ caption: PropTypes.string,
+ url: PropTypes.string,
+};
+
+export default CompletionItem;
diff --git a/src/console/components/console/CompletionTitle.jsx b/src/console/components/console/CompletionTitle.jsx
deleted file mode 100644
index 4fcba3f..0000000
--- a/src/console/components/console/CompletionTitle.jsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-const CompletionTitle = (props) => {
- return
- {props.title}
- ;
-};
-
-CompletionTitle.propTypes = {
- title: PropTypes.string,
-};
-
-export default CompletionTitle;
diff --git a/src/console/components/console/CompletionTitle.tsx b/src/console/components/console/CompletionTitle.tsx
new file mode 100644
index 0000000..4fcba3f
--- /dev/null
+++ b/src/console/components/console/CompletionTitle.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const CompletionTitle = (props) => {
+ return
+ {props.title}
+ ;
+};
+
+CompletionTitle.propTypes = {
+ title: PropTypes.string,
+};
+
+export default CompletionTitle;
diff --git a/src/console/components/console/Input.jsx b/src/console/components/console/Input.jsx
deleted file mode 100644
index cbd3348..0000000
--- a/src/console/components/console/Input.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-class Input extends React.Component {
- focus() {
- this.input.focus();
- }
-
- render() {
- let prompt = '';
- if (this.props.mode === 'command') {
- prompt = ':';
- } else if (this.props.mode === 'find') {
- prompt = '/';
- }
-
- return (
-
-
- { prompt }
-
- { this.input = c; }}
- onBlur={this.props.onBlur}
- onKeyDown={this.props.onKeyDown}
- onChange={this.props.onChange}
- value={this.props.value}
- />
-
- );
- }
-}
-
-Input.propTypes = {
- mode: PropTypes.string,
- value: PropTypes.string,
- onBlur: PropTypes.func,
- onKeyDown: PropTypes.func,
- onChange: PropTypes.func,
-};
-
-export default Input;
diff --git a/src/console/components/console/Input.tsx b/src/console/components/console/Input.tsx
new file mode 100644
index 0000000..cbd3348
--- /dev/null
+++ b/src/console/components/console/Input.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+class Input extends React.Component {
+ focus() {
+ this.input.focus();
+ }
+
+ render() {
+ let prompt = '';
+ if (this.props.mode === 'command') {
+ prompt = ':';
+ } else if (this.props.mode === 'find') {
+ prompt = '/';
+ }
+
+ return (
+
+
+ { prompt }
+
+ { this.input = c; }}
+ onBlur={this.props.onBlur}
+ onKeyDown={this.props.onKeyDown}
+ onChange={this.props.onChange}
+ value={this.props.value}
+ />
+
+ );
+ }
+}
+
+Input.propTypes = {
+ mode: PropTypes.string,
+ value: PropTypes.string,
+ onBlur: PropTypes.func,
+ onKeyDown: PropTypes.func,
+ onChange: PropTypes.func,
+};
+
+export default Input;
diff --git a/src/console/components/console/Message.jsx b/src/console/components/console/Message.jsx
deleted file mode 100644
index dd96248..0000000
--- a/src/console/components/console/Message.jsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-const Message = (props) => {
- switch (props.mode) {
- case 'error':
- return (
-
- { props.children }
-
- );
- case 'info':
- return (
-
- { props.children }
-
- );
- }
-};
-
-Message.propTypes = {
- children: PropTypes.string,
-};
-
-export default Message;
diff --git a/src/console/components/console/Message.tsx b/src/console/components/console/Message.tsx
new file mode 100644
index 0000000..dd96248
--- /dev/null
+++ b/src/console/components/console/Message.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const Message = (props) => {
+ switch (props.mode) {
+ case 'error':
+ return (
+
+ { props.children }
+
+ );
+ case 'info':
+ return (
+
+ { props.children }
+
+ );
+ }
+};
+
+Message.propTypes = {
+ children: PropTypes.string,
+};
+
+export default Message;
diff --git a/src/console/index.jsx b/src/console/index.jsx
deleted file mode 100644
index 3190a9a..0000000
--- a/src/console/index.jsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import messages from 'shared/messages';
-import reducers from 'console/reducers';
-import { createStore, applyMiddleware } from 'redux';
-import promise from 'redux-promise';
-import * as consoleActions from 'console/actions/console';
-import { Provider } from 'react-redux';
-import Console from './components/Console';
-import React from 'react';
-import ReactDOM from 'react-dom';
-
-const store = createStore(
- reducers,
- applyMiddleware(promise),
-);
-
-window.addEventListener('load', () => {
- let wrapper = document.getElementById('vimvixen-console');
- ReactDOM.render(
-
-
- ,
- wrapper);
-});
-
-const onMessage = (message) => {
- switch (message.type) {
- case messages.CONSOLE_SHOW_COMMAND:
- return store.dispatch(consoleActions.showCommand(message.command));
- case messages.CONSOLE_SHOW_FIND:
- return store.dispatch(consoleActions.showFind());
- case messages.CONSOLE_SHOW_ERROR:
- return store.dispatch(consoleActions.showError(message.text));
- case messages.CONSOLE_SHOW_INFO:
- return store.dispatch(consoleActions.showInfo(message.text));
- case messages.CONSOLE_HIDE:
- return store.dispatch(consoleActions.hide());
- }
-};
-
-browser.runtime.onMessage.addListener(onMessage);
-let port = browser.runtime.connect({ name: 'vimvixen-console' });
-port.onMessage.addListener(onMessage);
diff --git a/src/console/index.tsx b/src/console/index.tsx
new file mode 100644
index 0000000..3190a9a
--- /dev/null
+++ b/src/console/index.tsx
@@ -0,0 +1,42 @@
+import messages from 'shared/messages';
+import reducers from 'console/reducers';
+import { createStore, applyMiddleware } from 'redux';
+import promise from 'redux-promise';
+import * as consoleActions from 'console/actions/console';
+import { Provider } from 'react-redux';
+import Console from './components/Console';
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+const store = createStore(
+ reducers,
+ applyMiddleware(promise),
+);
+
+window.addEventListener('load', () => {
+ let wrapper = document.getElementById('vimvixen-console');
+ ReactDOM.render(
+
+
+ ,
+ wrapper);
+});
+
+const onMessage = (message) => {
+ switch (message.type) {
+ case messages.CONSOLE_SHOW_COMMAND:
+ return store.dispatch(consoleActions.showCommand(message.command));
+ case messages.CONSOLE_SHOW_FIND:
+ return store.dispatch(consoleActions.showFind());
+ case messages.CONSOLE_SHOW_ERROR:
+ return store.dispatch(consoleActions.showError(message.text));
+ case messages.CONSOLE_SHOW_INFO:
+ return store.dispatch(consoleActions.showInfo(message.text));
+ case messages.CONSOLE_HIDE:
+ return store.dispatch(consoleActions.hide());
+ }
+};
+
+browser.runtime.onMessage.addListener(onMessage);
+let port = browser.runtime.connect({ name: 'vimvixen-console' });
+port.onMessage.addListener(onMessage);
diff --git a/src/console/reducers/index.js b/src/console/reducers/index.js
deleted file mode 100644
index 614a72f..0000000
--- a/src/console/reducers/index.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import actions from 'console/actions';
-
-const defaultState = {
- mode: '',
- messageText: '',
- consoleText: '',
- completionSource: '',
- completions: [],
- select: -1,
- viewIndex: 0,
-};
-
-const nextSelection = (state) => {
- if (state.completions.length === 0) {
- return -1;
- }
- if (state.select < 0) {
- return 0;
- }
-
- let length = state.completions
- .map(g => g.items.length)
- .reduce((x, y) => x + y);
- if (state.select + 1 < length) {
- return state.select + 1;
- }
- return -1;
-};
-
-const prevSelection = (state) => {
- let length = state.completions
- .map(g => g.items.length)
- .reduce((x, y) => x + y);
- if (state.select < 0) {
- return length - 1;
- }
- return state.select - 1;
-};
-
-const nextConsoleText = (completions, select, defaults) => {
- if (select < 0) {
- return defaults;
- }
- let items = completions.map(g => g.items).reduce((g1, g2) => g1.concat(g2));
- return items[select].content;
-};
-
-// eslint-disable-next-line max-lines-per-function
-export default function reducer(state = defaultState, action = {}) {
- switch (action.type) {
- case actions.CONSOLE_HIDE:
- return { ...state,
- mode: '', };
- case actions.CONSOLE_SHOW_COMMAND:
- return { ...state,
- mode: 'command',
- consoleText: action.text,
- completions: []};
- case actions.CONSOLE_SHOW_FIND:
- return { ...state,
- mode: 'find',
- consoleText: '',
- completions: []};
- case actions.CONSOLE_SHOW_ERROR:
- return { ...state,
- mode: 'error',
- messageText: action.text, };
- case actions.CONSOLE_SHOW_INFO:
- return { ...state,
- mode: 'info',
- messageText: action.text, };
- case actions.CONSOLE_HIDE_COMMAND:
- return {
- ...state,
- mode: state.mode === 'command' || state.mode === 'find' ? '' : state.mode,
- };
- case actions.CONSOLE_SET_CONSOLE_TEXT:
- return { ...state,
- consoleText: action.consoleText, };
- case actions.CONSOLE_SET_COMPLETIONS:
- return { ...state,
- completions: action.completions,
- completionSource: action.completionSource,
- select: -1 };
- case actions.CONSOLE_COMPLETION_NEXT: {
- let select = nextSelection(state);
- return { ...state,
- select: select,
- consoleText: nextConsoleText(
- state.completions, select, state.completionSource) };
- }
- case actions.CONSOLE_COMPLETION_PREV: {
- let select = prevSelection(state);
- return { ...state,
- select: select,
- consoleText: nextConsoleText(
- state.completions, select, state.completionSource) };
- }
- default:
- return state;
- }
-}
diff --git a/src/console/reducers/index.ts b/src/console/reducers/index.ts
new file mode 100644
index 0000000..614a72f
--- /dev/null
+++ b/src/console/reducers/index.ts
@@ -0,0 +1,102 @@
+import actions from 'console/actions';
+
+const defaultState = {
+ mode: '',
+ messageText: '',
+ consoleText: '',
+ completionSource: '',
+ completions: [],
+ select: -1,
+ viewIndex: 0,
+};
+
+const nextSelection = (state) => {
+ if (state.completions.length === 0) {
+ return -1;
+ }
+ if (state.select < 0) {
+ return 0;
+ }
+
+ let length = state.completions
+ .map(g => g.items.length)
+ .reduce((x, y) => x + y);
+ if (state.select + 1 < length) {
+ return state.select + 1;
+ }
+ return -1;
+};
+
+const prevSelection = (state) => {
+ let length = state.completions
+ .map(g => g.items.length)
+ .reduce((x, y) => x + y);
+ if (state.select < 0) {
+ return length - 1;
+ }
+ return state.select - 1;
+};
+
+const nextConsoleText = (completions, select, defaults) => {
+ if (select < 0) {
+ return defaults;
+ }
+ let items = completions.map(g => g.items).reduce((g1, g2) => g1.concat(g2));
+ return items[select].content;
+};
+
+// eslint-disable-next-line max-lines-per-function
+export default function reducer(state = defaultState, action = {}) {
+ switch (action.type) {
+ case actions.CONSOLE_HIDE:
+ return { ...state,
+ mode: '', };
+ case actions.CONSOLE_SHOW_COMMAND:
+ return { ...state,
+ mode: 'command',
+ consoleText: action.text,
+ completions: []};
+ case actions.CONSOLE_SHOW_FIND:
+ return { ...state,
+ mode: 'find',
+ consoleText: '',
+ completions: []};
+ case actions.CONSOLE_SHOW_ERROR:
+ return { ...state,
+ mode: 'error',
+ messageText: action.text, };
+ case actions.CONSOLE_SHOW_INFO:
+ return { ...state,
+ mode: 'info',
+ messageText: action.text, };
+ case actions.CONSOLE_HIDE_COMMAND:
+ return {
+ ...state,
+ mode: state.mode === 'command' || state.mode === 'find' ? '' : state.mode,
+ };
+ case actions.CONSOLE_SET_CONSOLE_TEXT:
+ return { ...state,
+ consoleText: action.consoleText, };
+ case actions.CONSOLE_SET_COMPLETIONS:
+ return { ...state,
+ completions: action.completions,
+ completionSource: action.completionSource,
+ select: -1 };
+ case actions.CONSOLE_COMPLETION_NEXT: {
+ let select = nextSelection(state);
+ return { ...state,
+ select: select,
+ consoleText: nextConsoleText(
+ state.completions, select, state.completionSource) };
+ }
+ case actions.CONSOLE_COMPLETION_PREV: {
+ let select = prevSelection(state);
+ return { ...state,
+ select: select,
+ consoleText: nextConsoleText(
+ state.completions, select, state.completionSource) };
+ }
+ default:
+ return state;
+ }
+}
diff --git a/src/content/actions/addon.js b/src/content/actions/addon.js
deleted file mode 100644
index b30cf16..0000000
--- a/src/content/actions/addon.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import messages from 'shared/messages';
-import actions from 'content/actions';
-
-const enable = () => setEnabled(true);
-
-const disable = () => setEnabled(false);
-
-const setEnabled = async(enabled) => {
- await browser.runtime.sendMessage({
- type: messages.ADDON_ENABLED_RESPONSE,
- enabled,
- });
- return {
- type: actions.ADDON_SET_ENABLED,
- enabled,
- };
-};
-
-export { enable, disable, setEnabled };
diff --git a/src/content/actions/addon.ts b/src/content/actions/addon.ts
new file mode 100644
index 0000000..b30cf16
--- /dev/null
+++ b/src/content/actions/addon.ts
@@ -0,0 +1,19 @@
+import messages from 'shared/messages';
+import actions from 'content/actions';
+
+const enable = () => setEnabled(true);
+
+const disable = () => setEnabled(false);
+
+const setEnabled = async(enabled) => {
+ await browser.runtime.sendMessage({
+ type: messages.ADDON_ENABLED_RESPONSE,
+ enabled,
+ });
+ return {
+ type: actions.ADDON_SET_ENABLED,
+ enabled,
+ };
+};
+
+export { enable, disable, setEnabled };
diff --git a/src/content/actions/find.js b/src/content/actions/find.js
deleted file mode 100644
index e08d7e5..0000000
--- a/src/content/actions/find.js
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-// window.find(aString, aCaseSensitive, aBackwards, aWrapAround,
-// aWholeWord, aSearchInFrames);
-//
-// NOTE: window.find is not standard API
-// https://developer.mozilla.org/en-US/docs/Web/API/Window/find
-
-import messages from 'shared/messages';
-import actions from 'content/actions';
-import * as consoleFrames from '../console-frames';
-
-const find = (string, backwards) => {
- let caseSensitive = false;
- let wrapScan = true;
-
-
- // NOTE: aWholeWord dows not implemented, and aSearchInFrames does not work
- // because of same origin policy
- let found = window.find(string, caseSensitive, backwards, wrapScan);
- if (found) {
- return found;
- }
- window.getSelection().removeAllRanges();
- return window.find(string, caseSensitive, backwards, wrapScan);
-};
-
-const findNext = async(currentKeyword, reset, backwards) => {
- if (reset) {
- window.getSelection().removeAllRanges();
- }
-
- let keyword = currentKeyword;
- if (currentKeyword) {
- browser.runtime.sendMessage({
- type: messages.FIND_SET_KEYWORD,
- keyword: currentKeyword,
- });
- } else {
- keyword = await browser.runtime.sendMessage({
- type: messages.FIND_GET_KEYWORD,
- });
- }
- if (!keyword) {
- return consoleFrames.postError('No previous search keywords');
- }
- let found = find(keyword, backwards);
- if (found) {
- consoleFrames.postInfo('Pattern found: ' + keyword);
- } else {
- consoleFrames.postError('Pattern not found: ' + keyword);
- }
-
- return {
- type: actions.FIND_SET_KEYWORD,
- keyword,
- found,
- };
-};
-
-const next = (currentKeyword, reset) => {
- return findNext(currentKeyword, reset, false);
-};
-
-const prev = (currentKeyword, reset) => {
- return findNext(currentKeyword, reset, true);
-};
-
-export { next, prev };
diff --git a/src/content/actions/find.ts b/src/content/actions/find.ts
new file mode 100644
index 0000000..e08d7e5
--- /dev/null
+++ b/src/content/actions/find.ts
@@ -0,0 +1,68 @@
+//
+// window.find(aString, aCaseSensitive, aBackwards, aWrapAround,
+// aWholeWord, aSearchInFrames);
+//
+// NOTE: window.find is not standard API
+// https://developer.mozilla.org/en-US/docs/Web/API/Window/find
+
+import messages from 'shared/messages';
+import actions from 'content/actions';
+import * as consoleFrames from '../console-frames';
+
+const find = (string, backwards) => {
+ let caseSensitive = false;
+ let wrapScan = true;
+
+
+ // NOTE: aWholeWord dows not implemented, and aSearchInFrames does not work
+ // because of same origin policy
+ let found = window.find(string, caseSensitive, backwards, wrapScan);
+ if (found) {
+ return found;
+ }
+ window.getSelection().removeAllRanges();
+ return window.find(string, caseSensitive, backwards, wrapScan);
+};
+
+const findNext = async(currentKeyword, reset, backwards) => {
+ if (reset) {
+ window.getSelection().removeAllRanges();
+ }
+
+ let keyword = currentKeyword;
+ if (currentKeyword) {
+ browser.runtime.sendMessage({
+ type: messages.FIND_SET_KEYWORD,
+ keyword: currentKeyword,
+ });
+ } else {
+ keyword = await browser.runtime.sendMessage({
+ type: messages.FIND_GET_KEYWORD,
+ });
+ }
+ if (!keyword) {
+ return consoleFrames.postError('No previous search keywords');
+ }
+ let found = find(keyword, backwards);
+ if (found) {
+ consoleFrames.postInfo('Pattern found: ' + keyword);
+ } else {
+ consoleFrames.postError('Pattern not found: ' + keyword);
+ }
+
+ return {
+ type: actions.FIND_SET_KEYWORD,
+ keyword,
+ found,
+ };
+};
+
+const next = (currentKeyword, reset) => {
+ return findNext(currentKeyword, reset, false);
+};
+
+const prev = (currentKeyword, reset) => {
+ return findNext(currentKeyword, reset, true);
+};
+
+export { next, prev };
diff --git a/src/content/actions/follow-controller.js b/src/content/actions/follow-controller.js
deleted file mode 100644
index 006b248..0000000
--- a/src/content/actions/follow-controller.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import actions from 'content/actions';
-
-const enable = (newTab, background) => {
- return {
- type: actions.FOLLOW_CONTROLLER_ENABLE,
- newTab,
- background,
- };
-};
-
-const disable = () => {
- return {
- type: actions.FOLLOW_CONTROLLER_DISABLE,
- };
-};
-
-const keyPress = (key) => {
- return {
- type: actions.FOLLOW_CONTROLLER_KEY_PRESS,
- key: key
- };
-};
-
-const backspace = () => {
- return {
- type: actions.FOLLOW_CONTROLLER_BACKSPACE,
- };
-};
-
-export { enable, disable, keyPress, backspace };
diff --git a/src/content/actions/follow-controller.ts b/src/content/actions/follow-controller.ts
new file mode 100644
index 0000000..006b248
--- /dev/null
+++ b/src/content/actions/follow-controller.ts
@@ -0,0 +1,30 @@
+import actions from 'content/actions';
+
+const enable = (newTab, background) => {
+ return {
+ type: actions.FOLLOW_CONTROLLER_ENABLE,
+ newTab,
+ background,
+ };
+};
+
+const disable = () => {
+ return {
+ type: actions.FOLLOW_CONTROLLER_DISABLE,
+ };
+};
+
+const keyPress = (key) => {
+ return {
+ type: actions.FOLLOW_CONTROLLER_KEY_PRESS,
+ key: key
+ };
+};
+
+const backspace = () => {
+ return {
+ type: actions.FOLLOW_CONTROLLER_BACKSPACE,
+ };
+};
+
+export { enable, disable, keyPress, backspace };
diff --git a/src/content/actions/index.js b/src/content/actions/index.js
deleted file mode 100644
index 0a16fdf..0000000
--- a/src/content/actions/index.js
+++ /dev/null
@@ -1,31 +0,0 @@
-export default {
- // Enable/disable
- ADDON_SET_ENABLED: 'addon.set.enabled',
-
- // Settings
- SETTING_SET: 'setting.set',
-
- // User input
- INPUT_KEY_PRESS: 'input.key.press',
- INPUT_CLEAR_KEYS: 'input.clear.keys',
-
- // Completion
- COMPLETION_SET_ITEMS: 'completion.set.items',
- COMPLETION_SELECT_NEXT: 'completions.select.next',
- COMPLETION_SELECT_PREV: 'completions.select.prev',
-
- // Follow
- FOLLOW_CONTROLLER_ENABLE: 'follow.controller.enable',
- FOLLOW_CONTROLLER_DISABLE: 'follow.controller.disable',
- FOLLOW_CONTROLLER_KEY_PRESS: 'follow.controller.key.press',
- FOLLOW_CONTROLLER_BACKSPACE: 'follow.controller.backspace',
-
- // Find
- FIND_SET_KEYWORD: 'find.set.keyword',
-
- // Mark
- MARK_START_SET: 'mark.start.set',
- MARK_START_JUMP: 'mark.start.jump',
- MARK_CANCEL: 'mark.cancel',
- MARK_SET_LOCAL: 'mark.set.local',
-};
diff --git a/src/content/actions/index.ts b/src/content/actions/index.ts
new file mode 100644
index 0000000..0a16fdf
--- /dev/null
+++ b/src/content/actions/index.ts
@@ -0,0 +1,31 @@
+export default {
+ // Enable/disable
+ ADDON_SET_ENABLED: 'addon.set.enabled',
+
+ // Settings
+ SETTING_SET: 'setting.set',
+
+ // User input
+ INPUT_KEY_PRESS: 'input.key.press',
+ INPUT_CLEAR_KEYS: 'input.clear.keys',
+
+ // Completion
+ COMPLETION_SET_ITEMS: 'completion.set.items',
+ COMPLETION_SELECT_NEXT: 'completions.select.next',
+ COMPLETION_SELECT_PREV: 'completions.select.prev',
+
+ // Follow
+ FOLLOW_CONTROLLER_ENABLE: 'follow.controller.enable',
+ FOLLOW_CONTROLLER_DISABLE: 'follow.controller.disable',
+ FOLLOW_CONTROLLER_KEY_PRESS: 'follow.controller.key.press',
+ FOLLOW_CONTROLLER_BACKSPACE: 'follow.controller.backspace',
+
+ // Find
+ FIND_SET_KEYWORD: 'find.set.keyword',
+
+ // Mark
+ MARK_START_SET: 'mark.start.set',
+ MARK_START_JUMP: 'mark.start.jump',
+ MARK_CANCEL: 'mark.cancel',
+ MARK_SET_LOCAL: 'mark.set.local',
+};
diff --git a/src/content/actions/input.js b/src/content/actions/input.js
deleted file mode 100644
index 465a486..0000000
--- a/src/content/actions/input.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import actions from 'content/actions';
-
-const keyPress = (key) => {
- return {
- type: actions.INPUT_KEY_PRESS,
- key,
- };
-};
-
-const clearKeys = () => {
- return {
- type: actions.INPUT_CLEAR_KEYS
- };
-};
-
-export { keyPress, clearKeys };
diff --git a/src/content/actions/input.ts b/src/content/actions/input.ts
new file mode 100644
index 0000000..465a486
--- /dev/null
+++ b/src/content/actions/input.ts
@@ -0,0 +1,16 @@
+import actions from 'content/actions';
+
+const keyPress = (key) => {
+ return {
+ type: actions.INPUT_KEY_PRESS,
+ key,
+ };
+};
+
+const clearKeys = () => {
+ return {
+ type: actions.INPUT_CLEAR_KEYS
+ };
+};
+
+export { keyPress, clearKeys };
diff --git a/src/content/actions/mark.js b/src/content/actions/mark.js
deleted file mode 100644
index 712a811..0000000
--- a/src/content/actions/mark.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import actions from 'content/actions';
-import messages from 'shared/messages';
-
-const startSet = () => {
- return { type: actions.MARK_START_SET };
-};
-
-const startJump = () => {
- return { type: actions.MARK_START_JUMP };
-};
-
-const cancel = () => {
- return { type: actions.MARK_CANCEL };
-};
-
-const setLocal = (key, x, y) => {
- return {
- type: actions.MARK_SET_LOCAL,
- key,
- x,
- y,
- };
-};
-
-const setGlobal = (key, x, y) => {
- browser.runtime.sendMessage({
- type: messages.MARK_SET_GLOBAL,
- key,
- x,
- y,
- });
- return { type: '' };
-};
-
-const jumpGlobal = (key) => {
- browser.runtime.sendMessage({
- type: messages.MARK_JUMP_GLOBAL,
- key,
- });
- return { type: '' };
-};
-
-export {
- startSet, startJump, cancel, setLocal,
- setGlobal, jumpGlobal,
-};
diff --git a/src/content/actions/mark.ts b/src/content/actions/mark.ts
new file mode 100644
index 0000000..712a811
--- /dev/null
+++ b/src/content/actions/mark.ts
@@ -0,0 +1,46 @@
+import actions from 'content/actions';
+import messages from 'shared/messages';
+
+const startSet = () => {
+ return { type: actions.MARK_START_SET };
+};
+
+const startJump = () => {
+ return { type: actions.MARK_START_JUMP };
+};
+
+const cancel = () => {
+ return { type: actions.MARK_CANCEL };
+};
+
+const setLocal = (key, x, y) => {
+ return {
+ type: actions.MARK_SET_LOCAL,
+ key,
+ x,
+ y,
+ };
+};
+
+const setGlobal = (key, x, y) => {
+ browser.runtime.sendMessage({
+ type: messages.MARK_SET_GLOBAL,
+ key,
+ x,
+ y,
+ });
+ return { type: '' };
+};
+
+const jumpGlobal = (key) => {
+ browser.runtime.sendMessage({
+ type: messages.MARK_JUMP_GLOBAL,
+ key,
+ });
+ return { type: '' };
+};
+
+export {
+ startSet, startJump, cancel, setLocal,
+ setGlobal, jumpGlobal,
+};
diff --git a/src/content/actions/operation.js b/src/content/actions/operation.js
deleted file mode 100644
index ed9b2cf..0000000
--- a/src/content/actions/operation.js
+++ /dev/null
@@ -1,104 +0,0 @@
-import operations from 'shared/operations';
-import messages from 'shared/messages';
-import * as scrolls from 'content/scrolls';
-import * as navigates from 'content/navigates';
-import * as focuses from 'content/focuses';
-import * as urls from 'content/urls';
-import * as consoleFrames from 'content/console-frames';
-import * as addonActions from './addon';
-import * as markActions from './mark';
-import * as properties from 'shared/settings/properties';
-
-// eslint-disable-next-line complexity, max-lines-per-function
-const exec = (operation, settings, addonEnabled) => {
- let smoothscroll = settings.properties.smoothscroll ||
- properties.defaults.smoothscroll;
- switch (operation.type) {
- case operations.ADDON_ENABLE:
- return addonActions.enable();
- case operations.ADDON_DISABLE:
- return addonActions.disable();
- case operations.ADDON_TOGGLE_ENABLED:
- return addonActions.setEnabled(!addonEnabled);
- case operations.FIND_NEXT:
- window.top.postMessage(JSON.stringify({
- type: messages.FIND_NEXT,
- }), '*');
- break;
- case operations.FIND_PREV:
- window.top.postMessage(JSON.stringify({
- type: messages.FIND_PREV,
- }), '*');
- break;
- case operations.SCROLL_VERTICALLY:
- scrolls.scrollVertically(operation.count, smoothscroll);
- break;
- case operations.SCROLL_HORIZONALLY:
- scrolls.scrollHorizonally(operation.count, smoothscroll);
- break;
- case operations.SCROLL_PAGES:
- scrolls.scrollPages(operation.count, smoothscroll);
- break;
- case operations.SCROLL_TOP:
- scrolls.scrollToTop(smoothscroll);
- break;
- case operations.SCROLL_BOTTOM:
- scrolls.scrollToBottom(smoothscroll);
- break;
- case operations.SCROLL_HOME:
- scrolls.scrollToHome(smoothscroll);
- break;
- case operations.SCROLL_END:
- scrolls.scrollToEnd(smoothscroll);
- break;
- case operations.FOLLOW_START:
- window.top.postMessage(JSON.stringify({
- type: messages.FOLLOW_START,
- newTab: operation.newTab,
- background: operation.background,
- }), '*');
- break;
- case operations.MARK_SET_PREFIX:
- return markActions.startSet();
- case operations.MARK_JUMP_PREFIX:
- return markActions.startJump();
- case operations.NAVIGATE_HISTORY_PREV:
- navigates.historyPrev(window);
- break;
- case operations.NAVIGATE_HISTORY_NEXT:
- navigates.historyNext(window);
- break;
- case operations.NAVIGATE_LINK_PREV:
- navigates.linkPrev(window);
- break;
- case operations.NAVIGATE_LINK_NEXT:
- navigates.linkNext(window);
- break;
- case operations.NAVIGATE_PARENT:
- navigates.parent(window);
- break;
- case operations.NAVIGATE_ROOT:
- navigates.root(window);
- break;
- case operations.FOCUS_INPUT:
- focuses.focusInput();
- break;
- case operations.URLS_YANK:
- urls.yank(window);
- consoleFrames.postInfo('Yanked ' + window.location.href);
- break;
- case operations.URLS_PASTE:
- urls.paste(
- window, operation.newTab ? operation.newTab : false, settings.search
- );
- break;
- default:
- browser.runtime.sendMessage({
- type: messages.BACKGROUND_OPERATION,
- operation,
- });
- }
- return { type: '' };
-};
-
-export { exec };
diff --git a/src/content/actions/operation.ts b/src/content/actions/operation.ts
new file mode 100644
index 0000000..ed9b2cf
--- /dev/null
+++ b/src/content/actions/operation.ts
@@ -0,0 +1,104 @@
+import operations from 'shared/operations';
+import messages from 'shared/messages';
+import * as scrolls from 'content/scrolls';
+import * as navigates from 'content/navigates';
+import * as focuses from 'content/focuses';
+import * as urls from 'content/urls';
+import * as consoleFrames from 'content/console-frames';
+import * as addonActions from './addon';
+import * as markActions from './mark';
+import * as properties from 'shared/settings/properties';
+
+// eslint-disable-next-line complexity, max-lines-per-function
+const exec = (operation, settings, addonEnabled) => {
+ let smoothscroll = settings.properties.smoothscroll ||
+ properties.defaults.smoothscroll;
+ switch (operation.type) {
+ case operations.ADDON_ENABLE:
+ return addonActions.enable();
+ case operations.ADDON_DISABLE:
+ return addonActions.disable();
+ case operations.ADDON_TOGGLE_ENABLED:
+ return addonActions.setEnabled(!addonEnabled);
+ case operations.FIND_NEXT:
+ window.top.postMessage(JSON.stringify({
+ type: messages.FIND_NEXT,
+ }), '*');
+ break;
+ case operations.FIND_PREV:
+ window.top.postMessage(JSON.stringify({
+ type: messages.FIND_PREV,
+ }), '*');
+ break;
+ case operations.SCROLL_VERTICALLY:
+ scrolls.scrollVertically(operation.count, smoothscroll);
+ break;
+ case operations.SCROLL_HORIZONALLY:
+ scrolls.scrollHorizonally(operation.count, smoothscroll);
+ break;
+ case operations.SCROLL_PAGES:
+ scrolls.scrollPages(operation.count, smoothscroll);
+ break;
+ case operations.SCROLL_TOP:
+ scrolls.scrollToTop(smoothscroll);
+ break;
+ case operations.SCROLL_BOTTOM:
+ scrolls.scrollToBottom(smoothscroll);
+ break;
+ case operations.SCROLL_HOME:
+ scrolls.scrollToHome(smoothscroll);
+ break;
+ case operations.SCROLL_END:
+ scrolls.scrollToEnd(smoothscroll);
+ break;
+ case operations.FOLLOW_START:
+ window.top.postMessage(JSON.stringify({
+ type: messages.FOLLOW_START,
+ newTab: operation.newTab,
+ background: operation.background,
+ }), '*');
+ break;
+ case operations.MARK_SET_PREFIX:
+ return markActions.startSet();
+ case operations.MARK_JUMP_PREFIX:
+ return markActions.startJump();
+ case operations.NAVIGATE_HISTORY_PREV:
+ navigates.historyPrev(window);
+ break;
+ case operations.NAVIGATE_HISTORY_NEXT:
+ navigates.historyNext(window);
+ break;
+ case operations.NAVIGATE_LINK_PREV:
+ navigates.linkPrev(window);
+ break;
+ case operations.NAVIGATE_LINK_NEXT:
+ navigates.linkNext(window);
+ break;
+ case operations.NAVIGATE_PARENT:
+ navigates.parent(window);
+ break;
+ case operations.NAVIGATE_ROOT:
+ navigates.root(window);
+ break;
+ case operations.FOCUS_INPUT:
+ focuses.focusInput();
+ break;
+ case operations.URLS_YANK:
+ urls.yank(window);
+ consoleFrames.postInfo('Yanked ' + window.location.href);
+ break;
+ case operations.URLS_PASTE:
+ urls.paste(
+ window, operation.newTab ? operation.newTab : false, settings.search
+ );
+ break;
+ default:
+ browser.runtime.sendMessage({
+ type: messages.BACKGROUND_OPERATION,
+ operation,
+ });
+ }
+ return { type: '' };
+};
+
+export { exec };
diff --git a/src/content/actions/setting.js b/src/content/actions/setting.js
deleted file mode 100644
index 1c15dd7..0000000
--- a/src/content/actions/setting.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import actions from 'content/actions';
-import * as keyUtils from 'shared/utils/keys';
-import operations from 'shared/operations';
-import messages from 'shared/messages';
-
-const reservedKeymaps = {
- '': { type: operations.CANCEL },
- '': { type: operations.CANCEL },
-};
-
-const set = (value) => {
- let entries = [];
- if (value.keymaps) {
- let keymaps = { ...value.keymaps, ...reservedKeymaps };
- entries = Object.entries(keymaps).map((entry) => {
- return [
- keyUtils.fromMapKeys(entry[0]),
- entry[1],
- ];
- });
- }
-
- return {
- type: actions.SETTING_SET,
- value: { ...value,
- keymaps: entries, }
- };
-};
-
-const load = async() => {
- let settings = await browser.runtime.sendMessage({
- type: messages.SETTINGS_QUERY,
- });
- return set(settings);
-};
-
-export { set, load };
diff --git a/src/content/actions/setting.ts b/src/content/actions/setting.ts
new file mode 100644
index 0000000..1c15dd7
--- /dev/null
+++ b/src/content/actions/setting.ts
@@ -0,0 +1,37 @@
+import actions from 'content/actions';
+import * as keyUtils from 'shared/utils/keys';
+import operations from 'shared/operations';
+import messages from 'shared/messages';
+
+const reservedKeymaps = {
+ '': { type: operations.CANCEL },
+ '': { type: operations.CANCEL },
+};
+
+const set = (value) => {
+ let entries = [];
+ if (value.keymaps) {
+ let keymaps = { ...value.keymaps, ...reservedKeymaps };
+ entries = Object.entries(keymaps).map((entry) => {
+ return [
+ keyUtils.fromMapKeys(entry[0]),
+ entry[1],
+ ];
+ });
+ }
+
+ return {
+ type: actions.SETTING_SET,
+ value: { ...value,
+ keymaps: entries, }
+ };
+};
+
+const load = async() => {
+ let settings = await browser.runtime.sendMessage({
+ type: messages.SETTINGS_QUERY,
+ });
+ return set(settings);
+};
+
+export { set, load };
diff --git a/src/content/components/common/follow.js b/src/content/components/common/follow.js
deleted file mode 100644
index 63ce603..0000000
--- a/src/content/components/common/follow.js
+++ /dev/null
@@ -1,200 +0,0 @@
-import messages from 'shared/messages';
-import Hint from './hint';
-import * as dom from 'shared/utils/dom';
-
-const TARGET_SELECTOR = [
- 'a', 'button', 'input', 'textarea', 'area',
- '[contenteditable=true]', '[contenteditable=""]', '[tabindex]',
- '[role="button"]', 'summary'
-].join(',');
-
-
-const inViewport = (win, element, viewSize, framePosition) => {
- let {
- top, left, bottom, right
- } = dom.viewportRect(element);
- let doc = win.document;
- let frameWidth = doc.documentElement.clientWidth;
- let frameHeight = doc.documentElement.clientHeight;
-
- if (right < 0 || bottom < 0 || top > frameHeight || left > frameWidth) {
- // out of frame
- return false;
- }
- if (right + framePosition.x < 0 || bottom + framePosition.y < 0 ||
- left + framePosition.x > viewSize.width ||
- top + framePosition.y > viewSize.height) {
- // out of viewport
- return false;
- }
- return true;
-};
-
-const isAriaHiddenOrAriaDisabled = (win, element) => {
- if (!element || win.document.documentElement === element) {
- return false;
- }
- for (let attr of ['aria-hidden', 'aria-disabled']) {
- if (element.hasAttribute(attr)) {
- let hidden = element.getAttribute(attr).toLowerCase();
- if (hidden === '' || hidden === 'true') {
- return true;
- }
- }
- }
- return isAriaHiddenOrAriaDisabled(win, element.parentNode);
-};
-
-export default class Follow {
- constructor(win, store) {
- this.win = win;
- this.store = store;
- this.newTab = false;
- this.background = false;
- this.hints = {};
- this.targets = [];
-
- messages.onMessage(this.onMessage.bind(this));
- }
-
- key(key) {
- if (Object.keys(this.hints).length === 0) {
- return false;
- }
- this.win.parent.postMessage(JSON.stringify({
- type: messages.FOLLOW_KEY_PRESS,
- key: key.key,
- ctrlKey: key.ctrlKey,
- }), '*');
- return true;
- }
-
- openLink(element) {
- // Browser prevent new tab by link with target='_blank'
- if (!this.newTab && element.getAttribute('target') !== '_blank') {
- element.click();
- return;
- }
-
- let href = element.getAttribute('href');
-
- // eslint-disable-next-line no-script-url
- if (!href || href === '#' || href.toLowerCase().startsWith('javascript:')) {
- return;
- }
- return browser.runtime.sendMessage({
- type: messages.OPEN_URL,
- url: element.href,
- newTab: true,
- background: this.background,
- });
- }
-
- countHints(sender, viewSize, framePosition) {
- this.targets = Follow.getTargetElements(this.win, viewSize, framePosition);
- sender.postMessage(JSON.stringify({
- type: messages.FOLLOW_RESPONSE_COUNT_TARGETS,
- count: this.targets.length,
- }), '*');
- }
-
- createHints(keysArray, newTab, background) {
- if (keysArray.length !== this.targets.length) {
- throw new Error('illegal hint count');
- }
-
- this.newTab = newTab;
- this.background = background;
- this.hints = {};
- for (let i = 0; i < keysArray.length; ++i) {
- let keys = keysArray[i];
- let hint = new Hint(this.targets[i], keys);
- this.hints[keys] = hint;
- }
- }
-
- showHints(keys) {
- Object.keys(this.hints).filter(key => key.startsWith(keys))
- .forEach(key => this.hints[key].show());
- Object.keys(this.hints).filter(key => !key.startsWith(keys))
- .forEach(key => this.hints[key].hide());
- }
-
- removeHints() {
- Object.keys(this.hints).forEach((key) => {
- this.hints[key].remove();
- });
- this.hints = {};
- this.targets = [];
- }
-
- activateHints(keys) {
- let hint = this.hints[keys];
- if (!hint) {
- return;
- }
- let element = hint.target;
- switch (element.tagName.toLowerCase()) {
- case 'a':
- case 'area':
- return this.openLink(element);
- case 'input':
- switch (element.type) {
- case 'file':
- case 'checkbox':
- case 'radio':
- case 'submit':
- case 'reset':
- case 'button':
- case 'image':
- case 'color':
- return element.click();
- default:
- return element.focus();
- }
- case 'textarea':
- return element.focus();
- case 'button':
- case 'summary':
- return element.click();
- default:
- if (dom.isContentEditable(element)) {
- return element.focus();
- } else if (element.hasAttribute('tabindex')) {
- return element.click();
- }
- }
- }
-
- onMessage(message, sender) {
- switch (message.type) {
- case messages.FOLLOW_REQUEST_COUNT_TARGETS:
- return this.countHints(sender, message.viewSize, message.framePosition);
- case messages.FOLLOW_CREATE_HINTS:
- return this.createHints(
- message.keysArray, message.newTab, message.background);
- case messages.FOLLOW_SHOW_HINTS:
- return this.showHints(message.keys);
- case messages.FOLLOW_ACTIVATE:
- return this.activateHints(message.keys);
- case messages.FOLLOW_REMOVE_HINTS:
- return this.removeHints(message.keys);
- }
- }
-
- static getTargetElements(win, viewSize, framePosition) {
- let all = win.document.querySelectorAll(TARGET_SELECTOR);
- let filtered = Array.prototype.filter.call(all, (element) => {
- let style = win.getComputedStyle(element);
-
- // AREA's 'display' in Browser style is 'none'
- return (element.tagName === 'AREA' || style.display !== 'none') &&
- style.visibility !== 'hidden' &&
- element.type !== 'hidden' &&
- element.offsetHeight > 0 &&
- !isAriaHiddenOrAriaDisabled(win, element) &&
- inViewport(win, element, viewSize, framePosition);
- });
- return filtered;
- }
-}
diff --git a/src/content/components/common/follow.ts b/src/content/components/common/follow.ts
new file mode 100644
index 0000000..63ce603
--- /dev/null
+++ b/src/content/components/common/follow.ts
@@ -0,0 +1,200 @@
+import messages from 'shared/messages';
+import Hint from './hint';
+import * as dom from 'shared/utils/dom';
+
+const TARGET_SELECTOR = [
+ 'a', 'button', 'input', 'textarea', 'area',
+ '[contenteditable=true]', '[contenteditable=""]', '[tabindex]',
+ '[role="button"]', 'summary'
+].join(',');
+
+
+const inViewport = (win, element, viewSize, framePosition) => {
+ let {
+ top, left, bottom, right
+ } = dom.viewportRect(element);
+ let doc = win.document;
+ let frameWidth = doc.documentElement.clientWidth;
+ let frameHeight = doc.documentElement.clientHeight;
+
+ if (right < 0 || bottom < 0 || top > frameHeight || left > frameWidth) {
+ // out of frame
+ return false;
+ }
+ if (right + framePosition.x < 0 || bottom + framePosition.y < 0 ||
+ left + framePosition.x > viewSize.width ||
+ top + framePosition.y > viewSize.height) {
+ // out of viewport
+ return false;
+ }
+ return true;
+};
+
+const isAriaHiddenOrAriaDisabled = (win, element) => {
+ if (!element || win.document.documentElement === element) {
+ return false;
+ }
+ for (let attr of ['aria-hidden', 'aria-disabled']) {
+ if (element.hasAttribute(attr)) {
+ let hidden = element.getAttribute(attr).toLowerCase();
+ if (hidden === '' || hidden === 'true') {
+ return true;
+ }
+ }
+ }
+ return isAriaHiddenOrAriaDisabled(win, element.parentNode);
+};
+
+export default class Follow {
+ constructor(win, store) {
+ this.win = win;
+ this.store = store;
+ this.newTab = false;
+ this.background = false;
+ this.hints = {};
+ this.targets = [];
+
+ messages.onMessage(this.onMessage.bind(this));
+ }
+
+ key(key) {
+ if (Object.keys(this.hints).length === 0) {
+ return false;
+ }
+ this.win.parent.postMessage(JSON.stringify({
+ type: messages.FOLLOW_KEY_PRESS,
+ key: key.key,
+ ctrlKey: key.ctrlKey,
+ }), '*');
+ return true;
+ }
+
+ openLink(element) {
+ // Browser prevent new tab by link with target='_blank'
+ if (!this.newTab && element.getAttribute('target') !== '_blank') {
+ element.click();
+ return;
+ }
+
+ let href = element.getAttribute('href');
+
+ // eslint-disable-next-line no-script-url
+ if (!href || href === '#' || href.toLowerCase().startsWith('javascript:')) {
+ return;
+ }
+ return browser.runtime.sendMessage({
+ type: messages.OPEN_URL,
+ url: element.href,
+ newTab: true,
+ background: this.background,
+ });
+ }
+
+ countHints(sender, viewSize, framePosition) {
+ this.targets = Follow.getTargetElements(this.win, viewSize, framePosition);
+ sender.postMessage(JSON.stringify({
+ type: messages.FOLLOW_RESPONSE_COUNT_TARGETS,
+ count: this.targets.length,
+ }), '*');
+ }
+
+ createHints(keysArray, newTab, background) {
+ if (keysArray.length !== this.targets.length) {
+ throw new Error('illegal hint count');
+ }
+
+ this.newTab = newTab;
+ this.background = background;
+ this.hints = {};
+ for (let i = 0; i < keysArray.length; ++i) {
+ let keys = keysArray[i];
+ let hint = new Hint(this.targets[i], keys);
+ this.hints[keys] = hint;
+ }
+ }
+
+ showHints(keys) {
+ Object.keys(this.hints).filter(key => key.startsWith(keys))
+ .forEach(key => this.hints[key].show());
+ Object.keys(this.hints).filter(key => !key.startsWith(keys))
+ .forEach(key => this.hints[key].hide());
+ }
+
+ removeHints() {
+ Object.keys(this.hints).forEach((key) => {
+ this.hints[key].remove();
+ });
+ this.hints = {};
+ this.targets = [];
+ }
+
+ activateHints(keys) {
+ let hint = this.hints[keys];
+ if (!hint) {
+ return;
+ }
+ let element = hint.target;
+ switch (element.tagName.toLowerCase()) {
+ case 'a':
+ case 'area':
+ return this.openLink(element);
+ case 'input':
+ switch (element.type) {
+ case 'file':
+ case 'checkbox':
+ case 'radio':
+ case 'submit':
+ case 'reset':
+ case 'button':
+ case 'image':
+ case 'color':
+ return element.click();
+ default:
+ return element.focus();
+ }
+ case 'textarea':
+ return element.focus();
+ case 'button':
+ case 'summary':
+ return element.click();
+ default:
+ if (dom.isContentEditable(element)) {
+ return element.focus();
+ } else if (element.hasAttribute('tabindex')) {
+ return element.click();
+ }
+ }
+ }
+
+ onMessage(message, sender) {
+ switch (message.type) {
+ case messages.FOLLOW_REQUEST_COUNT_TARGETS:
+ return this.countHints(sender, message.viewSize, message.framePosition);
+ case messages.FOLLOW_CREATE_HINTS:
+ return this.createHints(
+ message.keysArray, message.newTab, message.background);
+ case messages.FOLLOW_SHOW_HINTS:
+ return this.showHints(message.keys);
+ case messages.FOLLOW_ACTIVATE:
+ return this.activateHints(message.keys);
+ case messages.FOLLOW_REMOVE_HINTS:
+ return this.removeHints(message.keys);
+ }
+ }
+
+ static getTargetElements(win, viewSize, framePosition) {
+ let all = win.document.querySelectorAll(TARGET_SELECTOR);
+ let filtered = Array.prototype.filter.call(all, (element) => {
+ let style = win.getComputedStyle(element);
+
+ // AREA's 'display' in Browser style is 'none'
+ return (element.tagName === 'AREA' || style.display !== 'none') &&
+ style.visibility !== 'hidden' &&
+ element.type !== 'hidden' &&
+ element.offsetHeight > 0 &&
+ !isAriaHiddenOrAriaDisabled(win, element) &&
+ inViewport(win, element, viewSize, framePosition);
+ });
+ return filtered;
+ }
+}
diff --git a/src/content/components/common/hint.js b/src/content/components/common/hint.js
deleted file mode 100644
index 1472587..0000000
--- a/src/content/components/common/hint.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import * as dom from 'shared/utils/dom';
-
-const hintPosition = (element) => {
- let { left, top, right, bottom } = dom.viewportRect(element);
-
- if (element.tagName !== 'AREA') {
- return { x: left, y: top };
- }
-
- return {
- x: (left + right) / 2,
- y: (top + bottom) / 2,
- };
-};
-
-export default class Hint {
- constructor(target, tag) {
- if (!(document.body instanceof HTMLElement)) {
- throw new TypeError('target is not an HTMLElement');
- }
-
- this.target = target;
-
- let doc = target.ownerDocument;
- let { x, y } = hintPosition(target);
- let { scrollX, scrollY } = window;
-
- this.element = doc.createElement('span');
- this.element.className = 'vimvixen-hint';
- this.element.textContent = tag;
- this.element.style.left = x + scrollX + 'px';
- this.element.style.top = y + scrollY + 'px';
-
- this.show();
- doc.body.append(this.element);
- }
-
- show() {
- this.element.style.display = 'inline';
- }
-
- hide() {
- this.element.style.display = 'none';
- }
-
- remove() {
- this.element.remove();
- }
-}
diff --git a/src/content/components/common/hint.ts b/src/content/components/common/hint.ts
new file mode 100644
index 0000000..1472587
--- /dev/null
+++ b/src/content/components/common/hint.ts
@@ -0,0 +1,49 @@
+import * as dom from 'shared/utils/dom';
+
+const hintPosition = (element) => {
+ let { left, top, right, bottom } = dom.viewportRect(element);
+
+ if (element.tagName !== 'AREA') {
+ return { x: left, y: top };
+ }
+
+ return {
+ x: (left + right) / 2,
+ y: (top + bottom) / 2,
+ };
+};
+
+export default class Hint {
+ constructor(target, tag) {
+ if (!(document.body instanceof HTMLElement)) {
+ throw new TypeError('target is not an HTMLElement');
+ }
+
+ this.target = target;
+
+ let doc = target.ownerDocument;
+ let { x, y } = hintPosition(target);
+ let { scrollX, scrollY } = window;
+
+ this.element = doc.createElement('span');
+ this.element.className = 'vimvixen-hint';
+ this.element.textContent = tag;
+ this.element.style.left = x + scrollX + 'px';
+ this.element.style.top = y + scrollY + 'px';
+
+ this.show();
+ doc.body.append(this.element);
+ }
+
+ show() {
+ this.element.style.display = 'inline';
+ }
+
+ hide() {
+ this.element.style.display = 'none';
+ }
+
+ remove() {
+ this.element.remove();
+ }
+}
diff --git a/src/content/components/common/index.js b/src/content/components/common/index.js
deleted file mode 100644
index bcab4fa..0000000
--- a/src/content/components/common/index.js
+++ /dev/null
@@ -1,55 +0,0 @@
-import InputComponent from './input';
-import FollowComponent from './follow';
-import MarkComponent from './mark';
-import KeymapperComponent from './keymapper';
-import * as settingActions from 'content/actions/setting';
-import messages from 'shared/messages';
-import * as addonActions from '../../actions/addon';
-import * as blacklists from 'shared/blacklists';
-
-export default class Common {
- constructor(win, store) {
- const input = new InputComponent(win.document.body, store);
- const follow = new FollowComponent(win, store);
- const mark = new MarkComponent(win.document.body, store);
- const keymapper = new KeymapperComponent(store);
-
- input.onKey(key => follow.key(key));
- input.onKey(key => mark.key(key));
- input.onKey(key => keymapper.key(key));
-
- this.win = win;
- this.store = store;
- this.prevEnabled = undefined;
- this.prevBlacklist = undefined;
-
- this.reloadSettings();
-
- messages.onMessage(this.onMessage.bind(this));
- }
-
- onMessage(message) {
- let { enabled } = this.store.getState().addon;
- switch (message.type) {
- case messages.SETTINGS_CHANGED:
- return this.reloadSettings();
- case messages.ADDON_TOGGLE_ENABLED:
- this.store.dispatch(addonActions.setEnabled(!enabled));
- }
- }
-
- reloadSettings() {
- try {
- this.store.dispatch(settingActions.load()).then(({ value: settings }) => {
- let enabled = !blacklists.includes(
- settings.blacklist, this.win.location.href
- );
- this.store.dispatch(addonActions.setEnabled(enabled));
- });
- } catch (e) {
- // Sometime sendMessage fails when background script is not ready.
- console.warn(e);
- setTimeout(() => this.reloadSettings(), 500);
- }
- }
-}
diff --git a/src/content/components/common/index.ts b/src/content/components/common/index.ts
new file mode 100644
index 0000000..bcab4fa
--- /dev/null
+++ b/src/content/components/common/index.ts
@@ -0,0 +1,55 @@
+import InputComponent from './input';
+import FollowComponent from './follow';
+import MarkComponent from './mark';
+import KeymapperComponent from './keymapper';
+import * as settingActions from 'content/actions/setting';
+import messages from 'shared/messages';
+import * as addonActions from '../../actions/addon';
+import * as blacklists from 'shared/blacklists';
+
+export default class Common {
+ constructor(win, store) {
+ const input = new InputComponent(win.document.body, store);
+ const follow = new FollowComponent(win, store);
+ const mark = new MarkComponent(win.document.body, store);
+ const keymapper = new KeymapperComponent(store);
+
+ input.onKey(key => follow.key(key));
+ input.onKey(key => mark.key(key));
+ input.onKey(key => keymapper.key(key));
+
+ this.win = win;
+ this.store = store;
+ this.prevEnabled = undefined;
+ this.prevBlacklist = undefined;
+
+ this.reloadSettings();
+
+ messages.onMessage(this.onMessage.bind(this));
+ }
+
+ onMessage(message) {
+ let { enabled } = this.store.getState().addon;
+ switch (message.type) {
+ case messages.SETTINGS_CHANGED:
+ return this.reloadSettings();
+ case messages.ADDON_TOGGLE_ENABLED:
+ this.store.dispatch(addonActions.setEnabled(!enabled));
+ }
+ }
+
+ reloadSettings() {
+ try {
+ this.store.dispatch(settingActions.load()).then(({ value: settings }) => {
+ let enabled = !blacklists.includes(
+ settings.blacklist, this.win.location.href
+ );
+ this.store.dispatch(addonActions.setEnabled(enabled));
+ });
+ } catch (e) {
+ // Sometime sendMessage fails when background script is not ready.
+ console.warn(e);
+ setTimeout(() => this.reloadSettings(), 500);
+ }
+ }
+}
diff --git a/src/content/components/common/input.js b/src/content/components/common/input.js
deleted file mode 100644
index eefaf10..0000000
--- a/src/content/components/common/input.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import * as dom from 'shared/utils/dom';
-import * as keys from 'shared/utils/keys';
-
-const cancelKey = (e) => {
- return e.key === 'Escape' || e.key === '[' && e.ctrlKey;
-};
-
-export default class InputComponent {
- constructor(target) {
- this.pressed = {};
- this.onKeyListeners = [];
-
- target.addEventListener('keypress', this.onKeyPress.bind(this));
- target.addEventListener('keydown', this.onKeyDown.bind(this));
- target.addEventListener('keyup', this.onKeyUp.bind(this));
- }
-
- onKey(cb) {
- this.onKeyListeners.push(cb);
- }
-
- onKeyPress(e) {
- if (this.pressed[e.key] && this.pressed[e.key] !== 'keypress') {
- return;
- }
- this.pressed[e.key] = 'keypress';
- this.capture(e);
- }
-
- onKeyDown(e) {
- if (this.pressed[e.key] && this.pressed[e.key] !== 'keydown') {
- return;
- }
- this.pressed[e.key] = 'keydown';
- this.capture(e);
- }
-
- onKeyUp(e) {
- delete this.pressed[e.key];
- }
-
- capture(e) {
- if (this.fromInput(e)) {
- if (cancelKey(e) && e.target.blur) {
- e.target.blur();
- }
- return;
- }
- if (['Shift', 'Control', 'Alt', 'OS'].includes(e.key)) {
- // pressing only meta key is ignored
- return;
- }
-
- let key = keys.fromKeyboardEvent(e);
-
- for (let listener of this.onKeyListeners) {
- let stop = listener(key);
- if (stop) {
- e.preventDefault();
- e.stopPropagation();
- break;
- }
- }
- }
-
- fromInput(e) {
- if (!e.target) {
- return false;
- }
- return e.target instanceof HTMLInputElement ||
- e.target instanceof HTMLTextAreaElement ||
- e.target instanceof HTMLSelectElement ||
- dom.isContentEditable(e.target);
- }
-}
diff --git a/src/content/components/common/input.ts b/src/content/components/common/input.ts
new file mode 100644
index 0000000..eefaf10
--- /dev/null
+++ b/src/content/components/common/input.ts
@@ -0,0 +1,75 @@
+import * as dom from 'shared/utils/dom';
+import * as keys from 'shared/utils/keys';
+
+const cancelKey = (e) => {
+ return e.key === 'Escape' || e.key === '[' && e.ctrlKey;
+};
+
+export default class InputComponent {
+ constructor(target) {
+ this.pressed = {};
+ this.onKeyListeners = [];
+
+ target.addEventListener('keypress', this.onKeyPress.bind(this));
+ target.addEventListener('keydown', this.onKeyDown.bind(this));
+ target.addEventListener('keyup', this.onKeyUp.bind(this));
+ }
+
+ onKey(cb) {
+ this.onKeyListeners.push(cb);
+ }
+
+ onKeyPress(e) {
+ if (this.pressed[e.key] && this.pressed[e.key] !== 'keypress') {
+ return;
+ }
+ this.pressed[e.key] = 'keypress';
+ this.capture(e);
+ }
+
+ onKeyDown(e) {
+ if (this.pressed[e.key] && this.pressed[e.key] !== 'keydown') {
+ return;
+ }
+ this.pressed[e.key] = 'keydown';
+ this.capture(e);
+ }
+
+ onKeyUp(e) {
+ delete this.pressed[e.key];
+ }
+
+ capture(e) {
+ if (this.fromInput(e)) {
+ if (cancelKey(e) && e.target.blur) {
+ e.target.blur();
+ }
+ return;
+ }
+ if (['Shift', 'Control', 'Alt', 'OS'].includes(e.key)) {
+ // pressing only meta key is ignored
+ return;
+ }
+
+ let key = keys.fromKeyboardEvent(e);
+
+ for (let listener of this.onKeyListeners) {
+ let stop = listener(key);
+ if (stop) {
+ e.preventDefault();
+ e.stopPropagation();
+ break;
+ }
+ }
+ }
+
+ fromInput(e) {
+ if (!e.target) {
+ return false;
+ }
+ return e.target instanceof HTMLInputElement ||
+ e.target instanceof HTMLTextAreaElement ||
+ e.target instanceof HTMLSelectElement ||
+ dom.isContentEditable(e.target);
+ }
+}
diff --git a/src/content/components/common/keymapper.js b/src/content/components/common/keymapper.js
deleted file mode 100644
index ec0d093..0000000
--- a/src/content/components/common/keymapper.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import * as inputActions from 'content/actions/input';
-import * as operationActions from 'content/actions/operation';
-import operations from 'shared/operations';
-import * as keyUtils from 'shared/utils/keys';
-
-const mapStartsWith = (mapping, keys) => {
- if (mapping.length < keys.length) {
- return false;
- }
- for (let i = 0; i < keys.length; ++i) {
- if (!keyUtils.equals(mapping[i], keys[i])) {
- return false;
- }
- }
- return true;
-};
-
-export default class KeymapperComponent {
- constructor(store) {
- this.store = store;
- }
-
- // eslint-disable-next-line max-statements
- key(key) {
- this.store.dispatch(inputActions.keyPress(key));
-
- let state = this.store.getState();
- let input = state.input;
- let keymaps = new Map(state.setting.keymaps);
-
- let matched = Array.from(keymaps.keys()).filter((mapping) => {
- return mapStartsWith(mapping, input.keys);
- });
- if (!state.addon.enabled) {
- // available keymaps are only ADDON_ENABLE and ADDON_TOGGLE_ENABLED if
- // the addon disabled
- matched = matched.filter((keys) => {
- let type = keymaps.get(keys).type;
- return type === operations.ADDON_ENABLE ||
- type === operations.ADDON_TOGGLE_ENABLED;
- });
- }
- if (matched.length === 0) {
- this.store.dispatch(inputActions.clearKeys());
- return false;
- } else if (matched.length > 1 ||
- matched.length === 1 && input.keys.length < matched[0].length) {
- return true;
- }
- let operation = keymaps.get(matched[0]);
- let act = operationActions.exec(
- operation, state.setting, state.addon.enabled
- );
- this.store.dispatch(act);
- this.store.dispatch(inputActions.clearKeys());
- return true;
- }
-}
diff --git a/src/content/components/common/keymapper.ts b/src/content/components/common/keymapper.ts
new file mode 100644
index 0000000..ec0d093
--- /dev/null
+++ b/src/content/components/common/keymapper.ts
@@ -0,0 +1,58 @@
+import * as inputActions from 'content/actions/input';
+import * as operationActions from 'content/actions/operation';
+import operations from 'shared/operations';
+import * as keyUtils from 'shared/utils/keys';
+
+const mapStartsWith = (mapping, keys) => {
+ if (mapping.length < keys.length) {
+ return false;
+ }
+ for (let i = 0; i < keys.length; ++i) {
+ if (!keyUtils.equals(mapping[i], keys[i])) {
+ return false;
+ }
+ }
+ return true;
+};
+
+export default class KeymapperComponent {
+ constructor(store) {
+ this.store = store;
+ }
+
+ // eslint-disable-next-line max-statements
+ key(key) {
+ this.store.dispatch(inputActions.keyPress(key));
+
+ let state = this.store.getState();
+ let input = state.input;
+ let keymaps = new Map(state.setting.keymaps);
+
+ let matched = Array.from(keymaps.keys()).filter((mapping) => {
+ return mapStartsWith(mapping, input.keys);
+ });
+ if (!state.addon.enabled) {
+ // available keymaps are only ADDON_ENABLE and ADDON_TOGGLE_ENABLED if
+ // the addon disabled
+ matched = matched.filter((keys) => {
+ let type = keymaps.get(keys).type;
+ return type === operations.ADDON_ENABLE ||
+ type === operations.ADDON_TOGGLE_ENABLED;
+ });
+ }
+ if (matched.length === 0) {
+ this.store.dispatch(inputActions.clearKeys());
+ return false;
+ } else if (matched.length > 1 ||
+ matched.length === 1 && input.keys.length < matched[0].length) {
+ return true;
+ }
+ let operation = keymaps.get(matched[0]);
+ let act = operationActions.exec(
+ operation, state.setting, state.addon.enabled
+ );
+ this.store.dispatch(act);
+ this.store.dispatch(inputActions.clearKeys());
+ return true;
+ }
+}
diff --git a/src/content/components/common/mark.js b/src/content/components/common/mark.js
deleted file mode 100644
index 0f838a9..0000000
--- a/src/content/components/common/mark.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import * as markActions from 'content/actions/mark';
-import * as scrolls from 'content/scrolls';
-import * as consoleFrames from 'content/console-frames';
-import * as properties from 'shared/settings/properties';
-
-const cancelKey = (key) => {
- return key.key === 'Esc' || key.key === '[' && key.ctrlKey;
-};
-
-const globalKey = (key) => {
- return (/^[A-Z0-9]$/).test(key);
-};
-
-export default class MarkComponent {
- constructor(body, store) {
- this.body = body;
- this.store = store;
- }
-
- // eslint-disable-next-line max-statements
- key(key) {
- let { mark: markStage, setting } = this.store.getState();
- let smoothscroll = setting.properties.smoothscroll ||
- properties.defaults.smoothscroll;
-
- if (!markStage.setMode && !markStage.jumpMode) {
- return false;
- }
-
- if (cancelKey(key)) {
- this.store.dispatch(markActions.cancel());
- return true;
- }
-
- if (key.ctrlKey || key.metaKey || key.altKey) {
- consoleFrames.postError('Unknown mark');
- } else if (globalKey(key.key) && markStage.setMode) {
- this.doSetGlobal(key);
- } else if (globalKey(key.key) && markStage.jumpMode) {
- this.doJumpGlobal(key);
- } else if (markStage.setMode) {
- this.doSet(key);
- } else if (markStage.jumpMode) {
- this.doJump(markStage.marks, key, smoothscroll);
- }
-
- this.store.dispatch(markActions.cancel());
- return true;
- }
-
- doSet(key) {
- let { x, y } = scrolls.getScroll();
- this.store.dispatch(markActions.setLocal(key.key, x, y));
- }
-
- doJump(marks, key, smoothscroll) {
- if (!marks[key.key]) {
- consoleFrames.postError('Mark is not set');
- return;
- }
-
- let { x, y } = marks[key.key];
- scrolls.scrollTo(x, y, smoothscroll);
- }
-
- doSetGlobal(key) {
- let { x, y } = scrolls.getScroll();
- this.store.dispatch(markActions.setGlobal(key.key, x, y));
- }
-
- doJumpGlobal(key) {
- this.store.dispatch(markActions.jumpGlobal(key.key));
- }
-}
diff --git a/src/content/components/common/mark.ts b/src/content/components/common/mark.ts
new file mode 100644
index 0000000..0f838a9
--- /dev/null
+++ b/src/content/components/common/mark.ts
@@ -0,0 +1,74 @@
+import * as markActions from 'content/actions/mark';
+import * as scrolls from 'content/scrolls';
+import * as consoleFrames from 'content/console-frames';
+import * as properties from 'shared/settings/properties';
+
+const cancelKey = (key) => {
+ return key.key === 'Esc' || key.key === '[' && key.ctrlKey;
+};
+
+const globalKey = (key) => {
+ return (/^[A-Z0-9]$/).test(key);
+};
+
+export default class MarkComponent {
+ constructor(body, store) {
+ this.body = body;
+ this.store = store;
+ }
+
+ // eslint-disable-next-line max-statements
+ key(key) {
+ let { mark: markStage, setting } = this.store.getState();
+ let smoothscroll = setting.properties.smoothscroll ||
+ properties.defaults.smoothscroll;
+
+ if (!markStage.setMode && !markStage.jumpMode) {
+ return false;
+ }
+
+ if (cancelKey(key)) {
+ this.store.dispatch(markActions.cancel());
+ return true;
+ }
+
+ if (key.ctrlKey || key.metaKey || key.altKey) {
+ consoleFrames.postError('Unknown mark');
+ } else if (globalKey(key.key) && markStage.setMode) {
+ this.doSetGlobal(key);
+ } else if (globalKey(key.key) && markStage.jumpMode) {
+ this.doJumpGlobal(key);
+ } else if (markStage.setMode) {
+ this.doSet(key);
+ } else if (markStage.jumpMode) {
+ this.doJump(markStage.marks, key, smoothscroll);
+ }
+
+ this.store.dispatch(markActions.cancel());
+ return true;
+ }
+
+ doSet(key) {
+ let { x, y } = scrolls.getScroll();
+ this.store.dispatch(markActions.setLocal(key.key, x, y));
+ }
+
+ doJump(marks, key, smoothscroll) {
+ if (!marks[key.key]) {
+ consoleFrames.postError('Mark is not set');
+ return;
+ }
+
+ let { x, y } = marks[key.key];
+ scrolls.scrollTo(x, y, smoothscroll);
+ }
+
+ doSetGlobal(key) {
+ let { x, y } = scrolls.getScroll();
+ this.store.dispatch(markActions.setGlobal(key.key, x, y));
+ }
+
+ doJumpGlobal(key) {
+ this.store.dispatch(markActions.jumpGlobal(key.key));
+ }
+}
diff --git a/src/content/components/frame-content.js b/src/content/components/frame-content.js
deleted file mode 100644
index ca999ba..0000000
--- a/src/content/components/frame-content.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import CommonComponent from './common';
-
-export default CommonComponent;
diff --git a/src/content/components/frame-content.ts b/src/content/components/frame-content.ts
new file mode 100644
index 0000000..ca999ba
--- /dev/null
+++ b/src/content/components/frame-content.ts
@@ -0,0 +1,3 @@
+import CommonComponent from './common';
+
+export default CommonComponent;
diff --git a/src/content/components/top-content/find.js b/src/content/components/top-content/find.js
deleted file mode 100644
index 4d46d79..0000000
--- a/src/content/components/top-content/find.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import * as findActions from 'content/actions/find';
-import messages from 'shared/messages';
-
-export default class FindComponent {
- constructor(win, store) {
- this.win = win;
- this.store = store;
-
- messages.onMessage(this.onMessage.bind(this));
- }
-
- onMessage(message) {
- switch (message.type) {
- case messages.CONSOLE_ENTER_FIND:
- return this.start(message.text);
- case messages.FIND_NEXT:
- return this.next();
- case messages.FIND_PREV:
- return this.prev();
- }
- }
-
- start(text) {
- let state = this.store.getState().find;
-
- if (text.length === 0) {
- return this.store.dispatch(findActions.next(state.keyword, true));
- }
- return this.store.dispatch(findActions.next(text, true));
- }
-
- next() {
- let state = this.store.getState().find;
- return this.store.dispatch(findActions.next(state.keyword, false));
- }
-
- prev() {
- let state = this.store.getState().find;
- return this.store.dispatch(findActions.prev(state.keyword, false));
- }
-}
diff --git a/src/content/components/top-content/find.ts b/src/content/components/top-content/find.ts
new file mode 100644
index 0000000..4d46d79
--- /dev/null
+++ b/src/content/components/top-content/find.ts
@@ -0,0 +1,41 @@
+import * as findActions from 'content/actions/find';
+import messages from 'shared/messages';
+
+export default class FindComponent {
+ constructor(win, store) {
+ this.win = win;
+ this.store = store;
+
+ messages.onMessage(this.onMessage.bind(this));
+ }
+
+ onMessage(message) {
+ switch (message.type) {
+ case messages.CONSOLE_ENTER_FIND:
+ return this.start(message.text);
+ case messages.FIND_NEXT:
+ return this.next();
+ case messages.FIND_PREV:
+ return this.prev();
+ }
+ }
+
+ start(text) {
+ let state = this.store.getState().find;
+
+ if (text.length === 0) {
+ return this.store.dispatch(findActions.next(state.keyword, true));
+ }
+ return this.store.dispatch(findActions.next(text, true));
+ }
+
+ next() {
+ let state = this.store.getState().find;
+ return this.store.dispatch(findActions.next(state.keyword, false));
+ }
+
+ prev() {
+ let state = this.store.getState().find;
+ return this.store.dispatch(findActions.prev(state.keyword, false));
+ }
+}
diff --git a/src/content/components/top-content/follow-controller.js b/src/content/components/top-content/follow-controller.js
deleted file mode 100644
index 7f36604..0000000
--- a/src/content/components/top-content/follow-controller.js
+++ /dev/null
@@ -1,147 +0,0 @@
-import * as followControllerActions from 'content/actions/follow-controller';
-import messages from 'shared/messages';
-import HintKeyProducer from 'content/hint-key-producer';
-import * as properties from 'shared/settings/properties';
-
-const broadcastMessage = (win, message) => {
- let json = JSON.stringify(message);
- let frames = [window.self].concat(Array.from(window.frames));
- frames.forEach(frame => frame.postMessage(json, '*'));
-};
-
-export default class FollowController {
- constructor(win, store) {
- this.win = win;
- this.store = store;
- this.state = {};
- this.keys = [];
- this.producer = null;
-
- messages.onMessage(this.onMessage.bind(this));
-
- store.subscribe(() => {
- this.update();
- });
- }
-
- onMessage(message, sender) {
- switch (message.type) {
- case messages.FOLLOW_START:
- return this.store.dispatch(
- followControllerActions.enable(message.newTab, message.background));
- case messages.FOLLOW_RESPONSE_COUNT_TARGETS:
- return this.create(message.count, sender);
- case messages.FOLLOW_KEY_PRESS:
- return this.keyPress(message.key, message.ctrlKey);
- }
- }
-
- update() {
- let prevState = this.state;
- this.state = this.store.getState().followController;
-
- if (!prevState.enabled && this.state.enabled) {
- this.count();
- } else if (prevState.enabled && !this.state.enabled) {
- this.remove();
- } else if (prevState.keys !== this.state.keys) {
- this.updateHints();
- }
- }
-
- updateHints() {
- let shown = this.keys.filter(key => key.startsWith(this.state.keys));
- if (shown.length === 1) {
- this.activate();
- this.store.dispatch(followControllerActions.disable());
- }
-
- broadcastMessage(this.win, {
- type: messages.FOLLOW_SHOW_HINTS,
- keys: this.state.keys,
- });
- }
-
- activate() {
- broadcastMessage(this.win, {
- type: messages.FOLLOW_ACTIVATE,
- keys: this.state.keys,
- });
- }
-
- keyPress(key, ctrlKey) {
- if (key === '[' && ctrlKey) {
- this.store.dispatch(followControllerActions.disable());
- return true;
- }
- switch (key) {
- case 'Enter':
- this.activate();
- this.store.dispatch(followControllerActions.disable());
- break;
- case 'Esc':
- this.store.dispatch(followControllerActions.disable());
- break;
- case 'Backspace':
- case 'Delete':
- this.store.dispatch(followControllerActions.backspace());
- break;
- default:
- if (this.hintchars().includes(key)) {
- this.store.dispatch(followControllerActions.keyPress(key));
- }
- break;
- }
- return true;
- }
-
- count() {
- this.producer = new HintKeyProducer(this.hintchars());
- let doc = this.win.document;
- let viewWidth = this.win.innerWidth || doc.documentElement.clientWidth;
- let viewHeight = this.win.innerHeight || doc.documentElement.clientHeight;
- let frameElements = this.win.document.querySelectorAll('frame,iframe');
-
- this.win.postMessage(JSON.stringify({
- type: messages.FOLLOW_REQUEST_COUNT_TARGETS,
- viewSize: { width: viewWidth, height: viewHeight },
- framePosition: { x: 0, y: 0 },
- }), '*');
- frameElements.forEach((element) => {
- let { left: frameX, top: frameY } = element.getBoundingClientRect();
- let message = JSON.stringify({
- type: messages.FOLLOW_REQUEST_COUNT_TARGETS,
- viewSize: { width: viewWidth, height: viewHeight },
- framePosition: { x: frameX, y: frameY },
- });
- element.contentWindow.postMessage(message, '*');
- });
- }
-
- create(count, sender) {
- let produced = [];
- for (let i = 0; i < count; ++i) {
- produced.push(this.producer.produce());
- }
- this.keys = this.keys.concat(produced);
-
- sender.postMessage(JSON.stringify({
- type: messages.FOLLOW_CREATE_HINTS,
- keysArray: produced,
- newTab: this.state.newTab,
- background: this.state.background,
- }), '*');
- }
-
- remove() {
- this.keys = [];
- broadcastMessage(this.win, {
- type: messages.FOLLOW_REMOVE_HINTS,
- });
- }
-
- hintchars() {
- return this.store.getState().setting.properties.hintchars ||
- properties.defaults.hintchars;
- }
-}
diff --git a/src/content/components/top-content/follow-controller.ts b/src/content/components/top-content/follow-controller.ts
new file mode 100644
index 0000000..7f36604
--- /dev/null
+++ b/src/content/components/top-content/follow-controller.ts
@@ -0,0 +1,147 @@
+import * as followControllerActions from 'content/actions/follow-controller';
+import messages from 'shared/messages';
+import HintKeyProducer from 'content/hint-key-producer';
+import * as properties from 'shared/settings/properties';
+
+const broadcastMessage = (win, message) => {
+ let json = JSON.stringify(message);
+ let frames = [window.self].concat(Array.from(window.frames));
+ frames.forEach(frame => frame.postMessage(json, '*'));
+};
+
+export default class FollowController {
+ constructor(win, store) {
+ this.win = win;
+ this.store = store;
+ this.state = {};
+ this.keys = [];
+ this.producer = null;
+
+ messages.onMessage(this.onMessage.bind(this));
+
+ store.subscribe(() => {
+ this.update();
+ });
+ }
+
+ onMessage(message, sender) {
+ switch (message.type) {
+ case messages.FOLLOW_START:
+ return this.store.dispatch(
+ followControllerActions.enable(message.newTab, message.background));
+ case messages.FOLLOW_RESPONSE_COUNT_TARGETS:
+ return this.create(message.count, sender);
+ case messages.FOLLOW_KEY_PRESS:
+ return this.keyPress(message.key, message.ctrlKey);
+ }
+ }
+
+ update() {
+ let prevState = this.state;
+ this.state = this.store.getState().followController;
+
+ if (!prevState.enabled && this.state.enabled) {
+ this.count();
+ } else if (prevState.enabled && !this.state.enabled) {
+ this.remove();
+ } else if (prevState.keys !== this.state.keys) {
+ this.updateHints();
+ }
+ }
+
+ updateHints() {
+ let shown = this.keys.filter(key => key.startsWith(this.state.keys));
+ if (shown.length === 1) {
+ this.activate();
+ this.store.dispatch(followControllerActions.disable());
+ }
+
+ broadcastMessage(this.win, {
+ type: messages.FOLLOW_SHOW_HINTS,
+ keys: this.state.keys,
+ });
+ }
+
+ activate() {
+ broadcastMessage(this.win, {
+ type: messages.FOLLOW_ACTIVATE,
+ keys: this.state.keys,
+ });
+ }
+
+ keyPress(key, ctrlKey) {
+ if (key === '[' && ctrlKey) {
+ this.store.dispatch(followControllerActions.disable());
+ return true;
+ }
+ switch (key) {
+ case 'Enter':
+ this.activate();
+ this.store.dispatch(followControllerActions.disable());
+ break;
+ case 'Esc':
+ this.store.dispatch(followControllerActions.disable());
+ break;
+ case 'Backspace':
+ case 'Delete':
+ this.store.dispatch(followControllerActions.backspace());
+ break;
+ default:
+ if (this.hintchars().includes(key)) {
+ this.store.dispatch(followControllerActions.keyPress(key));
+ }
+ break;
+ }
+ return true;
+ }
+
+ count() {
+ this.producer = new HintKeyProducer(this.hintchars());
+ let doc = this.win.document;
+ let viewWidth = this.win.innerWidth || doc.documentElement.clientWidth;
+ let viewHeight = this.win.innerHeight || doc.documentElement.clientHeight;
+ let frameElements = this.win.document.querySelectorAll('frame,iframe');
+
+ this.win.postMessage(JSON.stringify({
+ type: messages.FOLLOW_REQUEST_COUNT_TARGETS,
+ viewSize: { width: viewWidth, height: viewHeight },
+ framePosition: { x: 0, y: 0 },
+ }), '*');
+ frameElements.forEach((element) => {
+ let { left: frameX, top: frameY } = element.getBoundingClientRect();
+ let message = JSON.stringify({
+ type: messages.FOLLOW_REQUEST_COUNT_TARGETS,
+ viewSize: { width: viewWidth, height: viewHeight },
+ framePosition: { x: frameX, y: frameY },
+ });
+ element.contentWindow.postMessage(message, '*');
+ });
+ }
+
+ create(count, sender) {
+ let produced = [];
+ for (let i = 0; i < count; ++i) {
+ produced.push(this.producer.produce());
+ }
+ this.keys = this.keys.concat(produced);
+
+ sender.postMessage(JSON.stringify({
+ type: messages.FOLLOW_CREATE_HINTS,
+ keysArray: produced,
+ newTab: this.state.newTab,
+ background: this.state.background,
+ }), '*');
+ }
+
+ remove() {
+ this.keys = [];
+ broadcastMessage(this.win, {
+ type: messages.FOLLOW_REMOVE_HINTS,
+ });
+ }
+
+ hintchars() {
+ return this.store.getState().setting.properties.hintchars ||
+ properties.defaults.hintchars;
+ }
+}
diff --git a/src/content/components/top-content/index.js b/src/content/components/top-content/index.js
deleted file mode 100644
index 1aaef1b..0000000
--- a/src/content/components/top-content/index.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import CommonComponent from '../common';
-import FollowController from './follow-controller';
-import FindComponent from './find';
-import * as consoleFrames from '../../console-frames';
-import messages from 'shared/messages';
-import * as scrolls from 'content/scrolls';
-
-export default class TopContent {
-
- constructor(win, store) {
- this.win = win;
- this.store = store;
-
- new CommonComponent(win, store); // eslint-disable-line no-new
- new FollowController(win, store); // eslint-disable-line no-new
- new FindComponent(win, store); // eslint-disable-line no-new
-
- // TODO make component
- consoleFrames.initialize(this.win.document);
-
- messages.onMessage(this.onMessage.bind(this));
- }
-
- onMessage(message) {
- let addonState = this.store.getState().addon;
-
- switch (message.type) {
- case messages.CONSOLE_UNFOCUS:
- this.win.focus();
- consoleFrames.blur(window.document);
- return Promise.resolve();
- case messages.ADDON_ENABLED_QUERY:
- return Promise.resolve({
- type: messages.ADDON_ENABLED_RESPONSE,
- enabled: addonState.enabled,
- });
- case messages.TAB_SCROLL_TO:
- return scrolls.scrollTo(message.x, message.y, false);
- }
- }
-}
diff --git a/src/content/components/top-content/index.ts b/src/content/components/top-content/index.ts
new file mode 100644
index 0000000..1aaef1b
--- /dev/null
+++ b/src/content/components/top-content/index.ts
@@ -0,0 +1,41 @@
+import CommonComponent from '../common';
+import FollowController from './follow-controller';
+import FindComponent from './find';
+import * as consoleFrames from '../../console-frames';
+import messages from 'shared/messages';
+import * as scrolls from 'content/scrolls';
+
+export default class TopContent {
+
+ constructor(win, store) {
+ this.win = win;
+ this.store = store;
+
+ new CommonComponent(win, store); // eslint-disable-line no-new
+ new FollowController(win, store); // eslint-disable-line no-new
+ new FindComponent(win, store); // eslint-disable-line no-new
+
+ // TODO make component
+ consoleFrames.initialize(this.win.document);
+
+ messages.onMessage(this.onMessage.bind(this));
+ }
+
+ onMessage(message) {
+ let addonState = this.store.getState().addon;
+
+ switch (message.type) {
+ case messages.CONSOLE_UNFOCUS:
+ this.win.focus();
+ consoleFrames.blur(window.document);
+ return Promise.resolve();
+ case messages.ADDON_ENABLED_QUERY:
+ return Promise.resolve({
+ type: messages.ADDON_ENABLED_RESPONSE,
+ enabled: addonState.enabled,
+ });
+ case messages.TAB_SCROLL_TO:
+ return scrolls.scrollTo(message.x, message.y, false);
+ }
+ }
+}
diff --git a/src/content/console-frames.js b/src/content/console-frames.js
deleted file mode 100644
index ecb5a87..0000000
--- a/src/content/console-frames.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import messages from 'shared/messages';
-
-const initialize = (doc) => {
- let iframe = doc.createElement('iframe');
- iframe.src = browser.runtime.getURL('build/console.html');
- iframe.id = 'vimvixen-console-frame';
- iframe.className = 'vimvixen-console-frame';
- doc.body.append(iframe);
-
- return iframe;
-};
-
-const blur = (doc) => {
- let iframe = doc.getElementById('vimvixen-console-frame');
- iframe.blur();
-};
-
-const postError = (text) => {
- browser.runtime.sendMessage({
- type: messages.CONSOLE_FRAME_MESSAGE,
- message: {
- type: messages.CONSOLE_SHOW_ERROR,
- text,
- },
- });
-};
-
-const postInfo = (text) => {
- browser.runtime.sendMessage({
- type: messages.CONSOLE_FRAME_MESSAGE,
- message: {
- type: messages.CONSOLE_SHOW_INFO,
- text,
- },
- });
-};
-
-export { initialize, blur, postError, postInfo };
diff --git a/src/content/console-frames.ts b/src/content/console-frames.ts
new file mode 100644
index 0000000..ecb5a87
--- /dev/null
+++ b/src/content/console-frames.ts
@@ -0,0 +1,38 @@
+import messages from 'shared/messages';
+
+const initialize = (doc) => {
+ let iframe = doc.createElement('iframe');
+ iframe.src = browser.runtime.getURL('build/console.html');
+ iframe.id = 'vimvixen-console-frame';
+ iframe.className = 'vimvixen-console-frame';
+ doc.body.append(iframe);
+
+ return iframe;
+};
+
+const blur = (doc) => {
+ let iframe = doc.getElementById('vimvixen-console-frame');
+ iframe.blur();
+};
+
+const postError = (text) => {
+ browser.runtime.sendMessage({
+ type: messages.CONSOLE_FRAME_MESSAGE,
+ message: {
+ type: messages.CONSOLE_SHOW_ERROR,
+ text,
+ },
+ });
+};
+
+const postInfo = (text) => {
+ browser.runtime.sendMessage({
+ type: messages.CONSOLE_FRAME_MESSAGE,
+ message: {
+ type: messages.CONSOLE_SHOW_INFO,
+ text,
+ },
+ });
+};
+
+export { initialize, blur, postError, postInfo };
diff --git a/src/content/focuses.js b/src/content/focuses.js
deleted file mode 100644
index a6f6cc8..0000000
--- a/src/content/focuses.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import * as doms from 'shared/utils/dom';
-
-const focusInput = () => {
- let inputTypes = ['email', 'number', 'search', 'tel', 'text', 'url'];
- let inputSelector = inputTypes.map(type => `input[type=${type}]`).join(',');
- let targets = window.document.querySelectorAll(inputSelector + ',textarea');
- let target = Array.from(targets).find(doms.isVisible);
- if (target) {
- target.focus();
- }
-};
-
-export { focusInput };
diff --git a/src/content/focuses.ts b/src/content/focuses.ts
new file mode 100644
index 0000000..a6f6cc8
--- /dev/null
+++ b/src/content/focuses.ts
@@ -0,0 +1,13 @@
+import * as doms from 'shared/utils/dom';
+
+const focusInput = () => {
+ let inputTypes = ['email', 'number', 'search', 'tel', 'text', 'url'];
+ let inputSelector = inputTypes.map(type => `input[type=${type}]`).join(',');
+ let targets = window.document.querySelectorAll(inputSelector + ',textarea');
+ let target = Array.from(targets).find(doms.isVisible);
+ if (target) {
+ target.focus();
+ }
+};
+
+export { focusInput };
diff --git a/src/content/hint-key-producer.js b/src/content/hint-key-producer.js
deleted file mode 100644
index 14b23b6..0000000
--- a/src/content/hint-key-producer.js
+++ /dev/null
@@ -1,33 +0,0 @@
-export default class HintKeyProducer {
- constructor(charset) {
- if (charset.length === 0) {
- throw new TypeError('charset is empty');
- }
-
- this.charset = charset;
- this.counter = [];
- }
-
- produce() {
- this.increment();
-
- return this.counter.map(x => this.charset[x]).join('');
- }
-
- increment() {
- let max = this.charset.length - 1;
- if (this.counter.every(x => x === max)) {
- this.counter = new Array(this.counter.length + 1).fill(0);
- return;
- }
-
- this.counter.reverse();
- let len = this.charset.length;
- let num = this.counter.reduce((x, y, index) => x + y * len ** index) + 1;
- for (let i = 0; i < this.counter.length; ++i) {
- this.counter[i] = num % len;
- num = ~~(num / len);
- }
- this.counter.reverse();
- }
-}
diff --git a/src/content/hint-key-producer.ts b/src/content/hint-key-producer.ts
new file mode 100644
index 0000000..14b23b6
--- /dev/null
+++ b/src/content/hint-key-producer.ts
@@ -0,0 +1,33 @@
+export default class HintKeyProducer {
+ constructor(charset) {
+ if (charset.length === 0) {
+ throw new TypeError('charset is empty');
+ }
+
+ this.charset = charset;
+ this.counter = [];
+ }
+
+ produce() {
+ this.increment();
+
+ return this.counter.map(x => this.charset[x]).join('');
+ }
+
+ increment() {
+ let max = this.charset.length - 1;
+ if (this.counter.every(x => x === max)) {
+ this.counter = new Array(this.counter.length + 1).fill(0);
+ return;
+ }
+
+ this.counter.reverse();
+ let len = this.charset.length;
+ let num = this.counter.reduce((x, y, index) => x + y * len ** index) + 1;
+ for (let i = 0; i < this.counter.length; ++i) {
+ this.counter[i] = num % len;
+ num = ~~(num / len);
+ }
+ this.counter.reverse();
+ }
+}
diff --git a/src/content/index.js b/src/content/index.js
deleted file mode 100644
index 9edb712..0000000
--- a/src/content/index.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { createStore, applyMiddleware } from 'redux';
-import promise from 'redux-promise';
-import reducers from 'content/reducers';
-import TopContentComponent from './components/top-content';
-import FrameContentComponent from './components/frame-content';
-import consoleFrameStyle from './site-style';
-
-const store = createStore(
- reducers,
- applyMiddleware(promise),
-);
-
-if (window.self === window.top) {
- new TopContentComponent(window, store); // eslint-disable-line no-new
-} else {
- new FrameContentComponent(window, store); // eslint-disable-line no-new
-}
-
-let style = window.document.createElement('style');
-style.textContent = consoleFrameStyle.default;
-window.document.head.appendChild(style);
diff --git a/src/content/index.ts b/src/content/index.ts
new file mode 100644
index 0000000..9edb712
--- /dev/null
+++ b/src/content/index.ts
@@ -0,0 +1,21 @@
+import { createStore, applyMiddleware } from 'redux';
+import promise from 'redux-promise';
+import reducers from 'content/reducers';
+import TopContentComponent from './components/top-content';
+import FrameContentComponent from './components/frame-content';
+import consoleFrameStyle from './site-style';
+
+const store = createStore(
+ reducers,
+ applyMiddleware(promise),
+);
+
+if (window.self === window.top) {
+ new TopContentComponent(window, store); // eslint-disable-line no-new
+} else {
+ new FrameContentComponent(window, store); // eslint-disable-line no-new
+}
+
+let style = window.document.createElement('style');
+style.textContent = consoleFrameStyle.default;
+window.document.head.appendChild(style);
diff --git a/src/content/navigates.js b/src/content/navigates.js
deleted file mode 100644
index c9baa30..0000000
--- a/src/content/navigates.js
+++ /dev/null
@@ -1,78 +0,0 @@
-const REL_PATTERN = {
- prev: /^(?:prev(?:ious)?|older)\b|\u2039|\u2190|\xab|\u226a|<>/i,
-};
-
-// Return the last element in the document matching the supplied selector
-// and the optional filter, or null if there are no matches.
-const selectLast = (win, selector, filter) => {
- let nodes = win.document.querySelectorAll(selector);
-
- if (filter) {
- nodes = Array.from(nodes).filter(filter);
- }
-
- return nodes.length ? nodes[nodes.length - 1] : null;
-};
-
-const historyPrev = (win) => {
- win.history.back();
-};
-
-const historyNext = (win) => {
- win.history.forward();
-};
-
-// Code common to linkPrev and linkNext which navigates to the specified page.
-const linkRel = (win, rel) => {
- let link = selectLast(win, `link[rel~=${rel}][href]`);
-
- if (link) {
- win.location = link.href;
- return;
- }
-
- const pattern = REL_PATTERN[rel];
-
- link = selectLast(win, `a[rel~=${rel}][href]`) ||
- // `innerText` is much slower than `textContent`, but produces much better
- // (i.e. less unexpected) results
- selectLast(win, 'a[href]', lnk => pattern.test(lnk.innerText));
-
- if (link) {
- link.click();
- }
-};
-
-const linkPrev = (win) => {
- linkRel(win, 'prev');
-};
-
-const linkNext = (win) => {
- linkRel(win, 'next');
-};
-
-const parent = (win) => {
- const loc = win.location;
- if (loc.hash !== '') {
- loc.hash = '';
- return;
- } else if (loc.search !== '') {
- loc.search = '';
- return;
- }
-
- const basenamePattern = /\/[^/]+$/;
- const lastDirPattern = /\/[^/]+\/$/;
- if (basenamePattern.test(loc.pathname)) {
- loc.pathname = loc.pathname.replace(basenamePattern, '/');
- } else if (lastDirPattern.test(loc.pathname)) {
- loc.pathname = loc.pathname.replace(lastDirPattern, '/');
- }
-};
-
-const root = (win) => {
- win.location = win.location.origin;
-};
-
-export { historyPrev, historyNext, linkPrev, linkNext, parent, root };
diff --git a/src/content/navigates.ts b/src/content/navigates.ts
new file mode 100644
index 0000000..c9baa30
--- /dev/null
+++ b/src/content/navigates.ts
@@ -0,0 +1,78 @@
+const REL_PATTERN = {
+ prev: /^(?:prev(?:ious)?|older)\b|\u2039|\u2190|\xab|\u226a|<>/i,
+};
+
+// Return the last element in the document matching the supplied selector
+// and the optional filter, or null if there are no matches.
+const selectLast = (win, selector, filter) => {
+ let nodes = win.document.querySelectorAll(selector);
+
+ if (filter) {
+ nodes = Array.from(nodes).filter(filter);
+ }
+
+ return nodes.length ? nodes[nodes.length - 1] : null;
+};
+
+const historyPrev = (win) => {
+ win.history.back();
+};
+
+const historyNext = (win) => {
+ win.history.forward();
+};
+
+// Code common to linkPrev and linkNext which navigates to the specified page.
+const linkRel = (win, rel) => {
+ let link = selectLast(win, `link[rel~=${rel}][href]`);
+
+ if (link) {
+ win.location = link.href;
+ return;
+ }
+
+ const pattern = REL_PATTERN[rel];
+
+ link = selectLast(win, `a[rel~=${rel}][href]`) ||
+ // `innerText` is much slower than `textContent`, but produces much better
+ // (i.e. less unexpected) results
+ selectLast(win, 'a[href]', lnk => pattern.test(lnk.innerText));
+
+ if (link) {
+ link.click();
+ }
+};
+
+const linkPrev = (win) => {
+ linkRel(win, 'prev');
+};
+
+const linkNext = (win) => {
+ linkRel(win, 'next');
+};
+
+const parent = (win) => {
+ const loc = win.location;
+ if (loc.hash !== '') {
+ loc.hash = '';
+ return;
+ } else if (loc.search !== '') {
+ loc.search = '';
+ return;
+ }
+
+ const basenamePattern = /\/[^/]+$/;
+ const lastDirPattern = /\/[^/]+\/$/;
+ if (basenamePattern.test(loc.pathname)) {
+ loc.pathname = loc.pathname.replace(basenamePattern, '/');
+ } else if (lastDirPattern.test(loc.pathname)) {
+ loc.pathname = loc.pathname.replace(lastDirPattern, '/');
+ }
+};
+
+const root = (win) => {
+ win.location = win.location.origin;
+};
+
+export { historyPrev, historyNext, linkPrev, linkNext, parent, root };
diff --git a/src/content/reducers/addon.js b/src/content/reducers/addon.js
deleted file mode 100644
index 0def55a..0000000
--- a/src/content/reducers/addon.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import actions from 'content/actions';
-
-const defaultState = {
- enabled: true,
-};
-
-export default function reducer(state = defaultState, action = {}) {
- switch (action.type) {
- case actions.ADDON_SET_ENABLED:
- return { ...state,
- enabled: action.enabled, };
- default:
- return state;
- }
-}
diff --git a/src/content/reducers/addon.ts b/src/content/reducers/addon.ts
new file mode 100644
index 0000000..0def55a
--- /dev/null
+++ b/src/content/reducers/addon.ts
@@ -0,0 +1,15 @@
+import actions from 'content/actions';
+
+const defaultState = {
+ enabled: true,
+};
+
+export default function reducer(state = defaultState, action = {}) {
+ switch (action.type) {
+ case actions.ADDON_SET_ENABLED:
+ return { ...state,
+ enabled: action.enabled, };
+ default:
+ return state;
+ }
+}
diff --git a/src/content/reducers/find.js b/src/content/reducers/find.js
deleted file mode 100644
index 4560e2c..0000000
--- a/src/content/reducers/find.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import actions from 'content/actions';
-
-const defaultState = {
- keyword: null,
- found: false,
-};
-
-export default function reducer(state = defaultState, action = {}) {
- switch (action.type) {
- case actions.FIND_SET_KEYWORD:
- return { ...state,
- keyword: action.keyword,
- found: action.found, };
- default:
- return state;
- }
-}
diff --git a/src/content/reducers/find.ts b/src/content/reducers/find.ts
new file mode 100644
index 0000000..4560e2c
--- /dev/null
+++ b/src/content/reducers/find.ts
@@ -0,0 +1,17 @@
+import actions from 'content/actions';
+
+const defaultState = {
+ keyword: null,
+ found: false,
+};
+
+export default function reducer(state = defaultState, action = {}) {
+ switch (action.type) {
+ case actions.FIND_SET_KEYWORD:
+ return { ...state,
+ keyword: action.keyword,
+ found: action.found, };
+ default:
+ return state;
+ }
+}
diff --git a/src/content/reducers/follow-controller.js b/src/content/reducers/follow-controller.js
deleted file mode 100644
index 5869c47..0000000
--- a/src/content/reducers/follow-controller.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import actions from 'content/actions';
-
-const defaultState = {
- enabled: false,
- newTab: false,
- background: false,
- keys: '',
-};
-
-export default function reducer(state = defaultState, action = {}) {
- switch (action.type) {
- case actions.FOLLOW_CONTROLLER_ENABLE:
- return { ...state,
- enabled: true,
- newTab: action.newTab,
- background: action.background,
- keys: '', };
- case actions.FOLLOW_CONTROLLER_DISABLE:
- return { ...state,
- enabled: false, };
- case actions.FOLLOW_CONTROLLER_KEY_PRESS:
- return { ...state,
- keys: state.keys + action.key, };
- case actions.FOLLOW_CONTROLLER_BACKSPACE:
- return { ...state,
- keys: state.keys.slice(0, -1), };
- default:
- return state;
- }
-}
diff --git a/src/content/reducers/follow-controller.ts b/src/content/reducers/follow-controller.ts
new file mode 100644
index 0000000..5869c47
--- /dev/null
+++ b/src/content/reducers/follow-controller.ts
@@ -0,0 +1,30 @@
+import actions from 'content/actions';
+
+const defaultState = {
+ enabled: false,
+ newTab: false,
+ background: false,
+ keys: '',
+};
+
+export default function reducer(state = defaultState, action = {}) {
+ switch (action.type) {
+ case actions.FOLLOW_CONTROLLER_ENABLE:
+ return { ...state,
+ enabled: true,
+ newTab: action.newTab,
+ background: action.background,
+ keys: '', };
+ case actions.FOLLOW_CONTROLLER_DISABLE:
+ return { ...state,
+ enabled: false, };
+ case actions.FOLLOW_CONTROLLER_KEY_PRESS:
+ return { ...state,
+ keys: state.keys + action.key, };
+ case actions.FOLLOW_CONTROLLER_BACKSPACE:
+ return { ...state,
+ keys: state.keys.slice(0, -1), };
+ default:
+ return state;
+ }
+}
diff --git a/src/content/reducers/index.js b/src/content/reducers/index.js
deleted file mode 100644
index bf612a3..0000000
--- a/src/content/reducers/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { combineReducers } from 'redux';
-import addon from './addon';
-import find from './find';
-import setting from './setting';
-import input from './input';
-import followController from './follow-controller';
-import mark from './mark';
-
-export default combineReducers({
- addon, find, setting, input, followController, mark,
-});
diff --git a/src/content/reducers/index.ts b/src/content/reducers/index.ts
new file mode 100644
index 0000000..bf612a3
--- /dev/null
+++ b/src/content/reducers/index.ts
@@ -0,0 +1,11 @@
+import { combineReducers } from 'redux';
+import addon from './addon';
+import find from './find';
+import setting from './setting';
+import input from './input';
+import followController from './follow-controller';
+import mark from './mark';
+
+export default combineReducers({
+ addon, find, setting, input, followController, mark,
+});
diff --git a/src/content/reducers/input.js b/src/content/reducers/input.js
deleted file mode 100644
index 23e7dd2..0000000
--- a/src/content/reducers/input.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import actions from 'content/actions';
-
-const defaultState = {
- keys: []
-};
-
-export default function reducer(state = defaultState, action = {}) {
- switch (action.type) {
- case actions.INPUT_KEY_PRESS:
- return { ...state,
- keys: state.keys.concat([action.key]), };
- case actions.INPUT_CLEAR_KEYS:
- return { ...state,
- keys: [], };
- default:
- return state;
- }
-}
diff --git a/src/content/reducers/input.ts b/src/content/reducers/input.ts
new file mode 100644
index 0000000..23e7dd2
--- /dev/null
+++ b/src/content/reducers/input.ts
@@ -0,0 +1,18 @@
+import actions from 'content/actions';
+
+const defaultState = {
+ keys: []
+};
+
+export default function reducer(state = defaultState, action = {}) {
+ switch (action.type) {
+ case actions.INPUT_KEY_PRESS:
+ return { ...state,
+ keys: state.keys.concat([action.key]), };
+ case actions.INPUT_CLEAR_KEYS:
+ return { ...state,
+ keys: [], };
+ default:
+ return state;
+ }
+}
diff --git a/src/content/reducers/mark.js b/src/content/reducers/mark.js
deleted file mode 100644
index 2c96cc5..0000000
--- a/src/content/reducers/mark.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import actions from 'content/actions';
-
-const defaultState = {
- setMode: false,
- jumpMode: false,
- marks: {},
-};
-
-export default function reducer(state = defaultState, action = {}) {
- switch (action.type) {
- case actions.MARK_START_SET:
- return { ...state, setMode: true };
- case actions.MARK_START_JUMP:
- return { ...state, jumpMode: true };
- case actions.MARK_CANCEL:
- return { ...state, setMode: false, jumpMode: false };
- case actions.MARK_SET_LOCAL: {
- let marks = { ...state.marks };
- marks[action.key] = { x: action.x, y: action.y };
- return { ...state, setMode: false, marks };
- }
- default:
- return state;
- }
-}
diff --git a/src/content/reducers/mark.ts b/src/content/reducers/mark.ts
new file mode 100644
index 0000000..2c96cc5
--- /dev/null
+++ b/src/content/reducers/mark.ts
@@ -0,0 +1,25 @@
+import actions from 'content/actions';
+
+const defaultState = {
+ setMode: false,
+ jumpMode: false,
+ marks: {},
+};
+
+export default function reducer(state = defaultState, action = {}) {
+ switch (action.type) {
+ case actions.MARK_START_SET:
+ return { ...state, setMode: true };
+ case actions.MARK_START_JUMP:
+ return { ...state, jumpMode: true };
+ case actions.MARK_CANCEL:
+ return { ...state, setMode: false, jumpMode: false };
+ case actions.MARK_SET_LOCAL: {
+ let marks = { ...state.marks };
+ marks[action.key] = { x: action.x, y: action.y };
+ return { ...state, setMode: false, marks };
+ }
+ default:
+ return state;
+ }
+}
diff --git a/src/content/reducers/setting.js b/src/content/reducers/setting.js
deleted file mode 100644
index a49db6d..0000000
--- a/src/content/reducers/setting.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import actions from 'content/actions';
-
-const defaultState = {
- // keymaps is and arrays of key-binding pairs, which is entries of Map
- keymaps: [],
-};
-
-export default function reducer(state = defaultState, action = {}) {
- switch (action.type) {
- case actions.SETTING_SET:
- return { ...action.value };
- default:
- return state;
- }
-}
-
diff --git a/src/content/reducers/setting.ts b/src/content/reducers/setting.ts
new file mode 100644
index 0000000..a49db6d
--- /dev/null
+++ b/src/content/reducers/setting.ts
@@ -0,0 +1,16 @@
+import actions from 'content/actions';
+
+const defaultState = {
+ // keymaps is and arrays of key-binding pairs, which is entries of Map
+ keymaps: [],
+};
+
+export default function reducer(state = defaultState, action = {}) {
+ switch (action.type) {
+ case actions.SETTING_SET:
+ return { ...action.value };
+ default:
+ return state;
+ }
+}
+
diff --git a/src/content/scrolls.js b/src/content/scrolls.js
deleted file mode 100644
index f3124a1..0000000
--- a/src/content/scrolls.js
+++ /dev/null
@@ -1,174 +0,0 @@
-import * as doms from 'shared/utils/dom';
-
-const SCROLL_DELTA_X = 64;
-const SCROLL_DELTA_Y = 64;
-
-// dirty way to store scrolling state on globally
-let scrolling = false;
-let lastTimeoutId = null;
-
-const isScrollableStyle = (element) => {
- let { overflowX, overflowY } = window.getComputedStyle(element);
- return !(overflowX !== 'scroll' && overflowX !== 'auto' &&
- overflowY !== 'scroll' && overflowY !== 'auto');
-};
-
-const isOverflowed = (element) => {
- return element.scrollWidth > element.clientWidth ||
- element.scrollHeight > element.clientHeight;
-};
-
-// Find a visiable and scrollable element by depth-first search. Currently
-// this method is called by each scrolling, and the returned value of this
-// method is not cached. That does not cause performance issue because in the
-// most pages, the window is root element i,e, documentElement.
-const findScrollable = (element) => {
- if (isScrollableStyle(element) && isOverflowed(element)) {
- return element;
- }
-
- let children = Array.from(element.children).filter(doms.isVisible);
- for (let child of children) {
- let scrollable = findScrollable(child);
- if (scrollable) {
- return scrollable;
- }
- }
- return null;
-};
-
-const scrollTarget = () => {
- if (isOverflowed(window.document.documentElement)) {
- return window.document.documentElement;
- }
- if (isOverflowed(window.document.body)) {
- return window.document.body;
- }
- let target = findScrollable(window.document.documentElement);
- if (target) {
- return target;
- }
- return window.document.documentElement;
-};
-
-const resetScrolling = () => {
- scrolling = false;
-};
-
-class Scroller {
- constructor(element, smooth) {
- this.element = element;
- this.smooth = smooth;
- }
-
- scrollTo(x, y) {
- if (!this.smooth) {
- this.element.scrollTo(x, y);
- return;
- }
- this.element.scrollTo({
- left: x,
- top: y,
- behavior: 'smooth',
- });
- this.prepareReset();
- }
-
- scrollBy(x, y) {
- let left = this.element.scrollLeft + x;
- let top = this.element.scrollTop + y;
- this.scrollTo(left, top);
- }
-
- prepareReset() {
- scrolling = true;
- if (lastTimeoutId) {
- clearTimeout(lastTimeoutId);
- lastTimeoutId = null;
- }
- lastTimeoutId = setTimeout(resetScrolling, 100);
- }
-}
-
-class RoughtScroller {
- constructor(element) {
- this.element = element;
- }
-
- scroll(x, y) {
- this.element.scrollTo(x, y);
- }
-}
-
-const getScroll = () => {
- let target = scrollTarget();
- return { x: target.scrollLeft, y: target.scrollTop };
-};
-
-const scrollVertically = (count, smooth) => {
- let target = scrollTarget();
- let delta = SCROLL_DELTA_Y * count;
- if (scrolling) {
- delta = SCROLL_DELTA_Y * count * 4;
- }
- new Scroller(target, smooth).scrollBy(0, delta);
-};
-
-const scrollHorizonally = (count, smooth) => {
- let target = scrollTarget();
- let delta = SCROLL_DELTA_X * count;
- if (scrolling) {
- delta = SCROLL_DELTA_X * count * 4;
- }
- new Scroller(target, smooth).scrollBy(delta, 0);
-};
-
-const scrollPages = (count, smooth) => {
- let target = scrollTarget();
- let height = target.clientHeight;
- let delta = height * count;
- if (scrolling) {
- delta = height * count;
- }
- new Scroller(target, smooth).scrollBy(0, delta);
-};
-
-const scrollTo = (x, y, smooth) => {
- let target = scrollTarget();
- new Scroller(target, smooth).scrollTo(x, y);
-};
-
-const scrollToTop = (smooth) => {
- let target = scrollTarget();
- let x = target.scrollLeft;
- let y = 0;
- new Scroller(target, smooth).scrollTo(x, y);
-};
-
-const scrollToBottom = (smooth) => {
- let target = scrollTarget();
- let x = target.scrollLeft;
- let y = target.scrollHeight;
- new Scroller(target, smooth).scrollTo(x, y);
-};
-
-const scrollToHome = (smooth) => {
- let target = scrollTarget();
- let x = 0;
- let y = target.scrollTop;
- new Scroller(target, smooth).scrollTo(x, y);
-};
-
-const scrollToEnd = (smooth) => {
- let target = scrollTarget();
- let x = target.scrollWidth;
- let y = target.scrollTop;
- new Scroller(target, smooth).scrollTo(x, y);
-};
-
-export {
- getScroll,
- scrollVertically, scrollHorizonally, scrollPages,
- scrollTo,
- scrollToTop, scrollToBottom, scrollToHome, scrollToEnd
-};
diff --git a/src/content/scrolls.ts b/src/content/scrolls.ts
new file mode 100644
index 0000000..f3124a1
--- /dev/null
+++ b/src/content/scrolls.ts
@@ -0,0 +1,174 @@
+import * as doms from 'shared/utils/dom';
+
+const SCROLL_DELTA_X = 64;
+const SCROLL_DELTA_Y = 64;
+
+// dirty way to store scrolling state on globally
+let scrolling = false;
+let lastTimeoutId = null;
+
+const isScrollableStyle = (element) => {
+ let { overflowX, overflowY } = window.getComputedStyle(element);
+ return !(overflowX !== 'scroll' && overflowX !== 'auto' &&
+ overflowY !== 'scroll' && overflowY !== 'auto');
+};
+
+const isOverflowed = (element) => {
+ return element.scrollWidth > element.clientWidth ||
+ element.scrollHeight > element.clientHeight;
+};
+
+// Find a visiable and scrollable element by depth-first search. Currently
+// this method is called by each scrolling, and the returned value of this
+// method is not cached. That does not cause performance issue because in the
+// most pages, the window is root element i,e, documentElement.
+const findScrollable = (element) => {
+ if (isScrollableStyle(element) && isOverflowed(element)) {
+ return element;
+ }
+
+ let children = Array.from(element.children).filter(doms.isVisible);
+ for (let child of children) {
+ let scrollable = findScrollable(child);
+ if (scrollable) {
+ return scrollable;
+ }
+ }
+ return null;
+};
+
+const scrollTarget = () => {
+ if (isOverflowed(window.document.documentElement)) {
+ return window.document.documentElement;
+ }
+ if (isOverflowed(window.document.body)) {
+ return window.document.body;
+ }
+ let target = findScrollable(window.document.documentElement);
+ if (target) {
+ return target;
+ }
+ return window.document.documentElement;
+};
+
+const resetScrolling = () => {
+ scrolling = false;
+};
+
+class Scroller {
+ constructor(element, smooth) {
+ this.element = element;
+ this.smooth = smooth;
+ }
+
+ scrollTo(x, y) {
+ if (!this.smooth) {
+ this.element.scrollTo(x, y);
+ return;
+ }
+ this.element.scrollTo({
+ left: x,
+ top: y,
+ behavior: 'smooth',
+ });
+ this.prepareReset();
+ }
+
+ scrollBy(x, y) {
+ let left = this.element.scrollLeft + x;
+ let top = this.element.scrollTop + y;
+ this.scrollTo(left, top);
+ }
+
+ prepareReset() {
+ scrolling = true;
+ if (lastTimeoutId) {
+ clearTimeout(lastTimeoutId);
+ lastTimeoutId = null;
+ }
+ lastTimeoutId = setTimeout(resetScrolling, 100);
+ }
+}
+
+class RoughtScroller {
+ constructor(element) {
+ this.element = element;
+ }
+
+ scroll(x, y) {
+ this.element.scrollTo(x, y);
+ }
+}
+
+const getScroll = () => {
+ let target = scrollTarget();
+ return { x: target.scrollLeft, y: target.scrollTop };
+};
+
+const scrollVertically = (count, smooth) => {
+ let target = scrollTarget();
+ let delta = SCROLL_DELTA_Y * count;
+ if (scrolling) {
+ delta = SCROLL_DELTA_Y * count * 4;
+ }
+ new Scroller(target, smooth).scrollBy(0, delta);
+};
+
+const scrollHorizonally = (count, smooth) => {
+ let target = scrollTarget();
+ let delta = SCROLL_DELTA_X * count;
+ if (scrolling) {
+ delta = SCROLL_DELTA_X * count * 4;
+ }
+ new Scroller(target, smooth).scrollBy(delta, 0);
+};
+
+const scrollPages = (count, smooth) => {
+ let target = scrollTarget();
+ let height = target.clientHeight;
+ let delta = height * count;
+ if (scrolling) {
+ delta = height * count;
+ }
+ new Scroller(target, smooth).scrollBy(0, delta);
+};
+
+const scrollTo = (x, y, smooth) => {
+ let target = scrollTarget();
+ new Scroller(target, smooth).scrollTo(x, y);
+};
+
+const scrollToTop = (smooth) => {
+ let target = scrollTarget();
+ let x = target.scrollLeft;
+ let y = 0;
+ new Scroller(target, smooth).scrollTo(x, y);
+};
+
+const scrollToBottom = (smooth) => {
+ let target = scrollTarget();
+ let x = target.scrollLeft;
+ let y = target.scrollHeight;
+ new Scroller(target, smooth).scrollTo(x, y);
+};
+
+const scrollToHome = (smooth) => {
+ let target = scrollTarget();
+ let x = 0;
+ let y = target.scrollTop;
+ new Scroller(target, smooth).scrollTo(x, y);
+};
+
+const scrollToEnd = (smooth) => {
+ let target = scrollTarget();
+ let x = target.scrollWidth;
+ let y = target.scrollTop;
+ new Scroller(target, smooth).scrollTo(x, y);
+};
+
+export {
+ getScroll,
+ scrollVertically, scrollHorizonally, scrollPages,
+ scrollTo,
+ scrollToTop, scrollToBottom, scrollToHome, scrollToEnd
+};
diff --git a/src/content/site-style.js b/src/content/site-style.js
deleted file mode 100644
index e7a82a5..0000000
--- a/src/content/site-style.js
+++ /dev/null
@@ -1,26 +0,0 @@
-exports.default = `
-.vimvixen-console-frame {
- margin: 0;
- padding: 0;
- bottom: 0;
- left: 0;
- width: 100%;
- height: 100%;
- position: fixed;
- z-index: 2147483647;
- border: none;
- background-color: unset;
- pointer-events:none;
-}
-
-.vimvixen-hint {
- background-color: yellow;
- border: 1px solid gold;
- font-weight: bold;
- position: absolute;
- text-transform: uppercase;
- z-index: 2147483647;
- font-size: 12px;
- color: black;
-}
-`;
diff --git a/src/content/site-style.ts b/src/content/site-style.ts
new file mode 100644
index 0000000..e7a82a5
--- /dev/null
+++ b/src/content/site-style.ts
@@ -0,0 +1,26 @@
+exports.default = `
+.vimvixen-console-frame {
+ margin: 0;
+ padding: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ position: fixed;
+ z-index: 2147483647;
+ border: none;
+ background-color: unset;
+ pointer-events:none;
+}
+
+.vimvixen-hint {
+ background-color: yellow;
+ border: 1px solid gold;
+ font-weight: bold;
+ position: absolute;
+ text-transform: uppercase;
+ z-index: 2147483647;
+ font-size: 12px;
+ color: black;
+}
+`;
diff --git a/src/content/urls.js b/src/content/urls.js
deleted file mode 100644
index 6e7ea31..0000000
--- a/src/content/urls.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import messages from 'shared/messages';
-import * as urls from '../shared/urls';
-
-const yank = (win) => {
- let input = win.document.createElement('input');
- win.document.body.append(input);
-
- input.style.position = 'fixed';
- input.style.top = '-100px';
- input.value = win.location.href;
- input.select();
-
- win.document.execCommand('copy');
-
- input.remove();
-};
-
-const paste = (win, newTab, searchSettings) => {
- let textarea = win.document.createElement('textarea');
- win.document.body.append(textarea);
-
- textarea.style.position = 'fixed';
- textarea.style.top = '-100px';
- textarea.contentEditable = 'true';
- textarea.focus();
-
- if (win.document.execCommand('paste')) {
- let value = textarea.textContent;
- let url = urls.searchUrl(value, searchSettings);
- browser.runtime.sendMessage({
- type: messages.OPEN_URL,
- url,
- newTab,
- });
- }
-
- textarea.remove();
-};
-
-export { yank, paste };
diff --git a/src/content/urls.ts b/src/content/urls.ts
new file mode 100644
index 0000000..6e7ea31
--- /dev/null
+++ b/src/content/urls.ts
@@ -0,0 +1,40 @@
+import messages from 'shared/messages';
+import * as urls from '../shared/urls';
+
+const yank = (win) => {
+ let input = win.document.createElement('input');
+ win.document.body.append(input);
+
+ input.style.position = 'fixed';
+ input.style.top = '-100px';
+ input.value = win.location.href;
+ input.select();
+
+ win.document.execCommand('copy');
+
+ input.remove();
+};
+
+const paste = (win, newTab, searchSettings) => {
+ let textarea = win.document.createElement('textarea');
+ win.document.body.append(textarea);
+
+ textarea.style.position = 'fixed';
+ textarea.style.top = '-100px';
+ textarea.contentEditable = 'true';
+ textarea.focus();
+
+ if (win.document.execCommand('paste')) {
+ let value = textarea.textContent;
+ let url = urls.searchUrl(value, searchSettings);
+ browser.runtime.sendMessage({
+ type: messages.OPEN_URL,
+ url,
+ newTab,
+ });
+ }
+
+ textarea.remove();
+};
+
+export { yank, paste };
diff --git a/src/settings/actions/index.js b/src/settings/actions/index.js
deleted file mode 100644
index 016f2a5..0000000
--- a/src/settings/actions/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export default {
- // Settings
- SETTING_SET_SETTINGS: 'setting.set.settings',
- SETTING_SHOW_ERROR: 'setting.show.error',
- SETTING_SWITCH_TO_FORM: 'setting.switch.to.form',
- SETTING_SWITCH_TO_JSON: 'setting.switch.to.json',
-};
diff --git a/src/settings/actions/index.ts b/src/settings/actions/index.ts
new file mode 100644
index 0000000..016f2a5
--- /dev/null
+++ b/src/settings/actions/index.ts
@@ -0,0 +1,7 @@
+export default {
+ // Settings
+ SETTING_SET_SETTINGS: 'setting.set.settings',
+ SETTING_SHOW_ERROR: 'setting.show.error',
+ SETTING_SWITCH_TO_FORM: 'setting.switch.to.form',
+ SETTING_SWITCH_TO_JSON: 'setting.switch.to.json',
+};
diff --git a/src/settings/actions/setting.js b/src/settings/actions/setting.js
deleted file mode 100644
index db63a45..0000000
--- a/src/settings/actions/setting.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import actions from 'settings/actions';
-import * as validator from 'shared/settings/validator';
-import * as settingsValues from 'shared/settings/values';
-import * as settingsStorage from 'shared/settings/storage';
-import keymaps from '../keymaps';
-
-const load = async() => {
- let settings = await settingsStorage.loadRaw();
- return set(settings);
-};
-
-const save = async(settings) => {
- try {
- if (settings.source === 'json') {
- let value = JSON.parse(settings.json);
- validator.validate(value);
- }
- } catch (e) {
- return {
- type: actions.SETTING_SHOW_ERROR,
- error: e.toString(),
- json: settings.json,
- };
- }
- await settingsStorage.save(settings);
- return set(settings);
-};
-
-const switchToForm = (json) => {
- try {
- validator.validate(JSON.parse(json));
- let form = settingsValues.formFromJson(json, keymaps.allowedOps);
- return {
- type: actions.SETTING_SWITCH_TO_FORM,
- form,
- };
- } catch (e) {
- return {
- type: actions.SETTING_SHOW_ERROR,
- error: e.toString(),
- json,
- };
- }
-};
-
-const switchToJson = (form) => {
- let json = settingsValues.jsonFromForm(form);
- return {
- type: actions.SETTING_SWITCH_TO_JSON,
- json,
- };
-};
-
-const set = (settings) => {
- return {
- type: actions.SETTING_SET_SETTINGS,
- source: settings.source,
- json: settings.json,
- form: settings.form,
- };
-};
-
-export { load, save, set, switchToForm, switchToJson };
diff --git a/src/settings/actions/setting.ts b/src/settings/actions/setting.ts
new file mode 100644
index 0000000..db63a45
--- /dev/null
+++ b/src/settings/actions/setting.ts
@@ -0,0 +1,63 @@
+import actions from 'settings/actions';
+import * as validator from 'shared/settings/validator';
+import * as settingsValues from 'shared/settings/values';
+import * as settingsStorage from 'shared/settings/storage';
+import keymaps from '../keymaps';
+
+const load = async() => {
+ let settings = await settingsStorage.loadRaw();
+ return set(settings);
+};
+
+const save = async(settings) => {
+ try {
+ if (settings.source === 'json') {
+ let value = JSON.parse(settings.json);
+ validator.validate(value);
+ }
+ } catch (e) {
+ return {
+ type: actions.SETTING_SHOW_ERROR,
+ error: e.toString(),
+ json: settings.json,
+ };
+ }
+ await settingsStorage.save(settings);
+ return set(settings);
+};
+
+const switchToForm = (json) => {
+ try {
+ validator.validate(JSON.parse(json));
+ let form = settingsValues.formFromJson(json, keymaps.allowedOps);
+ return {
+ type: actions.SETTING_SWITCH_TO_FORM,
+ form,
+ };
+ } catch (e) {
+ return {
+ type: actions.SETTING_SHOW_ERROR,
+ error: e.toString(),
+ json,
+ };
+ }
+};
+
+const switchToJson = (form) => {
+ let json = settingsValues.jsonFromForm(form);
+ return {
+ type: actions.SETTING_SWITCH_TO_JSON,
+ json,
+ };
+};
+
+const set = (settings) => {
+ return {
+ type: actions.SETTING_SET_SETTINGS,
+ source: settings.source,
+ json: settings.json,
+ form: settings.form,
+ };
+};
+
+export { load, save, set, switchToForm, switchToJson };
diff --git a/src/settings/components/form/BlacklistForm.jsx b/src/settings/components/form/BlacklistForm.jsx
deleted file mode 100644
index c470758..0000000
--- a/src/settings/components/form/BlacklistForm.jsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import './BlacklistForm.scss';
-import AddButton from '../ui/AddButton';
-import DeleteButton from '../ui/DeleteButton';
-import React from 'react';
-import PropTypes from 'prop-types';
-
-class BlacklistForm extends React.Component {
-
- render() {
- return
- {
- this.props.value.map((url, index) => {
- return
-
-
-
;
- })
- }
-
-
;
- }
-
- bindValue(e) {
- let name = e.target.name;
- let index = e.target.getAttribute('data-index');
- let next = this.props.value ? this.props.value.slice() : [];
-
- if (name === 'url') {
- next[index] = e.target.value;
- } else if (name === 'add') {
- next.push('');
- } else if (name === 'delete') {
- next.splice(index, 1);
- }
-
- this.props.onChange(next);
- if (name === 'delete') {
- this.props.onBlur();
- }
- }
-}
-
-BlacklistForm.propTypes = {
- value: PropTypes.arrayOf(PropTypes.string),
- onChange: PropTypes.func,
- onBlur: PropTypes.func,
-};
-
-BlacklistForm.defaultProps = {
- value: [],
- onChange: () => {},
- onBlur: () => {},
-};
-
-export default BlacklistForm;
diff --git a/src/settings/components/form/BlacklistForm.tsx b/src/settings/components/form/BlacklistForm.tsx
new file mode 100644
index 0000000..c470758
--- /dev/null
+++ b/src/settings/components/form/BlacklistForm.tsx
@@ -0,0 +1,63 @@
+import './BlacklistForm.scss';
+import AddButton from '../ui/AddButton';
+import DeleteButton from '../ui/DeleteButton';
+import React from 'react';
+import PropTypes from 'prop-types';
+
+class BlacklistForm extends React.Component {
+
+ render() {
+ return
+ {
+ this.props.value.map((url, index) => {
+ return
+
+
+
;
+ })
+ }
+
+
;
+ }
+
+ bindValue(e) {
+ let name = e.target.name;
+ let index = e.target.getAttribute('data-index');
+ let next = this.props.value ? this.props.value.slice() : [];
+
+ if (name === 'url') {
+ next[index] = e.target.value;
+ } else if (name === 'add') {
+ next.push('');
+ } else if (name === 'delete') {
+ next.splice(index, 1);
+ }
+
+ this.props.onChange(next);
+ if (name === 'delete') {
+ this.props.onBlur();
+ }
+ }
+}
+
+BlacklistForm.propTypes = {
+ value: PropTypes.arrayOf(PropTypes.string),
+ onChange: PropTypes.func,
+ onBlur: PropTypes.func,
+};
+
+BlacklistForm.defaultProps = {
+ value: [],
+ onChange: () => {},
+ onBlur: () => {},
+};
+
+export default BlacklistForm;
diff --git a/src/settings/components/form/KeymapsForm.jsx b/src/settings/components/form/KeymapsForm.jsx
deleted file mode 100644
index 01acf61..0000000
--- a/src/settings/components/form/KeymapsForm.jsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import './KeymapsForm.scss';
-import React from 'react';
-import PropTypes from 'prop-types';
-import Input from '../ui/Input';
-import keymaps from '../../keymaps';
-
-class KeymapsForm extends React.Component {
-
- render() {
- return
- {
- keymaps.fields.map((group, index) => {
- return
- {
- group.map((field) => {
- let name = field[0];
- let label = field[1];
- let value = this.props.value[name] || '';
- return ;
- })
- }
-
;
- })
- }
-
;
- }
-
- bindValue(e) {
- let next = { ...this.props.value };
- next[e.target.name] = e.target.value;
-
- this.props.onChange(next);
- }
-}
-
-KeymapsForm.propTypes = {
- value: PropTypes.objectOf(PropTypes.string),
- onChange: PropTypes.func,
-};
-
-KeymapsForm.defaultProps = {
- value: {},
- onChange: () => {},
-};
-
-export default KeymapsForm;
diff --git a/src/settings/components/form/KeymapsForm.tsx b/src/settings/components/form/KeymapsForm.tsx
new file mode 100644
index 0000000..01acf61
--- /dev/null
+++ b/src/settings/components/form/KeymapsForm.tsx
@@ -0,0 +1,51 @@
+import './KeymapsForm.scss';
+import React from 'react';
+import PropTypes from 'prop-types';
+import Input from '../ui/Input';
+import keymaps from '../../keymaps';
+
+class KeymapsForm extends React.Component {
+
+ render() {
+ return
+ {
+ keymaps.fields.map((group, index) => {
+ return
+ {
+ group.map((field) => {
+ let name = field[0];
+ let label = field[1];
+ let value = this.props.value[name] || '';
+ return ;
+ })
+ }
+
;
+ })
+ }
+
;
+ }
+
+ bindValue(e) {
+ let next = { ...this.props.value };
+ next[e.target.name] = e.target.value;
+
+ this.props.onChange(next);
+ }
+}
+
+KeymapsForm.propTypes = {
+ value: PropTypes.objectOf(PropTypes.string),
+ onChange: PropTypes.func,
+};
+
+KeymapsForm.defaultProps = {
+ value: {},
+ onChange: () => {},
+};
+
+export default KeymapsForm;
diff --git a/src/settings/components/form/PropertiesForm.jsx b/src/settings/components/form/PropertiesForm.jsx
deleted file mode 100644
index 979fdd8..0000000
--- a/src/settings/components/form/PropertiesForm.jsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import './PropertiesForm.scss';
-import React from 'react';
-import PropTypes from 'prop-types';
-
-class PropertiesForm extends React.Component {
-
- render() {
- let types = this.props.types;
- let value = this.props.value;
-
- return
- {
- Object.keys(types).map((name) => {
- let type = types[name];
- let inputType = null;
- if (type === 'string') {
- inputType = 'text';
- } else if (type === 'number') {
- inputType = 'number';
- } else if (type === 'boolean') {
- inputType = 'checkbox';
- }
- return
-
-
;
- })
- }
-
;
- }
-
- bindValue(e) {
- let name = e.target.name;
- let next = { ...this.props.value };
- if (e.target.type.toLowerCase() === 'checkbox') {
- next[name] = e.target.checked;
- } else if (e.target.type.toLowerCase() === 'number') {
- next[name] = Number(e.target.value);
- } else {
- next[name] = e.target.value;
- }
-
- this.props.onChange(next);
- }
-}
-
-PropertiesForm.propTypes = {
- value: PropTypes.objectOf(PropTypes.any),
- onChange: PropTypes.func,
-};
-
-PropertiesForm.defaultProps = {
- value: {},
- onChange: () => {},
-};
-
-export default PropertiesForm;
diff --git a/src/settings/components/form/PropertiesForm.tsx b/src/settings/components/form/PropertiesForm.tsx
new file mode 100644
index 0000000..979fdd8
--- /dev/null
+++ b/src/settings/components/form/PropertiesForm.tsx
@@ -0,0 +1,65 @@
+import './PropertiesForm.scss';
+import React from 'react';
+import PropTypes from 'prop-types';
+
+class PropertiesForm extends React.Component {
+
+ render() {
+ let types = this.props.types;
+ let value = this.props.value;
+
+ return
+ {
+ Object.keys(types).map((name) => {
+ let type = types[name];
+ let inputType = null;
+ if (type === 'string') {
+ inputType = 'text';
+ } else if (type === 'number') {
+ inputType = 'number';
+ } else if (type === 'boolean') {
+ inputType = 'checkbox';
+ }
+ return
+
+
;
+ })
+ }
+
;
+ }
+
+ bindValue(e) {
+ let name = e.target.name;
+ let next = { ...this.props.value };
+ if (e.target.type.toLowerCase() === 'checkbox') {
+ next[name] = e.target.checked;
+ } else if (e.target.type.toLowerCase() === 'number') {
+ next[name] = Number(e.target.value);
+ } else {
+ next[name] = e.target.value;
+ }
+
+ this.props.onChange(next);
+ }
+}
+
+PropertiesForm.propTypes = {
+ value: PropTypes.objectOf(PropTypes.any),
+ onChange: PropTypes.func,
+};
+
+PropertiesForm.defaultProps = {
+ value: {},
+ onChange: () => {},
+};
+
+export default PropertiesForm;
diff --git a/src/settings/components/form/SearchForm.jsx b/src/settings/components/form/SearchForm.jsx
deleted file mode 100644
index 6b0bd01..0000000
--- a/src/settings/components/form/SearchForm.jsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import './SearchForm.scss';
-import React from 'react';
-import PropTypes from 'prop-types';
-import AddButton from '../ui/AddButton';
-import DeleteButton from '../ui/DeleteButton';
-
-class SearchForm extends React.Component {
-
- render() {
- let value = this.props.value;
- if (!value.engines) {
- value.engines = [];
- }
-
- return
-
- {
- value.engines.map((engine, index) => {
- return
;
- })
- }
-
-
;
- }
-
- bindValue(e) {
- let value = this.props.value;
- let name = e.target.name;
- let index = e.target.getAttribute('data-index');
- let next = {
- default: value.default,
- engines: value.engines ? value.engines.slice() : [],
- };
-
- if (name === 'name') {
- next.engines[index][0] = e.target.value;
- next.default = this.props.value.engines[index][0];
- } else if (name === 'url') {
- next.engines[index][1] = e.target.value;
- } else if (name === 'default') {
- next.default = this.props.value.engines[index][0];
- } else if (name === 'add') {
- next.engines.push(['', '']);
- } else if (name === 'delete') {
- next.engines.splice(index, 1);
- }
-
- this.props.onChange(next);
- if (name === 'delete' || name === 'default') {
- this.props.onBlur();
- }
- }
-}
-
-SearchForm.propTypes = {
- value: PropTypes.shape({
- default: PropTypes.string,
- engines: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
- }),
- onChange: PropTypes.func,
-};
-
-SearchForm.defaultProps = {
- value: { default: '', engines: []},
- onChange: () => {},
-};
-
-export default SearchForm;
diff --git a/src/settings/components/form/SearchForm.tsx b/src/settings/components/form/SearchForm.tsx
new file mode 100644
index 0000000..6b0bd01
--- /dev/null
+++ b/src/settings/components/form/SearchForm.tsx
@@ -0,0 +1,92 @@
+import './SearchForm.scss';
+import React from 'react';
+import PropTypes from 'prop-types';
+import AddButton from '../ui/AddButton';
+import DeleteButton from '../ui/DeleteButton';
+
+class SearchForm extends React.Component {
+
+ render() {
+ let value = this.props.value;
+ if (!value.engines) {
+ value.engines = [];
+ }
+
+ return
+
+ {
+ value.engines.map((engine, index) => {
+ return
;
+ })
+ }
+
+
;
+ }
+
+ bindValue(e) {
+ let value = this.props.value;
+ let name = e.target.name;
+ let index = e.target.getAttribute('data-index');
+ let next = {
+ default: value.default,
+ engines: value.engines ? value.engines.slice() : [],
+ };
+
+ if (name === 'name') {
+ next.engines[index][0] = e.target.value;
+ next.default = this.props.value.engines[index][0];
+ } else if (name === 'url') {
+ next.engines[index][1] = e.target.value;
+ } else if (name === 'default') {
+ next.default = this.props.value.engines[index][0];
+ } else if (name === 'add') {
+ next.engines.push(['', '']);
+ } else if (name === 'delete') {
+ next.engines.splice(index, 1);
+ }
+
+ this.props.onChange(next);
+ if (name === 'delete' || name === 'default') {
+ this.props.onBlur();
+ }
+ }
+}
+
+SearchForm.propTypes = {
+ value: PropTypes.shape({
+ default: PropTypes.string,
+ engines: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
+ }),
+ onChange: PropTypes.func,
+};
+
+SearchForm.defaultProps = {
+ value: { default: '', engines: []},
+ onChange: () => {},
+};
+
+export default SearchForm;
diff --git a/src/settings/components/index.jsx b/src/settings/components/index.jsx
deleted file mode 100644
index 4ef59d7..0000000
--- a/src/settings/components/index.jsx
+++ /dev/null
@@ -1,153 +0,0 @@
-import './site.scss';
-import React from 'react';
-import { connect } from 'react-redux';
-import Input from './ui/Input';
-import SearchForm from './form/SearchForm';
-import KeymapsForm from './form/KeymapsForm';
-import BlacklistForm from './form/BlacklistForm';
-import PropertiesForm from './form/PropertiesForm';
-import * as properties from 'shared/settings/properties';
-import * as settingActions from 'settings/actions/setting';
-
-const DO_YOU_WANT_TO_CONTINUE =
- 'Some settings in JSON can be lost when migrating. ' +
- 'Do you want to continue?';
-
-class SettingsComponent extends React.Component {
- componentDidMount() {
- this.props.dispatch(settingActions.load());
- }
-
- renderFormFields(form) {
- return
-
-
-
-
-
;
- }
-
- renderJsonFields(json, error) {
- return
-
-
;
- }
-
- render() {
- let fields = null;
- let disabled = this.props.error.length > 0;
- if (this.props.source === 'form') {
- fields = this.renderFormFields(this.props.form);
- } else if (this.props.source === 'json') {
- fields = this.renderJsonFields(this.props.json, this.props.error);
- }
- return (
-
-
Configure Vim-Vixen
-
-
- );
- }
-
- bindForm(name, value) {
- let settings = {
- source: this.props.source,
- json: this.props.json,
- form: { ...this.props.form },
- };
- settings.form[name] = value;
- this.props.dispatch(settingActions.set(settings));
- }
-
- bindJson(e) {
- let settings = {
- source: this.props.source,
- json: e.target.value,
- form: this.props.form,
- };
- this.props.dispatch(settingActions.set(settings));
- }
-
- bindSource(e) {
- let from = this.props.source;
- let to = e.target.value;
-
- if (from === 'form' && to === 'json') {
- this.props.dispatch(settingActions.switchToJson(this.props.form));
- } else if (from === 'json' && to === 'form') {
- let b = window.confirm(DO_YOU_WANT_TO_CONTINUE);
- if (!b) {
- this.forceUpdate();
- return;
- }
- this.props.dispatch(settingActions.switchToForm(this.props.json));
- }
- }
-
- save() {
- let settings = this.props.store.getState();
- this.props.dispatch(settingActions.save(settings));
- }
-}
-
-const mapStateToProps = state => state;
-
-export default connect(mapStateToProps)(SettingsComponent);
diff --git a/src/settings/components/index.tsx b/src/settings/components/index.tsx
new file mode 100644
index 0000000..4ef59d7
--- /dev/null
+++ b/src/settings/components/index.tsx
@@ -0,0 +1,153 @@
+import './site.scss';
+import React from 'react';
+import { connect } from 'react-redux';
+import Input from './ui/Input';
+import SearchForm from './form/SearchForm';
+import KeymapsForm from './form/KeymapsForm';
+import BlacklistForm from './form/BlacklistForm';
+import PropertiesForm from './form/PropertiesForm';
+import * as properties from 'shared/settings/properties';
+import * as settingActions from 'settings/actions/setting';
+
+const DO_YOU_WANT_TO_CONTINUE =
+ 'Some settings in JSON can be lost when migrating. ' +
+ 'Do you want to continue?';
+
+class SettingsComponent extends React.Component {
+ componentDidMount() {
+ this.props.dispatch(settingActions.load());
+ }
+
+ renderFormFields(form) {
+ return
+
+
+
+
+
;
+ }
+
+ renderJsonFields(json, error) {
+ return
+
+
;
+ }
+
+ render() {
+ let fields = null;
+ let disabled = this.props.error.length > 0;
+ if (this.props.source === 'form') {
+ fields = this.renderFormFields(this.props.form);
+ } else if (this.props.source === 'json') {
+ fields = this.renderJsonFields(this.props.json, this.props.error);
+ }
+ return (
+
+
Configure Vim-Vixen
+
+
+ );
+ }
+
+ bindForm(name, value) {
+ let settings = {
+ source: this.props.source,
+ json: this.props.json,
+ form: { ...this.props.form },
+ };
+ settings.form[name] = value;
+ this.props.dispatch(settingActions.set(settings));
+ }
+
+ bindJson(e) {
+ let settings = {
+ source: this.props.source,
+ json: e.target.value,
+ form: this.props.form,
+ };
+ this.props.dispatch(settingActions.set(settings));
+ }
+
+ bindSource(e) {
+ let from = this.props.source;
+ let to = e.target.value;
+
+ if (from === 'form' && to === 'json') {
+ this.props.dispatch(settingActions.switchToJson(this.props.form));
+ } else if (from === 'json' && to === 'form') {
+ let b = window.confirm(DO_YOU_WANT_TO_CONTINUE);
+ if (!b) {
+ this.forceUpdate();
+ return;
+ }
+ this.props.dispatch(settingActions.switchToForm(this.props.json));
+ }
+ }
+
+ save() {
+ let settings = this.props.store.getState();
+ this.props.dispatch(settingActions.save(settings));
+ }
+}
+
+const mapStateToProps = state => state;
+
+export default connect(mapStateToProps)(SettingsComponent);
diff --git a/src/settings/components/ui/AddButton.jsx b/src/settings/components/ui/AddButton.jsx
deleted file mode 100644
index 185a03b..0000000
--- a/src/settings/components/ui/AddButton.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import './AddButton.scss';
-import React from 'react';
-
-class AddButton extends React.Component {
- render() {
- return ;
- }
-}
-
-export default AddButton;
diff --git a/src/settings/components/ui/AddButton.tsx b/src/settings/components/ui/AddButton.tsx
new file mode 100644
index 0000000..185a03b
--- /dev/null
+++ b/src/settings/components/ui/AddButton.tsx
@@ -0,0 +1,12 @@
+import './AddButton.scss';
+import React from 'react';
+
+class AddButton extends React.Component {
+ render() {
+ return ;
+ }
+}
+
+export default AddButton;
diff --git a/src/settings/components/ui/DeleteButton.jsx b/src/settings/components/ui/DeleteButton.jsx
deleted file mode 100644
index 75811cd..0000000
--- a/src/settings/components/ui/DeleteButton.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import './DeleteButton.scss';
-import React from 'react';
-
-class DeleteButton extends React.Component {
- render() {
- return ;
- }
-}
-
-export default DeleteButton;
diff --git a/src/settings/components/ui/DeleteButton.tsx b/src/settings/components/ui/DeleteButton.tsx
new file mode 100644
index 0000000..75811cd
--- /dev/null
+++ b/src/settings/components/ui/DeleteButton.tsx
@@ -0,0 +1,12 @@
+import './DeleteButton.scss';
+import React from 'react';
+
+class DeleteButton extends React.Component {
+ render() {
+ return ;
+ }
+}
+
+export default DeleteButton;
diff --git a/src/settings/components/ui/Input.jsx b/src/settings/components/ui/Input.jsx
deleted file mode 100644
index 13a246b..0000000
--- a/src/settings/components/ui/Input.jsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import './Input.scss';
-
-class Input extends React.Component {
-
- renderText(props) {
- let inputClassName = props.error ? 'input-error' : '';
- return
-
-
-
;
- }
-
- renderRadio(props) {
- let inputClassName = props.error ? 'input-error' : '';
- return
-
-
;
- }
-
- renderTextArea(props) {
- let inputClassName = props.error ? 'input-error' : '';
- return
-
-
-
{ this.props.error }
-
;
- }
-
- render() {
- let { type } = this.props;
-
- switch (this.props.type) {
- case 'text':
- return this.renderText(this.props);
- case 'radio':
- return this.renderRadio(this.props);
- case 'textarea':
- return this.renderTextArea(this.props);
- default:
- console.warn(`Unsupported input type ${type}`);
- }
- return null;
- }
-}
-
-Input.propTypes = {
- type: PropTypes.string,
- error: PropTypes.string,
- label: PropTypes.string,
- value: PropTypes.string,
-};
-
-export default Input;
diff --git a/src/settings/components/ui/Input.tsx b/src/settings/components/ui/Input.tsx
new file mode 100644
index 0000000..13a246b
--- /dev/null
+++ b/src/settings/components/ui/Input.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import './Input.scss';
+
+class Input extends React.Component {
+
+ renderText(props) {
+ let inputClassName = props.error ? 'input-error' : '';
+ return
+
+
+
;
+ }
+
+ renderRadio(props) {
+ let inputClassName = props.error ? 'input-error' : '';
+ return
+
+
;
+ }
+
+ renderTextArea(props) {
+ let inputClassName = props.error ? 'input-error' : '';
+ return
+
+
+
{ this.props.error }
+
;
+ }
+
+ render() {
+ let { type } = this.props;
+
+ switch (this.props.type) {
+ case 'text':
+ return this.renderText(this.props);
+ case 'radio':
+ return this.renderRadio(this.props);
+ case 'textarea':
+ return this.renderTextArea(this.props);
+ default:
+ console.warn(`Unsupported input type ${type}`);
+ }
+ return null;
+ }
+}
+
+Input.propTypes = {
+ type: PropTypes.string,
+ error: PropTypes.string,
+ label: PropTypes.string,
+ value: PropTypes.string,
+};
+
+export default Input;
diff --git a/src/settings/index.jsx b/src/settings/index.jsx
deleted file mode 100644
index 6aec7a0..0000000
--- a/src/settings/index.jsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import SettingsComponent from './components';
-import reducer from './reducers/setting';
-import { Provider } from 'react-redux';
-import promise from 'redux-promise';
-import { createStore, applyMiddleware } from 'redux';
-
-const store = createStore(
- reducer,
- applyMiddleware(promise),
-);
-
-document.addEventListener('DOMContentLoaded', () => {
- let wrapper = document.getElementById('vimvixen-settings');
- ReactDOM.render(
-
-
- ,
- wrapper
- );
-});
diff --git a/src/settings/index.tsx b/src/settings/index.tsx
new file mode 100644
index 0000000..6aec7a0
--- /dev/null
+++ b/src/settings/index.tsx
@@ -0,0 +1,22 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import SettingsComponent from './components';
+import reducer from './reducers/setting';
+import { Provider } from 'react-redux';
+import promise from 'redux-promise';
+import { createStore, applyMiddleware } from 'redux';
+
+const store = createStore(
+ reducer,
+ applyMiddleware(promise),
+);
+
+document.addEventListener('DOMContentLoaded', () => {
+ let wrapper = document.getElementById('vimvixen-settings');
+ ReactDOM.render(
+
+
+ ,
+ wrapper
+ );
+});
diff --git a/src/settings/keymaps.js b/src/settings/keymaps.js
deleted file mode 100644
index ccfc74c..0000000
--- a/src/settings/keymaps.js
+++ /dev/null
@@ -1,74 +0,0 @@
-const fields = [
- [
- ['scroll.vertically?{"count":1}', 'Scroll down'],
- ['scroll.vertically?{"count":-1}', 'Scroll up'],
- ['scroll.horizonally?{"count":-1}', 'Scroll left'],
- ['scroll.horizonally?{"count":1}', 'Scroll right'],
- ['scroll.home', 'Scroll to leftmost'],
- ['scroll.end', 'Scroll to rightmost'],
- ['scroll.top', 'Scroll to top'],
- ['scroll.bottom', 'Scroll to bottom'],
- ['scroll.pages?{"count":-0.5}', 'Scroll up by half of screen'],
- ['scroll.pages?{"count":0.5}', 'Scroll down by half of screen'],
- ['scroll.pages?{"count":-1}', 'Scroll up by a screen'],
- ['scroll.pages?{"count":1}', 'Scroll down by a screen'],
- ], [
- ['mark.set.prefix', 'Set mark at current position'],
- ['mark.jump.prefix', 'Jump to the mark'],
- ], [
- ['tabs.close', 'Close a tab'],
- ['tabs.close.right', 'Close tabs to the right'],
- ['tabs.reopen', 'Reopen closed tab'],
- ['tabs.next?{"count":1}', 'Select next Tab'],
- ['tabs.prev?{"count":1}', 'Select prev Tab'],
- ['tabs.first', 'Select first tab'],
- ['tabs.last', 'Select last tab'],
- ['tabs.reload?{"cache":false}', 'Reload current tab'],
- ['tabs.reload?{"cache":true}', 'Reload with no caches'],
- ['tabs.pin.toggle', 'Toggle pinned state'],
- ['tabs.duplicate', 'Duplicate a tab'],
- ], [
- ['follow.start?{"newTab":false}', 'Follow a link'],
- ['follow.start?{"newTab":true}', 'Follow a link in new tab'],
- ['navigate.history.prev', 'Go back in histories'],
- ['navigate.history.next', 'Go forward in histories'],
- ['navigate.link.next', 'Open next link'],
- ['navigate.link.prev', 'Open previous link'],
- ['navigate.parent', 'Go to parent directory'],
- ['navigate.root', 'Go to root directory'],
- ['page.source', 'Open page source'],
- ['page.home', 'Open start page to current tab'],
- ['page.home?{"newTab":true}', 'Open start page in new tab'],
- ['focus.input', 'Focus input'],
- ], [
- ['find.start', 'Start find mode'],
- ['find.next', 'Find next word'],
- ['find.prev', 'Find previous word'],
- ], [
- ['command.show', 'Open console'],
- ['command.show.open?{"alter":false}', 'Open URL'],
- ['command.show.open?{"alter":true}', 'Alter URL'],
- ['command.show.tabopen?{"alter":false}', 'Open URL in new Tab'],
- ['command.show.tabopen?{"alter":true}', 'Alter URL in new Tab'],
- ['command.show.winopen?{"alter":false}', 'Open URL in new window'],
- ['command.show.winopen?{"alter":true}', 'Alter URL in new window'],
- ['command.show.buffer', 'Open buffer command'],
- ['command.show.addbookmark?{"alter":true}', 'Open addbookmark command'],
- ], [
- ['addon.toggle.enabled', 'Enable or disable'],
- ['urls.yank', 'Copy current URL'],
- ['urls.paste?{"newTab":false}', 'Open clipboard\'s URL in current tab'],
- ['urls.paste?{"newTab":true}', 'Open clipboard\'s URL in new tab'],
- ['zoom.in', 'Zoom-in'],
- ['zoom.out', 'Zoom-out'],
- ['zoom.neutral', 'Reset zoom level'],
- ['page.source', 'Open a page source'],
- ]
-];
-
-const allowedOps = [].concat(...fields.map(group => group.map(e => e[0])));
-
-export default {
- fields,
- allowedOps,
-};
diff --git a/src/settings/keymaps.ts b/src/settings/keymaps.ts
new file mode 100644
index 0000000..ccfc74c
--- /dev/null
+++ b/src/settings/keymaps.ts
@@ -0,0 +1,74 @@
+const fields = [
+ [
+ ['scroll.vertically?{"count":1}', 'Scroll down'],
+ ['scroll.vertically?{"count":-1}', 'Scroll up'],
+ ['scroll.horizonally?{"count":-1}', 'Scroll left'],
+ ['scroll.horizonally?{"count":1}', 'Scroll right'],
+ ['scroll.home', 'Scroll to leftmost'],
+ ['scroll.end', 'Scroll to rightmost'],
+ ['scroll.top', 'Scroll to top'],
+ ['scroll.bottom', 'Scroll to bottom'],
+ ['scroll.pages?{"count":-0.5}', 'Scroll up by half of screen'],
+ ['scroll.pages?{"count":0.5}', 'Scroll down by half of screen'],
+ ['scroll.pages?{"count":-1}', 'Scroll up by a screen'],
+ ['scroll.pages?{"count":1}', 'Scroll down by a screen'],
+ ], [
+ ['mark.set.prefix', 'Set mark at current position'],
+ ['mark.jump.prefix', 'Jump to the mark'],
+ ], [
+ ['tabs.close', 'Close a tab'],
+ ['tabs.close.right', 'Close tabs to the right'],
+ ['tabs.reopen', 'Reopen closed tab'],
+ ['tabs.next?{"count":1}', 'Select next Tab'],
+ ['tabs.prev?{"count":1}', 'Select prev Tab'],
+ ['tabs.first', 'Select first tab'],
+ ['tabs.last', 'Select last tab'],
+ ['tabs.reload?{"cache":false}', 'Reload current tab'],
+ ['tabs.reload?{"cache":true}', 'Reload with no caches'],
+ ['tabs.pin.toggle', 'Toggle pinned state'],
+ ['tabs.duplicate', 'Duplicate a tab'],
+ ], [
+ ['follow.start?{"newTab":false}', 'Follow a link'],
+ ['follow.start?{"newTab":true}', 'Follow a link in new tab'],
+ ['navigate.history.prev', 'Go back in histories'],
+ ['navigate.history.next', 'Go forward in histories'],
+ ['navigate.link.next', 'Open next link'],
+ ['navigate.link.prev', 'Open previous link'],
+ ['navigate.parent', 'Go to parent directory'],
+ ['navigate.root', 'Go to root directory'],
+ ['page.source', 'Open page source'],
+ ['page.home', 'Open start page to current tab'],
+ ['page.home?{"newTab":true}', 'Open start page in new tab'],
+ ['focus.input', 'Focus input'],
+ ], [
+ ['find.start', 'Start find mode'],
+ ['find.next', 'Find next word'],
+ ['find.prev', 'Find previous word'],
+ ], [
+ ['command.show', 'Open console'],
+ ['command.show.open?{"alter":false}', 'Open URL'],
+ ['command.show.open?{"alter":true}', 'Alter URL'],
+ ['command.show.tabopen?{"alter":false}', 'Open URL in new Tab'],
+ ['command.show.tabopen?{"alter":true}', 'Alter URL in new Tab'],
+ ['command.show.winopen?{"alter":false}', 'Open URL in new window'],
+ ['command.show.winopen?{"alter":true}', 'Alter URL in new window'],
+ ['command.show.buffer', 'Open buffer command'],
+ ['command.show.addbookmark?{"alter":true}', 'Open addbookmark command'],
+ ], [
+ ['addon.toggle.enabled', 'Enable or disable'],
+ ['urls.yank', 'Copy current URL'],
+ ['urls.paste?{"newTab":false}', 'Open clipboard\'s URL in current tab'],
+ ['urls.paste?{"newTab":true}', 'Open clipboard\'s URL in new tab'],
+ ['zoom.in', 'Zoom-in'],
+ ['zoom.out', 'Zoom-out'],
+ ['zoom.neutral', 'Reset zoom level'],
+ ['page.source', 'Open a page source'],
+ ]
+];
+
+const allowedOps = [].concat(...fields.map(group => group.map(e => e[0])));
+
+export default {
+ fields,
+ allowedOps,
+};
diff --git a/src/settings/reducers/setting.js b/src/settings/reducers/setting.js
deleted file mode 100644
index 54033aa..0000000
--- a/src/settings/reducers/setting.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import actions from 'settings/actions';
-
-const defaultState = {
- source: '',
- json: '',
- form: null,
- error: '',
-};
-
-export default function reducer(state = defaultState, action = {}) {
- switch (action.type) {
- case actions.SETTING_SET_SETTINGS:
- return { ...state,
- source: action.source,
- json: action.json,
- form: action.form,
- error: '', };
- case actions.SETTING_SHOW_ERROR:
- return { ...state,
- error: action.error,
- json: action.json, };
- case actions.SETTING_SWITCH_TO_FORM:
- return { ...state,
- error: '',
- source: 'form',
- form: action.form, };
- case actions.SETTING_SWITCH_TO_JSON:
- return { ...state,
- error: '',
- source: 'json',
- json: action.json, };
- default:
- return state;
- }
-}
diff --git a/src/settings/reducers/setting.ts b/src/settings/reducers/setting.ts
new file mode 100644
index 0000000..54033aa
--- /dev/null
+++ b/src/settings/reducers/setting.ts
@@ -0,0 +1,35 @@
+import actions from 'settings/actions';
+
+const defaultState = {
+ source: '',
+ json: '',
+ form: null,
+ error: '',
+};
+
+export default function reducer(state = defaultState, action = {}) {
+ switch (action.type) {
+ case actions.SETTING_SET_SETTINGS:
+ return { ...state,
+ source: action.source,
+ json: action.json,
+ form: action.form,
+ error: '', };
+ case actions.SETTING_SHOW_ERROR:
+ return { ...state,
+ error: action.error,
+ json: action.json, };
+ case actions.SETTING_SWITCH_TO_FORM:
+ return { ...state,
+ error: '',
+ source: 'form',
+ form: action.form, };
+ case actions.SETTING_SWITCH_TO_JSON:
+ return { ...state,
+ error: '',
+ source: 'json',
+ json: action.json, };
+ default:
+ return state;
+ }
+}
diff --git a/src/shared/blacklists.js b/src/shared/blacklists.js
deleted file mode 100644
index 61720c3..0000000
--- a/src/shared/blacklists.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import * as re from 'shared/utils/re';
-
-const includes = (blacklist, url) => {
- let u = new URL(url);
- return blacklist.some((item) => {
- if (!item.includes('/')) {
- return re.fromWildcard(item).test(u.host);
- }
- return re.fromWildcard(item).test(u.host + u.pathname);
- });
-};
-
-export { includes };
diff --git a/src/shared/blacklists.ts b/src/shared/blacklists.ts
new file mode 100644
index 0000000..61720c3
--- /dev/null
+++ b/src/shared/blacklists.ts
@@ -0,0 +1,13 @@
+import * as re from 'shared/utils/re';
+
+const includes = (blacklist, url) => {
+ let u = new URL(url);
+ return blacklist.some((item) => {
+ if (!item.includes('/')) {
+ return re.fromWildcard(item).test(u.host);
+ }
+ return re.fromWildcard(item).test(u.host + u.pathname);
+ });
+};
+
+export { includes };
diff --git a/src/shared/messages.js b/src/shared/messages.js
deleted file mode 100644
index ddf3368..0000000
--- a/src/shared/messages.js
+++ /dev/null
@@ -1,71 +0,0 @@
-const onWebMessage = (listener) => {
- window.addEventListener('message', (event) => {
- let sender = event.source;
- let message = null;
- try {
- message = JSON.parse(event.data);
- } catch (e) {
- // ignore unexpected message
- return;
- }
- listener(message, sender);
- });
-};
-
-const onBackgroundMessage = (listener) => {
- browser.runtime.onMessage.addListener(listener);
-};
-
-const onMessage = (listener) => {
- onWebMessage(listener);
- onBackgroundMessage(listener);
-};
-
-export default {
- BACKGROUND_OPERATION: 'background.operation',
-
- CONSOLE_UNFOCUS: 'console.unfocus',
- CONSOLE_ENTER_COMMAND: 'console.enter.command',
- CONSOLE_ENTER_FIND: 'console.enter.find',
- CONSOLE_QUERY_COMPLETIONS: 'console.query.completions',
- CONSOLE_SHOW_COMMAND: 'console.show.command',
- CONSOLE_SHOW_ERROR: 'console.show.error',
- CONSOLE_SHOW_INFO: 'console.show.info',
- CONSOLE_SHOW_FIND: 'console.show.find',
- CONSOLE_HIDE: 'console.hide',
-
- FOLLOW_START: 'follow.start',
- FOLLOW_REQUEST_COUNT_TARGETS: 'follow.request.count.targets',
- FOLLOW_RESPONSE_COUNT_TARGETS: 'follow.response.count.targets',
- FOLLOW_CREATE_HINTS: 'follow.create.hints',
- FOLLOW_SHOW_HINTS: 'follow.update.hints',
- FOLLOW_REMOVE_HINTS: 'follow.remove.hints',
- FOLLOW_ACTIVATE: 'follow.activate',
- FOLLOW_KEY_PRESS: 'follow.key.press',
-
- MARK_SET_GLOBAL: 'mark.set.global',
- MARK_JUMP_GLOBAL: 'mark.jump.global',
-
- TAB_SCROLL_TO: 'tab.scroll.to',
-
- FIND_NEXT: 'find.next',
- FIND_PREV: 'find.prev',
- FIND_GET_KEYWORD: 'find.get.keyword',
- FIND_SET_KEYWORD: 'find.set.keyword',
-
- ADDON_ENABLED_QUERY: 'addon.enabled.query',
- ADDON_ENABLED_RESPONSE: 'addon.enabled.response',
- ADDON_TOGGLE_ENABLED: 'addon.toggle.enabled',
-
- OPEN_URL: 'open.url',
-
- SETTINGS_CHANGED: 'settings.changed',
- SETTINGS_QUERY: 'settings.query',
-
- WINDOW_TOP_MESSAGE: 'window.top.message',
- CONSOLE_FRAME_MESSAGE: 'console.frame.message',
-
- onWebMessage,
- onBackgroundMessage,
- onMessage,
-};
diff --git a/src/shared/messages.ts b/src/shared/messages.ts
new file mode 100644
index 0000000..ddf3368
--- /dev/null
+++ b/src/shared/messages.ts
@@ -0,0 +1,71 @@
+const onWebMessage = (listener) => {
+ window.addEventListener('message', (event) => {
+ let sender = event.source;
+ let message = null;
+ try {
+ message = JSON.parse(event.data);
+ } catch (e) {
+ // ignore unexpected message
+ return;
+ }
+ listener(message, sender);
+ });
+};
+
+const onBackgroundMessage = (listener) => {
+ browser.runtime.onMessage.addListener(listener);
+};
+
+const onMessage = (listener) => {
+ onWebMessage(listener);
+ onBackgroundMessage(listener);
+};
+
+export default {
+ BACKGROUND_OPERATION: 'background.operation',
+
+ CONSOLE_UNFOCUS: 'console.unfocus',
+ CONSOLE_ENTER_COMMAND: 'console.enter.command',
+ CONSOLE_ENTER_FIND: 'console.enter.find',
+ CONSOLE_QUERY_COMPLETIONS: 'console.query.completions',
+ CONSOLE_SHOW_COMMAND: 'console.show.command',
+ CONSOLE_SHOW_ERROR: 'console.show.error',
+ CONSOLE_SHOW_INFO: 'console.show.info',
+ CONSOLE_SHOW_FIND: 'console.show.find',
+ CONSOLE_HIDE: 'console.hide',
+
+ FOLLOW_START: 'follow.start',
+ FOLLOW_REQUEST_COUNT_TARGETS: 'follow.request.count.targets',
+ FOLLOW_RESPONSE_COUNT_TARGETS: 'follow.response.count.targets',
+ FOLLOW_CREATE_HINTS: 'follow.create.hints',
+ FOLLOW_SHOW_HINTS: 'follow.update.hints',
+ FOLLOW_REMOVE_HINTS: 'follow.remove.hints',
+ FOLLOW_ACTIVATE: 'follow.activate',
+ FOLLOW_KEY_PRESS: 'follow.key.press',
+
+ MARK_SET_GLOBAL: 'mark.set.global',
+ MARK_JUMP_GLOBAL: 'mark.jump.global',
+
+ TAB_SCROLL_TO: 'tab.scroll.to',
+
+ FIND_NEXT: 'find.next',
+ FIND_PREV: 'find.prev',
+ FIND_GET_KEYWORD: 'find.get.keyword',
+ FIND_SET_KEYWORD: 'find.set.keyword',
+
+ ADDON_ENABLED_QUERY: 'addon.enabled.query',
+ ADDON_ENABLED_RESPONSE: 'addon.enabled.response',
+ ADDON_TOGGLE_ENABLED: 'addon.toggle.enabled',
+
+ OPEN_URL: 'open.url',
+
+ SETTINGS_CHANGED: 'settings.changed',
+ SETTINGS_QUERY: 'settings.query',
+
+ WINDOW_TOP_MESSAGE: 'window.top.message',
+ CONSOLE_FRAME_MESSAGE: 'console.frame.message',
+
+ onWebMessage,
+ onBackgroundMessage,
+ onMessage,
+};
diff --git a/src/shared/operations.js b/src/shared/operations.js
deleted file mode 100644
index 8674f4d..0000000
--- a/src/shared/operations.js
+++ /dev/null
@@ -1,78 +0,0 @@
-export default {
- // Hide console, or cancel some user actions
- CANCEL: 'cancel',
-
- // Addons
- ADDON_ENABLE: 'addon.enable',
- ADDON_DISABLE: 'addon.disable',
- ADDON_TOGGLE_ENABLED: 'addon.toggle.enabled',
-
- // Command
- COMMAND_SHOW: 'command.show',
- COMMAND_SHOW_OPEN: 'command.show.open',
- COMMAND_SHOW_TABOPEN: 'command.show.tabopen',
- COMMAND_SHOW_WINOPEN: 'command.show.winopen',
- COMMAND_SHOW_BUFFER: 'command.show.buffer',
- COMMAND_SHOW_ADDBOOKMARK: 'command.show.addbookmark',
-
- // Scrolls
- SCROLL_VERTICALLY: 'scroll.vertically',
- SCROLL_HORIZONALLY: 'scroll.horizonally',
- SCROLL_PAGES: 'scroll.pages',
- SCROLL_TOP: 'scroll.top',
- SCROLL_BOTTOM: 'scroll.bottom',
- SCROLL_HOME: 'scroll.home',
- SCROLL_END: 'scroll.end',
-
- // Follows
- FOLLOW_START: 'follow.start',
-
- // Navigations
- NAVIGATE_HISTORY_PREV: 'navigate.history.prev',
- NAVIGATE_HISTORY_NEXT: 'navigate.history.next',
- NAVIGATE_LINK_PREV: 'navigate.link.prev',
- NAVIGATE_LINK_NEXT: 'navigate.link.next',
- NAVIGATE_PARENT: 'navigate.parent',
- NAVIGATE_ROOT: 'navigate.root',
-
- // Focus
- FOCUS_INPUT: 'focus.input',
-
- // Page
- PAGE_SOURCE: 'page.source',
- PAGE_HOME: 'page.home',
-
- // Tabs
- TAB_CLOSE: 'tabs.close',
- TAB_CLOSE_FORCE: 'tabs.close.force',
- TAB_CLOSE_RIGHT: 'tabs.close.right',
- TAB_REOPEN: 'tabs.reopen',
- TAB_PREV: 'tabs.prev',
- TAB_NEXT: 'tabs.next',
- TAB_FIRST: 'tabs.first',
- TAB_LAST: 'tabs.last',
- TAB_PREV_SEL: 'tabs.prevsel',
- TAB_RELOAD: 'tabs.reload',
- TAB_PIN: 'tabs.pin',
- TAB_UNPIN: 'tabs.unpin',
- TAB_TOGGLE_PINNED: 'tabs.pin.toggle',
- TAB_DUPLICATE: 'tabs.duplicate',
-
- // Zooms
- ZOOM_IN: 'zoom.in',
- ZOOM_OUT: 'zoom.out',
- ZOOM_NEUTRAL: 'zoom.neutral',
-
- // Url yank/paste
- URLS_YANK: 'urls.yank',
- URLS_PASTE: 'urls.paste',
-
- // Find
- FIND_START: 'find.start',
- FIND_NEXT: 'find.next',
- FIND_PREV: 'find.prev',
-
- // Mark
- MARK_SET_PREFIX: 'mark.set.prefix',
- MARK_JUMP_PREFIX: 'mark.jump.prefix',
-};
diff --git a/src/shared/operations.ts b/src/shared/operations.ts
new file mode 100644
index 0000000..8674f4d
--- /dev/null
+++ b/src/shared/operations.ts
@@ -0,0 +1,78 @@
+export default {
+ // Hide console, or cancel some user actions
+ CANCEL: 'cancel',
+
+ // Addons
+ ADDON_ENABLE: 'addon.enable',
+ ADDON_DISABLE: 'addon.disable',
+ ADDON_TOGGLE_ENABLED: 'addon.toggle.enabled',
+
+ // Command
+ COMMAND_SHOW: 'command.show',
+ COMMAND_SHOW_OPEN: 'command.show.open',
+ COMMAND_SHOW_TABOPEN: 'command.show.tabopen',
+ COMMAND_SHOW_WINOPEN: 'command.show.winopen',
+ COMMAND_SHOW_BUFFER: 'command.show.buffer',
+ COMMAND_SHOW_ADDBOOKMARK: 'command.show.addbookmark',
+
+ // Scrolls
+ SCROLL_VERTICALLY: 'scroll.vertically',
+ SCROLL_HORIZONALLY: 'scroll.horizonally',
+ SCROLL_PAGES: 'scroll.pages',
+ SCROLL_TOP: 'scroll.top',
+ SCROLL_BOTTOM: 'scroll.bottom',
+ SCROLL_HOME: 'scroll.home',
+ SCROLL_END: 'scroll.end',
+
+ // Follows
+ FOLLOW_START: 'follow.start',
+
+ // Navigations
+ NAVIGATE_HISTORY_PREV: 'navigate.history.prev',
+ NAVIGATE_HISTORY_NEXT: 'navigate.history.next',
+ NAVIGATE_LINK_PREV: 'navigate.link.prev',
+ NAVIGATE_LINK_NEXT: 'navigate.link.next',
+ NAVIGATE_PARENT: 'navigate.parent',
+ NAVIGATE_ROOT: 'navigate.root',
+
+ // Focus
+ FOCUS_INPUT: 'focus.input',
+
+ // Page
+ PAGE_SOURCE: 'page.source',
+ PAGE_HOME: 'page.home',
+
+ // Tabs
+ TAB_CLOSE: 'tabs.close',
+ TAB_CLOSE_FORCE: 'tabs.close.force',
+ TAB_CLOSE_RIGHT: 'tabs.close.right',
+ TAB_REOPEN: 'tabs.reopen',
+ TAB_PREV: 'tabs.prev',
+ TAB_NEXT: 'tabs.next',
+ TAB_FIRST: 'tabs.first',
+ TAB_LAST: 'tabs.last',
+ TAB_PREV_SEL: 'tabs.prevsel',
+ TAB_RELOAD: 'tabs.reload',
+ TAB_PIN: 'tabs.pin',
+ TAB_UNPIN: 'tabs.unpin',
+ TAB_TOGGLE_PINNED: 'tabs.pin.toggle',
+ TAB_DUPLICATE: 'tabs.duplicate',
+
+ // Zooms
+ ZOOM_IN: 'zoom.in',
+ ZOOM_OUT: 'zoom.out',
+ ZOOM_NEUTRAL: 'zoom.neutral',
+
+ // Url yank/paste
+ URLS_YANK: 'urls.yank',
+ URLS_PASTE: 'urls.paste',
+
+ // Find
+ FIND_START: 'find.start',
+ FIND_NEXT: 'find.next',
+ FIND_PREV: 'find.prev',
+
+ // Mark
+ MARK_SET_PREFIX: 'mark.set.prefix',
+ MARK_JUMP_PREFIX: 'mark.jump.prefix',
+};
diff --git a/src/shared/settings/default.js b/src/shared/settings/default.js
deleted file mode 100644
index 6523a74..0000000
--- a/src/shared/settings/default.js
+++ /dev/null
@@ -1,85 +0,0 @@
-export default {
- source: 'json',
- json: `{
- "keymaps": {
- "0": { "type": "scroll.home" },
- ":": { "type": "command.show" },
- "o": { "type": "command.show.open", "alter": false },
- "O": { "type": "command.show.open", "alter": true },
- "t": { "type": "command.show.tabopen", "alter": false },
- "T": { "type": "command.show.tabopen", "alter": true },
- "w": { "type": "command.show.winopen", "alter": false },
- "W": { "type": "command.show.winopen", "alter": true },
- "b": { "type": "command.show.buffer" },
- "a": { "type": "command.show.addbookmark", "alter": true },
- "k": { "type": "scroll.vertically", "count": -1 },
- "j": { "type": "scroll.vertically", "count": 1 },
- "h": { "type": "scroll.horizonally", "count": -1 },
- "l": { "type": "scroll.horizonally", "count": 1 },
- "": { "type": "scroll.pages", "count": -0.5 },
- "": { "type": "scroll.pages", "count": 0.5 },
- "": { "type": "scroll.pages", "count": -1 },
- "": { "type": "scroll.pages", "count": 1 },
- "gg": { "type": "scroll.top" },
- "G": { "type": "scroll.bottom" },
- "$": { "type": "scroll.end" },
- "d": { "type": "tabs.close" },
- "D": { "type": "tabs.close.right" },
- "!d": { "type": "tabs.close.force" },
- "u": { "type": "tabs.reopen" },
- "K": { "type": "tabs.prev", "count": 1 },
- "J": { "type": "tabs.next", "count": 1 },
- "gT": { "type": "tabs.prev", "count": 1 },
- "gt": { "type": "tabs.next", "count": 1 },
- "g0": { "type": "tabs.first" },
- "g$": { "type": "tabs.last" },
- "": { "type": "tabs.prevsel" },
- "r": { "type": "tabs.reload", "cache": false },
- "R": { "type": "tabs.reload", "cache": true },
- "zp": { "type": "tabs.pin.toggle" },
- "zd": { "type": "tabs.duplicate" },
- "zi": { "type": "zoom.in" },
- "zo": { "type": "zoom.out" },
- "zz": { "type": "zoom.neutral" },
- "f": { "type": "follow.start", "newTab": false },
- "F": { "type": "follow.start", "newTab": true, "background": false },
- "m": { "type": "mark.set.prefix" },
- "'": { "type": "mark.jump.prefix" },
- "H": { "type": "navigate.history.prev" },
- "L": { "type": "navigate.history.next" },
- "[[": { "type": "navigate.link.prev" },
- "]]": { "type": "navigate.link.next" },
- "gu": { "type": "navigate.parent" },
- "gU": { "type": "navigate.root" },
- "gi": { "type": "focus.input" },
- "gf": { "type": "page.source" },
- "gh": { "type": "page.home" },
- "gH": { "type": "page.home", "newTab": true },
- "y": { "type": "urls.yank" },
- "p": { "type": "urls.paste", "newTab": false },
- "P": { "type": "urls.paste", "newTab": true },
- "/": { "type": "find.start" },
- "n": { "type": "find.next" },
- "N": { "type": "find.prev" },
- "": { "type": "addon.toggle.enabled" }
- },
- "search": {
- "default": "google",
- "engines": {
- "google": "https://google.com/search?q={}",
- "yahoo": "https://search.yahoo.com/search?p={}",
- "bing": "https://www.bing.com/search?q={}",
- "duckduckgo": "https://duckduckgo.com/?q={}",
- "twitter": "https://twitter.com/search?q={}",
- "wikipedia": "https://en.wikipedia.org/w/index.php?search={}"
- }
- },
- "properties": {
- "hintchars": "abcdefghijklmnopqrstuvwxyz",
- "smoothscroll": false,
- "complete": "sbh"
- },
- "blacklist": [
- ]
-}`,
-};
diff --git a/src/shared/settings/default.ts b/src/shared/settings/default.ts
new file mode 100644
index 0000000..6523a74
--- /dev/null
+++ b/src/shared/settings/default.ts
@@ -0,0 +1,85 @@
+export default {
+ source: 'json',
+ json: `{
+ "keymaps": {
+ "0": { "type": "scroll.home" },
+ ":": { "type": "command.show" },
+ "o": { "type": "command.show.open", "alter": false },
+ "O": { "type": "command.show.open", "alter": true },
+ "t": { "type": "command.show.tabopen", "alter": false },
+ "T": { "type": "command.show.tabopen", "alter": true },
+ "w": { "type": "command.show.winopen", "alter": false },
+ "W": { "type": "command.show.winopen", "alter": true },
+ "b": { "type": "command.show.buffer" },
+ "a": { "type": "command.show.addbookmark", "alter": true },
+ "k": { "type": "scroll.vertically", "count": -1 },
+ "j": { "type": "scroll.vertically", "count": 1 },
+ "h": { "type": "scroll.horizonally", "count": -1 },
+ "l": { "type": "scroll.horizonally", "count": 1 },
+ "": { "type": "scroll.pages", "count": -0.5 },
+ "": { "type": "scroll.pages", "count": 0.5 },
+ "": { "type": "scroll.pages", "count": -1 },
+ "": { "type": "scroll.pages", "count": 1 },
+ "gg": { "type": "scroll.top" },
+ "G": { "type": "scroll.bottom" },
+ "$": { "type": "scroll.end" },
+ "d": { "type": "tabs.close" },
+ "D": { "type": "tabs.close.right" },
+ "!d": { "type": "tabs.close.force" },
+ "u": { "type": "tabs.reopen" },
+ "K": { "type": "tabs.prev", "count": 1 },
+ "J": { "type": "tabs.next", "count": 1 },
+ "gT": { "type": "tabs.prev", "count": 1 },
+ "gt": { "type": "tabs.next", "count": 1 },
+ "g0": { "type": "tabs.first" },
+ "g$": { "type": "tabs.last" },
+ "": { "type": "tabs.prevsel" },
+ "r": { "type": "tabs.reload", "cache": false },
+ "R": { "type": "tabs.reload", "cache": true },
+ "zp": { "type": "tabs.pin.toggle" },
+ "zd": { "type": "tabs.duplicate" },
+ "zi": { "type": "zoom.in" },
+ "zo": { "type": "zoom.out" },
+ "zz": { "type": "zoom.neutral" },
+ "f": { "type": "follow.start", "newTab": false },
+ "F": { "type": "follow.start", "newTab": true, "background": false },
+ "m": { "type": "mark.set.prefix" },
+ "'": { "type": "mark.jump.prefix" },
+ "H": { "type": "navigate.history.prev" },
+ "L": { "type": "navigate.history.next" },
+ "[[": { "type": "navigate.link.prev" },
+ "]]": { "type": "navigate.link.next" },
+ "gu": { "type": "navigate.parent" },
+ "gU": { "type": "navigate.root" },
+ "gi": { "type": "focus.input" },
+ "gf": { "type": "page.source" },
+ "gh": { "type": "page.home" },
+ "gH": { "type": "page.home", "newTab": true },
+ "y": { "type": "urls.yank" },
+ "p": { "type": "urls.paste", "newTab": false },
+ "P": { "type": "urls.paste", "newTab": true },
+ "/": { "type": "find.start" },
+ "n": { "type": "find.next" },
+ "N": { "type": "find.prev" },
+ "": { "type": "addon.toggle.enabled" }
+ },
+ "search": {
+ "default": "google",
+ "engines": {
+ "google": "https://google.com/search?q={}",
+ "yahoo": "https://search.yahoo.com/search?p={}",
+ "bing": "https://www.bing.com/search?q={}",
+ "duckduckgo": "https://duckduckgo.com/?q={}",
+ "twitter": "https://twitter.com/search?q={}",
+ "wikipedia": "https://en.wikipedia.org/w/index.php?search={}"
+ }
+ },
+ "properties": {
+ "hintchars": "abcdefghijklmnopqrstuvwxyz",
+ "smoothscroll": false,
+ "complete": "sbh"
+ },
+ "blacklist": [
+ ]
+}`,
+};
diff --git a/src/shared/settings/properties.js b/src/shared/settings/properties.js
deleted file mode 100644
index f8e61a0..0000000
--- a/src/shared/settings/properties.js
+++ /dev/null
@@ -1,24 +0,0 @@
-// describe types of a propety as:
-// mystr: 'string',
-// mynum: 'number',
-// mybool: 'boolean',
-const types = {
- hintchars: 'string',
- smoothscroll: 'boolean',
- complete: 'string',
-};
-
-// describe default values of a property
-const defaults = {
- hintchars: 'abcdefghijklmnopqrstuvwxyz',
- smoothscroll: false,
- complete: 'sbh',
-};
-
-const docs = {
- hintchars: 'hint characters on follow mode',
- smoothscroll: 'smooth scroll',
- complete: 'which are completed at the open page',
-};
-
-export { types, defaults, docs };
diff --git a/src/shared/settings/properties.ts b/src/shared/settings/properties.ts
new file mode 100644
index 0000000..f8e61a0
--- /dev/null
+++ b/src/shared/settings/properties.ts
@@ -0,0 +1,24 @@
+// describe types of a propety as:
+// mystr: 'string',
+// mynum: 'number',
+// mybool: 'boolean',
+const types = {
+ hintchars: 'string',
+ smoothscroll: 'boolean',
+ complete: 'string',
+};
+
+// describe default values of a property
+const defaults = {
+ hintchars: 'abcdefghijklmnopqrstuvwxyz',
+ smoothscroll: false,
+ complete: 'sbh',
+};
+
+const docs = {
+ hintchars: 'hint characters on follow mode',
+ smoothscroll: 'smooth scroll',
+ complete: 'which are completed at the open page',
+};
+
+export { types, defaults, docs };
diff --git a/src/shared/settings/storage.js b/src/shared/settings/storage.js
deleted file mode 100644
index 5dce3b0..0000000
--- a/src/shared/settings/storage.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import DefaultSettings from './default';
-import * as settingsValues from './values';
-
-const loadRaw = async() => {
- let { settings } = await browser.storage.local.get('settings');
- if (!settings) {
- return DefaultSettings;
- }
- return { ...DefaultSettings, ...settings };
-};
-
-const loadValue = async() => {
- let settings = await loadRaw();
- let value = JSON.parse(DefaultSettings.json);
- if (settings.source === 'json') {
- value = settingsValues.valueFromJson(settings.json);
- } else if (settings.source === 'form') {
- value = settingsValues.valueFromForm(settings.form);
- }
- if (!value.properties) {
- value.properties = {};
- }
- return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value };
-};
-
-const save = (settings) => {
- return browser.storage.local.set({
- settings,
- });
-};
-
-export { loadRaw, loadValue, save };
diff --git a/src/shared/settings/storage.ts b/src/shared/settings/storage.ts
new file mode 100644
index 0000000..5dce3b0
--- /dev/null
+++ b/src/shared/settings/storage.ts
@@ -0,0 +1,32 @@
+import DefaultSettings from './default';
+import * as settingsValues from './values';
+
+const loadRaw = async() => {
+ let { settings } = await browser.storage.local.get('settings');
+ if (!settings) {
+ return DefaultSettings;
+ }
+ return { ...DefaultSettings, ...settings };
+};
+
+const loadValue = async() => {
+ let settings = await loadRaw();
+ let value = JSON.parse(DefaultSettings.json);
+ if (settings.source === 'json') {
+ value = settingsValues.valueFromJson(settings.json);
+ } else if (settings.source === 'form') {
+ value = settingsValues.valueFromForm(settings.form);
+ }
+ if (!value.properties) {
+ value.properties = {};
+ }
+ return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value };
+};
+
+const save = (settings) => {
+ return browser.storage.local.set({
+ settings,
+ });
+};
+
+export { loadRaw, loadValue, save };
diff --git a/src/shared/settings/validator.js b/src/shared/settings/validator.js
deleted file mode 100644
index a800a52..0000000
--- a/src/shared/settings/validator.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import operations from 'shared/operations';
-import * as properties from './properties';
-
-const VALID_TOP_KEYS = ['keymaps', 'search', 'blacklist', 'properties'];
-const VALID_OPERATION_VALUES = Object.keys(operations).map((key) => {
- return operations[key];
-});
-
-const validateInvalidTopKeys = (settings) => {
- let invalidKey = Object.keys(settings).find((key) => {
- return !VALID_TOP_KEYS.includes(key);
- });
- if (invalidKey) {
- throw Error(`Unknown key: "${invalidKey}"`);
- }
-};
-
-const validateKeymaps = (keymaps) => {
- for (let key of Object.keys(keymaps)) {
- let value = keymaps[key];
- if (!VALID_OPERATION_VALUES.includes(value.type)) {
- throw Error(`Unknown operation: "${value.type}"`);
- }
- }
-};
-
-const validateSearch = (search) => {
- let engines = search.engines;
- for (let key of Object.keys(engines)) {
- if ((/\s/).test(key)) {
- throw new Error(
- `While space in search engine name is not allowed: "${key}"`
- );
- }
- let url = engines[key];
- if (!url.match(/{}/)) {
- throw new Error(`No {}-placeholders in URL of "${key}"`);
- }
- if (url.match(/{}/g).length > 1) {
- throw new Error(`Multiple {}-placeholders in URL of "${key}"`);
- }
- }
-
- if (!search.default) {
- throw new Error(`Default engine is not set`);
- }
- if (!Object.keys(engines).includes(search.default)) {
- throw new Error(`Default engine "${search.default}" not found`);
- }
-};
-
-const validateProperties = (props) => {
- for (let name of Object.keys(props)) {
- if (!properties.types[name]) {
- throw new Error(`Unknown property name: "${name}"`);
- }
- if (typeof props[name] !== properties.types[name]) {
- throw new Error(`Invalid type for property: "${name}"`);
- }
- }
-};
-
-const validate = (settings) => {
- validateInvalidTopKeys(settings);
- if (settings.keymaps) {
- validateKeymaps(settings.keymaps);
- }
- if (settings.search) {
- validateSearch(settings.search);
- }
- if (settings.properties) {
- validateProperties(settings.properties);
- }
-};
-
-export { validate };
diff --git a/src/shared/settings/validator.ts b/src/shared/settings/validator.ts
new file mode 100644
index 0000000..a800a52
--- /dev/null
+++ b/src/shared/settings/validator.ts
@@ -0,0 +1,76 @@
+import operations from 'shared/operations';
+import * as properties from './properties';
+
+const VALID_TOP_KEYS = ['keymaps', 'search', 'blacklist', 'properties'];
+const VALID_OPERATION_VALUES = Object.keys(operations).map((key) => {
+ return operations[key];
+});
+
+const validateInvalidTopKeys = (settings) => {
+ let invalidKey = Object.keys(settings).find((key) => {
+ return !VALID_TOP_KEYS.includes(key);
+ });
+ if (invalidKey) {
+ throw Error(`Unknown key: "${invalidKey}"`);
+ }
+};
+
+const validateKeymaps = (keymaps) => {
+ for (let key of Object.keys(keymaps)) {
+ let value = keymaps[key];
+ if (!VALID_OPERATION_VALUES.includes(value.type)) {
+ throw Error(`Unknown operation: "${value.type}"`);
+ }
+ }
+};
+
+const validateSearch = (search) => {
+ let engines = search.engines;
+ for (let key of Object.keys(engines)) {
+ if ((/\s/).test(key)) {
+ throw new Error(
+ `While space in search engine name is not allowed: "${key}"`
+ );
+ }
+ let url = engines[key];
+ if (!url.match(/{}/)) {
+ throw new Error(`No {}-placeholders in URL of "${key}"`);
+ }
+ if (url.match(/{}/g).length > 1) {
+ throw new Error(`Multiple {}-placeholders in URL of "${key}"`);
+ }
+ }
+
+ if (!search.default) {
+ throw new Error(`Default engine is not set`);
+ }
+ if (!Object.keys(engines).includes(search.default)) {
+ throw new Error(`Default engine "${search.default}" not found`);
+ }
+};
+
+const validateProperties = (props) => {
+ for (let name of Object.keys(props)) {
+ if (!properties.types[name]) {
+ throw new Error(`Unknown property name: "${name}"`);
+ }
+ if (typeof props[name] !== properties.types[name]) {
+ throw new Error(`Invalid type for property: "${name}"`);
+ }
+ }
+};
+
+const validate = (settings) => {
+ validateInvalidTopKeys(settings);
+ if (settings.keymaps) {
+ validateKeymaps(settings.keymaps);
+ }
+ if (settings.search) {
+ validateSearch(settings.search);
+ }
+ if (settings.properties) {
+ validateProperties(settings.properties);
+ }
+};
+
+export { validate };
diff --git a/src/shared/settings/values.js b/src/shared/settings/values.js
deleted file mode 100644
index 9828af6..0000000
--- a/src/shared/settings/values.js
+++ /dev/null
@@ -1,108 +0,0 @@
-import * as properties from './properties';
-
-const operationFromFormName = (name) => {
- let [type, argStr] = name.split('?');
- let args = {};
- if (argStr) {
- args = JSON.parse(argStr);
- }
- return { type, ...args };
-};
-
-const operationToFormName = (op) => {
- let type = op.type;
- let args = { ...op };
- delete args.type;
-
- if (Object.keys(args).length === 0) {
- return type;
- }
- return op.type + '?' + JSON.stringify(args);
-};
-
-const valueFromJson = (json) => {
- return JSON.parse(json);
-};
-
-const valueFromForm = (form) => {
- let keymaps = undefined;
- if (form.keymaps) {
- keymaps = {};
- for (let name of Object.keys(form.keymaps)) {
- let keys = form.keymaps[name];
- keymaps[keys] = operationFromFormName(name);
- }
- }
-
- let search = undefined;
- if (form.search) {
- search = { default: form.search.default };
-
- if (form.search.engines) {
- search.engines = {};
- for (let [name, url] of form.search.engines) {
- search.engines[name] = url;
- }
- }
- }
-
- return {
- keymaps,
- search,
- blacklist: form.blacklist,
- properties: form.properties
- };
-};
-
-const jsonFromValue = (value) => {
- return JSON.stringify(value, undefined, 2);
-};
-
-const formFromValue = (value, allowedOps) => {
- let keymaps = undefined;
-
- if (value.keymaps) {
- let allowedSet = new Set(allowedOps);
-
- keymaps = {};
- for (let keys of Object.keys(value.keymaps)) {
- let op = operationToFormName(value.keymaps[keys]);
- if (allowedSet.has(op)) {
- keymaps[op] = keys;
- }
- }
- }
-
- let search = undefined;
- if (value.search) {
- search = { default: value.search.default };
- if (value.search.engines) {
- search.engines = Object.keys(value.search.engines).map((name) => {
- return [name, value.search.engines[name]];
- });
- }
- }
-
- let formProperties = { ...properties.defaults, ...value.properties };
-
- return {
- keymaps,
- search,
- blacklist: value.blacklist,
- properties: formProperties,
- };
-};
-
-const jsonFromForm = (form) => {
- return jsonFromValue(valueFromForm(form));
-};
-
-const formFromJson = (json, allowedOps) => {
- let value = valueFromJson(json);
- return formFromValue(value, allowedOps);
-};
-
-export {
- valueFromJson, valueFromForm, jsonFromValue, formFromValue,
- jsonFromForm, formFromJson
-};
diff --git a/src/shared/settings/values.ts b/src/shared/settings/values.ts
new file mode 100644
index 0000000..9828af6
--- /dev/null
+++ b/src/shared/settings/values.ts
@@ -0,0 +1,108 @@
+import * as properties from './properties';
+
+const operationFromFormName = (name) => {
+ let [type, argStr] = name.split('?');
+ let args = {};
+ if (argStr) {
+ args = JSON.parse(argStr);
+ }
+ return { type, ...args };
+};
+
+const operationToFormName = (op) => {
+ let type = op.type;
+ let args = { ...op };
+ delete args.type;
+
+ if (Object.keys(args).length === 0) {
+ return type;
+ }
+ return op.type + '?' + JSON.stringify(args);
+};
+
+const valueFromJson = (json) => {
+ return JSON.parse(json);
+};
+
+const valueFromForm = (form) => {
+ let keymaps = undefined;
+ if (form.keymaps) {
+ keymaps = {};
+ for (let name of Object.keys(form.keymaps)) {
+ let keys = form.keymaps[name];
+ keymaps[keys] = operationFromFormName(name);
+ }
+ }
+
+ let search = undefined;
+ if (form.search) {
+ search = { default: form.search.default };
+
+ if (form.search.engines) {
+ search.engines = {};
+ for (let [name, url] of form.search.engines) {
+ search.engines[name] = url;
+ }
+ }
+ }
+
+ return {
+ keymaps,
+ search,
+ blacklist: form.blacklist,
+ properties: form.properties
+ };
+};
+
+const jsonFromValue = (value) => {
+ return JSON.stringify(value, undefined, 2);
+};
+
+const formFromValue = (value, allowedOps) => {
+ let keymaps = undefined;
+
+ if (value.keymaps) {
+ let allowedSet = new Set(allowedOps);
+
+ keymaps = {};
+ for (let keys of Object.keys(value.keymaps)) {
+ let op = operationToFormName(value.keymaps[keys]);
+ if (allowedSet.has(op)) {
+ keymaps[op] = keys;
+ }
+ }
+ }
+
+ let search = undefined;
+ if (value.search) {
+ search = { default: value.search.default };
+ if (value.search.engines) {
+ search.engines = Object.keys(value.search.engines).map((name) => {
+ return [name, value.search.engines[name]];
+ });
+ }
+ }
+
+ let formProperties = { ...properties.defaults, ...value.properties };
+
+ return {
+ keymaps,
+ search,
+ blacklist: value.blacklist,
+ properties: formProperties,
+ };
+};
+
+const jsonFromForm = (form) => {
+ return jsonFromValue(valueFromForm(form));
+};
+
+const formFromJson = (json, allowedOps) => {
+ let value = valueFromJson(json);
+ return formFromValue(value, allowedOps);
+};
+
+export {
+ valueFromJson, valueFromForm, jsonFromValue, formFromValue,
+ jsonFromForm, formFromJson
+};
diff --git a/src/shared/urls.js b/src/shared/urls.js
deleted file mode 100644
index 94b1220..0000000
--- a/src/shared/urls.js
+++ /dev/null
@@ -1,43 +0,0 @@
-const trimStart = (str) => {
- // NOTE String.trimStart is available on Firefox 61
- return str.replace(/^\s+/, '');
-};
-
-const SUPPORTED_PROTOCOLS = ['http:', 'https:', 'ftp:', 'mailto:', 'about:'];
-
-const searchUrl = (keywords, searchSettings) => {
- try {
- let u = new URL(keywords);
- if (SUPPORTED_PROTOCOLS.includes(u.protocol.toLowerCase())) {
- return u.href;
- }
- } catch (e) {
- // fallthrough
- }
- if (keywords.includes('.') && !keywords.includes(' ')) {
- return 'http://' + keywords;
- }
- let template = searchSettings.engines[searchSettings.default];
- let query = keywords;
-
- let first = trimStart(keywords).split(' ')[0];
- if (Object.keys(searchSettings.engines).includes(first)) {
- template = searchSettings.engines[first];
- query = trimStart(trimStart(keywords).slice(first.length));
- }
- return template.replace('{}', encodeURIComponent(query));
-};
-
-const normalizeUrl = (url) => {
- try {
- let u = new URL(url);
- if (SUPPORTED_PROTOCOLS.includes(u.protocol.toLowerCase())) {
- return u.href;
- }
- } catch (e) {
- // fallthrough
- }
- return 'http://' + url;
-};
-
-export { searchUrl, normalizeUrl };
diff --git a/src/shared/urls.ts b/src/shared/urls.ts
new file mode 100644
index 0000000..94b1220
--- /dev/null
+++ b/src/shared/urls.ts
@@ -0,0 +1,43 @@
+const trimStart = (str) => {
+ // NOTE String.trimStart is available on Firefox 61
+ return str.replace(/^\s+/, '');
+};
+
+const SUPPORTED_PROTOCOLS = ['http:', 'https:', 'ftp:', 'mailto:', 'about:'];
+
+const searchUrl = (keywords, searchSettings) => {
+ try {
+ let u = new URL(keywords);
+ if (SUPPORTED_PROTOCOLS.includes(u.protocol.toLowerCase())) {
+ return u.href;
+ }
+ } catch (e) {
+ // fallthrough
+ }
+ if (keywords.includes('.') && !keywords.includes(' ')) {
+ return 'http://' + keywords;
+ }
+ let template = searchSettings.engines[searchSettings.default];
+ let query = keywords;
+
+ let first = trimStart(keywords).split(' ')[0];
+ if (Object.keys(searchSettings.engines).includes(first)) {
+ template = searchSettings.engines[first];
+ query = trimStart(trimStart(keywords).slice(first.length));
+ }
+ return template.replace('{}', encodeURIComponent(query));
+};
+
+const normalizeUrl = (url) => {
+ try {
+ let u = new URL(url);
+ if (SUPPORTED_PROTOCOLS.includes(u.protocol.toLowerCase())) {
+ return u.href;
+ }
+ } catch (e) {
+ // fallthrough
+ }
+ return 'http://' + url;
+};
+
+export { searchUrl, normalizeUrl };
diff --git a/src/shared/utils/dom.js b/src/shared/utils/dom.js
deleted file mode 100644
index 974d534..0000000
--- a/src/shared/utils/dom.js
+++ /dev/null
@@ -1,108 +0,0 @@
-const isContentEditable = (element) => {
- return element.hasAttribute('contenteditable') && (
- element.getAttribute('contenteditable').toLowerCase() === 'true' ||
- element.getAttribute('contenteditable').toLowerCase() === ''
- );
-};
-
-const rectangleCoordsRect = (coords) => {
- let [left, top, right, bottom] = coords.split(',').map(n => Number(n));
- return { left, top, right, bottom };
-};
-
-const circleCoordsRect = (coords) => {
- let [x, y, r] = coords.split(',').map(n => Number(n));
- return {
- left: x - r,
- top: y - r,
- right: x + r,
- bottom: y + r,
- };
-};
-
-const polygonCoordsRect = (coords) => {
- let params = coords.split(',');
- let minx = Number(params[0]),
- maxx = Number(params[0]),
- miny = Number(params[1]),
- maxy = Number(params[1]);
- let len = Math.floor(params.length / 2);
- for (let i = 2; i < len; i += 2) {
- let x = Number(params[i]),
- y = Number(params[i + 1]);
- if (x < minx) {
- minx = x;
- }
- if (x > maxx) {
- maxx = x;
- }
- if (y < miny) {
- miny = y;
- }
- if (y > maxy) {
- maxy = y;
- }
- }
- return { left: minx, top: miny, right: maxx, bottom: maxy };
-};
-
-const viewportRect = (e) => {
- if (e.tagName !== 'AREA') {
- return e.getBoundingClientRect();
- }
-
- let mapElement = e.parentNode;
- let imgElement = document.querySelector(`img[usemap="#${mapElement.name}"]`);
- let {
- left: mapLeft,
- top: mapTop
- } = imgElement.getBoundingClientRect();
- let coords = e.getAttribute('coords');
- let rect = { left: 0, top: 0, right: 0, bottom: 0 };
- switch (e.getAttribute('shape')) {
- case 'rect':
- case 'rectangle':
- rect = rectangleCoordsRect(coords);
- break;
- case 'circ':
- case 'circle':
- rect = circleCoordsRect(coords);
- break;
- case 'poly':
- case 'polygon':
- rect = polygonCoordsRect(coords);
- break;
- }
- return {
- left: rect.left + mapLeft,
- top: rect.top + mapTop,
- right: rect.right + mapLeft,
- bottom: rect.bottom + mapTop,
- };
-};
-
-const isVisible = (element) => {
- let rect = element.getBoundingClientRect();
- let style = window.getComputedStyle(element);
-
- if (style.overflow !== 'visible' && (rect.width === 0 || rect.height === 0)) {
- return false;
- }
- if (rect.right < 0 && rect.bottom < 0) {
- return false;
- }
- if (window.innerWidth < rect.left && window.innerHeight < rect.top) {
- return false;
- }
- if (element.nodeName === 'INPUT' && element.type.toLowerCase() === 'hidden') {
- return false;
- }
-
- let { display, visibility } = window.getComputedStyle(element);
- if (display === 'none' || visibility === 'hidden') {
- return false;
- }
- return true;
-};
-
-export { isContentEditable, viewportRect, isVisible };
diff --git a/src/shared/utils/dom.ts b/src/shared/utils/dom.ts
new file mode 100644
index 0000000..974d534
--- /dev/null
+++ b/src/shared/utils/dom.ts
@@ -0,0 +1,108 @@
+const isContentEditable = (element) => {
+ return element.hasAttribute('contenteditable') && (
+ element.getAttribute('contenteditable').toLowerCase() === 'true' ||
+ element.getAttribute('contenteditable').toLowerCase() === ''
+ );
+};
+
+const rectangleCoordsRect = (coords) => {
+ let [left, top, right, bottom] = coords.split(',').map(n => Number(n));
+ return { left, top, right, bottom };
+};
+
+const circleCoordsRect = (coords) => {
+ let [x, y, r] = coords.split(',').map(n => Number(n));
+ return {
+ left: x - r,
+ top: y - r,
+ right: x + r,
+ bottom: y + r,
+ };
+};
+
+const polygonCoordsRect = (coords) => {
+ let params = coords.split(',');
+ let minx = Number(params[0]),
+ maxx = Number(params[0]),
+ miny = Number(params[1]),
+ maxy = Number(params[1]);
+ let len = Math.floor(params.length / 2);
+ for (let i = 2; i < len; i += 2) {
+ let x = Number(params[i]),
+ y = Number(params[i + 1]);
+ if (x < minx) {
+ minx = x;
+ }
+ if (x > maxx) {
+ maxx = x;
+ }
+ if (y < miny) {
+ miny = y;
+ }
+ if (y > maxy) {
+ maxy = y;
+ }
+ }
+ return { left: minx, top: miny, right: maxx, bottom: maxy };
+};
+
+const viewportRect = (e) => {
+ if (e.tagName !== 'AREA') {
+ return e.getBoundingClientRect();
+ }
+
+ let mapElement = e.parentNode;
+ let imgElement = document.querySelector(`img[usemap="#${mapElement.name}"]`);
+ let {
+ left: mapLeft,
+ top: mapTop
+ } = imgElement.getBoundingClientRect();
+ let coords = e.getAttribute('coords');
+ let rect = { left: 0, top: 0, right: 0, bottom: 0 };
+ switch (e.getAttribute('shape')) {
+ case 'rect':
+ case 'rectangle':
+ rect = rectangleCoordsRect(coords);
+ break;
+ case 'circ':
+ case 'circle':
+ rect = circleCoordsRect(coords);
+ break;
+ case 'poly':
+ case 'polygon':
+ rect = polygonCoordsRect(coords);
+ break;
+ }
+ return {
+ left: rect.left + mapLeft,
+ top: rect.top + mapTop,
+ right: rect.right + mapLeft,
+ bottom: rect.bottom + mapTop,
+ };
+};
+
+const isVisible = (element) => {
+ let rect = element.getBoundingClientRect();
+ let style = window.getComputedStyle(element);
+
+ if (style.overflow !== 'visible' && (rect.width === 0 || rect.height === 0)) {
+ return false;
+ }
+ if (rect.right < 0 && rect.bottom < 0) {
+ return false;
+ }
+ if (window.innerWidth < rect.left && window.innerHeight < rect.top) {
+ return false;
+ }
+ if (element.nodeName === 'INPUT' && element.type.toLowerCase() === 'hidden') {
+ return false;
+ }
+
+ let { display, visibility } = window.getComputedStyle(element);
+ if (display === 'none' || visibility === 'hidden') {
+ return false;
+ }
+ return true;
+};
+
+export { isContentEditable, viewportRect, isVisible };
diff --git a/src/shared/utils/keys.js b/src/shared/utils/keys.js
deleted file mode 100644
index f024069..0000000
--- a/src/shared/utils/keys.js
+++ /dev/null
@@ -1,89 +0,0 @@
-const modifiedKeyName = (name) => {
- if (name === ' ') {
- return 'Space';
- }
- if (name.length === 1) {
- return name;
- } else if (name === 'Escape') {
- return 'Esc';
- }
- return name;
-};
-
-const fromKeyboardEvent = (e) => {
- let key = modifiedKeyName(e.key);
- let shift = e.shiftKey;
- if (key.length === 1 && key.toUpperCase() === key.toLowerCase()) {
- // make shift false for symbols to enable key bindings by symbold keys.
- // But this limits key bindings by symbol keys with Shift (such as Shift+$>.
- shift = false;
- }
-
- return {
- key: modifiedKeyName(e.key),
- shiftKey: shift,
- ctrlKey: e.ctrlKey,
- altKey: e.altKey,
- metaKey: e.metaKey,
- };
-};
-
-const fromMapKey = (key) => {
- if (key.startsWith('<') && key.endsWith('>')) {
- let inner = key.slice(1, -1);
- let shift = inner.includes('S-');
- let base = inner.slice(inner.lastIndexOf('-') + 1);
- if (shift && base.length === 1) {
- base = base.toUpperCase();
- } else if (!shift && base.length === 1) {
- base = base.toLowerCase();
- }
- return {
- key: base,
- shiftKey: inner.includes('S-'),
- ctrlKey: inner.includes('C-'),
- altKey: inner.includes('A-'),
- metaKey: inner.includes('M-'),
- };
- }
- return {
- key: key,
- shiftKey: key.toLowerCase() !== key,
- ctrlKey: false,
- altKey: false,
- metaKey: false,
- };
-};
-
-const fromMapKeys = (keys) => {
- const fromMapKeysRecursive = (remainings, mappedKeys) => {
- if (remainings.length === 0) {
- return mappedKeys;
- }
-
- let nextPos = 1;
- if (remainings.startsWith('<')) {
- let ltPos = remainings.indexOf('>');
- if (ltPos > 0) {
- nextPos = ltPos + 1;
- }
- }
-
- return fromMapKeysRecursive(
- remainings.slice(nextPos),
- mappedKeys.concat([fromMapKey(remainings.slice(0, nextPos))])
- );
- };
-
- return fromMapKeysRecursive(keys, []);
-};
-
-const equals = (e1, e2) => {
- return e1.key === e2.key &&
- e1.ctrlKey === e2.ctrlKey &&
- e1.metaKey === e2.metaKey &&
- e1.altKey === e2.altKey &&
- e1.shiftKey === e2.shiftKey;
-};
-
-export { fromKeyboardEvent, fromMapKey, fromMapKeys, equals };
diff --git a/src/shared/utils/keys.ts b/src/shared/utils/keys.ts
new file mode 100644
index 0000000..f024069
--- /dev/null
+++ b/src/shared/utils/keys.ts
@@ -0,0 +1,89 @@
+const modifiedKeyName = (name) => {
+ if (name === ' ') {
+ return 'Space';
+ }
+ if (name.length === 1) {
+ return name;
+ } else if (name === 'Escape') {
+ return 'Esc';
+ }
+ return name;
+};
+
+const fromKeyboardEvent = (e) => {
+ let key = modifiedKeyName(e.key);
+ let shift = e.shiftKey;
+ if (key.length === 1 && key.toUpperCase() === key.toLowerCase()) {
+ // make shift false for symbols to enable key bindings by symbold keys.
+ // But this limits key bindings by symbol keys with Shift (such as Shift+$>.
+ shift = false;
+ }
+
+ return {
+ key: modifiedKeyName(e.key),
+ shiftKey: shift,
+ ctrlKey: e.ctrlKey,
+ altKey: e.altKey,
+ metaKey: e.metaKey,
+ };
+};
+
+const fromMapKey = (key) => {
+ if (key.startsWith('<') && key.endsWith('>')) {
+ let inner = key.slice(1, -1);
+ let shift = inner.includes('S-');
+ let base = inner.slice(inner.lastIndexOf('-') + 1);
+ if (shift && base.length === 1) {
+ base = base.toUpperCase();
+ } else if (!shift && base.length === 1) {
+ base = base.toLowerCase();
+ }
+ return {
+ key: base,
+ shiftKey: inner.includes('S-'),
+ ctrlKey: inner.includes('C-'),
+ altKey: inner.includes('A-'),
+ metaKey: inner.includes('M-'),
+ };
+ }
+ return {
+ key: key,
+ shiftKey: key.toLowerCase() !== key,
+ ctrlKey: false,
+ altKey: false,
+ metaKey: false,
+ };
+};
+
+const fromMapKeys = (keys) => {
+ const fromMapKeysRecursive = (remainings, mappedKeys) => {
+ if (remainings.length === 0) {
+ return mappedKeys;
+ }
+
+ let nextPos = 1;
+ if (remainings.startsWith('<')) {
+ let ltPos = remainings.indexOf('>');
+ if (ltPos > 0) {
+ nextPos = ltPos + 1;
+ }
+ }
+
+ return fromMapKeysRecursive(
+ remainings.slice(nextPos),
+ mappedKeys.concat([fromMapKey(remainings.slice(0, nextPos))])
+ );
+ };
+
+ return fromMapKeysRecursive(keys, []);
+};
+
+const equals = (e1, e2) => {
+ return e1.key === e2.key &&
+ e1.ctrlKey === e2.ctrlKey &&
+ e1.metaKey === e2.metaKey &&
+ e1.altKey === e2.altKey &&
+ e1.shiftKey === e2.shiftKey;
+};
+
+export { fromKeyboardEvent, fromMapKey, fromMapKeys, equals };
diff --git a/src/shared/utils/re.js b/src/shared/utils/re.js
deleted file mode 100644
index 7db9091..0000000
--- a/src/shared/utils/re.js
+++ /dev/null
@@ -1,6 +0,0 @@
-const fromWildcard = (pattern) => {
- let regexStr = '^' + pattern.replace(/\*/g, '.*') + '$';
- return new RegExp(regexStr);
-};
-
-export { fromWildcard };
diff --git a/src/shared/utils/re.ts b/src/shared/utils/re.ts
new file mode 100644
index 0000000..7db9091
--- /dev/null
+++ b/src/shared/utils/re.ts
@@ -0,0 +1,6 @@
+const fromWildcard = (pattern) => {
+ let regexStr = '^' + pattern.replace(/\*/g, '.*') + '$';
+ return new RegExp(regexStr);
+};
+
+export { fromWildcard };
--
cgit v1.2.3
From 0cffb09e249832291be73be039dc1b9bb38115f9 Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka
Date: Tue, 30 Apr 2019 21:50:46 +0900
Subject: Types on src/share
---
src/shared/blacklists.ts | 4 ++--
src/shared/messages.ts | 15 ++++++++++----
src/shared/operations.ts | 4 +++-
src/shared/settings/properties.ts | 6 +++---
src/shared/settings/storage.ts | 6 +++---
src/shared/settings/validator.ts | 12 ++++++------
src/shared/settings/values.ts | 24 +++++++++++------------
src/shared/urls.ts | 6 +++---
src/shared/utils/dom.ts | 41 ++++++++++++++++++++++++++-------------
src/shared/utils/keys.ts | 22 +++++++++++++++------
src/shared/utils/re.ts | 2 +-
11 files changed, 88 insertions(+), 54 deletions(-)
(limited to 'src')
diff --git a/src/shared/blacklists.ts b/src/shared/blacklists.ts
index 61720c3..61ee4de 100644
--- a/src/shared/blacklists.ts
+++ b/src/shared/blacklists.ts
@@ -1,6 +1,6 @@
-import * as re from 'shared/utils/re';
+import * as re from './utils/re';
-const includes = (blacklist, url) => {
+const includes = (blacklist: string[], url: string): boolean => {
let u = new URL(url);
return blacklist.some((item) => {
if (!item.includes('/')) {
diff --git a/src/shared/messages.ts b/src/shared/messages.ts
index ddf3368..2bc12d8 100644
--- a/src/shared/messages.ts
+++ b/src/shared/messages.ts
@@ -1,5 +1,8 @@
-const onWebMessage = (listener) => {
- window.addEventListener('message', (event) => {
+type WebMessageSender = Window | MessagePort | ServiceWorker | null;
+type WebMessageListener = (msg: any, sender: WebMessageSender | null) => void;
+
+const onWebMessage = (listener: WebMessageListener) => {
+ window.addEventListener('message', (event: MessageEvent) => {
let sender = event.source;
let message = null;
try {
@@ -12,11 +15,15 @@ const onWebMessage = (listener) => {
});
};
-const onBackgroundMessage = (listener) => {
+const onBackgroundMessage = (
+ listener: (msg: any, sender: browser.runtime.MessageSender,
+) => void) => {
browser.runtime.onMessage.addListener(listener);
};
-const onMessage = (listener) => {
+const onMessage = (
+ listener: (msg: any, sender: WebMessageSender | browser.runtime.MessageSender,
+) => void) => {
onWebMessage(listener);
onBackgroundMessage(listener);
};
diff --git a/src/shared/operations.ts b/src/shared/operations.ts
index 8674f4d..d59723e 100644
--- a/src/shared/operations.ts
+++ b/src/shared/operations.ts
@@ -1,4 +1,4 @@
-export default {
+const operations: { [key: string]: string } = {
// Hide console, or cancel some user actions
CANCEL: 'cancel',
@@ -76,3 +76,5 @@ export default {
MARK_SET_PREFIX: 'mark.set.prefix',
MARK_JUMP_PREFIX: 'mark.jump.prefix',
};
+
+export default operations;
diff --git a/src/shared/settings/properties.ts b/src/shared/settings/properties.ts
index f8e61a0..7d037df 100644
--- a/src/shared/settings/properties.ts
+++ b/src/shared/settings/properties.ts
@@ -2,20 +2,20 @@
// mystr: 'string',
// mynum: 'number',
// mybool: 'boolean',
-const types = {
+const types: { [key: string]: string } = {
hintchars: 'string',
smoothscroll: 'boolean',
complete: 'string',
};
// describe default values of a property
-const defaults = {
+const defaults: { [key: string]: string | number | boolean } = {
hintchars: 'abcdefghijklmnopqrstuvwxyz',
smoothscroll: false,
complete: 'sbh',
};
-const docs = {
+const docs: { [key: string]: string } = {
hintchars: 'hint characters on follow mode',
smoothscroll: 'smooth scroll',
complete: 'which are completed at the open page',
diff --git a/src/shared/settings/storage.ts b/src/shared/settings/storage.ts
index 5dce3b0..90a3a66 100644
--- a/src/shared/settings/storage.ts
+++ b/src/shared/settings/storage.ts
@@ -1,12 +1,12 @@
import DefaultSettings from './default';
import * as settingsValues from './values';
-const loadRaw = async() => {
+const loadRaw = async(): Promise => {
let { settings } = await browser.storage.local.get('settings');
if (!settings) {
return DefaultSettings;
}
- return { ...DefaultSettings, ...settings };
+ return { ...DefaultSettings, ...settings as object };
};
const loadValue = async() => {
@@ -23,7 +23,7 @@ const loadValue = async() => {
return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value };
};
-const save = (settings) => {
+const save = (settings: any): Promise => {
return browser.storage.local.set({
settings,
});
diff --git a/src/shared/settings/validator.ts b/src/shared/settings/validator.ts
index a800a52..0483931 100644
--- a/src/shared/settings/validator.ts
+++ b/src/shared/settings/validator.ts
@@ -1,4 +1,4 @@
-import operations from 'shared/operations';
+import operations from '../operations';
import * as properties from './properties';
const VALID_TOP_KEYS = ['keymaps', 'search', 'blacklist', 'properties'];
@@ -6,7 +6,7 @@ const VALID_OPERATION_VALUES = Object.keys(operations).map((key) => {
return operations[key];
});
-const validateInvalidTopKeys = (settings) => {
+const validateInvalidTopKeys = (settings: any): void => {
let invalidKey = Object.keys(settings).find((key) => {
return !VALID_TOP_KEYS.includes(key);
});
@@ -15,7 +15,7 @@ const validateInvalidTopKeys = (settings) => {
}
};
-const validateKeymaps = (keymaps) => {
+const validateKeymaps = (keymaps: any): void => {
for (let key of Object.keys(keymaps)) {
let value = keymaps[key];
if (!VALID_OPERATION_VALUES.includes(value.type)) {
@@ -24,7 +24,7 @@ const validateKeymaps = (keymaps) => {
}
};
-const validateSearch = (search) => {
+const validateSearch = (search: any): void => {
let engines = search.engines;
for (let key of Object.keys(engines)) {
if ((/\s/).test(key)) {
@@ -49,7 +49,7 @@ const validateSearch = (search) => {
}
};
-const validateProperties = (props) => {
+const validateProperties = (props: any): void => {
for (let name of Object.keys(props)) {
if (!properties.types[name]) {
throw new Error(`Unknown property name: "${name}"`);
@@ -60,7 +60,7 @@ const validateProperties = (props) => {
}
};
-const validate = (settings) => {
+const validate = (settings: any): void => {
validateInvalidTopKeys(settings);
if (settings.keymaps) {
validateKeymaps(settings.keymaps);
diff --git a/src/shared/settings/values.ts b/src/shared/settings/values.ts
index 9828af6..cb6a668 100644
--- a/src/shared/settings/values.ts
+++ b/src/shared/settings/values.ts
@@ -1,6 +1,6 @@
import * as properties from './properties';
-const operationFromFormName = (name) => {
+const operationFromFormName = (name: string): any => {
let [type, argStr] = name.split('?');
let args = {};
if (argStr) {
@@ -9,7 +9,7 @@ const operationFromFormName = (name) => {
return { type, ...args };
};
-const operationToFormName = (op) => {
+const operationToFormName = (op: any): string => {
let type = op.type;
let args = { ...op };
delete args.type;
@@ -20,12 +20,12 @@ const operationToFormName = (op) => {
return op.type + '?' + JSON.stringify(args);
};
-const valueFromJson = (json) => {
+const valueFromJson = (json: string): object => {
return JSON.parse(json);
};
-const valueFromForm = (form) => {
- let keymaps = undefined;
+const valueFromForm = (form: any): object => {
+ let keymaps: any = undefined;
if (form.keymaps) {
keymaps = {};
for (let name of Object.keys(form.keymaps)) {
@@ -34,7 +34,7 @@ const valueFromForm = (form) => {
}
}
- let search = undefined;
+ let search: any = undefined;
if (form.search) {
search = { default: form.search.default };
@@ -54,12 +54,12 @@ const valueFromForm = (form) => {
};
};
-const jsonFromValue = (value) => {
+const jsonFromValue = (value: any): string => {
return JSON.stringify(value, undefined, 2);
};
-const formFromValue = (value, allowedOps) => {
- let keymaps = undefined;
+const formFromValue = (value: any, allowedOps: any[]): any => {
+ let keymaps: any = undefined;
if (value.keymaps) {
let allowedSet = new Set(allowedOps);
@@ -73,7 +73,7 @@ const formFromValue = (value, allowedOps) => {
}
}
- let search = undefined;
+ let search: any = undefined;
if (value.search) {
search = { default: value.search.default };
if (value.search.engines) {
@@ -93,11 +93,11 @@ const formFromValue = (value, allowedOps) => {
};
};
-const jsonFromForm = (form) => {
+const jsonFromForm = (form: any): string => {
return jsonFromValue(valueFromForm(form));
};
-const formFromJson = (json, allowedOps) => {
+const formFromJson = (json: string, allowedOps: any[]): any => {
let value = valueFromJson(json);
return formFromValue(value, allowedOps);
};
diff --git a/src/shared/urls.ts b/src/shared/urls.ts
index 94b1220..18349c8 100644
--- a/src/shared/urls.ts
+++ b/src/shared/urls.ts
@@ -1,11 +1,11 @@
-const trimStart = (str) => {
+const trimStart = (str: string): string => {
// NOTE String.trimStart is available on Firefox 61
return str.replace(/^\s+/, '');
};
const SUPPORTED_PROTOCOLS = ['http:', 'https:', 'ftp:', 'mailto:', 'about:'];
-const searchUrl = (keywords, searchSettings) => {
+const searchUrl = (keywords: string, searchSettings: any): string => {
try {
let u = new URL(keywords);
if (SUPPORTED_PROTOCOLS.includes(u.protocol.toLowerCase())) {
@@ -28,7 +28,7 @@ const searchUrl = (keywords, searchSettings) => {
return template.replace('{}', encodeURIComponent(query));
};
-const normalizeUrl = (url) => {
+const normalizeUrl = (url: string): string => {
try {
let u = new URL(url);
if (SUPPORTED_PROTOCOLS.includes(u.protocol.toLowerCase())) {
diff --git a/src/shared/utils/dom.ts b/src/shared/utils/dom.ts
index 974d534..c1f2190 100644
--- a/src/shared/utils/dom.ts
+++ b/src/shared/utils/dom.ts
@@ -1,16 +1,24 @@
-const isContentEditable = (element) => {
- return element.hasAttribute('contenteditable') && (
- element.getAttribute('contenteditable').toLowerCase() === 'true' ||
- element.getAttribute('contenteditable').toLowerCase() === ''
- );
+const isContentEditable = (element: Element): boolean => {
+ let value = element.getAttribute('contenteditable');
+ if (value === null) {
+ return false;
+ }
+ return value.toLowerCase() === 'true' || value.toLowerCase() === '';
};
-const rectangleCoordsRect = (coords) => {
+interface Rect {
+ left: number;
+ top: number;
+ right: number;
+ bottom: number;
+}
+
+const rectangleCoordsRect = (coords: string): Rect => {
let [left, top, right, bottom] = coords.split(',').map(n => Number(n));
return { left, top, right, bottom };
};
-const circleCoordsRect = (coords) => {
+const circleCoordsRect = (coords: string): Rect => {
let [x, y, r] = coords.split(',').map(n => Number(n));
return {
left: x - r,
@@ -20,7 +28,7 @@ const circleCoordsRect = (coords) => {
};
};
-const polygonCoordsRect = (coords) => {
+const polygonCoordsRect = (coords: string): Rect => {
let params = coords.split(',');
let minx = Number(params[0]),
maxx = Number(params[0]),
@@ -46,18 +54,24 @@ const polygonCoordsRect = (coords) => {
return { left: minx, top: miny, right: maxx, bottom: maxy };
};
-const viewportRect = (e) => {
+const viewportRect = (e: Element): Rect => {
if (e.tagName !== 'AREA') {
return e.getBoundingClientRect();
}
- let mapElement = e.parentNode;
- let imgElement = document.querySelector(`img[usemap="#${mapElement.name}"]`);
+ let mapElement = e.parentNode as HTMLMapElement;
+ let imgElement = document.querySelector(
+ `img[usemap="#${mapElement.name}"]`
+ ) as HTMLImageElement;
let {
left: mapLeft,
top: mapTop
} = imgElement.getBoundingClientRect();
let coords = e.getAttribute('coords');
+ if (!coords) {
+ return e.getBoundingClientRect();
+ }
+
let rect = { left: 0, top: 0, right: 0, bottom: 0 };
switch (e.getAttribute('shape')) {
case 'rect':
@@ -81,7 +95,7 @@ const viewportRect = (e) => {
};
};
-const isVisible = (element) => {
+const isVisible = (element: Element): boolean => {
let rect = element.getBoundingClientRect();
let style = window.getComputedStyle(element);
@@ -94,7 +108,8 @@ const isVisible = (element) => {
if (window.innerWidth < rect.left && window.innerHeight < rect.top) {
return false;
}
- if (element.nodeName === 'INPUT' && element.type.toLowerCase() === 'hidden') {
+ if (element instanceof HTMLInputElement &&
+ element.type.toLowerCase() === 'hidden') {
return false;
}
diff --git a/src/shared/utils/keys.ts b/src/shared/utils/keys.ts
index f024069..d9abef7 100644
--- a/src/shared/utils/keys.ts
+++ b/src/shared/utils/keys.ts
@@ -1,4 +1,12 @@
-const modifiedKeyName = (name) => {
+interface Key {
+ key: string;
+ shiftKey: boolean | undefined;
+ ctrlKey: boolean | undefined;
+ altKey: boolean | undefined;
+ metaKey: boolean | undefined;
+}
+
+const modifiedKeyName = (name: string): string => {
if (name === ' ') {
return 'Space';
}
@@ -10,7 +18,7 @@ const modifiedKeyName = (name) => {
return name;
};
-const fromKeyboardEvent = (e) => {
+const fromKeyboardEvent = (e: KeyboardEvent): Key => {
let key = modifiedKeyName(e.key);
let shift = e.shiftKey;
if (key.length === 1 && key.toUpperCase() === key.toLowerCase()) {
@@ -28,7 +36,7 @@ const fromKeyboardEvent = (e) => {
};
};
-const fromMapKey = (key) => {
+const fromMapKey = (key: string): Key => {
if (key.startsWith('<') && key.endsWith('>')) {
let inner = key.slice(1, -1);
let shift = inner.includes('S-');
@@ -55,8 +63,10 @@ const fromMapKey = (key) => {
};
};
-const fromMapKeys = (keys) => {
- const fromMapKeysRecursive = (remainings, mappedKeys) => {
+const fromMapKeys = (keys: string): Key[] => {
+ const fromMapKeysRecursive = (
+ remainings: string, mappedKeys: Key[],
+ ): Key[] => {
if (remainings.length === 0) {
return mappedKeys;
}
@@ -78,7 +88,7 @@ const fromMapKeys = (keys) => {
return fromMapKeysRecursive(keys, []);
};
-const equals = (e1, e2) => {
+const equals = (e1: Key, e2: Key): boolean => {
return e1.key === e2.key &&
e1.ctrlKey === e2.ctrlKey &&
e1.metaKey === e2.metaKey &&
diff --git a/src/shared/utils/re.ts b/src/shared/utils/re.ts
index 7db9091..34f4fa6 100644
--- a/src/shared/utils/re.ts
+++ b/src/shared/utils/re.ts
@@ -1,4 +1,4 @@
-const fromWildcard = (pattern) => {
+const fromWildcard = (pattern: string): RegExp => {
let regexStr = '^' + pattern.replace(/\*/g, '.*') + '$';
return new RegExp(regexStr);
};
--
cgit v1.2.3
From 678020a3a27713e77ec8d74483122b4258fbc829 Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka
Date: Wed, 1 May 2019 11:04:24 +0900
Subject: Types on src/background
---
.../controllers/AddonEnabledController.ts | 4 +-
src/background/controllers/CommandController.ts | 14 ++-
src/background/controllers/FindController.ts | 6 +-
src/background/controllers/LinkController.ts | 12 ++-
src/background/controllers/MarkController.ts | 10 +-
src/background/controllers/OperationController.ts | 13 ++-
src/background/controllers/SettingController.ts | 8 +-
src/background/controllers/VersionController.ts | 6 +-
src/background/controllers/version.ts | 13 ---
src/background/domains/CommandDocs.ts | 3 +-
src/background/domains/CompletionGroup.ts | 17 +---
src/background/domains/CompletionItem.ts | 29 ++----
src/background/domains/Completions.ts | 27 ------
src/background/domains/GlobalMark.ts | 28 +-----
src/background/infrastructures/ConsoleClient.ts | 10 +-
.../infrastructures/ContentMessageClient.ts | 12 +--
.../infrastructures/ContentMessageListener.ts | 78 ++++++++++-----
src/background/infrastructures/MemoryStorage.ts | 6 +-
src/background/presenters/IndicatorPresenter.ts | 4 +-
src/background/presenters/NotifyPresenter.ts | 8 +-
src/background/presenters/TabPresenter.ts | 44 +++++----
src/background/presenters/WindowPresenter.ts | 2 +-
src/background/repositories/BookmarkRepository.ts | 4 +-
.../repositories/BrowserSettingRepository.ts | 2 +-
.../repositories/CompletionsRepository.ts | 14 ++-
src/background/repositories/FindRepository.ts | 6 +-
src/background/repositories/MarkRepository.ts | 8 +-
.../repositories/PersistentSettingRepository.ts | 2 +-
src/background/repositories/SettingRepository.ts | 8 +-
src/background/repositories/VersionRepository.ts | 10 --
src/background/usecases/AddonEnabledUseCase.ts | 18 +++-
src/background/usecases/CommandUseCase.ts | 52 ++++++----
src/background/usecases/CompletionsUseCase.ts | 107 ++++++++++++---------
src/background/usecases/ConsoleUseCase.ts | 40 ++++----
src/background/usecases/FindUseCase.ts | 14 ++-
src/background/usecases/LinkUseCase.ts | 8 +-
src/background/usecases/MarkUseCase.ts | 20 ++--
src/background/usecases/SettingUseCase.ts | 8 +-
src/background/usecases/TabSelectUseCase.ts | 24 ++---
src/background/usecases/TabUseCase.ts | 36 ++++---
src/background/usecases/VersionUseCase.ts | 10 +-
src/background/usecases/ZoomUseCase.ts | 26 ++---
src/background/usecases/filters.ts | 38 ++++----
src/background/usecases/parsers.ts | 10 +-
src/content/scrolls.ts | 10 --
test/background/domains/GlobalMark.test.ts | 11 ---
test/background/repositories/Mark.test.ts | 3 +-
test/background/repositories/Version.ts | 34 -------
48 files changed, 446 insertions(+), 431 deletions(-)
delete mode 100644 src/background/controllers/version.ts
delete mode 100644 src/background/domains/Completions.ts
delete mode 100644 src/background/repositories/VersionRepository.ts
delete mode 100644 test/background/domains/GlobalMark.test.ts
delete mode 100644 test/background/repositories/Version.ts
(limited to 'src')
diff --git a/src/background/controllers/AddonEnabledController.ts b/src/background/controllers/AddonEnabledController.ts
index 9a3a521..251af25 100644
--- a/src/background/controllers/AddonEnabledController.ts
+++ b/src/background/controllers/AddonEnabledController.ts
@@ -1,11 +1,13 @@
import AddonEnabledUseCase from '../usecases/AddonEnabledUseCase';
export default class AddonEnabledController {
+ private addonEnabledUseCase: AddonEnabledUseCase;
+
constructor() {
this.addonEnabledUseCase = new AddonEnabledUseCase();
}
- indicate(enabled) {
+ indicate(enabled: boolean): Promise {
return this.addonEnabledUseCase.indicate(enabled);
}
}
diff --git a/src/background/controllers/CommandController.ts b/src/background/controllers/CommandController.ts
index b113709..f3a6b7f 100644
--- a/src/background/controllers/CommandController.ts
+++ b/src/background/controllers/CommandController.ts
@@ -1,19 +1,23 @@
import CompletionsUseCase from '../usecases/CompletionsUseCase';
import CommandUseCase from '../usecases/CommandUseCase';
-import Completions from '../domains/Completions';
+import CompletionGroup from '../domains/CompletionGroup';
-const trimStart = (str) => {
+const trimStart = (str: string): string => {
// NOTE String.trimStart is available on Firefox 61
return str.replace(/^\s+/, '');
};
export default class CommandController {
+ private completionsUseCase: CompletionsUseCase;
+
+ private commandIndicator: CommandUseCase;
+
constructor() {
this.completionsUseCase = new CompletionsUseCase();
this.commandIndicator = new CommandUseCase();
}
- getCompletions(line) {
+ getCompletions(line: string): Promise {
let trimmed = trimStart(line);
let words = trimmed.split(/ +/);
let name = words[0];
@@ -45,11 +49,11 @@ export default class CommandController {
case 'set':
return this.completionsUseCase.querySet(name, keywords);
}
- return Promise.resolve(Completions.empty());
+ return Promise.resolve([]);
}
// eslint-disable-next-line complexity
- exec(line) {
+ exec(line: string): Promise {
let trimmed = trimStart(line);
let words = trimmed.split(/ +/);
let name = words[0];
diff --git a/src/background/controllers/FindController.ts b/src/background/controllers/FindController.ts
index caeff98..28959e2 100644
--- a/src/background/controllers/FindController.ts
+++ b/src/background/controllers/FindController.ts
@@ -1,15 +1,17 @@
import FindUseCase from '../usecases/FindUseCase';
export default class FindController {
+ private findUseCase: FindUseCase;
+
constructor() {
this.findUseCase = new FindUseCase();
}
- getKeyword() {
+ getKeyword(): Promise {
return this.findUseCase.getKeyword();
}
- setKeyword(keyword) {
+ setKeyword(keyword: string): Promise {
return this.findUseCase.setKeyword(keyword);
}
}
diff --git a/src/background/controllers/LinkController.ts b/src/background/controllers/LinkController.ts
index 7e395b1..707b28a 100644
--- a/src/background/controllers/LinkController.ts
+++ b/src/background/controllers/LinkController.ts
@@ -1,15 +1,19 @@
import LinkUseCase from '../usecases/LinkUseCase';
export default class LinkController {
+ private linkUseCase: LinkUseCase;
+
constructor() {
this.linkUseCase = new LinkUseCase();
}
- openToTab(url, tabId) {
- this.linkUseCase.openToTab(url, tabId);
+ openToTab(url: string, tabId: number): Promise {
+ return this.linkUseCase.openToTab(url, tabId);
}
- openNewTab(url, openerId, background) {
- this.linkUseCase.openNewTab(url, openerId, background);
+ openNewTab(
+ url: string, openerId: number, background: boolean,
+ ): Promise {
+ return this.linkUseCase.openNewTab(url, openerId, background);
}
}
diff --git a/src/background/controllers/MarkController.ts b/src/background/controllers/MarkController.ts
index 0478369..419a08b 100644
--- a/src/background/controllers/MarkController.ts
+++ b/src/background/controllers/MarkController.ts
@@ -1,15 +1,17 @@
import MarkUseCase from '../usecases/MarkUseCase';
export default class MarkController {
+ private markUseCase: MarkUseCase;
+
constructor() {
this.markUseCase = new MarkUseCase();
}
- setGlobal(key, x, y) {
- this.markUseCase.setGlobal(key, x, y);
+ setGlobal(key: string, x: number, y: number): Promise {
+ return this.markUseCase.setGlobal(key, x, y);
}
- jumpGlobal(key) {
- this.markUseCase.jumpGlobal(key);
+ jumpGlobal(key: string): Promise {
+ return this.markUseCase.jumpGlobal(key);
}
}
diff --git a/src/background/controllers/OperationController.ts b/src/background/controllers/OperationController.ts
index 416aa9c..4e9c106 100644
--- a/src/background/controllers/OperationController.ts
+++ b/src/background/controllers/OperationController.ts
@@ -6,6 +6,16 @@ import TabSelectUseCase from '../usecases/TabSelectUseCase';
import ZoomUseCase from '../usecases/ZoomUseCase';
export default class OperationController {
+ private findUseCase: FindUseCase;
+
+ private consoleUseCase: ConsoleUseCase;
+
+ private tabUseCase: TabUseCase;
+
+ private tabSelectUseCase: TabSelectUseCase;
+
+ private zoomUseCase: ZoomUseCase;
+
constructor() {
this.findUseCase = new FindUseCase();
this.consoleUseCase = new ConsoleUseCase();
@@ -15,7 +25,7 @@ export default class OperationController {
}
// eslint-disable-next-line complexity, max-lines-per-function
- exec(operation) {
+ exec(operation: any): Promise {
switch (operation.type) {
case operations.TAB_CLOSE:
return this.tabUseCase.close(false);
@@ -72,6 +82,7 @@ export default class OperationController {
case operations.CANCEL:
return this.consoleUseCase.hideConsole();
}
+ throw new Error('unknown operation: ' + operation.type);
}
}
diff --git a/src/background/controllers/SettingController.ts b/src/background/controllers/SettingController.ts
index e895d72..f8b7302 100644
--- a/src/background/controllers/SettingController.ts
+++ b/src/background/controllers/SettingController.ts
@@ -2,16 +2,20 @@ import SettingUseCase from '../usecases/SettingUseCase';
import ContentMessageClient from '../infrastructures/ContentMessageClient';
export default class SettingController {
+ private settingUseCase: SettingUseCase;
+
+ private contentMessageClient: ContentMessageClient;
+
constructor() {
this.settingUseCase = new SettingUseCase();
this.contentMessageClient = new ContentMessageClient();
}
- getSetting() {
+ getSetting(): any {
return this.settingUseCase.get();
}
- async reload() {
+ async reload(): Promise {
await this.settingUseCase.reload();
this.contentMessageClient.broadcastSettingsChanged();
}
diff --git a/src/background/controllers/VersionController.ts b/src/background/controllers/VersionController.ts
index c596f9b..f402ed0 100644
--- a/src/background/controllers/VersionController.ts
+++ b/src/background/controllers/VersionController.ts
@@ -1,11 +1,13 @@
import VersionUseCase from '../usecases/VersionUseCase';
export default class VersionController {
+ private versionUseCase: VersionUseCase;
+
constructor() {
this.versionUseCase = new VersionUseCase();
}
- notify() {
- this.versionUseCase.notify();
+ notify(): void {
+ return this.versionUseCase.notify();
}
}
diff --git a/src/background/controllers/version.ts b/src/background/controllers/version.ts
deleted file mode 100644
index ec0f634..0000000
--- a/src/background/controllers/version.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import VersionInteractor from '../usecases/version';
-
-export default class VersionController {
- constructor() {
- this.versionInteractor = new VersionInteractor();
- }
-
- notifyIfUpdated() {
- browser.runtime.onInstalled.addListener(() => {
- return this.versionInteractor.notify();
- });
- }
-}
diff --git a/src/background/domains/CommandDocs.ts b/src/background/domains/CommandDocs.ts
index 734c68e..25ea62a 100644
--- a/src/background/domains/CommandDocs.ts
+++ b/src/background/domains/CommandDocs.ts
@@ -8,5 +8,4 @@ export default {
bdeletes: 'Close all tabs matched by keywords',
quit: 'Close the current tab',
quitall: 'Close all tabs',
-};
-
+} as {[key: string]: string};
diff --git a/src/background/domains/CompletionGroup.ts b/src/background/domains/CompletionGroup.ts
index 1749d72..1eea7d8 100644
--- a/src/background/domains/CompletionGroup.ts
+++ b/src/background/domains/CompletionGroup.ts
@@ -1,14 +1,7 @@
-export default class CompletionGroup {
- constructor(name, items) {
- this.name0 = name;
- this.items0 = items;
- }
+import CompletionItem from './CompletionItem';
- get name() {
- return this.name0;
- }
-
- get items() {
- return this.items0;
- }
+export default interface CompletionGroup {
+ name: string;
+ items: CompletionItem[];
+ // eslint-disable-next-line semi
}
diff --git a/src/background/domains/CompletionItem.ts b/src/background/domains/CompletionItem.ts
index c7ad8a1..657efaa 100644
--- a/src/background/domains/CompletionItem.ts
+++ b/src/background/domains/CompletionItem.ts
@@ -1,24 +1,7 @@
-export default class CompletionItem {
- constructor({ caption, content, url, icon }) {
- this.caption0 = caption;
- this.content0 = content;
- this.url0 = url;
- this.icon0 = icon;
- }
-
- get caption() {
- return this.caption0;
- }
-
- get content() {
- return this.content0;
- }
-
- get url() {
- return this.url0;
- }
-
- get icon() {
- return this.icon0;
- }
+export default interface CompletionItem {
+ readonly caption?: string;
+ readonly content?: string;
+ readonly url?: string;
+ readonly icon?: string;
+ // eslint-disable-next-line semi
}
diff --git a/src/background/domains/Completions.ts b/src/background/domains/Completions.ts
deleted file mode 100644
index f399743..0000000
--- a/src/background/domains/Completions.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-export default class Completions {
- constructor(groups) {
- this.g = groups;
- }
-
- get groups() {
- return this.g;
- }
-
- serialize() {
- return this.groups.map(group => ({
- name: group.name,
- items: group.items.map(item => ({
- caption: item.caption,
- content: item.content,
- url: item.url,
- icon: item.icon,
- })),
- }));
- }
-
- static empty() {
- return EMPTY_COMPLETIONS;
- }
-}
-
-let EMPTY_COMPLETIONS = new Completions([]);
diff --git a/src/background/domains/GlobalMark.ts b/src/background/domains/GlobalMark.ts
index f0586f1..0964373 100644
--- a/src/background/domains/GlobalMark.ts
+++ b/src/background/domains/GlobalMark.ts
@@ -1,24 +1,6 @@
-export default class GlobalMark {
- constructor(tabId, url, x, y) {
- this.tabId0 = tabId;
- this.url0 = url;
- this.x0 = x;
- this.y0 = y;
- }
-
- get tabId() {
- return this.tabId0;
- }
-
- get url() {
- return this.url0;
- }
-
- get x() {
- return this.x0;
- }
-
- get y() {
- return this.y0;
- }
+export interface GlobalMark {
+ readonly tabId: number;
+ readonly url: string;
+ readonly x: number;
+ readonly y: number;
}
diff --git a/src/background/infrastructures/ConsoleClient.ts b/src/background/infrastructures/ConsoleClient.ts
index f691515..7ad5d24 100644
--- a/src/background/infrastructures/ConsoleClient.ts
+++ b/src/background/infrastructures/ConsoleClient.ts
@@ -1,34 +1,34 @@
import messages from '../../shared/messages';
export default class ConsoleClient {
- showCommand(tabId, command) {
+ showCommand(tabId: number, command: string): Promise {
return browser.tabs.sendMessage(tabId, {
type: messages.CONSOLE_SHOW_COMMAND,
command,
});
}
- showFind(tabId) {
+ showFind(tabId: number): Promise {
return browser.tabs.sendMessage(tabId, {
type: messages.CONSOLE_SHOW_FIND
});
}
- showInfo(tabId, message) {
+ showInfo(tabId: number, message: string): Promise {
return browser.tabs.sendMessage(tabId, {
type: messages.CONSOLE_SHOW_INFO,
text: message,
});
}
- showError(tabId, message) {
+ showError(tabId: number, message: string): Promise {
return browser.tabs.sendMessage(tabId, {
type: messages.CONSOLE_SHOW_ERROR,
text: message,
});
}
- hide(tabId) {
+ hide(tabId: number): Promise {
return browser.tabs.sendMessage(tabId, {
type: messages.CONSOLE_HIDE,
});
diff --git a/src/background/infrastructures/ContentMessageClient.ts b/src/background/infrastructures/ContentMessageClient.ts
index 0fab5a3..20057c7 100644
--- a/src/background/infrastructures/ContentMessageClient.ts
+++ b/src/background/infrastructures/ContentMessageClient.ts
@@ -1,10 +1,10 @@
import messages from '../../shared/messages';
export default class ContentMessageClient {
- async broadcastSettingsChanged() {
+ async broadcastSettingsChanged(): Promise {
let tabs = await browser.tabs.query({});
for (let tab of tabs) {
- if (tab.url.startsWith('about:')) {
+ if (!tab.id || tab.url && tab.url.startsWith('about:')) {
continue;
}
browser.tabs.sendMessage(tab.id, {
@@ -13,20 +13,20 @@ export default class ContentMessageClient {
}
}
- async getAddonEnabled(tabId) {
+ async getAddonEnabled(tabId: number): Promise {
let { enabled } = await browser.tabs.sendMessage(tabId, {
type: messages.ADDON_ENABLED_QUERY,
- });
+ }) as { enabled: boolean };
return enabled;
}
- toggleAddonEnabled(tabId) {
+ toggleAddonEnabled(tabId: number): Promise {
return browser.tabs.sendMessage(tabId, {
type: messages.ADDON_TOGGLE_ENABLED,
});
}
- scrollTo(tabId, x, y) {
+ scrollTo(tabId: number, x: number, y: number): Promise {
return browser.tabs.sendMessage(tabId, {
type: messages.TAB_SCROLL_TO,
x,
diff --git a/src/background/infrastructures/ContentMessageListener.ts b/src/background/infrastructures/ContentMessageListener.ts
index 5b0f62e..81d3232 100644
--- a/src/background/infrastructures/ContentMessageListener.ts
+++ b/src/background/infrastructures/ContentMessageListener.ts
@@ -1,4 +1,5 @@
import messages from '../../shared/messages';
+import CompletionGroup from '../domains/CompletionGroup';
import CommandController from '../controllers/CommandController';
import SettingController from '../controllers/SettingController';
import FindController from '../controllers/FindController';
@@ -8,6 +9,22 @@ import OperationController from '../controllers/OperationController';
import MarkController from '../controllers/MarkController';
export default class ContentMessageListener {
+ private settingController: SettingController;
+
+ private commandController: CommandController;
+
+ private findController: FindController;
+
+ private addonEnabledController: AddonEnabledController;
+
+ private linkController: LinkController;
+
+ private backgroundOperationController: OperationController;
+
+ private markController: MarkController;
+
+ private consolePorts: {[tabId: number]: browser.runtime.Port};
+
constructor() {
this.settingController = new SettingController();
this.commandController = new CommandController();
@@ -20,20 +37,28 @@ export default class ContentMessageListener {
this.consolePorts = {};
}
- run() {
- browser.runtime.onMessage.addListener((message, sender) => {
+ run(): void {
+ browser.runtime.onMessage.addListener((
+ message: any, sender: browser.runtime.MessageSender,
+ ) => {
try {
- let ret = this.onMessage(message, sender);
+ let ret = this.onMessage(message, sender.tab as browser.tabs.Tab);
if (!(ret instanceof Promise)) {
return {};
}
return ret.catch((e) => {
+ if (!sender.tab || !sender.tab.id) {
+ return;
+ }
return browser.tabs.sendMessage(sender.tab.id, {
type: messages.CONSOLE_SHOW_ERROR,
text: e.message,
});
});
} catch (e) {
+ if (!sender.tab || !sender.tab.id) {
+ return;
+ }
return browser.tabs.sendMessage(sender.tab.id, {
type: messages.CONSOLE_SHOW_ERROR,
text: e.message,
@@ -43,7 +68,7 @@ export default class ContentMessageListener {
browser.runtime.onConnect.addListener(this.onConnected.bind(this));
}
- onMessage(message, sender) {
+ onMessage(message: any, senderTab: browser.tabs.Tab): Promise | any {
switch (message.type) {
case messages.CONSOLE_QUERY_COMPLETIONS:
return this.onConsoleQueryCompletions(message.text);
@@ -59,7 +84,10 @@ export default class ContentMessageListener {
return this.onAddonEnabledResponse(message.enabled);
case messages.OPEN_URL:
return this.onOpenUrl(
- message.newTab, message.url, sender.tab.id, message.background);
+ message.newTab,
+ message.url,
+ senderTab.id as number,
+ message.background);
case messages.BACKGROUND_OPERATION:
return this.onBackgroundOperation(message.operation);
case messages.MARK_SET_GLOBAL:
@@ -67,56 +95,60 @@ export default class ContentMessageListener {
case messages.MARK_JUMP_GLOBAL:
return this.onMarkJumpGlobal(message.key);
case messages.CONSOLE_FRAME_MESSAGE:
- return this.onConsoleFrameMessage(sender.tab.id, message.message);
+ return this.onConsoleFrameMessage(
+ senderTab.id as number, message.message,
+ );
}
+ throw new Error('unsupported message: ' + message.type);
}
- async onConsoleQueryCompletions(line) {
+ async onConsoleQueryCompletions(line: string): Promise {
let completions = await this.commandController.getCompletions(line);
- return Promise.resolve(completions.serialize());
+ return Promise.resolve(completions);
}
- onConsoleEnterCommand(text) {
+ onConsoleEnterCommand(text: string): Promise {
return this.commandController.exec(text);
}
-
- onSettingsQuery() {
+ onSettingsQuery(): Promise {
return this.settingController.getSetting();
}
- onFindGetKeyword() {
+ onFindGetKeyword(): Promise {
return this.findController.getKeyword();
}
- onFindSetKeyword(keyword) {
+ onFindSetKeyword(keyword: string): Promise {
return this.findController.setKeyword(keyword);
}
- onAddonEnabledResponse(enabled) {
+ onAddonEnabledResponse(enabled: boolean): Promise {
return this.addonEnabledController.indicate(enabled);
}
- onOpenUrl(newTab, url, openerId, background) {
+ onOpenUrl(
+ newTab: boolean, url: string, openerId: number, background: boolean,
+ ): Promise {
if (newTab) {
return this.linkController.openNewTab(url, openerId, background);
}
return this.linkController.openToTab(url, openerId);
}
- onBackgroundOperation(operation) {
+ onBackgroundOperation(operation: any): Promise {
return this.backgroundOperationController.exec(operation);
}
- onMarkSetGlobal(key, x, y) {
+ onMarkSetGlobal(key: string, x: number, y: number): Promise {
return this.markController.setGlobal(key, x, y);
}
- onMarkJumpGlobal(key) {
+ onMarkJumpGlobal(key: string): Promise {
return this.markController.jumpGlobal(key);
}
- onConsoleFrameMessage(tabId, message) {
+ onConsoleFrameMessage(tabId: number, message: any): void {
let port = this.consolePorts[tabId];
if (!port) {
return;
@@ -124,12 +156,14 @@ export default class ContentMessageListener {
port.postMessage(message);
}
- onConnected(port) {
+ onConnected(port: browser.runtime.Port): void {
if (port.name !== 'vimvixen-console') {
return;
}
- let id = port.sender.tab.id;
- this.consolePorts[id] = port;
+ if (port.sender && port.sender.tab && port.sender.tab.id) {
+ let id = port.sender.tab.id;
+ this.consolePorts[id] = port;
+ }
}
}
diff --git a/src/background/infrastructures/MemoryStorage.ts b/src/background/infrastructures/MemoryStorage.ts
index 3a7e4f2..baf9ffa 100644
--- a/src/background/infrastructures/MemoryStorage.ts
+++ b/src/background/infrastructures/MemoryStorage.ts
@@ -1,7 +1,7 @@
-const db = {};
+const db: {[key: string]: any} = {};
export default class MemoryStorage {
- set(name, value) {
+ set(name: string, value: any): void {
let data = JSON.stringify(value);
if (typeof data === 'undefined') {
throw new Error('value is not serializable');
@@ -9,7 +9,7 @@ export default class MemoryStorage {
db[name] = data;
}
- get(name) {
+ get(name: string): any {
let data = db[name];
if (!data) {
return undefined;
diff --git a/src/background/presenters/IndicatorPresenter.ts b/src/background/presenters/IndicatorPresenter.ts
index 5737519..d9a615a 100644
--- a/src/background/presenters/IndicatorPresenter.ts
+++ b/src/background/presenters/IndicatorPresenter.ts
@@ -1,12 +1,12 @@
export default class IndicatorPresenter {
- indicate(enabled) {
+ indicate(enabled: boolean): Promise {
let path = enabled
? 'resources/enabled_32x32.png'
: 'resources/disabled_32x32.png';
return browser.browserAction.setIcon({ path });
}
- onClick(listener) {
+ onClick(listener: (arg: browser.tabs.Tab) => void): void {
browser.browserAction.onClicked.addListener(listener);
}
}
diff --git a/src/background/presenters/NotifyPresenter.ts b/src/background/presenters/NotifyPresenter.ts
index a81f227..c83c205 100644
--- a/src/background/presenters/NotifyPresenter.ts
+++ b/src/background/presenters/NotifyPresenter.ts
@@ -1,8 +1,12 @@
const NOTIFICATION_ID = 'vimvixen-update';
export default class NotifyPresenter {
- notify(title, message, onclick) {
- const listener = (id) => {
+ notify(
+ title: string,
+ message: string,
+ onclick: () => void,
+ ): Promise {
+ const listener = (id: string) => {
if (id !== NOTIFICATION_ID) {
return;
}
diff --git a/src/background/presenters/TabPresenter.ts b/src/background/presenters/TabPresenter.ts
index 744be39..33c6513 100644
--- a/src/background/presenters/TabPresenter.ts
+++ b/src/background/presenters/TabPresenter.ts
@@ -3,27 +3,29 @@ import MemoryStorage from '../infrastructures/MemoryStorage';
const CURRENT_SELECTED_KEY = 'tabs.current.selected';
const LAST_SELECTED_KEY = 'tabs.last.selected';
+type Tab = browser.tabs.Tab;
+
export default class TabPresenter {
- open(url, tabId) {
+ open(url: string, tabId?: number): Promise {
return browser.tabs.update(tabId, { url });
}
- create(url, opts) {
+ create(url: string, opts?: object): Promise {
return browser.tabs.create({ url, ...opts });
}
- async getCurrent() {
+ async getCurrent(): Promise {
let tabs = await browser.tabs.query({
active: true, currentWindow: true
});
return tabs[0];
}
- getAll() {
+ getAll(): Promise {
return browser.tabs.query({ currentWindow: true });
}
- async getLastSelectedId() {
+ async getLastSelectedId(): Promise {
let cache = new MemoryStorage();
let tabId = await cache.get(LAST_SELECTED_KEY);
if (tabId === null || typeof tabId === 'undefined') {
@@ -32,25 +34,25 @@ export default class TabPresenter {
return tabId;
}
- async getByKeyword(keyword, excludePinned = false) {
+ async getByKeyword(keyword: string, excludePinned = false): Promise {
let tabs = await browser.tabs.query({ currentWindow: true });
return tabs.filter((t) => {
- return t.url.toLowerCase().includes(keyword.toLowerCase()) ||
+ return t.url && t.url.toLowerCase().includes(keyword.toLowerCase()) ||
t.title && t.title.toLowerCase().includes(keyword.toLowerCase());
}).filter((t) => {
return !(excludePinned && t.pinned);
});
}
- select(tabId) {
+ select(tabId: number): Promise {
return browser.tabs.update(tabId, { active: true });
}
- remove(ids) {
+ remove(ids: number[]): Promise {
return browser.tabs.remove(ids);
}
- async reopen() {
+ async reopen(): Promise {
let window = await browser.windows.getCurrent();
let sessions = await browser.sessions.getRecentlyClosed();
let session = sessions.find((s) => {
@@ -59,39 +61,43 @@ export default class TabPresenter {
if (!session) {
return;
}
- if (session.tab) {
+ if (session.tab && session.tab.sessionId) {
return browser.sessions.restore(session.tab.sessionId);
}
- return browser.sessions.restore(session.window.sessionId);
+ if (session.window && session.window.sessionId) {
+ return browser.sessions.restore(session.window.sessionId);
+ }
}
- reload(tabId, cache) {
+ reload(tabId: number, cache: boolean): Promise {
return browser.tabs.reload(tabId, { bypassCache: cache });
}
- setPinned(tabId, pinned) {
+ setPinned(tabId: number, pinned: boolean): Promise {
return browser.tabs.update(tabId, { pinned });
}
- duplicate(id) {
+ duplicate(id: number): Promise {
return browser.tabs.duplicate(id);
}
- getZoom(tabId) {
+ getZoom(tabId: number): Promise {
return browser.tabs.getZoom(tabId);
}
- setZoom(tabId, factor) {
+ setZoom(tabId: number, factor: number): Promise {
return browser.tabs.setZoom(tabId, factor);
}
- onSelected(listener) {
+ onSelected(
+ listener: (arg: { tabId: number, windowId: number}) => void,
+ ): void {
browser.tabs.onActivated.addListener(listener);
}
}
let tabPresenter = new TabPresenter();
-tabPresenter.onSelected((tab) => {
+tabPresenter.onSelected((tab: any) => {
let cache = new MemoryStorage();
let lastId = cache.get(CURRENT_SELECTED_KEY);
diff --git a/src/background/presenters/WindowPresenter.ts b/src/background/presenters/WindowPresenter.ts
index a82c4a2..e04f258 100644
--- a/src/background/presenters/WindowPresenter.ts
+++ b/src/background/presenters/WindowPresenter.ts
@@ -1,5 +1,5 @@
export default class WindowPresenter {
- create(url) {
+ create(url: string): Promise {
return browser.windows.create({ url });
}
}
diff --git a/src/background/repositories/BookmarkRepository.ts b/src/background/repositories/BookmarkRepository.ts
index 99f7ec4..b4da509 100644
--- a/src/background/repositories/BookmarkRepository.ts
+++ b/src/background/repositories/BookmarkRepository.ts
@@ -1,5 +1,7 @@
export default class BookmarkRepository {
- async create(title, url) {
+ async create(
+ title: string, url: string
+ ): Promise {
let item = await browser.bookmarks.create({
type: 'bookmark',
title,
diff --git a/src/background/repositories/BrowserSettingRepository.ts b/src/background/repositories/BrowserSettingRepository.ts
index a9d2c06..48c72a5 100644
--- a/src/background/repositories/BrowserSettingRepository.ts
+++ b/src/background/repositories/BrowserSettingRepository.ts
@@ -1,7 +1,7 @@
import * as urls from '../../shared/urls';
export default class BrowserSettingRepository {
- async getHomepageUrls() {
+ async getHomepageUrls(): Promise {
let { value } = await browser.browserSettings.homepageOverride.get({});
return value.split('|').map(urls.normalizeUrl);
}
diff --git a/src/background/repositories/CompletionsRepository.ts b/src/background/repositories/CompletionsRepository.ts
index 1318d36..18af587 100644
--- a/src/background/repositories/CompletionsRepository.ts
+++ b/src/background/repositories/CompletionsRepository.ts
@@ -1,7 +1,13 @@
+type Tab = browser.tabs.Tab;
+type BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode;
+
export default class CompletionsRepository {
- async queryBookmarks(keywords) {
+ async queryBookmarks(keywords: string): Promise {
let items = await browser.bookmarks.search({ query: keywords });
return items.filter((item) => {
+ if (!item.url) {
+ return false;
+ }
let url = undefined;
try {
url = new URL(item.url);
@@ -12,17 +18,17 @@ export default class CompletionsRepository {
});
}
- queryHistories(keywords) {
+ queryHistories(keywords: string): Promise {
return browser.history.search({
text: keywords,
startTime: 0,
});
}
- async queryTabs(keywords, excludePinned) {
+ async queryTabs(keywords: string, excludePinned: boolean): Promise {
let tabs = await browser.tabs.query({ currentWindow: true });
return tabs.filter((t) => {
- return t.url.toLowerCase().includes(keywords.toLowerCase()) ||
+ return t.url && t.url.toLowerCase().includes(keywords.toLowerCase()) ||
t.title && t.title.toLowerCase().includes(keywords.toLowerCase());
}).filter((t) => {
return !(excludePinned && t.pinned);
diff --git a/src/background/repositories/FindRepository.ts b/src/background/repositories/FindRepository.ts
index 74ec914..bf286e6 100644
--- a/src/background/repositories/FindRepository.ts
+++ b/src/background/repositories/FindRepository.ts
@@ -3,15 +3,17 @@ import MemoryStorage from '../infrastructures/MemoryStorage';
const FIND_KEYWORD_KEY = 'find-keyword';
export default class FindRepository {
+ private cache: MemoryStorage;
+
constructor() {
this.cache = new MemoryStorage();
}
- getKeyword() {
+ getKeyword(): Promise {
return Promise.resolve(this.cache.get(FIND_KEYWORD_KEY));
}
- setKeyword(keyword) {
+ setKeyword(keyword: string): Promise {
this.cache.set(FIND_KEYWORD_KEY, keyword);
return Promise.resolve();
}
diff --git a/src/background/repositories/MarkRepository.ts b/src/background/repositories/MarkRepository.ts
index 282c712..69c85f6 100644
--- a/src/background/repositories/MarkRepository.ts
+++ b/src/background/repositories/MarkRepository.ts
@@ -4,21 +4,23 @@ import GlobalMark from '../domains/GlobalMark';
const MARK_KEY = 'mark';
export default class MarkRepository {
+ private cache: MemoryStorage;
+
constructor() {
this.cache = new MemoryStorage();
}
- getMark(key) {
+ getMark(key: string): Promise {
let marks = this.getOrEmptyMarks();
let data = marks[key];
if (!data) {
return Promise.resolve(undefined);
}
- let mark = new GlobalMark(data.tabId, data.url, data.x, data.y);
+ let mark = { tabId: data.tabId, url: data.url, x: data.x, y: data.y };
return Promise.resolve(mark);
}
- setMark(key, mark) {
+ setMark(key: string, mark: GlobalMark): Promise {
let marks = this.getOrEmptyMarks();
marks[key] = { tabId: mark.tabId, url: mark.url, x: mark.x, y: mark.y };
this.cache.set(MARK_KEY, marks);
diff --git a/src/background/repositories/PersistentSettingRepository.ts b/src/background/repositories/PersistentSettingRepository.ts
index 4cab107..3f2f4a1 100644
--- a/src/background/repositories/PersistentSettingRepository.ts
+++ b/src/background/repositories/PersistentSettingRepository.ts
@@ -1,7 +1,7 @@
import Setting from '../domains/Setting';
export default class SettingRepository {
- async load() {
+ async load(): Promise {
let { settings } = await browser.storage.local.get('settings');
if (!settings) {
return null;
diff --git a/src/background/repositories/SettingRepository.ts b/src/background/repositories/SettingRepository.ts
index c4667a9..15355ba 100644
--- a/src/background/repositories/SettingRepository.ts
+++ b/src/background/repositories/SettingRepository.ts
@@ -3,19 +3,21 @@ import MemoryStorage from '../infrastructures/MemoryStorage';
const CACHED_SETTING_KEY = 'setting';
export default class SettingRepository {
+ private cache: MemoryStorage;
+
constructor() {
this.cache = new MemoryStorage();
}
- get() {
+ get(): Promise {
return Promise.resolve(this.cache.get(CACHED_SETTING_KEY));
}
- update(value) {
+ update(value: any): any {
return this.cache.set(CACHED_SETTING_KEY, value);
}
- async setProperty(name, value) {
+ async setProperty(name: string, value: string): Promise {
let current = await this.get();
current.properties[name] = value;
return this.update(current);
diff --git a/src/background/repositories/VersionRepository.ts b/src/background/repositories/VersionRepository.ts
deleted file mode 100644
index 4c71d05..0000000
--- a/src/background/repositories/VersionRepository.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export default class VersionRepository {
- async get() {
- let { version } = await browser.storage.local.get('version');
- return version;
- }
-
- update(version) {
- return browser.storage.local.set({ version });
- }
-}
diff --git a/src/background/usecases/AddonEnabledUseCase.ts b/src/background/usecases/AddonEnabledUseCase.ts
index bb2c347..0a6fb03 100644
--- a/src/background/usecases/AddonEnabledUseCase.ts
+++ b/src/background/usecases/AddonEnabledUseCase.ts
@@ -3,10 +3,20 @@ import TabPresenter from '../presenters/TabPresenter';
import ContentMessageClient from '../infrastructures/ContentMessageClient';
export default class AddonEnabledUseCase {
+ private indicatorPresentor: IndicatorPresenter;
+
+ private tabPresenter: TabPresenter;
+
+ private contentMessageClient: ContentMessageClient;
+
constructor() {
this.indicatorPresentor = new IndicatorPresenter();
- this.indicatorPresentor.onClick(tab => this.onIndicatorClick(tab.id));
+ this.indicatorPresentor.onClick((tab) => {
+ if (tab.id) {
+ this.onIndicatorClick(tab.id);
+ }
+ });
this.tabPresenter = new TabPresenter();
this.tabPresenter.onSelected(info => this.onTabSelected(info.tabId));
@@ -14,15 +24,15 @@ export default class AddonEnabledUseCase {
this.contentMessageClient = new ContentMessageClient();
}
- indicate(enabled) {
+ indicate(enabled: boolean): Promise {
return this.indicatorPresentor.indicate(enabled);
}
- onIndicatorClick(tabId) {
+ onIndicatorClick(tabId: number): Promise {
return this.contentMessageClient.toggleAddonEnabled(tabId);
}
- async onTabSelected(tabId) {
+ async onTabSelected(tabId: number): Promise {
let enabled = await this.contentMessageClient.getAddonEnabled(tabId);
return this.indicatorPresentor.indicate(enabled);
}
diff --git a/src/background/usecases/CommandUseCase.ts b/src/background/usecases/CommandUseCase.ts
index 9ec46fe..e0e3ada 100644
--- a/src/background/usecases/CommandUseCase.ts
+++ b/src/background/usecases/CommandUseCase.ts
@@ -6,9 +6,21 @@ import SettingRepository from '../repositories/SettingRepository';
import BookmarkRepository from '../repositories/BookmarkRepository';
import ConsoleClient from '../infrastructures/ConsoleClient';
import ContentMessageClient from '../infrastructures/ContentMessageClient';
-import * as properties from 'shared/settings/properties';
+import * as properties from '../../shared/settings/properties';
export default class CommandIndicator {
+ private tabPresenter: TabPresenter;
+
+ private windowPresenter: WindowPresenter;
+
+ private settingRepository: SettingRepository;
+
+ private bookmarkRepository: BookmarkRepository;
+
+ private consoleClient: ConsoleClient;
+
+ private contentMessageClient: ContentMessageClient;
+
constructor() {
this.tabPresenter = new TabPresenter();
this.windowPresenter = new WindowPresenter();
@@ -19,34 +31,34 @@ export default class CommandIndicator {
this.contentMessageClient = new ContentMessageClient();
}
- async open(keywords) {
+ async open(keywords: string): Promise {
let url = await this.urlOrSearch(keywords);
return this.tabPresenter.open(url);
}
- async tabopen(keywords) {
+ async tabopen(keywords: string): Promise {
let url = await this.urlOrSearch(keywords);
return this.tabPresenter.create(url);
}
- async winopen(keywords) {
+ async winopen(keywords: string): Promise {
let url = await this.urlOrSearch(keywords);
return this.windowPresenter.create(url);
}
// eslint-disable-next-line max-statements
- async buffer(keywords) {
+ async buffer(keywords: string): Promise {
if (keywords.length === 0) {
return;
}
- if (!isNaN(keywords)) {
+ if (!isNaN(Number(keywords))) {
let tabs = await this.tabPresenter.getAll();
let index = parseInt(keywords, 10) - 1;
if (index < 0 || tabs.length <= index) {
throw new RangeError(`tab ${index + 1} does not exist`);
}
- return this.tabPresenter.select(tabs[index].id);
+ return this.tabPresenter.select(tabs[index].id as number);
} else if (keywords.trim() === '%') {
// Select current window
return;
@@ -66,13 +78,13 @@ export default class CommandIndicator {
}
for (let tab of tabs) {
if (tab.index > current.index) {
- return this.tabPresenter.select(tab.id);
+ return this.tabPresenter.select(tab.id as number);
}
}
- return this.tabPresenter.select(tabs[0].id);
+ return this.tabPresenter.select(tabs[0].id as number);
}
- async bdelete(force, keywords) {
+ async bdelete(force: boolean, keywords: string): Promise {
let excludePinned = !force;
let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned);
if (tabs.length === 0) {
@@ -80,35 +92,35 @@ export default class CommandIndicator {
} else if (tabs.length > 1) {
throw new Error('More than one match for ' + keywords);
}
- return this.tabPresenter.remove([tabs[0].id]);
+ return this.tabPresenter.remove([tabs[0].id as number]);
}
- async bdeletes(force, keywords) {
+ async bdeletes(force: boolean, keywords: string): Promise {
let excludePinned = !force;
let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned);
- let ids = tabs.map(tab => tab.id);
+ let ids = tabs.map(tab => tab.id as number);
return this.tabPresenter.remove(ids);
}
- async quit() {
+ async quit(): Promise {
let tab = await this.tabPresenter.getCurrent();
- return this.tabPresenter.remove([tab.id]);
+ return this.tabPresenter.remove([tab.id as number]);
}
- async quitAll() {
+ async quitAll(): Promise {
let tabs = await this.tabPresenter.getAll();
- let ids = tabs.map(tab => tab.id);
+ let ids = tabs.map(tab => tab.id as number);
this.tabPresenter.remove(ids);
}
- async addbookmark(title) {
+ async addbookmark(title: string): Promise {
let tab = await this.tabPresenter.getCurrent();
let item = await this.bookmarkRepository.create(title, tab.url);
let message = 'Saved current page: ' + item.url;
return this.consoleClient.showInfo(tab.id, message);
}
- async set(keywords) {
+ async set(keywords: string): Promise {
if (keywords.length === 0) {
return;
}
@@ -118,7 +130,7 @@ export default class CommandIndicator {
return this.contentMessageClient.broadcastSettingsChanged();
}
- async urlOrSearch(keywords) {
+ async urlOrSearch(keywords: string): Promise {
let settings = await this.settingRepository.get();
return urls.searchUrl(keywords, settings.search);
}
diff --git a/src/background/usecases/CompletionsUseCase.ts b/src/background/usecases/CompletionsUseCase.ts
index 7dc30ac..037d6eb 100644
--- a/src/background/usecases/CompletionsUseCase.ts
+++ b/src/background/usecases/CompletionsUseCase.ts
@@ -1,6 +1,5 @@
-import CompletionItem from '../domains/CompletionItem';
-import CompletionGroup from '../domains/CompletionGroup';
import Completions from '../domains/Completions';
+import CompletionGroup from '../domains/CompletionGroup';
import CommandDocs from '../domains/CommandDocs';
import CompletionsRepository from '../repositories/CompletionsRepository';
import * as filters from './filters';
@@ -10,14 +9,23 @@ import * as properties from '../../shared/settings/properties';
const COMPLETION_ITEM_LIMIT = 10;
+type Tab = browser.tabs.Tab;
+type HistoryItem = browser.history.HistoryItem;
+
export default class CompletionsUseCase {
+ private tabPresenter: TabPresenter;
+
+ private completionsRepository: CompletionsRepository;
+
+ private settingRepository: SettingRepository;
+
constructor() {
this.tabPresenter = new TabPresenter();
this.completionsRepository = new CompletionsRepository();
this.settingRepository = new SettingRepository();
}
- queryConsoleCommand(prefix) {
+ queryConsoleCommand(prefix: string): Promise {
let keys = Object.keys(CommandDocs);
let items = keys
.filter(name => name.startsWith(prefix))
@@ -28,16 +36,14 @@ export default class CompletionsUseCase {
}));
if (items.length === 0) {
- return Promise.resolve(Completions.empty());
+ return Promise.resolve([]);
}
- return Promise.resolve(
- new Completions([new CompletionGroup('Console Command', items)])
- );
+ return Promise.resolve([{ name: 'Console CompletionGroup', items }]);
}
- async queryOpen(name, keywords) {
+ async queryOpen(name: string, keywords: string): Promise {
let settings = await this.settingRepository.get();
- let groups = [];
+ let groups: CompletionGroup[] = [];
let complete = settings.properties.complete || properties.defaults.complete;
for (let c of complete) {
@@ -45,31 +51,31 @@ export default class CompletionsUseCase {
// eslint-disable-next-line no-await-in-loop
let engines = await this.querySearchEngineItems(name, keywords);
if (engines.length > 0) {
- groups.push(new CompletionGroup('Search Engines', engines));
+ groups.push({ name: 'Search Engines', items: engines });
}
} else if (c === 'h') {
// eslint-disable-next-line no-await-in-loop
let histories = await this.queryHistoryItems(name, keywords);
if (histories.length > 0) {
- groups.push(new CompletionGroup('History', histories));
+ groups.push({ name: 'History', items: histories });
}
} else if (c === 'b') {
// eslint-disable-next-line no-await-in-loop
let bookmarks = await this.queryBookmarkItems(name, keywords);
if (bookmarks.length > 0) {
- groups.push(new CompletionGroup('Bookmarks', bookmarks));
+ groups.push({ name: 'Bookmarks', items: bookmarks });
}
}
}
- return new Completions(groups);
+ return groups;
}
// eslint-disable-next-line max-statements
- async queryBuffer(name, keywords) {
+ async queryBuffer(name: string, keywords: string): Promise {
let lastId = await this.tabPresenter.getLastSelectedId();
let trimmed = keywords.trim();
- let tabs = [];
- if (trimmed.length > 0 && !isNaN(trimmed)) {
+ let tabs: Tab[] = [];
+ if (trimmed.length > 0 && !isNaN(Number(trimmed))) {
let all = await this.tabPresenter.getAll();
let index = parseInt(trimmed, 10) - 1;
if (index >= 0 && index < all.length) {
@@ -77,18 +83,18 @@ export default class CompletionsUseCase {
}
} else if (trimmed === '%') {
let all = await this.tabPresenter.getAll();
- let tab = all.find(t => t.active);
+ let tab = all.find(t => t.active) as Tab;
tabs = [tab];
} else if (trimmed === '#') {
if (typeof lastId !== 'undefined' && lastId !== null) {
let all = await this.tabPresenter.getAll();
- let tab = all.find(t => t.id === lastId);
+ let tab = all.find(t => t.id === lastId) as Tab;
tabs = [tab];
}
} else {
tabs = await this.completionsRepository.queryTabs(keywords, false);
}
- const flag = (tab) => {
+ const flag = (tab: Tab) => {
if (tab.active) {
return '%';
} else if (tab.id === lastId) {
@@ -96,87 +102,90 @@ export default class CompletionsUseCase {
}
return ' ';
};
- let items = tabs.map(tab => new CompletionItem({
+ let items = tabs.map(tab => ({
caption: tab.index + 1 + ': ' + flag(tab) + ' ' + tab.title,
content: name + ' ' + tab.title,
url: tab.url,
- icon: tab.favIconUrl
+ icon: tab.favIconUrl,
}));
if (items.length === 0) {
- return Promise.resolve(Completions.empty());
+ return Promise.resolve([]);
}
- return new Completions([new CompletionGroup('Buffers', items)]);
+ return [{ name: 'Buffers', items }];
}
- queryBdelete(name, keywords) {
+ queryBdelete(name: string, keywords: string): Promise {
return this.queryTabs(name, true, keywords);
}
- queryBdeleteForce(name, keywords) {
+ queryBdeleteForce(
+ name: string, keywords: string,
+ ): Promise {
return this.queryTabs(name, false, keywords);
}
- querySet(name, keywords) {
+ querySet(name: string, keywords: string): Promise {
let items = Object.keys(properties.docs).map((key) => {
if (properties.types[key] === 'boolean') {
return [
- new CompletionItem({
+ {
caption: key,
content: name + ' ' + key,
url: 'Enable ' + properties.docs[key],
- }),
- new CompletionItem({
+ }, {
caption: 'no' + key,
content: name + ' no' + key,
url: 'Disable ' + properties.docs[key],
- }),
+ }
];
}
return [
- new CompletionItem({
+ {
caption: key,
content: name + ' ' + key,
url: 'Set ' + properties.docs[key],
- })
+ }
];
});
- items = items.reduce((acc, val) => acc.concat(val), []);
- items = items.filter((item) => {
+ let flatten = items.reduce((acc, val) => acc.concat(val), []);
+ flatten = flatten.filter((item) => {
return item.caption.startsWith(keywords);
});
- if (items.length === 0) {
- return Promise.resolve(Completions.empty());
+ if (flatten.length === 0) {
+ return Promise.resolve([]);
}
return Promise.resolve(
- new Completions([new CompletionGroup('Properties', items)])
+ [{ name: 'Properties', items: flatten }],
);
}
- async queryTabs(name, excludePinned, args) {
+ async queryTabs(
+ name: string, excludePinned: boolean, args: string,
+ ): Promise {
let tabs = await this.completionsRepository.queryTabs(args, excludePinned);
- let items = tabs.map(tab => new CompletionItem({
+ let items = tabs.map(tab => ({
caption: tab.title,
content: name + ' ' + tab.title,
url: tab.url,
icon: tab.favIconUrl
}));
if (items.length === 0) {
- return Promise.resolve(Completions.empty());
+ return Promise.resolve([]);
}
- return new Completions([new CompletionGroup('Buffers', items)]);
+ return [{ name: 'Buffers', items }];
}
- async querySearchEngineItems(name, keywords) {
+ async querySearchEngineItems(name: string, keywords: string) {
let settings = await this.settingRepository.get();
let engines = Object.keys(settings.search.engines)
.filter(key => key.startsWith(keywords));
- return engines.map(key => new CompletionItem({
+ return engines.map(key => ({
caption: key,
content: name + ' ' + key,
}));
}
- async queryHistoryItems(name, keywords) {
+ async queryHistoryItems(name: string, keywords: string) {
let histories = await this.completionsRepository.queryHistories(keywords);
histories = [histories]
.map(filters.filterBlankTitle)
@@ -184,19 +193,21 @@ export default class CompletionsUseCase {
.map(filters.filterByTailingSlash)
.map(pages => filters.filterByPathname(pages, COMPLETION_ITEM_LIMIT))
.map(pages => filters.filterByOrigin(pages, COMPLETION_ITEM_LIMIT))[0]
- .sort((x, y) => x.visitCount < y.visitCount)
+ .sort((x: HistoryItem, y: HistoryItem) => {
+ return Number(x.visitCount) < Number(y.visitCount);
+ })
.slice(0, COMPLETION_ITEM_LIMIT);
- return histories.map(page => new CompletionItem({
+ return histories.map(page => ({
caption: page.title,
content: name + ' ' + page.url,
url: page.url
}));
}
- async queryBookmarkItems(name, keywords) {
+ async queryBookmarkItems(name: string, keywords: string) {
let bookmarks = await this.completionsRepository.queryBookmarks(keywords);
return bookmarks.slice(0, COMPLETION_ITEM_LIMIT)
- .map(page => new CompletionItem({
+ .map(page => ({
caption: page.title,
content: name + ' ' + page.url,
url: page.url
diff --git a/src/background/usecases/ConsoleUseCase.ts b/src/background/usecases/ConsoleUseCase.ts
index e8e5d4a..60c0439 100644
--- a/src/background/usecases/ConsoleUseCase.ts
+++ b/src/background/usecases/ConsoleUseCase.ts
@@ -2,60 +2,64 @@ import TabPresenter from '../presenters/TabPresenter';
import ConsoleClient from '../infrastructures/ConsoleClient';
export default class ConsoleUseCase {
+ private tabPresenter: TabPresenter;
+
+ private consoleClient: ConsoleClient;
+
constructor() {
this.tabPresenter = new TabPresenter();
this.consoleClient = new ConsoleClient();
}
- async showCommand() {
+ async showCommand(): Promise {
let tab = await this.tabPresenter.getCurrent();
- return this.consoleClient.showCommand(tab.id, '');
+ return this.consoleClient.showCommand(tab.id as number, '');
}
- async showOpenCommand(alter) {
+ async showOpenCommand(alter: boolean): Promise {
let tab = await this.tabPresenter.getCurrent();
let command = 'open ';
if (alter) {
- command += tab.url;
+ command += tab.url || '';
}
- return this.consoleClient.showCommand(tab.id, command);
+ return this.consoleClient.showCommand(tab.id as number, command);
}
- async showTabopenCommand(alter) {
+ async showTabopenCommand(alter: boolean): Promise {
let tab = await this.tabPresenter.getCurrent();
let command = 'tabopen ';
if (alter) {
- command += tab.url;
+ command += tab.url || '';
}
- return this.consoleClient.showCommand(tab.id, command);
+ return this.consoleClient.showCommand(tab.id as number, command);
}
- async showWinopenCommand(alter) {
+ async showWinopenCommand(alter: boolean): Promise {
let tab = await this.tabPresenter.getCurrent();
let command = 'winopen ';
if (alter) {
- command += tab.url;
+ command += tab.url || '';
}
- return this.consoleClient.showCommand(tab.id, command);
+ return this.consoleClient.showCommand(tab.id as number, command);
}
- async showBufferCommand() {
+ async showBufferCommand(): Promise {
let tab = await this.tabPresenter.getCurrent();
let command = 'buffer ';
- return this.consoleClient.showCommand(tab.id, command);
+ return this.consoleClient.showCommand(tab.id as number, command);
}
- async showAddbookmarkCommand(alter) {
+ async showAddbookmarkCommand(alter: boolean): Promise {
let tab = await this.tabPresenter.getCurrent();
let command = 'addbookmark ';
if (alter) {
- command += tab.title;
+ command += tab.title || '';
}
- return this.consoleClient.showCommand(tab.id, command);
+ return this.consoleClient.showCommand(tab.id as number, command);
}
- async hideConsole() {
+ async hideConsole(): Promise {
let tab = await this.tabPresenter.getCurrent();
- return this.consoleClient.hide(tab.id);
+ return this.consoleClient.hide(tab.id as number);
}
}
diff --git a/src/background/usecases/FindUseCase.ts b/src/background/usecases/FindUseCase.ts
index 224e4a9..d567800 100644
--- a/src/background/usecases/FindUseCase.ts
+++ b/src/background/usecases/FindUseCase.ts
@@ -3,22 +3,28 @@ import TabPresenter from '../presenters/TabPresenter';
import ConsoleClient from '../infrastructures/ConsoleClient';
export default class FindUseCase {
+ private tabPresenter: TabPresenter;
+
+ private findRepository: FindRepository;
+
+ private consoleClient: ConsoleClient;
+
constructor() {
this.tabPresenter = new TabPresenter();
this.findRepository = new FindRepository();
this.consoleClient = new ConsoleClient();
}
- getKeyword() {
+ getKeyword(): Promise {
return this.findRepository.getKeyword();
}
- setKeyword(keyword) {
+ setKeyword(keyword: string): Promise {
return this.findRepository.setKeyword(keyword);
}
- async findStart() {
+ async findStart(): Promise {
let tab = await this.tabPresenter.getCurrent();
- return this.consoleClient.showFind(tab.id);
+ return this.consoleClient.showFind(tab.id as number);
}
}
diff --git a/src/background/usecases/LinkUseCase.ts b/src/background/usecases/LinkUseCase.ts
index 89412c5..2f4df7b 100644
--- a/src/background/usecases/LinkUseCase.ts
+++ b/src/background/usecases/LinkUseCase.ts
@@ -1,17 +1,17 @@
-import SettingRepository from '../repositories/SettingRepository';
import TabPresenter from '../presenters/TabPresenter';
export default class LinkUseCase {
+ private tabPresenter: TabPresenter;
+
constructor() {
- this.settingRepository = new SettingRepository();
this.tabPresenter = new TabPresenter();
}
- openToTab(url, tabId) {
+ openToTab(url: string, tabId: number): Promise {
return this.tabPresenter.open(url, tabId);
}
- openNewTab(url, openerId, background) {
+ openNewTab(url: string, openerId: number, background: boolean): Promise {
return this.tabPresenter.create(url, {
openerTabId: openerId, active: !background
});
diff --git a/src/background/usecases/MarkUseCase.ts b/src/background/usecases/MarkUseCase.ts
index 39c796b..8b544aa 100644
--- a/src/background/usecases/MarkUseCase.ts
+++ b/src/background/usecases/MarkUseCase.ts
@@ -1,10 +1,17 @@
-import GlobalMark from '../domains/GlobalMark';
import TabPresenter from '../presenters/TabPresenter';
import MarkRepository from '../repositories/MarkRepository';
import ConsoleClient from '../infrastructures/ConsoleClient';
import ContentMessageClient from '../infrastructures/ContentMessageClient';
export default class MarkUseCase {
+ private tabPresenter: TabPresenter;
+
+ private markRepository: MarkRepository;
+
+ private consoleClient: ConsoleClient;
+
+ private contentMessageClient: ContentMessageClient;
+
constructor() {
this.tabPresenter = new TabPresenter();
this.markRepository = new MarkRepository();
@@ -12,18 +19,19 @@ export default class MarkUseCase {
this.contentMessageClient = new ContentMessageClient();
}
- async setGlobal(key, x, y) {
+ async setGlobal(key: string, x: number, y: number): Promise {
let tab = await this.tabPresenter.getCurrent();
- let mark = new GlobalMark(tab.id, tab.url, x, y);
+ let mark = { tabId: tab.id, url: tab.url, x, y };
return this.markRepository.setMark(key, mark);
}
- async jumpGlobal(key) {
+ async jumpGlobal(key: string): Promise {
let current = await this.tabPresenter.getCurrent();
let mark = await this.markRepository.getMark(key);
if (!mark) {
- return this.consoleClient.showError(current.id, 'Mark is not set');
+ return this.consoleClient.showError(
+ current.id as number, 'Mark is not set');
}
return this.contentMessageClient.scrollTo(
@@ -32,7 +40,7 @@ export default class MarkUseCase {
return this.tabPresenter.select(mark.tabId);
}).catch(async() => {
let tab = await this.tabPresenter.create(mark.url);
- let mark2 = new GlobalMark(tab.id, mark.url, mark.x, mark.y);
+ let mark2 = { tabId: tab.id, url: mark.url, x: mark.x, y: mark.y };
return this.markRepository.setMark(key, mark2);
});
}
diff --git a/src/background/usecases/SettingUseCase.ts b/src/background/usecases/SettingUseCase.ts
index 9e17408..b66ce02 100644
--- a/src/background/usecases/SettingUseCase.ts
+++ b/src/background/usecases/SettingUseCase.ts
@@ -4,16 +4,20 @@ import PersistentSettingRepository from '../repositories/PersistentSettingReposi
import SettingRepository from '../repositories/SettingRepository';
export default class SettingUseCase {
+ private persistentSettingRepository: PersistentSettingRepository;
+
+ private settingRepository: SettingRepository;
+
constructor() {
this.persistentSettingRepository = new PersistentSettingRepository();
this.settingRepository = new SettingRepository();
}
- get() {
+ get(): Promise {
return this.settingRepository.get();
}
- async reload() {
+ async reload(): Promise {
let settings = await this.persistentSettingRepository.load();
if (!settings) {
settings = Setting.defaultSettings();
diff --git a/src/background/usecases/TabSelectUseCase.ts b/src/background/usecases/TabSelectUseCase.ts
index 16b3e14..a0b52f0 100644
--- a/src/background/usecases/TabSelectUseCase.ts
+++ b/src/background/usecases/TabSelectUseCase.ts
@@ -1,11 +1,13 @@
import TabPresenter from '../presenters/TabPresenter';
export default class TabSelectUseCase {
+ private tabPresenter: TabPresenter;
+
constructor() {
this.tabPresenter = new TabPresenter();
}
- async selectPrev(count) {
+ async selectPrev(count: number): Promise {
let tabs = await this.tabPresenter.getAll();
if (tabs.length < 2) {
return;
@@ -15,10 +17,10 @@ export default class TabSelectUseCase {
return;
}
let select = (tab.index - count + tabs.length) % tabs.length;
- return this.tabPresenter.select(tabs[select].id);
+ return this.tabPresenter.select(tabs[select].id as number);
}
- async selectNext(count) {
+ async selectNext(count: number): Promise {
let tabs = await this.tabPresenter.getAll();
if (tabs.length < 2) {
return;
@@ -28,24 +30,24 @@ export default class TabSelectUseCase {
return;
}
let select = (tab.index + count) % tabs.length;
- return this.tabPresenter.select(tabs[select].id);
+ return this.tabPresenter.select(tabs[select].id as number);
}
- async selectFirst() {
+ async selectFirst(): Promise {
let tabs = await this.tabPresenter.getAll();
- return this.tabPresenter.select(tabs[0].id);
+ return this.tabPresenter.select(tabs[0].id as number);
}
- async selectLast() {
+ async selectLast(): Promise {
let tabs = await this.tabPresenter.getAll();
- return this.tabPresenter.select(tabs[tabs.length - 1].id);
+ return this.tabPresenter.select(tabs[tabs.length - 1].id as number);
}
- async selectPrevSelected() {
+ async selectPrevSelected(): Promise {
let tabId = await this.tabPresenter.getLastSelectedId();
if (tabId === null || typeof tabId === 'undefined') {
- return;
+ return Promise.resolve();
}
- this.tabPresenter.select(tabId);
+ return this.tabPresenter.select(tabId);
}
}
diff --git a/src/background/usecases/TabUseCase.ts b/src/background/usecases/TabUseCase.ts
index d930842..1615333 100644
--- a/src/background/usecases/TabUseCase.ts
+++ b/src/background/usecases/TabUseCase.ts
@@ -2,20 +2,24 @@ import TabPresenter from '../presenters/TabPresenter';
import BrowserSettingRepository from '../repositories/BrowserSettingRepository';
export default class TabUseCase {
+ private tabPresenter: TabPresenter;
+
+ private browserSettingRepository: BrowserSettingRepository;
+
constructor() {
this.tabPresenter = new TabPresenter();
this.browserSettingRepository = new BrowserSettingRepository();
}
- async close(force) {
+ async close(force: boolean): Promise {
let tab = await this.tabPresenter.getCurrent();
if (!force && tab.pinned) {
- return;
+ return Promise.resolve();
}
- return this.tabPresenter.remove([tab.id]);
+ return this.tabPresenter.remove([tab.id as number]);
}
- async closeRight() {
+ async closeRight(): Promise {
let tabs = await this.tabPresenter.getAll();
tabs.sort((t1, t2) => t1.index - t2.index);
let index = tabs.findIndex(t => t.active);
@@ -25,42 +29,42 @@ export default class TabUseCase {
for (let i = index + 1; i < tabs.length; ++i) {
let tab = tabs[i];
if (!tab.pinned) {
- this.tabPresenter.remove(tab.id);
+ this.tabPresenter.remove([tab.id as number]);
}
}
}
- reopen() {
+ reopen(): Promise {
return this.tabPresenter.reopen();
}
- async reload(cache) {
+ async reload(cache: boolean): Promise {
let tab = await this.tabPresenter.getCurrent();
- return this.tabPresenter.reload(tab.id, cache);
+ return this.tabPresenter.reload(tab.id as number, cache);
}
- async setPinned(pinned) {
+ async setPinned(pinned: boolean): Promise {
let tab = await this.tabPresenter.getCurrent();
- return this.tabPresenter.setPinned(tab.id, pinned);
+ return this.tabPresenter.setPinned(tab.id as number, pinned);
}
- async togglePinned() {
+ async togglePinned(): Promise {
let tab = await this.tabPresenter.getCurrent();
- return this.tabPresenter.setPinned(tab.id, !tab.pinned);
+ return this.tabPresenter.setPinned(tab.id as number, !tab.pinned);
}
- async duplicate() {
+ async duplicate(): Promise {
let tab = await this.tabPresenter.getCurrent();
- return this.tabPresenter.duplicate(tab.id);
+ return this.tabPresenter.duplicate(tab.id as number);
}
- async openPageSource() {
+ async openPageSource(): Promise {
let tab = await this.tabPresenter.getCurrent();
let url = 'view-source:' + tab.url;
return this.tabPresenter.create(url);
}
- async openHome(newTab) {
+ async openHome(newTab: boolean): Promise {
let tab = await this.tabPresenter.getCurrent();
let urls = await this.browserSettingRepository.getHomepageUrls();
if (urls.length === 1 && urls[0] === 'about:home') {
diff --git a/src/background/usecases/VersionUseCase.ts b/src/background/usecases/VersionUseCase.ts
index ed5112b..207f9e2 100644
--- a/src/background/usecases/VersionUseCase.ts
+++ b/src/background/usecases/VersionUseCase.ts
@@ -3,21 +3,25 @@ import TabPresenter from '../presenters/TabPresenter';
import NotifyPresenter from '../presenters/NotifyPresenter';
export default class VersionUseCase {
+ private tabPresenter: TabPresenter;
+
+ private notifyPresenter: NotifyPresenter;
+
constructor() {
this.tabPresenter = new TabPresenter();
this.notifyPresenter = new NotifyPresenter();
}
- notify() {
+ notify(): Promise {
let title = `Vim Vixen ${manifest.version} has been installed`;
let message = 'Click here to see release notes';
let url = this.releaseNoteUrl(manifest.version);
- this.notifyPresenter.notify(title, message, () => {
+ return this.notifyPresenter.notify(title, message, () => {
this.tabPresenter.create(url);
});
}
- releaseNoteUrl(version) {
+ releaseNoteUrl(version?: string): string {
if (version) {
return `https://github.com/ueokande/vim-vixen/releases/tag/${version}`;
}
diff --git a/src/background/usecases/ZoomUseCase.ts b/src/background/usecases/ZoomUseCase.ts
index 692d6d9..661c3cd 100644
--- a/src/background/usecases/ZoomUseCase.ts
+++ b/src/background/usecases/ZoomUseCase.ts
@@ -1,35 +1,39 @@
import TabPresenter from '../presenters/TabPresenter';
-const ZOOM_SETTINGS = [
+const ZOOM_SETTINGS: number[] = [
0.33, 0.50, 0.66, 0.75, 0.80, 0.90, 1.00,
1.10, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00
];
export default class ZoomUseCase {
+ private tabPresenter: TabPresenter;
+
constructor() {
this.tabPresenter = new TabPresenter();
}
- async zoomIn(tabId) {
+ async zoomIn(): Promise {
let tab = await this.tabPresenter.getCurrent();
- let current = await this.tabPresenter.getZoom(tab.id);
+ let tabId = tab.id as number;
+ let current = await this.tabPresenter.getZoom(tabId);
let factor = ZOOM_SETTINGS.find(f => f > current);
if (factor) {
- return this.tabPresenter.setZoom(tabId, factor);
+ return this.tabPresenter.setZoom(tabId as number, factor);
}
}
- async zoomOut(tabId) {
+ async zoomOut(): Promise {
let tab = await this.tabPresenter.getCurrent();
- let current = await this.tabPresenter.getZoom(tab.id);
- let factor = [].concat(ZOOM_SETTINGS).reverse().find(f => f < current);
+ let tabId = tab.id as number;
+ let current = await this.tabPresenter.getZoom(tabId);
+ let factor = ZOOM_SETTINGS.slice(0).reverse().find(f => f < current);
if (factor) {
- return this.tabPresenter.setZoom(tabId, factor);
+ return this.tabPresenter.setZoom(tabId as number, factor);
}
}
- zoomNutoral(tabId) {
- return this.tabPresenter.setZoom(tabId, 1);
+ async zoomNutoral(): Promise {
+ let tab = await this.tabPresenter.getCurrent();
+ return this.tabPresenter.setZoom(tab.id as number, 1);
}
-
}
diff --git a/src/background/usecases/filters.ts b/src/background/usecases/filters.ts
index d057dca..44eb56f 100644
--- a/src/background/usecases/filters.ts
+++ b/src/background/usecases/filters.ts
@@ -1,40 +1,42 @@
-const filterHttp = (items) => {
- let httpsHosts = items.map(x => new URL(x.url))
+type Item = browser.history.HistoryItem;
+
+const filterHttp = (items: Item[]): Item[] => {
+ let httpsHosts = items.map(x => new URL(x.url as string))
.filter(x => x.protocol === 'https:')
.map(x => x.host);
- httpsHosts = new Set(httpsHosts);
+ let hostsSet = new Set(httpsHosts);
- return items.filter((item) => {
- let url = new URL(item.url);
- return url.protocol === 'https:' || !httpsHosts.has(url.host);
+ return items.filter((item: Item) => {
+ let url = new URL(item.url as string);
+ return url.protocol === 'https:' || !hostsSet.has(url.host);
});
};
-const filterBlankTitle = (items) => {
+const filterBlankTitle = (items: Item[]): Item[] => {
return items.filter(item => item.title && item.title !== '');
};
-const filterByTailingSlash = (items) => {
- let urls = items.map(item => new URL(item.url));
+const filterByTailingSlash = (items: Item[]): Item[] => {
+ let urls = items.map(item => new URL(item.url as string));
let simplePaths = urls
.filter(url => url.hash === '' && url.search === '')
.map(url => url.origin + url.pathname);
- simplePaths = new Set(simplePaths);
+ let pathsSet = new Set(simplePaths);
return items.filter((item) => {
- let url = new URL(item.url);
+ let url = new URL(item.url as string);
if (url.hash !== '' || url.search !== '' ||
url.pathname.slice(-1) !== '/') {
return true;
}
- return !simplePaths.has(url.origin + url.pathname.slice(0, -1));
+ return !pathsSet.has(url.origin + url.pathname.slice(0, -1));
});
};
-const filterByPathname = (items, min) => {
- let hash = {};
+const filterByPathname = (items: Item[], min: number): Item[] => {
+ let hash: {[key: string]: Item} = {};
for (let item of items) {
- let url = new URL(item.url);
+ let url = new URL(item.url as string);
let pathname = url.origin + url.pathname;
if (!hash[pathname]) {
hash[pathname] = item;
@@ -49,10 +51,10 @@ const filterByPathname = (items, min) => {
return filtered;
};
-const filterByOrigin = (items, min) => {
- let hash = {};
+const filterByOrigin = (items: Item[], min: number): Item[] => {
+ let hash: {[key: string]: Item} = {};
for (let item of items) {
- let origin = new URL(item.url).origin;
+ let origin = new URL(item.url as string).origin;
if (!hash[origin]) {
hash[origin] = item;
} else if (hash[origin].url.length > item.url.length) {
diff --git a/src/background/usecases/parsers.ts b/src/background/usecases/parsers.ts
index 43c8177..3616ac3 100644
--- a/src/background/usecases/parsers.ts
+++ b/src/background/usecases/parsers.ts
@@ -1,4 +1,4 @@
-const mustNumber = (v) => {
+const mustNumber = (v: any): number => {
let num = Number(v);
if (isNaN(num)) {
throw new Error('Not number: ' + v);
@@ -6,8 +6,11 @@ const mustNumber = (v) => {
return num;
};
-const parseSetOption = (word, types) => {
- let [key, value] = word.split('=');
+const parseSetOption = (
+ word: string,
+ types: { [key: string]: string },
+): any[] => {
+ let [key, value]: any[] = word.split('=');
if (value === undefined) {
value = !key.startsWith('no');
key = value ? key : key.slice(2);
@@ -26,6 +29,7 @@ const parseSetOption = (word, types) => {
case 'number': return [key, mustNumber(value)];
case 'boolean': return [key, value];
}
+ throw new Error('Unknown property type: ' + type);
};
export { parseSetOption };
diff --git a/src/content/scrolls.ts b/src/content/scrolls.ts
index f3124a1..bbf2491 100644
--- a/src/content/scrolls.ts
+++ b/src/content/scrolls.ts
@@ -90,16 +90,6 @@ 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 };
diff --git a/test/background/domains/GlobalMark.test.ts b/test/background/domains/GlobalMark.test.ts
deleted file mode 100644
index ed636e9..0000000
--- a/test/background/domains/GlobalMark.test.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import GlobalMark from 'background/domains/GlobalMark';
-
-describe('background/domains/global-mark', () => {
- describe('constructor and getter', () => {
- let mark = new GlobalMark(1, 'http://example.com', 10, 30);
- expect(mark.tabId).to.equal(1);
- expect(mark.url).to.equal('http://example.com');
- expect(mark.x).to.equal(10);
- expect(mark.y).to.equal(30);
- });
-});
diff --git a/test/background/repositories/Mark.test.ts b/test/background/repositories/Mark.test.ts
index 2a5b099..167e512 100644
--- a/test/background/repositories/Mark.test.ts
+++ b/test/background/repositories/Mark.test.ts
@@ -9,12 +9,11 @@ describe('background/repositories/mark', () => {
});
it('get and set', async() => {
- let mark = new GlobalMark(1, 'http://example.com', 10, 30);
+ let mark = { tabId: 1, url: 'http://example.com', x: 10, y: 30 };
repository.setMark('A', mark);
let got = await repository.getMark('A');
- expect(got).to.be.a('object');
expect(got.tabId).to.equal(1);
expect(got.url).to.equal('http://example.com');
expect(got.x).to.equal(10);
diff --git a/test/background/repositories/Version.ts b/test/background/repositories/Version.ts
deleted file mode 100644
index c7fa88b..0000000
--- a/test/background/repositories/Version.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import VersionRepository from 'background/repositories/Version';
-
-describe("background/repositories/version", () => {
- let versionRepository;
-
- beforeEach(() => {
- versionRepository = new VersionRepository;
- });
-
- describe('#get', () => {
- beforeEach(() => {
- return browser.storage.local.remove('version');
- });
-
- it('loads saved version', async() => {
- await browser.storage.local.set({ version: '1.2.3' });
- let version = await this.versionRepository.get();
- expect(version).to.equal('1.2.3');
- });
-
- it('returns undefined if no versions in storage', async() => {
- let version = await storage.load();
- expect(version).to.be.a('undefined');
- });
- });
-
- describe('#update', () => {
- it('saves version string', async() => {
- await versionRepository.update('2.3.4');
- let { version } = await browser.storage.local.get('version');
- expect(version).to.equal('2.3.4');
- });
- });
-});
--
cgit v1.2.3
From 0452370df43cc4263f268e7064f824d7e6e489b3 Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka
Date: Wed, 1 May 2019 23:10:37 +0900
Subject: Types on src/console
---
src/background/usecases/CompletionsUseCase.ts | 12 ++--
src/console/actions/console.ts | 34 +++++-----
src/console/actions/index.ts | 76 ++++++++++++++++++----
src/console/components/Console.tsx | 43 +++++++-----
src/console/components/console/Completion.tsx | 45 ++++++++-----
src/console/components/console/CompletionItem.tsx | 9 ++-
src/console/components/console/CompletionTitle.tsx | 11 ++--
src/console/components/console/Input.tsx | 31 +++++----
src/console/components/console/Message.tsx | 13 ++--
src/console/index.tsx | 10 +--
src/console/reducers/index.ts | 23 +++++--
test/console/actions/console.test.ts | 2 +-
test/console/reducers/console.test.ts | 2 +-
13 files changed, 206 insertions(+), 105 deletions(-)
(limited to 'src')
diff --git a/src/background/usecases/CompletionsUseCase.ts b/src/background/usecases/CompletionsUseCase.ts
index 037d6eb..f3a808b 100644
--- a/src/background/usecases/CompletionsUseCase.ts
+++ b/src/background/usecases/CompletionsUseCase.ts
@@ -1,4 +1,3 @@
-import Completions from '../domains/Completions';
import CompletionGroup from '../domains/CompletionGroup';
import CommandDocs from '../domains/CommandDocs';
import CompletionsRepository from '../repositories/CompletionsRepository';
@@ -25,7 +24,7 @@ export default class CompletionsUseCase {
this.settingRepository = new SettingRepository();
}
- queryConsoleCommand(prefix: string): Promise {
+ queryConsoleCommand(prefix: string): Promise {
let keys = Object.keys(CommandDocs);
let items = keys
.filter(name => name.startsWith(prefix))
@@ -38,10 +37,10 @@ export default class CompletionsUseCase {
if (items.length === 0) {
return Promise.resolve([]);
}
- return Promise.resolve([{ name: 'Console CompletionGroup', items }]);
+ return Promise.resolve([{ name: 'Console Command', items }]);
}
- async queryOpen(name: string, keywords: string): Promise {
+ async queryOpen(name: string, keywords: string): Promise {
let settings = await this.settingRepository.get();
let groups: CompletionGroup[] = [];
@@ -71,7 +70,10 @@ export default class CompletionsUseCase {
}
// eslint-disable-next-line max-statements
- async queryBuffer(name: string, keywords: string): Promise {
+ async queryBuffer(
+ name: string,
+ keywords: string,
+ ): Promise {
let lastId = await this.tabPresenter.getLastSelectedId();
let trimmed = keywords.trim();
let tabs: Tab[] = [];
diff --git a/src/console/actions/console.ts b/src/console/actions/console.ts
index 3713a76..ceb419c 100644
--- a/src/console/actions/console.ts
+++ b/src/console/actions/console.ts
@@ -1,40 +1,40 @@
-import messages from 'shared/messages';
-import actions from 'console/actions';
+import messages from '../../shared/messages';
+import * as actions from './index';
-const hide = () => {
+const hide = (): actions.ConsoleAction => {
return {
type: actions.CONSOLE_HIDE,
};
};
-const showCommand = (text) => {
+const showCommand = (text: string): actions.ConsoleAction => {
return {
type: actions.CONSOLE_SHOW_COMMAND,
text: text
};
};
-const showFind = () => {
+const showFind = (): actions.ConsoleAction => {
return {
type: actions.CONSOLE_SHOW_FIND,
};
};
-const showError = (text) => {
+const showError = (text: string): actions.ConsoleAction => {
return {
type: actions.CONSOLE_SHOW_ERROR,
text: text
};
};
-const showInfo = (text) => {
+const showInfo = (text: string): actions.ConsoleAction => {
return {
type: actions.CONSOLE_SHOW_INFO,
text: text
};
};
-const hideCommand = () => {
+const hideCommand = (): actions.ConsoleAction => {
window.top.postMessage(JSON.stringify({
type: messages.CONSOLE_UNFOCUS,
}), '*');
@@ -43,15 +43,17 @@ const hideCommand = () => {
};
};
-const enterCommand = async(text) => {
+const enterCommand = async(
+ text: string,
+): Promise => {
await browser.runtime.sendMessage({
type: messages.CONSOLE_ENTER_COMMAND,
text,
});
- return hideCommand(text);
+ return hideCommand();
};
-const enterFind = (text) => {
+const enterFind = (text: string): actions.ConsoleAction => {
window.top.postMessage(JSON.stringify({
type: messages.CONSOLE_ENTER_FIND,
text,
@@ -59,14 +61,14 @@ const enterFind = (text) => {
return hideCommand();
};
-const setConsoleText = (consoleText) => {
+const setConsoleText = (consoleText: string): actions.ConsoleAction => {
return {
type: actions.CONSOLE_SET_CONSOLE_TEXT,
consoleText,
};
};
-const getCompletions = async(text) => {
+const getCompletions = async(text: string): Promise => {
let completions = await browser.runtime.sendMessage({
type: messages.CONSOLE_QUERY_COMPLETIONS,
text,
@@ -78,13 +80,13 @@ const getCompletions = async(text) => {
};
};
-const completionNext = () => {
+const completionNext = (): actions.ConsoleAction => {
return {
type: actions.CONSOLE_COMPLETION_NEXT,
};
};
-const completionPrev = () => {
+const completionPrev = (): actions.ConsoleAction => {
return {
type: actions.CONSOLE_COMPLETION_PREV,
};
@@ -92,5 +94,5 @@ const completionPrev = () => {
export {
hide, showCommand, showFind, showError, showInfo, hideCommand, setConsoleText,
- enterCommand, enterFind, getCompletions, completionNext, completionPrev
+ enterCommand, enterFind, getCompletions, completionNext, completionPrev,
};
diff --git a/src/console/actions/index.ts b/src/console/actions/index.ts
index b394179..3770496 100644
--- a/src/console/actions/index.ts
+++ b/src/console/actions/index.ts
@@ -1,13 +1,63 @@
-export default {
- // console commands
- CONSOLE_HIDE: 'console.hide',
- CONSOLE_SHOW_COMMAND: 'console.show.command',
- CONSOLE_SHOW_ERROR: 'console.show.error',
- CONSOLE_SHOW_INFO: 'console.show.info',
- CONSOLE_HIDE_COMMAND: 'console.hide.command',
- CONSOLE_SET_CONSOLE_TEXT: 'console.set.command',
- CONSOLE_SET_COMPLETIONS: 'console.set.completions',
- CONSOLE_COMPLETION_NEXT: 'console.completion.next',
- CONSOLE_COMPLETION_PREV: 'console.completion.prev',
- CONSOLE_SHOW_FIND: 'console.show.find',
-};
+// console commands
+export const CONSOLE_HIDE = 'console.hide';
+export const CONSOLE_SHOW_COMMAND = 'console.show.command';
+export const CONSOLE_SHOW_ERROR = 'console.show.error';
+export const CONSOLE_SHOW_INFO = 'console.show.info';
+export const CONSOLE_HIDE_COMMAND = 'console.hide.command';
+export const CONSOLE_SET_CONSOLE_TEXT = 'console.set.command';
+export const CONSOLE_SET_COMPLETIONS = 'console.set.completions';
+export const CONSOLE_COMPLETION_NEXT = 'console.completion.next';
+export const CONSOLE_COMPLETION_PREV = 'console.completion.prev';
+export const CONSOLE_SHOW_FIND = 'console.show.find';
+
+interface HideAction {
+ type: typeof CONSOLE_HIDE;
+}
+
+interface ShowCommand {
+ type: typeof CONSOLE_SHOW_COMMAND;
+ text: string;
+}
+
+interface ShowFindAction {
+ type: typeof CONSOLE_SHOW_FIND;
+}
+
+interface ShowErrorAction {
+ type: typeof CONSOLE_SHOW_ERROR;
+ text: string;
+}
+
+interface ShowInfoAction {
+ type: typeof CONSOLE_SHOW_INFO;
+ text: string;
+}
+
+interface HideCommandAction {
+ type: typeof CONSOLE_HIDE_COMMAND;
+}
+
+interface SetConsoleTextAction {
+ type: typeof CONSOLE_SET_CONSOLE_TEXT;
+ consoleText: string;
+}
+
+interface SetCompletionsAction {
+ type: typeof CONSOLE_SET_COMPLETIONS;
+ completions: any[];
+ completionSource: string;
+}
+
+interface CompletionNextAction {
+ type: typeof CONSOLE_COMPLETION_NEXT;
+}
+
+interface CompletionPrevAction {
+ type: typeof CONSOLE_COMPLETION_PREV;
+}
+
+export type ConsoleAction =
+ HideAction | ShowCommand | ShowFindAction | ShowErrorAction |
+ ShowInfoAction | HideCommandAction | SetConsoleTextAction |
+ SetCompletionsAction | CompletionNextAction | CompletionPrevAction;
+
diff --git a/src/console/components/Console.tsx b/src/console/components/Console.tsx
index 5427e43..09c0f50 100644
--- a/src/console/components/Console.tsx
+++ b/src/console/components/Console.tsx
@@ -1,7 +1,6 @@
import './console.scss';
import { connect } from 'react-redux';
import React from 'react';
-import PropTypes from 'prop-types';
import Input from './console/Input';
import Completion from './console/Completion';
import Message from './console/Message';
@@ -9,14 +8,29 @@ import * as consoleActions from '../../console/actions/console';
const COMPLETION_MAX_ITEMS = 33;
-class Console extends React.Component {
+interface Props {
+ mode?: string;
+ consoleText?: string;
+ messageText?: string;
+ children?: string;
+}
+
+class Console extends React.Component {
+ private input: HTMLInputElement | null;
+
+ constructor(props: Props) {
+ super(props);
+
+ this.input = null;
+ }
+
onBlur() {
if (this.props.mode === 'command' || this.props.mode === 'find') {
return this.props.dispatch(consoleActions.hideCommand());
}
}
- doEnter(e) {
+ doEnter(e: React.KeyboardEvent) {
e.stopPropagation();
e.preventDefault();
@@ -28,19 +42,19 @@ class Console extends React.Component {
}
}
- selectNext(e) {
+ selectNext(e: React.KeyboardEvent) {
this.props.dispatch(consoleActions.completionNext());
e.stopPropagation();
e.preventDefault();
}
- selectPrev(e) {
+ selectPrev(e: React.KeyboardEvent) {
this.props.dispatch(consoleActions.completionPrev());
e.stopPropagation();
e.preventDefault();
}
- onKeyDown(e) {
+ onKeyDown(e: React.KeyboardEvent) {
if (e.keyCode === KeyboardEvent.DOM_VK_ESCAPE && e.ctrlKey) {
this.props.dispatch(consoleActions.hideCommand());
}
@@ -81,7 +95,7 @@ class Console extends React.Component {
}
}
- onChange(e) {
+ onChange(e: React.ChangeEvent) {
let text = e.target.value;
this.props.dispatch(consoleActions.setConsoleText(text));
if (this.props.mode === 'command') {
@@ -90,7 +104,7 @@ class Console extends React.Component {
}
- componentDidUpdate(prevProps) {
+ componentDidUpdate(prevProps: Props) {
if (!this.input) {
return;
}
@@ -134,16 +148,11 @@ class Console extends React.Component {
focus() {
window.focus();
- this.input.focus();
+ if (this.input) {
+ this.input.focus();
+ }
}
}
-Console.propTypes = {
- mode: PropTypes.string,
- consoleText: PropTypes.string,
- messageText: PropTypes.string,
- children: PropTypes.string,
-};
-
-const mapStateToProps = state => state;
+const mapStateToProps = (state: any) => state;
export default connect(mapStateToProps)(Console);
diff --git a/src/console/components/console/Completion.tsx b/src/console/components/console/Completion.tsx
index 5477cb6..169a39c 100644
--- a/src/console/components/console/Completion.tsx
+++ b/src/console/components/console/Completion.tsx
@@ -1,15 +1,36 @@
import React from 'react';
-import PropTypes from 'prop-types';
import CompletionItem from './CompletionItem';
import CompletionTitle from './CompletionTitle';
-class Completion extends React.Component {
- constructor() {
- super();
+interface Item {
+ icon?: string;
+ caption?: string;
+ url?: string;
+}
+
+interface Group {
+ name: string;
+ items: Item[];
+}
+
+interface Props {
+ select: number;
+ size: number;
+ completions: Group[];
+}
+
+interface State {
+ viewOffset: number;
+ select: number;
+}
+
+class Completion extends React.Component {
+ constructor(props: Props) {
+ super(props);
this.state = { viewOffset: 0, select: -1 };
}
- static getDerivedStateFromProps(nextProps, prevState) {
+ static getDerivedStateFromProps(nextProps: Props, prevState: State) {
if (prevState.select === nextProps.select) {
return null;
}
@@ -24,6 +45,7 @@ class Completion extends React.Component {
}
index += g.items.length;
}
+ return -1;
})();
let viewOffset = 0;
@@ -70,17 +92,4 @@ class Completion extends React.Component {
}
}
-Completion.propTypes = {
- select: PropTypes.number,
- size: PropTypes.number,
- completions: PropTypes.arrayOf(PropTypes.shape({
- name: PropTypes.string,
- items: PropTypes.arrayOf(PropTypes.shape({
- icon: PropTypes.string,
- caption: PropTypes.string,
- url: PropTypes.string,
- })),
- })),
-};
-
export default Completion;
diff --git a/src/console/components/console/CompletionItem.tsx b/src/console/components/console/CompletionItem.tsx
index 3dc552b..1cbf3de 100644
--- a/src/console/components/console/CompletionItem.tsx
+++ b/src/console/components/console/CompletionItem.tsx
@@ -1,7 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
-const CompletionItem = (props) => {
+interface Props {
+ highlight: boolean;
+ caption?: string;
+ url?: string;
+ icon?: string;
+}
+
+const CompletionItem = (props: Props) => {
let className = 'vimvixen-console-completion-item';
if (props.highlight) {
className += ' vimvixen-completion-selected';
diff --git a/src/console/components/console/CompletionTitle.tsx b/src/console/components/console/CompletionTitle.tsx
index 4fcba3f..2543619 100644
--- a/src/console/components/console/CompletionTitle.tsx
+++ b/src/console/components/console/CompletionTitle.tsx
@@ -1,14 +1,13 @@
import React from 'react';
-import PropTypes from 'prop-types';
-const CompletionTitle = (props) => {
+interface Props {
+ title: string;
+}
+
+const CompletionTitle = (props: Props) => {
return
{props.title}
;
};
-CompletionTitle.propTypes = {
- title: PropTypes.string,
-};
-
export default CompletionTitle;
diff --git a/src/console/components/console/Input.tsx b/src/console/components/console/Input.tsx
index cbd3348..d0348bd 100644
--- a/src/console/components/console/Input.tsx
+++ b/src/console/components/console/Input.tsx
@@ -1,9 +1,26 @@
import React from 'react';
-import PropTypes from 'prop-types';
-class Input extends React.Component {
+interface Props {
+ mode: string;
+ value: string;
+ onBlur: (e: React.FocusEvent) => void;
+ onKeyDown: (e: React.KeyboardEvent) => void;
+ onChange: (e: React.ChangeEvent) => void;
+}
+
+class Input extends React.Component {
+ private input: HTMLInputElement | null;
+
+ constructor(props: Props) {
+ super(props);
+
+ this.input = null;
+ }
+
focus() {
- this.input.focus();
+ if (this.input) {
+ this.input.focus();
+ }
}
render() {
@@ -32,12 +49,4 @@ class Input extends React.Component {
}
}
-Input.propTypes = {
- mode: PropTypes.string,
- value: PropTypes.string,
- onBlur: PropTypes.func,
- onKeyDown: PropTypes.func,
- onChange: PropTypes.func,
-};
-
export default Input;
diff --git a/src/console/components/console/Message.tsx b/src/console/components/console/Message.tsx
index dd96248..07a929e 100644
--- a/src/console/components/console/Message.tsx
+++ b/src/console/components/console/Message.tsx
@@ -1,7 +1,11 @@
import React from 'react';
-import PropTypes from 'prop-types';
-const Message = (props) => {
+interface Props {
+ mode: string;
+ children: string[];
+}
+
+const Message = (props: Props) => {
switch (props.mode) {
case 'error':
return (
@@ -16,10 +20,7 @@ const Message = (props) => {
);
}
-};
-
-Message.propTypes = {
- children: PropTypes.string,
+ return null;
};
export default Message;
diff --git a/src/console/index.tsx b/src/console/index.tsx
index 3190a9a..ee3a8ee 100644
--- a/src/console/index.tsx
+++ b/src/console/index.tsx
@@ -1,8 +1,8 @@
-import messages from 'shared/messages';
-import reducers from 'console/reducers';
+import messages from '../shared/messages';
+import reducers from './reducers';
import { createStore, applyMiddleware } from 'redux';
import promise from 'redux-promise';
-import * as consoleActions from 'console/actions/console';
+import * as consoleActions from './actions/console';
import { Provider } from 'react-redux';
import Console from './components/Console';
import React from 'react';
@@ -22,7 +22,7 @@ window.addEventListener('load', () => {
wrapper);
});
-const onMessage = (message) => {
+const onMessage = (message: any): any => {
switch (message.type) {
case messages.CONSOLE_SHOW_COMMAND:
return store.dispatch(consoleActions.showCommand(message.command));
@@ -38,5 +38,5 @@ const onMessage = (message) => {
};
browser.runtime.onMessage.addListener(onMessage);
-let port = browser.runtime.connect({ name: 'vimvixen-console' });
+let port = browser.runtime.connect(undefined, { name: 'vimvixen-console' });
port.onMessage.addListener(onMessage);
diff --git a/src/console/reducers/index.ts b/src/console/reducers/index.ts
index 614a72f..37ed715 100644
--- a/src/console/reducers/index.ts
+++ b/src/console/reducers/index.ts
@@ -1,4 +1,14 @@
-import actions from 'console/actions';
+import * as actions from '../actions';
+
+interface State {
+ mode: string;
+ messageText: string;
+ consoleText: string;
+ completionSource: string;
+ completions: any[],
+ select: number;
+ viewIndex: number;
+}
const defaultState = {
mode: '',
@@ -10,7 +20,7 @@ const defaultState = {
viewIndex: 0,
};
-const nextSelection = (state) => {
+const nextSelection = (state: State): number => {
if (state.completions.length === 0) {
return -1;
}
@@ -27,7 +37,7 @@ const nextSelection = (state) => {
return -1;
};
-const prevSelection = (state) => {
+const prevSelection = (state: State): number => {
let length = state.completions
.map(g => g.items.length)
.reduce((x, y) => x + y);
@@ -37,7 +47,7 @@ const prevSelection = (state) => {
return state.select - 1;
};
-const nextConsoleText = (completions, select, defaults) => {
+const nextConsoleText = (completions: any[], select: number, defaults: any) => {
if (select < 0) {
return defaults;
}
@@ -46,7 +56,10 @@ const nextConsoleText = (completions, select, defaults) => {
};
// eslint-disable-next-line max-lines-per-function
-export default function reducer(state = defaultState, action = {}) {
+export default function reducer(
+ state: State = defaultState,
+ action: actions.ConsoleAction,
+): State {
switch (action.type) {
case actions.CONSOLE_HIDE:
return { ...state,
diff --git a/test/console/actions/console.test.ts b/test/console/actions/console.test.ts
index 10cd9fe..e45d008 100644
--- a/test/console/actions/console.test.ts
+++ b/test/console/actions/console.test.ts
@@ -1,4 +1,4 @@
-import actions from 'console/actions';
+import * as actions from 'console/actions';
import * as consoleActions from 'console/actions/console';
describe("console actions", () => {
diff --git a/test/console/reducers/console.test.ts b/test/console/reducers/console.test.ts
index d5a38cf..47e7daf 100644
--- a/test/console/reducers/console.test.ts
+++ b/test/console/reducers/console.test.ts
@@ -1,4 +1,4 @@
-import actions from 'console/actions';
+import * as actions from 'console/actions';
import reducer from 'console/reducers';
describe("console reducer", () => {
--
cgit v1.2.3
From e69497fab457df486b2a7068bdd0283505461f8b Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka
Date: Thu, 2 May 2019 11:12:28 +0900
Subject: Types src/settings
---
src/settings/actions/index.ts | 39 +++++++++++++++----
src/settings/actions/setting.ts | 18 ++++-----
src/settings/components/form/BlacklistForm.tsx | 28 +++++++------
src/settings/components/form/KeymapsForm.tsx | 32 ++++++++-------
src/settings/components/form/PropertiesForm.tsx | 32 ++++++++-------
src/settings/components/form/SearchForm.tsx | 38 +++++++++---------
src/settings/components/index.tsx | 44 ++++++++++++---------
src/settings/components/ui/AddButton.tsx | 5 ++-
src/settings/components/ui/DeleteButton.tsx | 5 ++-
src/settings/components/ui/Input.tsx | 52 ++++++++++++++++++-------
src/settings/reducers/setting.ts | 16 ++++++--
test/settings/reducers/setting.test.ts | 2 +-
12 files changed, 194 insertions(+), 117 deletions(-)
(limited to 'src')
diff --git a/src/settings/actions/index.ts b/src/settings/actions/index.ts
index 016f2a5..75c6bb5 100644
--- a/src/settings/actions/index.ts
+++ b/src/settings/actions/index.ts
@@ -1,7 +1,32 @@
-export default {
- // Settings
- SETTING_SET_SETTINGS: 'setting.set.settings',
- SETTING_SHOW_ERROR: 'setting.show.error',
- SETTING_SWITCH_TO_FORM: 'setting.switch.to.form',
- SETTING_SWITCH_TO_JSON: 'setting.switch.to.json',
-};
+// Settings
+export const SETTING_SET_SETTINGS = 'setting.set.settings';
+export const SETTING_SHOW_ERROR = 'setting.show.error';
+export const SETTING_SWITCH_TO_FORM = 'setting.switch.to.form';
+export const SETTING_SWITCH_TO_JSON = 'setting.switch.to.json';
+
+interface SettingSetSettingsAcion {
+ type: typeof SETTING_SET_SETTINGS;
+ source: string;
+ json: string;
+ form: any;
+}
+
+interface SettingShowErrorAction {
+ type: typeof SETTING_SHOW_ERROR;
+ error: string;
+ json: string;
+}
+
+interface SettingSwitchToFormAction {
+ type: typeof SETTING_SWITCH_TO_FORM;
+ form: any;
+}
+
+interface SettingSwitchToJsonAction {
+ type: typeof SETTING_SWITCH_TO_JSON;
+ json: string;
+}
+
+export type SettingAction =
+ SettingSetSettingsAcion | SettingShowErrorAction |
+ SettingSwitchToFormAction | SettingSwitchToJsonAction;
diff --git a/src/settings/actions/setting.ts b/src/settings/actions/setting.ts
index db63a45..b03cd80 100644
--- a/src/settings/actions/setting.ts
+++ b/src/settings/actions/setting.ts
@@ -1,15 +1,15 @@
-import actions from 'settings/actions';
-import * as validator from 'shared/settings/validator';
-import * as settingsValues from 'shared/settings/values';
-import * as settingsStorage from 'shared/settings/storage';
+import * as actions from './index';
+import * as validator from '../../shared/settings/validator';
+import * as settingsValues from '../../shared/settings/values';
+import * as settingsStorage from '../../shared/settings/storage';
import keymaps from '../keymaps';
-const load = async() => {
+const load = async(): Promise => {
let settings = await settingsStorage.loadRaw();
return set(settings);
};
-const save = async(settings) => {
+const save = async(settings: any): Promise => {
try {
if (settings.source === 'json') {
let value = JSON.parse(settings.json);
@@ -26,7 +26,7 @@ const save = async(settings) => {
return set(settings);
};
-const switchToForm = (json) => {
+const switchToForm = (json: string): actions.SettingAction => {
try {
validator.validate(JSON.parse(json));
let form = settingsValues.formFromJson(json, keymaps.allowedOps);
@@ -43,7 +43,7 @@ const switchToForm = (json) => {
}
};
-const switchToJson = (form) => {
+const switchToJson = (form: any): actions.SettingAction => {
let json = settingsValues.jsonFromForm(form);
return {
type: actions.SETTING_SWITCH_TO_JSON,
@@ -51,7 +51,7 @@ const switchToJson = (form) => {
};
};
-const set = (settings) => {
+const set = (settings: any): actions.SettingAction => {
return {
type: actions.SETTING_SET_SETTINGS,
source: settings.source,
diff --git a/src/settings/components/form/BlacklistForm.tsx b/src/settings/components/form/BlacklistForm.tsx
index c470758..637bc1e 100644
--- a/src/settings/components/form/BlacklistForm.tsx
+++ b/src/settings/components/form/BlacklistForm.tsx
@@ -2,9 +2,19 @@ import './BlacklistForm.scss';
import AddButton from '../ui/AddButton';
import DeleteButton from '../ui/DeleteButton';
import React from 'react';
-import PropTypes from 'prop-types';
-class BlacklistForm extends React.Component {
+interface Props {
+ value: string[];
+ onChange: (value: string[]) => void;
+ onBlur: () => void;
+}
+
+class BlacklistForm extends React.Component {
+ public static defaultProps: Props = {
+ value: [],
+ onChange: () => {},
+ onBlur: () => {},
+ };
render() {
return
@@ -28,7 +38,7 @@ class BlacklistForm extends React.Component {
;
}
- bindValue(e) {
+ bindValue(e: any) {
let name = e.target.name;
let index = e.target.getAttribute('data-index');
let next = this.props.value ? this.props.value.slice() : [];
@@ -48,16 +58,4 @@ class BlacklistForm extends React.Component {
}
}
-BlacklistForm.propTypes = {
- value: PropTypes.arrayOf(PropTypes.string),
- onChange: PropTypes.func,
- onBlur: PropTypes.func,
-};
-
-BlacklistForm.defaultProps = {
- value: [],
- onChange: () => {},
- onBlur: () => {},
-};
-
export default BlacklistForm;
diff --git a/src/settings/components/form/KeymapsForm.tsx b/src/settings/components/form/KeymapsForm.tsx
index 01acf61..ab44464 100644
--- a/src/settings/components/form/KeymapsForm.tsx
+++ b/src/settings/components/form/KeymapsForm.tsx
@@ -1,10 +1,22 @@
import './KeymapsForm.scss';
import React from 'react';
-import PropTypes from 'prop-types';
import Input from '../ui/Input';
import keymaps from '../../keymaps';
-class KeymapsForm extends React.Component {
+type Value = {[key: string]: string};
+
+interface Props{
+ value: Value;
+ onChange: (e: Value) => void;
+ onBlur: () => void;
+}
+
+class KeymapsForm extends React.Component {
+ public static defaultProps: Props = {
+ value: {},
+ onChange: () => {},
+ onBlur: () => {},
+ }
render() {
return
@@ -19,7 +31,7 @@ class KeymapsForm extends React.Component {
return ;
})
@@ -30,22 +42,12 @@ class KeymapsForm extends React.Component {
;
}
- bindValue(e) {
+ bindValue(name: string, value: string) {
let next = { ...this.props.value };
- next[e.target.name] = e.target.value;
+ next[name] = value;
this.props.onChange(next);
}
}
-KeymapsForm.propTypes = {
- value: PropTypes.objectOf(PropTypes.string),
- onChange: PropTypes.func,
-};
-
-KeymapsForm.defaultProps = {
- value: {},
- onChange: () => {},
-};
-
export default KeymapsForm;
diff --git a/src/settings/components/form/PropertiesForm.tsx b/src/settings/components/form/PropertiesForm.tsx
index 979fdd8..0be5f5c 100644
--- a/src/settings/components/form/PropertiesForm.tsx
+++ b/src/settings/components/form/PropertiesForm.tsx
@@ -1,8 +1,20 @@
import './PropertiesForm.scss';
import React from 'react';
-import PropTypes from 'prop-types';
-class PropertiesForm extends React.Component {
+interface Props {
+ types: {[key: string]: string};
+ value: {[key: string]: any};
+ onChange: (value: any) => void;
+ onBlur: () => void;
+}
+
+class PropertiesForm extends React.Component {
+ public static defaultProps: Props = {
+ types: {},
+ value: {},
+ onChange: () => {},
+ onBlur: () => {},
+ };
render() {
let types = this.props.types;
@@ -12,13 +24,15 @@ class PropertiesForm extends React.Component {
{
Object.keys(types).map((name) => {
let type = types[name];
- let inputType = null;
+ let inputType = '';
if (type === 'string') {
inputType = 'text';
} else if (type === 'number') {
inputType = 'number';
} else if (type === 'boolean') {
inputType = 'checkbox';
+ } else {
+ return null;
}
return
;
}
- bindValue(e) {
+ bindValue(e: React.ChangeEvent) {
let name = e.target.name;
let next = { ...this.props.value };
if (e.target.type.toLowerCase() === 'checkbox') {
@@ -52,14 +66,4 @@ class PropertiesForm extends React.Component {
}
}
-PropertiesForm.propTypes = {
- value: PropTypes.objectOf(PropTypes.any),
- onChange: PropTypes.func,
-};
-
-PropertiesForm.defaultProps = {
- value: {},
- onChange: () => {},
-};
-
export default PropertiesForm;
diff --git a/src/settings/components/form/SearchForm.tsx b/src/settings/components/form/SearchForm.tsx
index 6b0bd01..737e291 100644
--- a/src/settings/components/form/SearchForm.tsx
+++ b/src/settings/components/form/SearchForm.tsx
@@ -1,10 +1,25 @@
import './SearchForm.scss';
import React from 'react';
-import PropTypes from 'prop-types';
import AddButton from '../ui/AddButton';
import DeleteButton from '../ui/DeleteButton';
-class SearchForm extends React.Component {
+interface Value {
+ default: string;
+ engines: string[][];
+}
+
+interface Props {
+ value: Value;
+ onChange: (value: Value) => void;
+ onBlur: () => void;
+}
+
+class SearchForm extends React.Component {
+ public static defaultProps: Props = {
+ value: { default: '', engines: []},
+ onChange: () => {},
+ onBlur: () => {},
+ }
render() {
let value = this.props.value;
@@ -47,11 +62,11 @@ class SearchForm extends React.Component {
;
}
- bindValue(e) {
+ bindValue(e: any) {
let value = this.props.value;
let name = e.target.name;
- let index = e.target.getAttribute('data-index');
- let next = {
+ let index = Number(e.target.getAttribute('data-index'));
+ let next: Value = {
default: value.default,
engines: value.engines ? value.engines.slice() : [],
};
@@ -76,17 +91,4 @@ class SearchForm extends React.Component {
}
}
-SearchForm.propTypes = {
- value: PropTypes.shape({
- default: PropTypes.string,
- engines: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
- }),
- onChange: PropTypes.func,
-};
-
-SearchForm.defaultProps = {
- value: { default: '', engines: []},
- onChange: () => {},
-};
-
export default SearchForm;
diff --git a/src/settings/components/index.tsx b/src/settings/components/index.tsx
index 4ef59d7..f56e93f 100644
--- a/src/settings/components/index.tsx
+++ b/src/settings/components/index.tsx
@@ -6,19 +6,29 @@ import SearchForm from './form/SearchForm';
import KeymapsForm from './form/KeymapsForm';
import BlacklistForm from './form/BlacklistForm';
import PropertiesForm from './form/PropertiesForm';
-import * as properties from 'shared/settings/properties';
-import * as settingActions from 'settings/actions/setting';
+import * as properties from '../../shared/settings/properties';
+import * as settingActions from '../../settings/actions/setting';
const DO_YOU_WANT_TO_CONTINUE =
'Some settings in JSON can be lost when migrating. ' +
'Do you want to continue?';
-class SettingsComponent extends React.Component {
+interface Props {
+ source: string;
+ json: string;
+ form: any;
+ error: string;
+
+ // FIXME
+ store: any;
+}
+
+class SettingsComponent extends React.Component {
componentDidMount() {
this.props.dispatch(settingActions.load());
}
- renderFormFields(form) {
+ renderFormFields(form: any) {
return
;
}
- renderJsonFields(json, error) {
+ renderJsonFields(json: string, error: string) {
return
@@ -90,7 +100,7 @@ class SettingsComponent extends React.Component {
label='Use form'
checked={this.props.source === 'form'}
value='form'
- onChange={this.bindSource.bind(this)}
+ onValueChange={this.bindSource.bind(this)}
disabled={disabled} />
{ fields }
@@ -107,7 +117,7 @@ class SettingsComponent extends React.Component {
);
}
- bindForm(name, value) {
+ bindForm(name: string, value: any) {
let settings = {
source: this.props.source,
json: this.props.json,
@@ -117,22 +127,20 @@ class SettingsComponent extends React.Component {
this.props.dispatch(settingActions.set(settings));
}
- bindJson(e) {
+ bindJson(_name: string, value: string) {
let settings = {
source: this.props.source,
- json: e.target.value,
+ json: value,
form: this.props.form,
};
this.props.dispatch(settingActions.set(settings));
}
- bindSource(e) {
+ bindSource(_name: string, value: string) {
let from = this.props.source;
- let to = e.target.value;
-
- if (from === 'form' && to === 'json') {
+ if (from === 'form' && value === 'json') {
this.props.dispatch(settingActions.switchToJson(this.props.form));
- } else if (from === 'json' && to === 'form') {
+ } else if (from === 'json' && value === 'form') {
let b = window.confirm(DO_YOU_WANT_TO_CONTINUE);
if (!b) {
this.forceUpdate();
@@ -148,6 +156,6 @@ class SettingsComponent extends React.Component {
}
}
-const mapStateToProps = state => state;
+const mapStateToProps = (state: any) => state;
export default connect(mapStateToProps)(SettingsComponent);
diff --git a/src/settings/components/ui/AddButton.tsx b/src/settings/components/ui/AddButton.tsx
index 185a03b..0577068 100644
--- a/src/settings/components/ui/AddButton.tsx
+++ b/src/settings/components/ui/AddButton.tsx
@@ -1,7 +1,10 @@
import './AddButton.scss';
import React from 'react';
-class AddButton extends React.Component {
+interface Props extends React.AllHTMLAttributes
{
+}
+
+class AddButton extends React.Component {
render() {
return {
+}
+
+class DeleteButton extends React.Component {
render() {
return {
+ name: string;
+ type: string;
+ error?: string;
+ label: string;
+ value: string;
+ onValueChange?: (name: string, value: string) => void;
+ onBlur?: (e: React.FocusEvent) => void;
+}
- renderText(props) {
+class Input extends React.Component {
+ renderText(props: Props) {
let inputClassName = props.error ? 'input-error' : '';
+ let pp = { ...props };
+ delete pp.onValueChange;
return
-
+
;
}
- renderRadio(props) {
+ renderRadio(props: Props) {
let inputClassName = props.error ? 'input-error' : '';
+ let pp = { ...props };
+ delete pp.onValueChange;
return
;
}
- renderTextArea(props) {
+ renderTextArea(props: Props) {
let inputClassName = props.error ? 'input-error' : '';
+ let pp = { ...props };
+ delete pp.onValueChange;
return
-
+
{ this.props.error }
;
}
@@ -48,13 +71,12 @@ class Input extends React.Component {
}
return null;
}
-}
-Input.propTypes = {
- type: PropTypes.string,
- error: PropTypes.string,
- label: PropTypes.string,
- value: PropTypes.string,
-};
+ bindOnChange(e: React.ChangeEvent) {
+ if (this.props.onValueChange) {
+ this.props.onValueChange(e.target.name, e.target.value);
+ }
+ }
+}
export default Input;
diff --git a/src/settings/reducers/setting.ts b/src/settings/reducers/setting.ts
index 54033aa..47c21bf 100644
--- a/src/settings/reducers/setting.ts
+++ b/src/settings/reducers/setting.ts
@@ -1,13 +1,23 @@
-import actions from 'settings/actions';
+import * as actions from '../actions';
-const defaultState = {
+interface State {
+ source: string;
+ json: string;
+ form: any;
+ error: string;
+}
+
+const defaultState: State = {
source: '',
json: '',
form: null,
error: '',
};
-export default function reducer(state = defaultState, action = {}) {
+export default function reducer(
+ state = defaultState,
+ action: actions.SettingAction,
+) {
switch (action.type) {
case actions.SETTING_SET_SETTINGS:
return { ...state,
diff --git a/test/settings/reducers/setting.test.ts b/test/settings/reducers/setting.test.ts
index c1a1648..6a874e8 100644
--- a/test/settings/reducers/setting.test.ts
+++ b/test/settings/reducers/setting.test.ts
@@ -1,4 +1,4 @@
-import actions from 'settings/actions';
+import * as actions from 'settings/actions';
import settingReducer from 'settings/reducers/setting';
describe("settings setting reducer", () => {
--
cgit v1.2.3
From d01db82c0dca352de2d7644c383d388fc3ec0366 Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka
Date: Thu, 2 May 2019 14:08:51 +0900
Subject: Types src/content
---
.eslintrc | 2 +
package.json | 1 +
src/background/controllers/OperationController.ts | 4 +-
src/background/controllers/VersionController.ts | 2 +-
src/background/domains/Setting.ts | 20 +-
src/background/infrastructures/ConsoleClient.ts | 2 +-
.../infrastructures/ContentMessageClient.ts | 2 +-
.../infrastructures/ContentMessageListener.ts | 6 +-
src/background/presenters/NotifyPresenter.ts | 6 +-
src/background/usecases/VersionUseCase.ts | 2 +-
src/console/actions/console.ts | 2 +-
src/console/index.tsx | 11 +-
src/content/MessageListener.ts | 32 ++
src/content/actions/addon.ts | 10 +-
src/content/actions/find.ts | 40 +-
src/content/actions/follow-controller.ts | 12 +-
src/content/actions/index.ts | 151 ++++--
src/content/actions/input.ts | 6 +-
src/content/actions/mark.ts | 20 +-
src/content/actions/operation.ts | 25 +-
src/content/actions/setting.ts | 14 +-
src/content/components/common/follow.ts | 79 +++-
src/content/components/common/hint.ts | 33 +-
src/content/components/common/index.ts | 43 +-
src/content/components/common/input.ts | 46 +-
src/content/components/common/keymapper.ts | 8 +-
src/content/components/common/mark.ts | 2 +-
src/content/components/top-content/find.ts | 25 +-
.../components/top-content/follow-controller.ts | 65 ++-
src/content/components/top-content/index.ts | 28 +-
src/content/console-frames.ts | 18 +-
src/content/focuses.ts | 8 +-
src/content/hint-key-producer.ts | 10 +-
src/content/index.ts | 9 +-
src/content/navigates.ts | 45 +-
src/content/reducers/addon.ts | 13 +-
src/content/reducers/find.ts | 14 +-
src/content/reducers/follow-controller.ts | 16 +-
src/content/reducers/index.ts | 22 +-
src/content/reducers/input.ts | 13 +-
src/content/reducers/mark.ts | 20 +-
src/content/reducers/setting.ts | 11 +-
src/content/scrolls.ts | 38 +-
src/content/store/index.ts | 8 +
src/content/urls.ts | 8 +-
src/shared/messages.ts | 346 +++++++++++---
src/shared/operations.ts | 523 ++++++++++++++++++---
src/shared/settings/validator.ts | 2 +-
src/shared/utils/keys.ts | 2 +-
test/content/actions/follow-controller.test.ts | 2 +-
test/content/actions/input.test.ts | 2 +-
test/content/actions/mark.test.ts | 2 +-
test/content/actions/setting.test.ts | 2 +-
test/content/components/common/input.test.ts | 14 +-
test/content/reducers/addon.test.ts | 2 +-
test/content/reducers/find.test.ts | 2 +-
test/content/reducers/follow-controller.test.ts | 2 +-
test/content/reducers/input.test.ts | 2 +-
test/content/reducers/mark.test.ts | 2 +-
test/content/reducers/setting.test.ts | 2 +-
test/shared/operations.test.ts | 41 ++
tsconfig.json | 9 +-
62 files changed, 1426 insertions(+), 483 deletions(-)
create mode 100644 src/content/MessageListener.ts
create mode 100644 src/content/store/index.ts
create mode 100644 test/shared/operations.test.ts
(limited to 'src')
diff --git a/.eslintrc b/.eslintrc
index fb60bc2..7845ca5 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -35,6 +35,7 @@
"indent": ["error", 2],
"jsx-quotes": ["error", "prefer-single"],
"max-classes-per-file": "off",
+ "max-lines": "off",
"max-params": ["error", 5],
"max-statements": ["error", 15],
"multiline-comment-style": "off",
@@ -47,6 +48,7 @@
"no-console": ["error", { "allow": ["warn", "error"] }],
"no-continue": "off",
"no-empty-function": "off",
+ "no-extra-parens": "off",
"no-magic-numbers": "off",
"no-mixed-operators": "off",
"no-plusplus": "off",
diff --git a/package.json b/package.json
index 5d44a1b..a799554 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
"build": "NODE_ENV=production webpack --mode production --progress --display-error-details",
"package": "npm run build && script/package",
"lint": "eslint --ext .js,.jsx,.ts,.tsx src",
+ "type-checks": "tsc",
"test": "karma start",
"test:e2e": "mocha --timeout 8000 e2e"
},
diff --git a/src/background/controllers/OperationController.ts b/src/background/controllers/OperationController.ts
index 4e9c106..fa09512 100644
--- a/src/background/controllers/OperationController.ts
+++ b/src/background/controllers/OperationController.ts
@@ -1,4 +1,4 @@
-import operations from '../../shared/operations';
+import * as operations from '../../shared/operations';
import FindUseCase from '../usecases/FindUseCase';
import ConsoleUseCase from '../usecases/ConsoleUseCase';
import TabUseCase from '../usecases/TabUseCase';
@@ -25,7 +25,7 @@ export default class OperationController {
}
// eslint-disable-next-line complexity, max-lines-per-function
- exec(operation: any): Promise {
+ exec(operation: operations.Operation): Promise {
switch (operation.type) {
case operations.TAB_CLOSE:
return this.tabUseCase.close(false);
diff --git a/src/background/controllers/VersionController.ts b/src/background/controllers/VersionController.ts
index f402ed0..2e2a197 100644
--- a/src/background/controllers/VersionController.ts
+++ b/src/background/controllers/VersionController.ts
@@ -7,7 +7,7 @@ export default class VersionController {
this.versionUseCase = new VersionUseCase();
}
- notify(): void {
+ notify(): Promise {
return this.versionUseCase.notify();
}
}
diff --git a/src/background/domains/Setting.ts b/src/background/domains/Setting.ts
index 106ec0f..b2b1ff2 100644
--- a/src/background/domains/Setting.ts
+++ b/src/background/domains/Setting.ts
@@ -1,22 +1,30 @@
import DefaultSettings from '../../shared/settings/default';
import * as settingsValues from '../../shared/settings/values';
+type SettingValue = {
+ source: string,
+ json: string,
+ form: any
+}
+
export default class Setting {
- constructor({ source, json, form }) {
+ private obj: SettingValue;
+
+ constructor({ source, json, form }: SettingValue) {
this.obj = {
source, json, form
};
}
- get source() {
+ get source(): string {
return this.obj.source;
}
- get json() {
+ get json(): string {
return this.obj.json;
}
- get form() {
+ get form(): any {
return this.obj.form;
}
@@ -33,11 +41,11 @@ export default class Setting {
return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value };
}
- serialize() {
+ serialize(): SettingValue {
return this.obj;
}
- static deserialize(obj) {
+ static deserialize(obj: SettingValue): Setting {
return new Setting({ source: obj.source, json: obj.json, form: obj.form });
}
diff --git a/src/background/infrastructures/ConsoleClient.ts b/src/background/infrastructures/ConsoleClient.ts
index 7ad5d24..c162634 100644
--- a/src/background/infrastructures/ConsoleClient.ts
+++ b/src/background/infrastructures/ConsoleClient.ts
@@ -1,4 +1,4 @@
-import messages from '../../shared/messages';
+import * as messages from '../../shared/messages';
export default class ConsoleClient {
showCommand(tabId: number, command: string): Promise {
diff --git a/src/background/infrastructures/ContentMessageClient.ts b/src/background/infrastructures/ContentMessageClient.ts
index 20057c7..d4bc476 100644
--- a/src/background/infrastructures/ContentMessageClient.ts
+++ b/src/background/infrastructures/ContentMessageClient.ts
@@ -1,4 +1,4 @@
-import messages from '../../shared/messages';
+import * as messages from '../../shared/messages';
export default class ContentMessageClient {
async broadcastSettingsChanged(): Promise {
diff --git a/src/background/infrastructures/ContentMessageListener.ts b/src/background/infrastructures/ContentMessageListener.ts
index 81d3232..1cc2696 100644
--- a/src/background/infrastructures/ContentMessageListener.ts
+++ b/src/background/infrastructures/ContentMessageListener.ts
@@ -1,4 +1,4 @@
-import messages from '../../shared/messages';
+import * as messages from '../../shared/messages';
import CompletionGroup from '../domains/CompletionGroup';
import CommandController from '../controllers/CommandController';
import SettingController from '../controllers/SettingController';
@@ -68,7 +68,9 @@ export default class ContentMessageListener {
browser.runtime.onConnect.addListener(this.onConnected.bind(this));
}
- onMessage(message: any, senderTab: browser.tabs.Tab): Promise | any {
+ onMessage(
+ message: messages.Message, senderTab: browser.tabs.Tab,
+ ): Promise | any {
switch (message.type) {
case messages.CONSOLE_QUERY_COMPLETIONS:
return this.onConsoleQueryCompletions(message.text);
diff --git a/src/background/presenters/NotifyPresenter.ts b/src/background/presenters/NotifyPresenter.ts
index c83c205..23932f7 100644
--- a/src/background/presenters/NotifyPresenter.ts
+++ b/src/background/presenters/NotifyPresenter.ts
@@ -1,11 +1,11 @@
const NOTIFICATION_ID = 'vimvixen-update';
export default class NotifyPresenter {
- notify(
+ async notify(
title: string,
message: string,
onclick: () => void,
- ): Promise {
+ ): Promise {
const listener = (id: string) => {
if (id !== NOTIFICATION_ID) {
return;
@@ -17,7 +17,7 @@ export default class NotifyPresenter {
};
browser.notifications.onClicked.addListener(listener);
- return browser.notifications.create(NOTIFICATION_ID, {
+ await browser.notifications.create(NOTIFICATION_ID, {
'type': 'basic',
'iconUrl': browser.extension.getURL('resources/icon_48x48.png'),
title,
diff --git a/src/background/usecases/VersionUseCase.ts b/src/background/usecases/VersionUseCase.ts
index 207f9e2..3a3cc2e 100644
--- a/src/background/usecases/VersionUseCase.ts
+++ b/src/background/usecases/VersionUseCase.ts
@@ -12,7 +12,7 @@ export default class VersionUseCase {
this.notifyPresenter = new NotifyPresenter();
}
- notify(): Promise {
+ notify(): Promise {
let title = `Vim Vixen ${manifest.version} has been installed`;
let message = 'Click here to see release notes';
let url = this.releaseNoteUrl(manifest.version);
diff --git a/src/console/actions/console.ts b/src/console/actions/console.ts
index ceb419c..b1494b0 100644
--- a/src/console/actions/console.ts
+++ b/src/console/actions/console.ts
@@ -1,4 +1,4 @@
-import messages from '../../shared/messages';
+import * as messages from '../../shared/messages';
import * as actions from './index';
const hide = (): actions.ConsoleAction => {
diff --git a/src/console/index.tsx b/src/console/index.tsx
index ee3a8ee..b655154 100644
--- a/src/console/index.tsx
+++ b/src/console/index.tsx
@@ -1,4 +1,4 @@
-import messages from '../shared/messages';
+import * as messages from '../shared/messages';
import reducers from './reducers';
import { createStore, applyMiddleware } from 'redux';
import promise from 'redux-promise';
@@ -23,15 +23,16 @@ window.addEventListener('load', () => {
});
const onMessage = (message: any): any => {
- switch (message.type) {
+ let msg = messages.valueOf(message);
+ switch (msg.type) {
case messages.CONSOLE_SHOW_COMMAND:
- return store.dispatch(consoleActions.showCommand(message.command));
+ return store.dispatch(consoleActions.showCommand(msg.command));
case messages.CONSOLE_SHOW_FIND:
return store.dispatch(consoleActions.showFind());
case messages.CONSOLE_SHOW_ERROR:
- return store.dispatch(consoleActions.showError(message.text));
+ return store.dispatch(consoleActions.showError(msg.text));
case messages.CONSOLE_SHOW_INFO:
- return store.dispatch(consoleActions.showInfo(message.text));
+ return store.dispatch(consoleActions.showInfo(msg.text));
case messages.CONSOLE_HIDE:
return store.dispatch(consoleActions.hide());
}
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 => setEnabled(true);
-const disable = () => setEnabled(false);
+const disable = (): Promise => setEnabled(false);
-const setEnabled = async(enabled) => {
+const setEnabled = async(enabled: boolean): Promise => {
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 = (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 (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 => {
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 => {
return findNext(currentKeyword, reset, false);
};
-const prev = (currentKeyword, reset) => {
+const prev = (
+ currentKeyword: string, reset: boolean,
+): Promise => {
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 => {
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 = {
'': { type: operations.CANCEL },
'': { 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 => {
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 => {
+ 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 => {
+ 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,
};
// 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(
+ win: Window,
+ selector: string,
+ filter?: (e: E) => boolean,
+): E | null {
+ let nodes = Array.from(
+ win.document.querySelectorAll(selector) as NodeListOf
+ );
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(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(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,
diff --git a/src/shared/messages.ts b/src/shared/messages.ts
index 2bc12d8..41b0f0b 100644
--- a/src/shared/messages.ts
+++ b/src/shared/messages.ts
@@ -1,78 +1,276 @@
-type WebMessageSender = Window | MessagePort | ServiceWorker | null;
-type WebMessageListener = (msg: any, sender: WebMessageSender | null) => void;
-
-const onWebMessage = (listener: WebMessageListener) => {
- 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);
- });
-};
+import * as operations from './operations';
-const onBackgroundMessage = (
- listener: (msg: any, sender: browser.runtime.MessageSender,
-) => void) => {
- browser.runtime.onMessage.addListener(listener);
-};
+export const BACKGROUND_OPERATION = 'background.operation';
-const onMessage = (
- listener: (msg: any, sender: WebMessageSender | browser.runtime.MessageSender,
-) => void) => {
- onWebMessage(listener);
- onBackgroundMessage(listener);
-};
+export const CONSOLE_UNFOCUS = 'console.unfocus';
+export const CONSOLE_ENTER_COMMAND = 'console.enter.command';
+export const CONSOLE_ENTER_FIND = 'console.enter.find';
+export const CONSOLE_QUERY_COMPLETIONS = 'console.query.completions';
+export const CONSOLE_SHOW_COMMAND = 'console.show.command';
+export const CONSOLE_SHOW_ERROR = 'console.show.error';
+export const CONSOLE_SHOW_INFO = 'console.show.info';
+export const CONSOLE_SHOW_FIND = 'console.show.find';
+export const CONSOLE_HIDE = 'console.hide';
+
+export const FOLLOW_START = 'follow.start';
+export const FOLLOW_REQUEST_COUNT_TARGETS = 'follow.request.count.targets';
+export const FOLLOW_RESPONSE_COUNT_TARGETS = 'follow.response.count.targets';
+export const FOLLOW_CREATE_HINTS = 'follow.create.hints';
+export const FOLLOW_SHOW_HINTS = 'follow.update.hints';
+export const FOLLOW_REMOVE_HINTS = 'follow.remove.hints';
+export const FOLLOW_ACTIVATE = 'follow.activate';
+export const FOLLOW_KEY_PRESS = 'follow.key.press';
+
+export const MARK_SET_GLOBAL = 'mark.set.global';
+export const MARK_JUMP_GLOBAL = 'mark.jump.global';
+
+export const TAB_SCROLL_TO = 'tab.scroll.to';
+
+export const FIND_NEXT = 'find.next';
+export const FIND_PREV = 'find.prev';
+export const FIND_GET_KEYWORD = 'find.get.keyword';
+export const FIND_SET_KEYWORD = 'find.set.keyword';
+
+export const ADDON_ENABLED_QUERY = 'addon.enabled.query';
+export const ADDON_ENABLED_RESPONSE = 'addon.enabled.response';
+export const ADDON_TOGGLE_ENABLED = 'addon.toggle.enabled';
+
+export const OPEN_URL = 'open.url';
+
+export const SETTINGS_CHANGED = 'settings.changed';
+export const SETTINGS_QUERY = 'settings.query';
+
+export const CONSOLE_FRAME_MESSAGE = 'console.frame.message';
+
+interface BackgroundOperationMessage {
+ type: typeof BACKGROUND_OPERATION;
+ operation: operations.Operation;
+}
+
+interface ConsoleUnfocusMessage {
+ type: typeof CONSOLE_UNFOCUS;
+}
+
+interface ConsoleEnterCommandMessage {
+ type: typeof CONSOLE_ENTER_COMMAND;
+ text: string;
+}
+
+interface ConsoleEnterFindMessage {
+ type: typeof CONSOLE_ENTER_FIND;
+ text: string;
+}
+
+interface ConsoleQueryCompletionsMessage {
+ type: typeof CONSOLE_QUERY_COMPLETIONS;
+ text: string;
+}
+
+interface ConsoleShowCommandMessage {
+ type: typeof CONSOLE_SHOW_COMMAND;
+ command: string;
+}
+
+interface ConsoleShowErrorMessage {
+ type: typeof CONSOLE_SHOW_ERROR;
+ text: string;
+}
+
+interface ConsoleShowInfoMessage {
+ type: typeof CONSOLE_SHOW_INFO;
+ text: string;
+}
+
+interface ConsoleShowFindMessage {
+ type: typeof CONSOLE_SHOW_FIND;
+}
+
+interface ConsoleHideMessage {
+ type: typeof CONSOLE_HIDE;
+}
+
+interface FollowStartMessage {
+ type: typeof FOLLOW_START;
+ newTab: boolean;
+ background: boolean;
+}
+
+interface FollowRequestCountTargetsMessage {
+ type: typeof FOLLOW_REQUEST_COUNT_TARGETS;
+ viewSize: { width: number, height: number };
+ framePosition: { x: number, y: number };
+}
+
+interface FollowResponseCountTargetsMessage {
+ type: typeof FOLLOW_RESPONSE_COUNT_TARGETS;
+ count: number;
+}
+
+interface FollowCreateHintsMessage {
+ type: typeof FOLLOW_CREATE_HINTS;
+ keysArray: string[];
+ newTab: boolean;
+ background: boolean;
+}
+
+interface FollowShowHintsMessage {
+ type: typeof FOLLOW_SHOW_HINTS;
+ keys: string;
+}
+
+interface FollowRemoveHintsMessage {
+ type: typeof FOLLOW_REMOVE_HINTS;
+}
+
+interface FollowActivateMessage {
+ type: typeof FOLLOW_ACTIVATE;
+ keys: string;
+}
+
+interface FollowKeyPressMessage {
+ type: typeof FOLLOW_KEY_PRESS;
+ key: string;
+ ctrlKey: boolean;
+}
+
+interface MarkSetGlobalMessage {
+ type: typeof MARK_SET_GLOBAL;
+ key: string;
+ x: number;
+ y: number;
+}
+
+interface MarkJumpGlobalMessage {
+ type: typeof MARK_JUMP_GLOBAL;
+ key: string;
+}
+
+interface TabScrollToMessage {
+ type: typeof TAB_SCROLL_TO;
+ x: number;
+ y: number;
+}
+
+interface FindNextMessage {
+ type: typeof FIND_NEXT;
+}
+
+interface FindPrevMessage {
+ type: typeof FIND_PREV;
+}
+
+interface FindGetKeywordMessage {
+ type: typeof FIND_GET_KEYWORD;
+}
+
+interface FindSetKeywordMessage {
+ type: typeof FIND_SET_KEYWORD;
+ keyword: string;
+ found: boolean;
+}
+
+interface AddonEnabledQueryMessage {
+ type: typeof ADDON_ENABLED_QUERY;
+}
+
+interface AddonEnabledResponseMessage {
+ type: typeof ADDON_ENABLED_RESPONSE;
+ enabled: boolean;
+}
+
+interface AddonToggleEnabledMessage {
+ type: typeof ADDON_TOGGLE_ENABLED;
+}
+
+interface OpenUrlMessage {
+ type: typeof OPEN_URL;
+ url: string;
+ newTab: boolean;
+ background: boolean;
+}
+
+interface SettingsChangedMessage {
+ type: typeof SETTINGS_CHANGED;
+}
+
+interface SettingsQueryMessage {
+ type: typeof SETTINGS_QUERY;
+}
+
+interface ConsoleFrameMessageMessage {
+ type: typeof CONSOLE_FRAME_MESSAGE;
+ message: any;
+}
+
+export type Message =
+ BackgroundOperationMessage |
+ ConsoleUnfocusMessage |
+ ConsoleEnterCommandMessage |
+ ConsoleEnterFindMessage |
+ ConsoleQueryCompletionsMessage |
+ ConsoleShowCommandMessage |
+ ConsoleShowErrorMessage |
+ ConsoleShowInfoMessage |
+ ConsoleShowFindMessage |
+ ConsoleHideMessage |
+ FollowStartMessage |
+ FollowRequestCountTargetsMessage |
+ FollowResponseCountTargetsMessage |
+ FollowCreateHintsMessage |
+ FollowShowHintsMessage |
+ FollowRemoveHintsMessage |
+ FollowActivateMessage |
+ FollowKeyPressMessage |
+ MarkSetGlobalMessage |
+ MarkJumpGlobalMessage |
+ TabScrollToMessage |
+ FindNextMessage |
+ FindPrevMessage |
+ FindGetKeywordMessage |
+ FindSetKeywordMessage |
+ AddonEnabledQueryMessage |
+ AddonEnabledResponseMessage |
+ AddonToggleEnabledMessage |
+ OpenUrlMessage |
+ SettingsChangedMessage |
+ SettingsQueryMessage |
+ ConsoleFrameMessageMessage;
-export default {
- BACKGROUND_OPERATION: 'background.operation',
-
- CONSOLE_UNFOCUS: 'console.unfocus',
- CONSOLE_ENTER_COMMAND: 'console.enter.command',
- CONSOLE_ENTER_FIND: 'console.enter.find',
- CONSOLE_QUERY_COMPLETIONS: 'console.query.completions',
- CONSOLE_SHOW_COMMAND: 'console.show.command',
- CONSOLE_SHOW_ERROR: 'console.show.error',
- CONSOLE_SHOW_INFO: 'console.show.info',
- CONSOLE_SHOW_FIND: 'console.show.find',
- CONSOLE_HIDE: 'console.hide',
-
- FOLLOW_START: 'follow.start',
- FOLLOW_REQUEST_COUNT_TARGETS: 'follow.request.count.targets',
- FOLLOW_RESPONSE_COUNT_TARGETS: 'follow.response.count.targets',
- FOLLOW_CREATE_HINTS: 'follow.create.hints',
- FOLLOW_SHOW_HINTS: 'follow.update.hints',
- FOLLOW_REMOVE_HINTS: 'follow.remove.hints',
- FOLLOW_ACTIVATE: 'follow.activate',
- FOLLOW_KEY_PRESS: 'follow.key.press',
-
- MARK_SET_GLOBAL: 'mark.set.global',
- MARK_JUMP_GLOBAL: 'mark.jump.global',
-
- TAB_SCROLL_TO: 'tab.scroll.to',
-
- FIND_NEXT: 'find.next',
- FIND_PREV: 'find.prev',
- FIND_GET_KEYWORD: 'find.get.keyword',
- FIND_SET_KEYWORD: 'find.set.keyword',
-
- ADDON_ENABLED_QUERY: 'addon.enabled.query',
- ADDON_ENABLED_RESPONSE: 'addon.enabled.response',
- ADDON_TOGGLE_ENABLED: 'addon.toggle.enabled',
-
- OPEN_URL: 'open.url',
-
- SETTINGS_CHANGED: 'settings.changed',
- SETTINGS_QUERY: 'settings.query',
-
- WINDOW_TOP_MESSAGE: 'window.top.message',
- CONSOLE_FRAME_MESSAGE: 'console.frame.message',
-
- onWebMessage,
- onBackgroundMessage,
- onMessage,
+// eslint-disable-next-line complexity
+export const valueOf = (o: any): Message => {
+ switch (o.type) {
+ case CONSOLE_UNFOCUS:
+ case CONSOLE_ENTER_COMMAND:
+ case CONSOLE_ENTER_FIND:
+ case CONSOLE_QUERY_COMPLETIONS:
+ case CONSOLE_SHOW_COMMAND:
+ case CONSOLE_SHOW_ERROR:
+ case CONSOLE_SHOW_INFO:
+ case CONSOLE_SHOW_FIND:
+ case CONSOLE_HIDE:
+ case FOLLOW_START:
+ case FOLLOW_REQUEST_COUNT_TARGETS:
+ case FOLLOW_RESPONSE_COUNT_TARGETS:
+ case FOLLOW_CREATE_HINTS:
+ case FOLLOW_SHOW_HINTS:
+ case FOLLOW_REMOVE_HINTS:
+ case FOLLOW_ACTIVATE:
+ case FOLLOW_KEY_PRESS:
+ case MARK_SET_GLOBAL:
+ case MARK_JUMP_GLOBAL:
+ case TAB_SCROLL_TO:
+ case FIND_NEXT:
+ case FIND_PREV:
+ case FIND_GET_KEYWORD:
+ case FIND_SET_KEYWORD:
+ case ADDON_ENABLED_QUERY:
+ case ADDON_ENABLED_RESPONSE:
+ case ADDON_TOGGLE_ENABLED:
+ case OPEN_URL:
+ case SETTINGS_CHANGED:
+ case SETTINGS_QUERY:
+ case CONSOLE_FRAME_MESSAGE:
+ return o;
+ }
+ throw new Error('unknown operation type: ' + o.type);
};
diff --git a/src/shared/operations.ts b/src/shared/operations.ts
index d59723e..cc22f75 100644
--- a/src/shared/operations.ts
+++ b/src/shared/operations.ts
@@ -1,80 +1,447 @@
-const operations: { [key: string]: string } = {
- // Hide console, or cancel some user actions
- CANCEL: 'cancel',
-
- // Addons
- ADDON_ENABLE: 'addon.enable',
- ADDON_DISABLE: 'addon.disable',
- ADDON_TOGGLE_ENABLED: 'addon.toggle.enabled',
-
- // Command
- COMMAND_SHOW: 'command.show',
- COMMAND_SHOW_OPEN: 'command.show.open',
- COMMAND_SHOW_TABOPEN: 'command.show.tabopen',
- COMMAND_SHOW_WINOPEN: 'command.show.winopen',
- COMMAND_SHOW_BUFFER: 'command.show.buffer',
- COMMAND_SHOW_ADDBOOKMARK: 'command.show.addbookmark',
-
- // Scrolls
- SCROLL_VERTICALLY: 'scroll.vertically',
- SCROLL_HORIZONALLY: 'scroll.horizonally',
- SCROLL_PAGES: 'scroll.pages',
- SCROLL_TOP: 'scroll.top',
- SCROLL_BOTTOM: 'scroll.bottom',
- SCROLL_HOME: 'scroll.home',
- SCROLL_END: 'scroll.end',
-
- // Follows
- FOLLOW_START: 'follow.start',
-
- // Navigations
- NAVIGATE_HISTORY_PREV: 'navigate.history.prev',
- NAVIGATE_HISTORY_NEXT: 'navigate.history.next',
- NAVIGATE_LINK_PREV: 'navigate.link.prev',
- NAVIGATE_LINK_NEXT: 'navigate.link.next',
- NAVIGATE_PARENT: 'navigate.parent',
- NAVIGATE_ROOT: 'navigate.root',
-
- // Focus
- FOCUS_INPUT: 'focus.input',
-
- // Page
- PAGE_SOURCE: 'page.source',
- PAGE_HOME: 'page.home',
-
- // Tabs
- TAB_CLOSE: 'tabs.close',
- TAB_CLOSE_FORCE: 'tabs.close.force',
- TAB_CLOSE_RIGHT: 'tabs.close.right',
- TAB_REOPEN: 'tabs.reopen',
- TAB_PREV: 'tabs.prev',
- TAB_NEXT: 'tabs.next',
- TAB_FIRST: 'tabs.first',
- TAB_LAST: 'tabs.last',
- TAB_PREV_SEL: 'tabs.prevsel',
- TAB_RELOAD: 'tabs.reload',
- TAB_PIN: 'tabs.pin',
- TAB_UNPIN: 'tabs.unpin',
- TAB_TOGGLE_PINNED: 'tabs.pin.toggle',
- TAB_DUPLICATE: 'tabs.duplicate',
-
- // Zooms
- ZOOM_IN: 'zoom.in',
- ZOOM_OUT: 'zoom.out',
- ZOOM_NEUTRAL: 'zoom.neutral',
-
- // Url yank/paste
- URLS_YANK: 'urls.yank',
- URLS_PASTE: 'urls.paste',
-
- // Find
- FIND_START: 'find.start',
- FIND_NEXT: 'find.next',
- FIND_PREV: 'find.prev',
-
- // Mark
- MARK_SET_PREFIX: 'mark.set.prefix',
- MARK_JUMP_PREFIX: 'mark.jump.prefix',
+// Hide console; or cancel some user actions
+export const CANCEL = 'cancel';
+
+// Addons
+export const ADDON_ENABLE = 'addon.enable';
+export const ADDON_DISABLE = 'addon.disable';
+export const ADDON_TOGGLE_ENABLED = 'addon.toggle.enabled';
+
+// Command
+export const COMMAND_SHOW = 'command.show';
+export const COMMAND_SHOW_OPEN = 'command.show.open';
+export const COMMAND_SHOW_TABOPEN = 'command.show.tabopen';
+export const COMMAND_SHOW_WINOPEN = 'command.show.winopen';
+export const COMMAND_SHOW_BUFFER = 'command.show.buffer';
+export const COMMAND_SHOW_ADDBOOKMARK = 'command.show.addbookmark';
+
+// Scrolls
+export const SCROLL_VERTICALLY = 'scroll.vertically';
+export const SCROLL_HORIZONALLY = 'scroll.horizonally';
+export const SCROLL_PAGES = 'scroll.pages';
+export const SCROLL_TOP = 'scroll.top';
+export const SCROLL_BOTTOM = 'scroll.bottom';
+export const SCROLL_HOME = 'scroll.home';
+export const SCROLL_END = 'scroll.end';
+
+// Follows
+export const FOLLOW_START = 'follow.start';
+
+// Navigations
+export const NAVIGATE_HISTORY_PREV = 'navigate.history.prev';
+export const NAVIGATE_HISTORY_NEXT = 'navigate.history.next';
+export const NAVIGATE_LINK_PREV = 'navigate.link.prev';
+export const NAVIGATE_LINK_NEXT = 'navigate.link.next';
+export const NAVIGATE_PARENT = 'navigate.parent';
+export const NAVIGATE_ROOT = 'navigate.root';
+
+// Focus
+export const FOCUS_INPUT = 'focus.input';
+
+// Page
+export const PAGE_SOURCE = 'page.source';
+export const PAGE_HOME = 'page.home';
+
+// Tabs
+export const TAB_CLOSE = 'tabs.close';
+export const TAB_CLOSE_FORCE = 'tabs.close.force';
+export const TAB_CLOSE_RIGHT = 'tabs.close.right';
+export const TAB_REOPEN = 'tabs.reopen';
+export const TAB_PREV = 'tabs.prev';
+export const TAB_NEXT = 'tabs.next';
+export const TAB_FIRST = 'tabs.first';
+export const TAB_LAST = 'tabs.last';
+export const TAB_PREV_SEL = 'tabs.prevsel';
+export const TAB_RELOAD = 'tabs.reload';
+export const TAB_PIN = 'tabs.pin';
+export const TAB_UNPIN = 'tabs.unpin';
+export const TAB_TOGGLE_PINNED = 'tabs.pin.toggle';
+export const TAB_DUPLICATE = 'tabs.duplicate';
+
+// Zooms
+export const ZOOM_IN = 'zoom.in';
+export const ZOOM_OUT = 'zoom.out';
+export const ZOOM_NEUTRAL = 'zoom.neutral';
+
+// Url yank/paste
+export const URLS_YANK = 'urls.yank';
+export const URLS_PASTE = 'urls.paste';
+
+// Find
+export const FIND_START = 'find.start';
+export const FIND_NEXT = 'find.next';
+export const FIND_PREV = 'find.prev';
+
+// Mark
+export const MARK_SET_PREFIX = 'mark.set.prefix';
+export const MARK_JUMP_PREFIX = 'mark.jump.prefix';
+
+export interface CancelOperation {
+ type: typeof CANCEL;
+}
+
+export interface AddonEnableOperation {
+ type: typeof ADDON_ENABLE;
+}
+
+export interface AddonDisableOperation {
+ type: typeof ADDON_DISABLE;
+}
+
+export interface AddonToggleEnabledOperation {
+ type: typeof ADDON_TOGGLE_ENABLED;
+}
+
+export interface CommandShowOperation {
+ type: typeof COMMAND_SHOW;
+}
+
+export interface CommandShowOpenOperation {
+ type: typeof COMMAND_SHOW_OPEN;
+ alter: boolean;
+}
+
+export interface CommandShowTabopenOperation {
+ type: typeof COMMAND_SHOW_TABOPEN;
+ alter: boolean;
+}
+
+export interface CommandShowWinopenOperation {
+ type: typeof COMMAND_SHOW_WINOPEN;
+ alter: boolean;
+}
+
+export interface CommandShowBufferOperation {
+ type: typeof COMMAND_SHOW_BUFFER;
+}
+
+export interface CommandShowAddbookmarkOperation {
+ type: typeof COMMAND_SHOW_ADDBOOKMARK;
+ alter: boolean;
+}
+
+export interface ScrollVerticallyOperation {
+ type: typeof SCROLL_VERTICALLY;
+ count: number;
+}
+
+export interface ScrollHorizonallyOperation {
+ type: typeof SCROLL_HORIZONALLY;
+ count: number;
+}
+
+export interface ScrollPagesOperation {
+ type: typeof SCROLL_PAGES;
+ count: number;
+}
+
+export interface ScrollTopOperation {
+ type: typeof SCROLL_TOP;
+}
+
+export interface ScrollBottomOperation {
+ type: typeof SCROLL_BOTTOM;
+}
+
+export interface ScrollHomeOperation {
+ type: typeof SCROLL_HOME;
+}
+
+export interface ScrollEndOperation {
+ type: typeof SCROLL_END;
+}
+
+export interface FollowStartOperation {
+ type: typeof FOLLOW_START;
+ newTab: boolean;
+ background: boolean;
+}
+
+export interface NavigateHistoryPrevOperation {
+ type: typeof NAVIGATE_HISTORY_PREV;
+}
+
+export interface NavigateHistoryNextOperation {
+ type: typeof NAVIGATE_HISTORY_NEXT;
+}
+
+export interface NavigateLinkPrevOperation {
+ type: typeof NAVIGATE_LINK_PREV;
+}
+
+export interface NavigateLinkNextOperation {
+ type: typeof NAVIGATE_LINK_NEXT;
+}
+
+export interface NavigateParentOperation {
+ type: typeof NAVIGATE_PARENT;
+}
+
+export interface NavigateRootOperation {
+ type: typeof NAVIGATE_ROOT;
+}
+
+export interface FocusInputOperation {
+ type: typeof FOCUS_INPUT;
+}
+
+export interface PageSourceOperation {
+ type: typeof PAGE_SOURCE;
+}
+
+export interface PageHomeOperation {
+ type: typeof PAGE_HOME;
+ newTab: boolean;
+}
+
+export interface TabCloseOperation {
+ type: typeof TAB_CLOSE;
+}
+
+export interface TabCloseForceOperation {
+ type: typeof TAB_CLOSE_FORCE;
+}
+
+export interface TabCloseRightOperation {
+ type: typeof TAB_CLOSE_RIGHT;
+}
+
+export interface TabReopenOperation {
+ type: typeof TAB_REOPEN;
+}
+
+export interface TabPrevOperation {
+ type: typeof TAB_PREV;
+}
+
+export interface TabNextOperation {
+ type: typeof TAB_NEXT;
+}
+
+export interface TabFirstOperation {
+ type: typeof TAB_FIRST;
+}
+
+export interface TabLastOperation {
+ type: typeof TAB_LAST;
+}
+
+export interface TabPrevSelOperation {
+ type: typeof TAB_PREV_SEL;
+}
+
+export interface TabReloadOperation {
+ type: typeof TAB_RELOAD;
+ cache: boolean;
+}
+
+export interface TabPinOperation {
+ type: typeof TAB_PIN;
+}
+
+export interface TabUnpinOperation {
+ type: typeof TAB_UNPIN;
+}
+
+export interface TabTogglePinnedOperation {
+ type: typeof TAB_TOGGLE_PINNED;
+}
+
+export interface TabDuplicateOperation {
+ type: typeof TAB_DUPLICATE;
+}
+
+export interface ZoomInOperation {
+ type: typeof ZOOM_IN;
+}
+
+export interface ZoomOutOperation {
+ type: typeof ZOOM_OUT;
+}
+
+export interface ZoomNeutralOperation {
+ type: typeof ZOOM_NEUTRAL;
+}
+
+export interface UrlsYankOperation {
+ type: typeof URLS_YANK;
+}
+
+export interface UrlsPasteOperation {
+ type: typeof URLS_PASTE;
+ newTab: boolean;
+}
+
+export interface FindStartOperation {
+ type: typeof FIND_START;
+}
+
+export interface FindNextOperation {
+ type: typeof FIND_NEXT;
+}
+
+export interface FindPrevOperation {
+ type: typeof FIND_PREV;
+}
+
+export interface MarkSetPrefixOperation {
+ type: typeof MARK_SET_PREFIX;
+}
+
+export interface MarkJumpPrefixOperation {
+ type: typeof MARK_JUMP_PREFIX;
+}
+
+export type Operation =
+ CancelOperation |
+ AddonEnableOperation |
+ AddonDisableOperation |
+ AddonToggleEnabledOperation |
+ CommandShowOperation |
+ CommandShowOpenOperation |
+ CommandShowTabopenOperation |
+ CommandShowWinopenOperation |
+ CommandShowBufferOperation |
+ CommandShowAddbookmarkOperation |
+ ScrollVerticallyOperation |
+ ScrollHorizonallyOperation |
+ ScrollPagesOperation |
+ ScrollTopOperation |
+ ScrollBottomOperation |
+ ScrollHomeOperation |
+ ScrollEndOperation |
+ FollowStartOperation |
+ NavigateHistoryPrevOperation |
+ NavigateHistoryNextOperation |
+ NavigateLinkPrevOperation |
+ NavigateLinkNextOperation |
+ NavigateParentOperation |
+ NavigateRootOperation |
+ FocusInputOperation |
+ PageSourceOperation |
+ PageHomeOperation |
+ TabCloseOperation |
+ TabCloseForceOperation |
+ TabCloseRightOperation |
+ TabReopenOperation |
+ TabPrevOperation |
+ TabNextOperation |
+ TabFirstOperation |
+ TabLastOperation |
+ TabPrevSelOperation |
+ TabReloadOperation |
+ TabPinOperation |
+ TabUnpinOperation |
+ TabTogglePinnedOperation |
+ TabDuplicateOperation |
+ ZoomInOperation |
+ ZoomOutOperation |
+ ZoomNeutralOperation |
+ UrlsYankOperation |
+ UrlsPasteOperation |
+ FindStartOperation |
+ FindNextOperation |
+ FindPrevOperation |
+ MarkSetPrefixOperation |
+ MarkJumpPrefixOperation;
+
+const assertOptionalBoolean = (obj: any, name: string) => {
+ if (Object.prototype.hasOwnProperty.call(obj, name) &&
+ typeof obj[name] !== 'boolean') {
+ throw new TypeError(`Not a boolean parameter '${name}'`);
+ }
+};
+
+const assertRequiredNumber = (obj: any, name: string) => {
+ if (!Object.prototype.hasOwnProperty.call(obj, name) ||
+ typeof obj[name] !== 'number') {
+ throw new TypeError(`Missing number parameter '${name}`);
+ }
};
-export default operations;
+// eslint-disable-next-line complexity, max-lines-per-function
+export const valueOf = (o: any): Operation => {
+ if (!Object.prototype.hasOwnProperty.call(o, 'type')) {
+ throw new TypeError(`missing 'type' field`);
+ }
+ switch (o.type) {
+ case COMMAND_SHOW_OPEN:
+ case COMMAND_SHOW_TABOPEN:
+ case COMMAND_SHOW_WINOPEN:
+ case COMMAND_SHOW_ADDBOOKMARK:
+ assertOptionalBoolean(o, 'alter');
+ return { type: o.type, alter: Boolean(o.alter) };
+ case SCROLL_VERTICALLY:
+ case SCROLL_HORIZONALLY:
+ case SCROLL_PAGES:
+ assertRequiredNumber(o, 'count');
+ return { type: o.type, count: Number(o.count) };
+ case FOLLOW_START:
+ assertOptionalBoolean(o, 'newTab');
+ assertOptionalBoolean(o, 'background');
+ return {
+ type: FOLLOW_START,
+ newTab: Boolean(typeof o.newTab === undefined ? false : o.newTab),
+ background: Boolean(typeof o.background === undefined ? true : o.background), // eslint-disable-line max-len
+ };
+ case PAGE_HOME:
+ assertOptionalBoolean(o, 'newTab');
+ return {
+ type: PAGE_HOME,
+ newTab: Boolean(typeof o.newTab === undefined ? false : o.newTab),
+ };
+ case TAB_RELOAD:
+ assertOptionalBoolean(o, 'cache');
+ return {
+ type: TAB_RELOAD,
+ cache: Boolean(typeof o.cache === undefined ? false : o.cache),
+ };
+ case URLS_PASTE:
+ assertOptionalBoolean(o, 'newTab');
+ return {
+ type: URLS_PASTE,
+ newTab: Boolean(typeof o.newTab === undefined ? false : o.newTab),
+ };
+ case CANCEL:
+ case ADDON_ENABLE:
+ case ADDON_DISABLE:
+ case ADDON_TOGGLE_ENABLED:
+ case COMMAND_SHOW:
+ case COMMAND_SHOW_BUFFER:
+ case SCROLL_TOP:
+ case SCROLL_BOTTOM:
+ case SCROLL_HOME:
+ case SCROLL_END:
+ case NAVIGATE_HISTORY_PREV:
+ case NAVIGATE_HISTORY_NEXT:
+ case NAVIGATE_LINK_PREV:
+ case NAVIGATE_LINK_NEXT:
+ case NAVIGATE_PARENT:
+ case NAVIGATE_ROOT:
+ case FOCUS_INPUT:
+ case PAGE_SOURCE:
+ case TAB_CLOSE:
+ case TAB_CLOSE_FORCE:
+ case TAB_CLOSE_RIGHT:
+ case TAB_REOPEN:
+ case TAB_PREV:
+ case TAB_NEXT:
+ case TAB_FIRST:
+ case TAB_LAST:
+ case TAB_PREV_SEL:
+ case TAB_PIN:
+ case TAB_UNPIN:
+ case TAB_TOGGLE_PINNED:
+ case TAB_DUPLICATE:
+ case ZOOM_IN:
+ case ZOOM_OUT:
+ case ZOOM_NEUTRAL:
+ case URLS_YANK:
+ case FIND_START:
+ case FIND_NEXT:
+ case FIND_PREV:
+ case MARK_SET_PREFIX:
+ case MARK_JUMP_PREFIX:
+ return { type: o.type };
+ }
+ throw new Error('unknown operation type: ' + o.type);
+};
diff --git a/src/shared/settings/validator.ts b/src/shared/settings/validator.ts
index 0483931..71cc466 100644
--- a/src/shared/settings/validator.ts
+++ b/src/shared/settings/validator.ts
@@ -1,4 +1,4 @@
-import operations from '../operations';
+import * as operations from '../operations';
import * as properties from './properties';
const VALID_TOP_KEYS = ['keymaps', 'search', 'blacklist', 'properties'];
diff --git a/src/shared/utils/keys.ts b/src/shared/utils/keys.ts
index d9abef7..e9b0365 100644
--- a/src/shared/utils/keys.ts
+++ b/src/shared/utils/keys.ts
@@ -1,4 +1,4 @@
-interface Key {
+export interface Key {
key: string;
shiftKey: boolean | undefined;
ctrlKey: boolean | undefined;
diff --git a/test/content/actions/follow-controller.test.ts b/test/content/actions/follow-controller.test.ts
index 718a90a..a4b1710 100644
--- a/test/content/actions/follow-controller.test.ts
+++ b/test/content/actions/follow-controller.test.ts
@@ -1,4 +1,4 @@
-import actions from 'content/actions';
+import * as actions from 'content/actions';
import * as followControllerActions from 'content/actions/follow-controller';
describe('follow-controller actions', () => {
diff --git a/test/content/actions/input.test.ts b/test/content/actions/input.test.ts
index fe9db5f..33238a5 100644
--- a/test/content/actions/input.test.ts
+++ b/test/content/actions/input.test.ts
@@ -1,4 +1,4 @@
-import actions from 'content/actions';
+import * as actions from 'content/actions';
import * as inputActions from 'content/actions/input';
describe("input actions", () => {
diff --git a/test/content/actions/mark.test.ts b/test/content/actions/mark.test.ts
index adbf06b..6c6d59e 100644
--- a/test/content/actions/mark.test.ts
+++ b/test/content/actions/mark.test.ts
@@ -1,4 +1,4 @@
-import actions from 'content/actions';
+import * as actions from 'content/actions';
import * as markActions from 'content/actions/mark';
describe('mark actions', () => {
diff --git a/test/content/actions/setting.test.ts b/test/content/actions/setting.test.ts
index 10f6807..0721d5d 100644
--- a/test/content/actions/setting.test.ts
+++ b/test/content/actions/setting.test.ts
@@ -1,4 +1,4 @@
-import actions from 'content/actions';
+import * as actions from 'content/actions';
import * as settingActions from 'content/actions/setting';
describe("setting actions", () => {
diff --git a/test/content/components/common/input.test.ts b/test/content/components/common/input.test.ts
index 2ba5507..f3a943c 100644
--- a/test/content/components/common/input.test.ts
+++ b/test/content/components/common/input.test.ts
@@ -21,12 +21,14 @@ describe('InputComponent', () => {
++b;
}
});
- component.onKeyDown({ key: 'a' });
- component.onKeyDown({ key: 'b' });
- component.onKeyPress({ key: 'a' });
- component.onKeyUp({ key: 'a' });
- component.onKeyPress({ key: 'b' });
- component.onKeyUp({ key: 'b' });
+
+ let elem = document.body;
+ component.onKeyDown({ key: 'a', target: elem });
+ component.onKeyDown({ key: 'b', target: elem });
+ component.onKeyPress({ key: 'a', target: elem });
+ component.onKeyUp({ key: 'a', target: elem });
+ component.onKeyPress({ key: 'b', target: elem });
+ component.onKeyUp({ key: 'b', target: elem });
expect(a).is.equals(1);
expect(b).is.equals(1);
diff --git a/test/content/reducers/addon.test.ts b/test/content/reducers/addon.test.ts
index d4eb845..fb05244 100644
--- a/test/content/reducers/addon.test.ts
+++ b/test/content/reducers/addon.test.ts
@@ -1,4 +1,4 @@
-import actions from 'content/actions';
+import * as actions from 'content/actions';
import addonReducer from 'content/reducers/addon';
describe("addon reducer", () => {
diff --git a/test/content/reducers/find.test.ts b/test/content/reducers/find.test.ts
index a8c30d7..66a2c67 100644
--- a/test/content/reducers/find.test.ts
+++ b/test/content/reducers/find.test.ts
@@ -1,4 +1,4 @@
-import actions from 'content/actions';
+import * as actions from 'content/actions';
import findReducer from 'content/reducers/find';
describe("find reducer", () => {
diff --git a/test/content/reducers/follow-controller.test.ts b/test/content/reducers/follow-controller.test.ts
index 8a4c2d4..39f326c 100644
--- a/test/content/reducers/follow-controller.test.ts
+++ b/test/content/reducers/follow-controller.test.ts
@@ -1,4 +1,4 @@
-import actions from 'content/actions';
+import * as actions from 'content/actions';
import followControllerReducer from 'content/reducers/follow-controller';
describe('follow-controller reducer', () => {
diff --git a/test/content/reducers/input.test.ts b/test/content/reducers/input.test.ts
index 0011943..f892201 100644
--- a/test/content/reducers/input.test.ts
+++ b/test/content/reducers/input.test.ts
@@ -1,4 +1,4 @@
-import actions from 'content/actions';
+import * as actions from 'content/actions';
import inputReducer from 'content/reducers/input';
describe("input reducer", () => {
diff --git a/test/content/reducers/mark.test.ts b/test/content/reducers/mark.test.ts
index 76efbf7..1a51c3e 100644
--- a/test/content/reducers/mark.test.ts
+++ b/test/content/reducers/mark.test.ts
@@ -1,4 +1,4 @@
-import actions from 'content/actions';
+import * as actions from 'content/actions';
import reducer from 'content/reducers/mark';
describe("mark reducer", () => {
diff --git a/test/content/reducers/setting.test.ts b/test/content/reducers/setting.test.ts
index 4e4c095..226fc58 100644
--- a/test/content/reducers/setting.test.ts
+++ b/test/content/reducers/setting.test.ts
@@ -1,4 +1,4 @@
-import actions from 'content/actions';
+import * as actions from 'content/actions';
import settingReducer from 'content/reducers/setting';
describe("content setting reducer", () => {
diff --git a/test/shared/operations.test.ts b/test/shared/operations.test.ts
new file mode 100644
index 0000000..42a3eed
--- /dev/null
+++ b/test/shared/operations.test.ts
@@ -0,0 +1,41 @@
+import * as operations from 'shared/operations';
+
+describe('operations', () => {
+ describe('#valueOf', () => {
+ it('returns an Operation', () => {
+ let op: operations.Operation = operations.valueOf({
+ type: operations.SCROLL_VERTICALLY,
+ count: 10,
+ });
+ expect(op.type).to.equal(operations.SCROLL_VERTICALLY);
+ expect(op.count).to.equal(10);
+ });
+
+ it('throws an Error on missing required parameter', () => {
+ expect(() => operations.valueOf({
+ type: operations.SCROLL_VERTICALLY,
+ })).to.throw(TypeError);
+ });
+
+ it('fills default valus of optional parameter', () => {
+ let op: operations.Operation = operations.valueOf({
+ type: operations.COMMAND_SHOW_OPEN,
+ });
+
+ expect(op.type).to.equal(operations.COMMAND_SHOW_OPEN)
+ expect(op.alter).to.be.false;
+ });
+
+ it('throws an Error on mismatch of parameter', () => {
+ expect(() => operations.valueOf({
+ type: operations.SCROLL_VERTICALLY,
+ count: '10',
+ })).to.throw(TypeError);
+
+ expect(() => valueOf({
+ type: operations.COMMAND_SHOW_OPEN,
+ alter: 'true',
+ })).to.throw(TypeError);
+ });
+ });
+})
diff --git a/tsconfig.json b/tsconfig.json
index 575601b..b61ee23 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,11 +2,11 @@
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
+ "lib": ["es6", "dom", "es2017"],
"allowJs": true,
"checkJs": true,
+ "noEmit": true,
"jsx": "react",
- "declaration": true,
- "declarationMap": true,
"sourceMap": true,
"outDir": "./build",
"removeComments": true,
@@ -29,5 +29,8 @@
"esModuleInterop": true,
"typeRoots": ["node_modules/@types", "node_modules/web-ext-types"]
- }
+ },
+ "include": [
+ "src"
+ ]
}
--
cgit v1.2.3
From a0882bbceb7ed71d56bf8557620449fbc3f19749 Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka
Date: Sun, 5 May 2019 08:03:29 +0900
Subject: Declare setting types
---
src/background/controllers/SettingController.ts | 3 +-
src/background/domains/GlobalMark.ts | 3 +-
src/background/domains/Setting.ts | 59 ---
.../repositories/PersistentSettingRepository.ts | 6 +-
src/background/repositories/SettingRepository.ts | 34 +-
src/background/usecases/CommandUseCase.ts | 7 +-
src/background/usecases/CompletionsUseCase.ts | 30 +-
src/background/usecases/SettingUseCase.ts | 17 +-
src/background/usecases/parsers.ts | 21 +-
src/content/actions/index.ts | 3 +-
src/content/actions/operation.ts | 4 +-
src/content/actions/setting.ts | 23 +-
src/content/components/common/index.ts | 5 +-
src/content/components/common/input.ts | 1 -
src/content/components/common/keymapper.ts | 28 +-
src/content/components/common/mark.ts | 10 +-
.../components/top-content/follow-controller.ts | 4 +-
src/content/reducers/setting.ts | 24 +-
src/settings/actions/index.ts | 16 +-
src/settings/actions/setting.ts | 60 +--
src/settings/components/form/KeymapsForm.tsx | 23 +-
src/settings/components/form/SearchForm.tsx | 30 +-
src/settings/components/index.tsx | 104 ++++--
src/settings/keymaps.ts | 3 -
src/settings/reducers/setting.ts | 22 +-
src/settings/storage.ts | 15 +
src/shared/SettingData.ts | 414 +++++++++++++++++++++
src/shared/Settings.ts | 200 ++++++++++
src/shared/operations.ts | 2 +-
src/shared/properties.ts | 50 +++
src/shared/property-defs.ts | 50 +++
src/shared/settings/default.ts | 85 -----
src/shared/settings/properties.ts | 24 --
src/shared/settings/storage.ts | 32 --
src/shared/settings/validator.ts | 76 ----
src/shared/settings/values.ts | 108 ------
test/background/usecases/parsers.test.ts | 41 +-
test/content/actions/setting.test.ts | 46 ++-
test/content/reducers/setting.test.ts | 21 +-
test/settings/components/form/KeymapsForm.test.tsx | 14 +-
.../components/form/SearchEngineForm.test.tsx | 60 ++-
test/settings/reducers/setting.test.ts | 3 +-
test/shared/SettingData.test.ts | 293 +++++++++++++++
test/shared/Settings.test.ts | 190 ++++++++++
test/shared/properties.test.js | 18 +
test/shared/property-defs.test.js | 18 +
test/shared/settings/validator.test.ts | 81 ----
test/shared/settings/values.test.ts | 138 -------
48 files changed, 1617 insertions(+), 902 deletions(-)
delete mode 100644 src/background/domains/Setting.ts
create mode 100644 src/settings/storage.ts
create mode 100644 src/shared/SettingData.ts
create mode 100644 src/shared/Settings.ts
create mode 100644 src/shared/properties.ts
create mode 100644 src/shared/property-defs.ts
delete mode 100644 src/shared/settings/default.ts
delete mode 100644 src/shared/settings/properties.ts
delete mode 100644 src/shared/settings/storage.ts
delete mode 100644 src/shared/settings/validator.ts
delete mode 100644 src/shared/settings/values.ts
create mode 100644 test/shared/SettingData.test.ts
create mode 100644 test/shared/Settings.test.ts
create mode 100644 test/shared/properties.test.js
create mode 100644 test/shared/property-defs.test.js
delete mode 100644 test/shared/settings/validator.test.ts
delete mode 100644 test/shared/settings/values.test.ts
(limited to 'src')
diff --git a/src/background/controllers/SettingController.ts b/src/background/controllers/SettingController.ts
index f8b7302..dfd2817 100644
--- a/src/background/controllers/SettingController.ts
+++ b/src/background/controllers/SettingController.ts
@@ -1,5 +1,6 @@
import SettingUseCase from '../usecases/SettingUseCase';
import ContentMessageClient from '../infrastructures/ContentMessageClient';
+import Settings from '../../shared/Settings';
export default class SettingController {
private settingUseCase: SettingUseCase;
@@ -11,7 +12,7 @@ export default class SettingController {
this.contentMessageClient = new ContentMessageClient();
}
- getSetting(): any {
+ getSetting(): Promise {
return this.settingUseCase.get();
}
diff --git a/src/background/domains/GlobalMark.ts b/src/background/domains/GlobalMark.ts
index 0964373..1ae912e 100644
--- a/src/background/domains/GlobalMark.ts
+++ b/src/background/domains/GlobalMark.ts
@@ -1,6 +1,7 @@
-export interface GlobalMark {
+export default interface GlobalMark {
readonly tabId: number;
readonly url: string;
readonly x: number;
readonly y: number;
+ // eslint-disable-next-line semi
}
diff --git a/src/background/domains/Setting.ts b/src/background/domains/Setting.ts
deleted file mode 100644
index b2b1ff2..0000000
--- a/src/background/domains/Setting.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import DefaultSettings from '../../shared/settings/default';
-import * as settingsValues from '../../shared/settings/values';
-
-type SettingValue = {
- source: string,
- json: string,
- form: any
-}
-
-export default class Setting {
- private obj: SettingValue;
-
- constructor({ source, json, form }: SettingValue) {
- this.obj = {
- source, json, form
- };
- }
-
- get source(): string {
- return this.obj.source;
- }
-
- get json(): string {
- return this.obj.json;
- }
-
- get form(): any {
- return this.obj.form;
- }
-
- value() {
- let value = JSON.parse(DefaultSettings.json);
- if (this.obj.source === 'json') {
- value = settingsValues.valueFromJson(this.obj.json);
- } else if (this.obj.source === 'form') {
- value = settingsValues.valueFromForm(this.obj.form);
- }
- if (!value.properties) {
- value.properties = {};
- }
- return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value };
- }
-
- serialize(): SettingValue {
- return this.obj;
- }
-
- static deserialize(obj: SettingValue): Setting {
- return new Setting({ source: obj.source, json: obj.json, form: obj.form });
- }
-
- static defaultSettings() {
- return new Setting({
- source: DefaultSettings.source,
- json: DefaultSettings.json,
- form: {},
- });
- }
-}
diff --git a/src/background/repositories/PersistentSettingRepository.ts b/src/background/repositories/PersistentSettingRepository.ts
index 3f2f4a1..18476fd 100644
--- a/src/background/repositories/PersistentSettingRepository.ts
+++ b/src/background/repositories/PersistentSettingRepository.ts
@@ -1,12 +1,12 @@
-import Setting from '../domains/Setting';
+import SettingData from '../../shared/SettingData';
export default class SettingRepository {
- async load(): Promise {
+ async load(): Promise {
let { settings } = await browser.storage.local.get('settings');
if (!settings) {
return null;
}
- return Setting.deserialize(settings);
+ return SettingData.valueOf(settings);
}
}
diff --git a/src/background/repositories/SettingRepository.ts b/src/background/repositories/SettingRepository.ts
index 15355ba..eb83a2c 100644
--- a/src/background/repositories/SettingRepository.ts
+++ b/src/background/repositories/SettingRepository.ts
@@ -1,4 +1,6 @@
import MemoryStorage from '../infrastructures/MemoryStorage';
+import Settings from '../../shared/Settings';
+import * as PropertyDefs from '../../shared/property-defs';
const CACHED_SETTING_KEY = 'setting';
@@ -9,17 +11,41 @@ export default class SettingRepository {
this.cache = new MemoryStorage();
}
- get(): Promise {
+ get(): Promise {
return Promise.resolve(this.cache.get(CACHED_SETTING_KEY));
}
- update(value: any): any {
+ update(value: Settings): void {
return this.cache.set(CACHED_SETTING_KEY, value);
}
- async setProperty(name: string, value: string): Promise {
+ async setProperty(
+ name: string, value: string | number | boolean,
+ ): Promise {
+ let def = PropertyDefs.defs.find(d => name === d.name);
+ if (!def) {
+ throw new Error('unknown property: ' + name);
+ }
+ if (typeof value !== def.type) {
+ throw new TypeError(`property type of ${name} mismatch: ${typeof value}`);
+ }
+ let newValue = value;
+ if (typeof value === 'string' && value === '') {
+ newValue = def.defaultValue;
+ }
+
let current = await this.get();
- current.properties[name] = value;
+ switch (name) {
+ case 'hintchars':
+ current.properties.hintchars = newValue as string;
+ break;
+ case 'smoothscroll':
+ current.properties.smoothscroll = newValue as boolean;
+ break;
+ case 'complete':
+ current.properties.complete = newValue as string;
+ break;
+ }
return this.update(current);
}
}
diff --git a/src/background/usecases/CommandUseCase.ts b/src/background/usecases/CommandUseCase.ts
index e0e3ada..2247d7b 100644
--- a/src/background/usecases/CommandUseCase.ts
+++ b/src/background/usecases/CommandUseCase.ts
@@ -6,7 +6,6 @@ import SettingRepository from '../repositories/SettingRepository';
import BookmarkRepository from '../repositories/BookmarkRepository';
import ConsoleClient from '../infrastructures/ConsoleClient';
import ContentMessageClient from '../infrastructures/ContentMessageClient';
-import * as properties from '../../shared/settings/properties';
export default class CommandIndicator {
private tabPresenter: TabPresenter;
@@ -115,16 +114,16 @@ export default class CommandIndicator {
async addbookmark(title: string): Promise {
let tab = await this.tabPresenter.getCurrent();
- let item = await this.bookmarkRepository.create(title, tab.url);
+ let item = await this.bookmarkRepository.create(title, tab.url as string);
let message = 'Saved current page: ' + item.url;
- return this.consoleClient.showInfo(tab.id, message);
+ return this.consoleClient.showInfo(tab.id as number, message);
}
async set(keywords: string): Promise {
if (keywords.length === 0) {
return;
}
- let [name, value] = parsers.parseSetOption(keywords, properties.types);
+ let [name, value] = parsers.parseSetOption(keywords);
await this.settingRepository.setProperty(name, value);
return this.contentMessageClient.broadcastSettingsChanged();
diff --git a/src/background/usecases/CompletionsUseCase.ts b/src/background/usecases/CompletionsUseCase.ts
index f3a808b..ae1ceed 100644
--- a/src/background/usecases/CompletionsUseCase.ts
+++ b/src/background/usecases/CompletionsUseCase.ts
@@ -4,7 +4,7 @@ import CompletionsRepository from '../repositories/CompletionsRepository';
import * as filters from './filters';
import SettingRepository from '../repositories/SettingRepository';
import TabPresenter from '../presenters/TabPresenter';
-import * as properties from '../../shared/settings/properties';
+import * as PropertyDefs from '../../shared/property-defs';
const COMPLETION_ITEM_LIMIT = 10;
@@ -44,7 +44,7 @@ export default class CompletionsUseCase {
let settings = await this.settingRepository.get();
let groups: CompletionGroup[] = [];
- let complete = settings.properties.complete || properties.defaults.complete;
+ let complete = settings.properties.complete;
for (let c of complete) {
if (c === 's') {
// eslint-disable-next-line no-await-in-loop
@@ -127,25 +127,25 @@ export default class CompletionsUseCase {
}
querySet(name: string, keywords: string): Promise {
- let items = Object.keys(properties.docs).map((key) => {
- if (properties.types[key] === 'boolean') {
+ let items = PropertyDefs.defs.map((def) => {
+ if (def.type === 'boolean') {
return [
{
- caption: key,
- content: name + ' ' + key,
- url: 'Enable ' + properties.docs[key],
+ caption: def.name,
+ content: name + ' ' + def.name,
+ url: 'Enable ' + def.description,
}, {
- caption: 'no' + key,
- content: name + ' no' + key,
- url: 'Disable ' + properties.docs[key],
+ caption: 'no' + def.name,
+ content: name + ' no' + def.name,
+ url: 'Disable ' + def.description
}
];
}
return [
{
- caption: key,
- content: name + ' ' + key,
- url: 'Set ' + properties.docs[key],
+ caption: def.name,
+ content: name + ' ' + def.name,
+ url: 'Set ' + def.description,
}
];
});
@@ -195,8 +195,8 @@ export default class CompletionsUseCase {
.map(filters.filterByTailingSlash)
.map(pages => filters.filterByPathname(pages, COMPLETION_ITEM_LIMIT))
.map(pages => filters.filterByOrigin(pages, COMPLETION_ITEM_LIMIT))[0]
- .sort((x: HistoryItem, y: HistoryItem) => {
- return Number(x.visitCount) < Number(y.visitCount);
+ .sort((x: HistoryItem, y: HistoryItem): number => {
+ return Number(x.visitCount) - Number(y.visitCount);
})
.slice(0, COMPLETION_ITEM_LIMIT);
return histories.map(page => ({
diff --git a/src/background/usecases/SettingUseCase.ts b/src/background/usecases/SettingUseCase.ts
index b66ce02..aa3b534 100644
--- a/src/background/usecases/SettingUseCase.ts
+++ b/src/background/usecases/SettingUseCase.ts
@@ -1,7 +1,8 @@
-import Setting from '../domains/Setting';
// eslint-disable-next-line max-len
import PersistentSettingRepository from '../repositories/PersistentSettingRepository';
import SettingRepository from '../repositories/SettingRepository';
+import { DefaultSettingData } from '../../shared/SettingData';
+import Settings from '../../shared/Settings';
export default class SettingUseCase {
private persistentSettingRepository: PersistentSettingRepository;
@@ -13,20 +14,18 @@ export default class SettingUseCase {
this.settingRepository = new SettingRepository();
}
- get(): Promise {
+ get(): Promise {
return this.settingRepository.get();
}
- async reload(): Promise {
- let settings = await this.persistentSettingRepository.load();
- if (!settings) {
- settings = Setting.defaultSettings();
+ async reload(): Promise {
+ let data = await this.persistentSettingRepository.load();
+ if (!data) {
+ data = DefaultSettingData;
}
- let value = settings.value();
-
+ let value = data.toSettings();
this.settingRepository.update(value);
-
return value;
}
}
diff --git a/src/background/usecases/parsers.ts b/src/background/usecases/parsers.ts
index 3616ac3..6135fd8 100644
--- a/src/background/usecases/parsers.ts
+++ b/src/background/usecases/parsers.ts
@@ -1,3 +1,5 @@
+import * as PropertyDefs from '../../shared//property-defs';
+
const mustNumber = (v: any): number => {
let num = Number(v);
if (isNaN(num)) {
@@ -7,29 +9,28 @@ const mustNumber = (v: any): number => {
};
const parseSetOption = (
- word: string,
- types: { [key: string]: string },
+ args: string,
): any[] => {
- let [key, value]: any[] = word.split('=');
+ let [key, value]: any[] = args.split('=');
if (value === undefined) {
value = !key.startsWith('no');
key = value ? key : key.slice(2);
}
- let type = types[key];
- if (!type) {
+ let def = PropertyDefs.defs.find(d => d.name === key);
+ if (!def) {
throw new Error('Unknown property: ' + key);
}
- if (type === 'boolean' && typeof value !== 'boolean' ||
- type !== 'boolean' && typeof value === 'boolean') {
- throw new Error('Invalid argument: ' + word);
+ if (def.type === 'boolean' && typeof value !== 'boolean' ||
+ def.type !== 'boolean' && typeof value === 'boolean') {
+ throw new Error('Invalid argument: ' + args);
}
- switch (type) {
+ switch (def.type) {
case 'string': return [key, value];
case 'number': return [key, mustNumber(value)];
case 'boolean': return [key, value];
}
- throw new Error('Unknown property type: ' + type);
+ throw new Error('Unknown property type: ' + def.type);
};
export { parseSetOption };
diff --git a/src/content/actions/index.ts b/src/content/actions/index.ts
index 18d0a69..a259211 100644
--- a/src/content/actions/index.ts
+++ b/src/content/actions/index.ts
@@ -1,4 +1,5 @@
import Redux from 'redux';
+import Settings from '../../shared/Settings';
// Enable/disable
export const ADDON_SET_ENABLED = 'addon.set.enabled';
@@ -45,7 +46,7 @@ export interface FindSetKeywordAction extends Redux.Action {
export interface SettingSetAction extends Redux.Action {
type: typeof SETTING_SET;
- value: any;
+ settings: Settings,
}
export interface InputKeyPressAction extends Redux.Action {
diff --git a/src/content/actions/operation.ts b/src/content/actions/operation.ts
index 6acb407..41e080b 100644
--- a/src/content/actions/operation.ts
+++ b/src/content/actions/operation.ts
@@ -8,7 +8,6 @@ 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 = (
@@ -16,8 +15,7 @@ const exec = (
settings: any,
addonEnabled: boolean,
): Promise | actions.Action => {
- let smoothscroll = settings.properties.smoothscroll ||
- properties.defaults.smoothscroll;
+ let smoothscroll = settings.properties.smoothscroll;
switch (operation.type) {
case operations.ADDON_ENABLE:
return addonActions.enable();
diff --git a/src/content/actions/setting.ts b/src/content/actions/setting.ts
index a8f049a..92f8559 100644
--- a/src/content/actions/setting.ts
+++ b/src/content/actions/setting.ts
@@ -1,29 +1,20 @@
import * as actions from './index';
-import * as keyUtils from '../../shared/utils/keys';
import * as operations from '../../shared/operations';
import * as messages from '../../shared/messages';
+import Settings, { Keymaps } from '../../shared/Settings';
-const reservedKeymaps = {
+const reservedKeymaps: Keymaps = {
'': { type: operations.CANCEL },
'': { type: operations.CANCEL },
};
-const set = (value: any): actions.SettingAction => {
- let entries: any[] = [];
- if (value.keymaps) {
- let keymaps = { ...value.keymaps, ...reservedKeymaps };
- entries = Object.entries(keymaps).map((entry) => {
- return [
- keyUtils.fromMapKeys(entry[0]),
- entry[1],
- ];
- });
- }
-
+const set = (settings: Settings): actions.SettingAction => {
return {
type: actions.SETTING_SET,
- value: { ...value,
- keymaps: entries, }
+ settings: {
+ ...settings,
+ keymaps: { ...settings.keymaps, ...reservedKeymaps },
+ }
};
};
diff --git a/src/content/components/common/index.ts b/src/content/components/common/index.ts
index 9b5164e..8bd697f 100644
--- a/src/content/components/common/index.ts
+++ b/src/content/components/common/index.ts
@@ -8,6 +8,7 @@ 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;
@@ -45,9 +46,9 @@ export default class Common {
reloadSettings() {
try {
this.store.dispatch(settingActions.load())
- .then(({ value: settings }: any) => {
+ .then((action: actions.SettingAction) => {
let enabled = !blacklists.includes(
- settings.blacklist, this.win.location.href
+ action.settings.blacklist, this.win.location.href
);
this.store.dispatch(addonActions.setEnabled(enabled));
});
diff --git a/src/content/components/common/input.ts b/src/content/components/common/input.ts
index 64eb5f3..1fe34c9 100644
--- a/src/content/components/common/input.ts
+++ b/src/content/components/common/input.ts
@@ -61,7 +61,6 @@ export default class InputComponent {
}
let key = keys.fromKeyboardEvent(e);
-
for (let listener of this.onKeyListeners) {
let stop = listener(key);
if (stop) {
diff --git a/src/content/components/common/keymapper.ts b/src/content/components/common/keymapper.ts
index d9c9834..c94bae0 100644
--- a/src/content/components/common/keymapper.ts
+++ b/src/content/components/common/keymapper.ts
@@ -3,7 +3,10 @@ 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(
+ 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.ts b/src/content/components/common/mark.ts
index 500d03b..686116c 100644
--- a/src/content/components/common/mark.ts
+++ b/src/content/components/common/mark.ts
@@ -1,7 +1,6 @@
-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';
+import * as markActions from '../../actions/mark';
+import * as scrolls from '../..//scrolls';
+import * as consoleFrames from '../..//console-frames';
const cancelKey = (key): boolean => {
return key.key === 'Esc' || key.key === '[' && key.ctrlKey;
@@ -20,8 +19,7 @@ export default class MarkComponent {
// eslint-disable-next-line max-statements
key(key) {
let { mark: markStage, setting } = this.store.getState();
- let smoothscroll = setting.properties.smoothscroll ||
- properties.defaults.smoothscroll;
+ let smoothscroll = setting.properties.smoothscroll;
if (!markStage.setMode && !markStage.jumpMode) {
return false;
diff --git a/src/content/components/top-content/follow-controller.ts b/src/content/components/top-content/follow-controller.ts
index be71f6e..d49b22a 100644
--- a/src/content/components/top-content/follow-controller.ts
+++ b/src/content/components/top-content/follow-controller.ts
@@ -2,7 +2,6 @@ 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: Window, message: messages.Message): void => {
let json = JSON.stringify(message);
@@ -162,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/reducers/setting.ts b/src/content/reducers/setting.ts
index fa8e8ee..a3dc3aa 100644
--- a/src/content/reducers/setting.ts
+++ b/src/content/reducers/setting.ts
@@ -1,12 +1,20 @@
import * as actions from '../actions';
+import * as keyUtils from '../../shared/utils/keys';
+import * as operations from '../../shared/operations';
+import { Properties } from '../../shared/Settings';
export interface State {
- keymaps: any[];
+ keymaps: { key: keyUtils.Key[], op: operations.Operation }[];
+ properties: Properties;
}
-const defaultState = {
- // keymaps is and arrays of key-binding pairs, which is entries of Map
+const defaultState: State = {
keymaps: [],
+ properties: {
+ complete: '',
+ smoothscroll: false,
+ hintchars: '',
+ },
};
export default function reducer(
@@ -15,7 +23,15 @@ export default function reducer(
): State {
switch (action.type) {
case actions.SETTING_SET:
- return { ...action.value };
+ return {
+ keymaps: Object.entries(action.settings.keymaps).map((entry) => {
+ return {
+ key: keyUtils.fromMapKeys(entry[0]),
+ op: entry[1],
+ };
+ }),
+ properties: action.settings.properties,
+ };
default:
return state;
}
diff --git a/src/settings/actions/index.ts b/src/settings/actions/index.ts
index 75c6bb5..b1e996e 100644
--- a/src/settings/actions/index.ts
+++ b/src/settings/actions/index.ts
@@ -1,3 +1,7 @@
+import {
+ JSONSettings, FormSettings, SettingSource,
+} from '../../shared/SettingData';
+
// Settings
export const SETTING_SET_SETTINGS = 'setting.set.settings';
export const SETTING_SHOW_ERROR = 'setting.show.error';
@@ -6,25 +10,25 @@ export const SETTING_SWITCH_TO_JSON = 'setting.switch.to.json';
interface SettingSetSettingsAcion {
type: typeof SETTING_SET_SETTINGS;
- source: string;
- json: string;
- form: any;
+ source: SettingSource;
+ json?: JSONSettings;
+ form?: FormSettings;
}
interface SettingShowErrorAction {
type: typeof SETTING_SHOW_ERROR;
error: string;
- json: string;
+ json: JSONSettings;
}
interface SettingSwitchToFormAction {
type: typeof SETTING_SWITCH_TO_FORM;
- form: any;
+ form: FormSettings,
}
interface SettingSwitchToJsonAction {
type: typeof SETTING_SWITCH_TO_JSON;
- json: string;
+ json: JSONSettings,
}
export type SettingAction =
diff --git a/src/settings/actions/setting.ts b/src/settings/actions/setting.ts
index b03cd80..9eb416e 100644
--- a/src/settings/actions/setting.ts
+++ b/src/settings/actions/setting.ts
@@ -1,35 +1,35 @@
import * as actions from './index';
-import * as validator from '../../shared/settings/validator';
-import * as settingsValues from '../../shared/settings/values';
-import * as settingsStorage from '../../shared/settings/storage';
-import keymaps from '../keymaps';
+import * as storages from '../storage';
+import SettingData, {
+ JSONSettings, FormSettings, SettingSource,
+} from '../../shared/SettingData';
const load = async(): Promise => {
- let settings = await settingsStorage.loadRaw();
- return set(settings);
+ let data = await storages.load();
+ return set(data);
};
-const save = async(settings: any): Promise => {
+const save = async(data: SettingData): Promise => {
try {
- if (settings.source === 'json') {
- let value = JSON.parse(settings.json);
- validator.validate(value);
+ if (data.getSource() === SettingSource.JSON) {
+ // toSettings exercise validation
+ data.toSettings();
}
} catch (e) {
return {
type: actions.SETTING_SHOW_ERROR,
error: e.toString(),
- json: settings.json,
+ json: data.getJSON(),
};
}
- await settingsStorage.save(settings);
- return set(settings);
+ await storages.save(data);
+ return set(data);
};
-const switchToForm = (json: string): actions.SettingAction => {
+const switchToForm = (json: JSONSettings): actions.SettingAction => {
try {
- validator.validate(JSON.parse(json));
- let form = settingsValues.formFromJson(json, keymaps.allowedOps);
+ // toSettings exercise validation
+ let form = FormSettings.fromSettings(json.toSettings());
return {
type: actions.SETTING_SWITCH_TO_FORM,
form,
@@ -43,21 +43,31 @@ const switchToForm = (json: string): actions.SettingAction => {
}
};
-const switchToJson = (form: any): actions.SettingAction => {
- let json = settingsValues.jsonFromForm(form);
+const switchToJson = (form: FormSettings): actions.SettingAction => {
+ let json = JSONSettings.fromSettings(form.toSettings());
return {
type: actions.SETTING_SWITCH_TO_JSON,
json,
};
};
-const set = (settings: any): actions.SettingAction => {
- return {
- type: actions.SETTING_SET_SETTINGS,
- source: settings.source,
- json: settings.json,
- form: settings.form,
- };
+const set = (data: SettingData): actions.SettingAction => {
+ let source = data.getSource();
+ switch (source) {
+ case SettingSource.JSON:
+ return {
+ type: actions.SETTING_SET_SETTINGS,
+ source: source,
+ json: data.getJSON(),
+ };
+ case SettingSource.Form:
+ return {
+ type: actions.SETTING_SET_SETTINGS,
+ source: source,
+ form: data.getForm(),
+ };
+ }
+ throw new Error(`unknown source: ${source}`);
};
export { load, save, set, switchToForm, switchToJson };
diff --git a/src/settings/components/form/KeymapsForm.tsx b/src/settings/components/form/KeymapsForm.tsx
index ab44464..ad4d0e7 100644
--- a/src/settings/components/form/KeymapsForm.tsx
+++ b/src/settings/components/form/KeymapsForm.tsx
@@ -2,32 +2,30 @@ import './KeymapsForm.scss';
import React from 'react';
import Input from '../ui/Input';
import keymaps from '../../keymaps';
+import { FormKeymaps } from '../../../shared/SettingData';
-type Value = {[key: string]: string};
-
-interface Props{
- value: Value;
- onChange: (e: Value) => void;
+interface Props {
+ value: FormKeymaps;
+ onChange: (e: FormKeymaps) => void;
onBlur: () => void;
}
class KeymapsForm extends React.Component {
public static defaultProps: Props = {
- value: {},
+ value: FormKeymaps.valueOf({}),
onChange: () => {},
onBlur: () => {},
}
render() {
+ let values = this.props.value.toJSON();
return
{
keymaps.fields.map((group, index) => {
return
{
- group.map((field) => {
- let name = field[0];
- let label = field[1];
- let value = this.props.value[name] || '';
+ group.map(([name, label]) => {
+ let value = values[name] || '';
return
{
}
bindValue(name: string, value: string) {
- let next = { ...this.props.value };
- next[name] = value;
-
- this.props.onChange(next);
+ this.props.onChange(this.props.value.buildWithOverride(name, value));
}
}
diff --git a/src/settings/components/form/SearchForm.tsx b/src/settings/components/form/SearchForm.tsx
index 737e291..67dbeba 100644
--- a/src/settings/components/form/SearchForm.tsx
+++ b/src/settings/components/form/SearchForm.tsx
@@ -2,31 +2,23 @@ import './SearchForm.scss';
import React from 'react';
import AddButton from '../ui/AddButton';
import DeleteButton from '../ui/DeleteButton';
-
-interface Value {
- default: string;
- engines: string[][];
-}
+import { FormSearch } from '../../../shared/SettingData';
interface Props {
- value: Value;
- onChange: (value: Value) => void;
+ value: FormSearch;
+ onChange: (value: FormSearch) => void;
onBlur: () => void;
}
class SearchForm extends React.Component
{
public static defaultProps: Props = {
- value: { default: '', engines: []},
+ value: FormSearch.valueOf({ default: '', engines: []}),
onChange: () => {},
onBlur: () => {},
}
render() {
- let value = this.props.value;
- if (!value.engines) {
- value.engines = [];
- }
-
+ let value = this.props.value.toJSON();
return
Name
@@ -63,28 +55,28 @@ class SearchForm extends React.Component
{
}
bindValue(e: any) {
- let value = this.props.value;
+ let value = this.props.value.toJSON();
let name = e.target.name;
let index = Number(e.target.getAttribute('data-index'));
- let next: Value = {
+ let next: typeof value = {
default: value.default,
- engines: value.engines ? value.engines.slice() : [],
+ engines: value.engines.slice(),
};
if (name === 'name') {
next.engines[index][0] = e.target.value;
- next.default = this.props.value.engines[index][0];
+ next.default = value.engines[index][0];
} else if (name === 'url') {
next.engines[index][1] = e.target.value;
} else if (name === 'default') {
- next.default = this.props.value.engines[index][0];
+ next.default = value.engines[index][0];
} else if (name === 'add') {
next.engines.push(['', '']);
} else if (name === 'delete') {
next.engines.splice(index, 1);
}
- this.props.onChange(next);
+ this.props.onChange(FormSearch.valueOf(next));
if (name === 'delete' || name === 'default') {
this.props.onBlur();
}
diff --git a/src/settings/components/index.tsx b/src/settings/components/index.tsx
index f56e93f..b4a0866 100644
--- a/src/settings/components/index.tsx
+++ b/src/settings/components/index.tsx
@@ -6,22 +6,26 @@ import SearchForm from './form/SearchForm';
import KeymapsForm from './form/KeymapsForm';
import BlacklistForm from './form/BlacklistForm';
import PropertiesForm from './form/PropertiesForm';
-import * as properties from '../../shared/settings/properties';
import * as settingActions from '../../settings/actions/setting';
+import SettingData, {
+ JSONSettings, FormKeymaps, FormSearch, FormSettings,
+} from '../../shared/SettingData';
+import { State as AppState } from '../reducers/setting';
+import * as settings from '../../shared/Settings';
+import * as PropertyDefs from '../../shared/property-defs';
const DO_YOU_WANT_TO_CONTINUE =
'Some settings in JSON can be lost when migrating. ' +
'Do you want to continue?';
-interface Props {
- source: string;
- json: string;
- form: any;
- error: string;
-
+type StateProps = ReturnType;
+interface DispatchProps {
+ dispatch: (action: any) => void,
+}
+type Props = StateProps & DispatchProps & {
// FIXME
store: any;
-}
+};
class SettingsComponent extends React.Component {
componentDidMount() {
@@ -29,12 +33,17 @@ class SettingsComponent extends React.Component {
}
renderFormFields(form: any) {
+ let types = PropertyDefs.defs.reduce(
+ (o: {[key: string]: string}, def) => {
+ o[def.name] = def.type;
+ return o;
+ }, {});
return
@@ -42,7 +51,7 @@ class SettingsComponent extends React.Component
{
this.bindForm('search', value)}
+ onChange={this.bindSearchForm.bind(this)}
onBlur={this.save.bind(this)}
/>
@@ -50,23 +59,23 @@ class SettingsComponent extends React.Component {
this.bindForm('blacklist', value)}
+ onChange={this.bindBlacklistForm.bind(this)}
onBlur={this.save.bind(this)}
/>
;
}
- renderJsonFields(json: string, error: string) {
+ renderJsonFields(json: JSONSettings, error: string) {
return
{
error={error}
onValueChange={this.bindJson.bind(this)}
onBlur={this.save.bind(this)}
- value={json}
+ value={json.toJSON()}
/>
;
}
@@ -87,7 +96,8 @@ class SettingsComponent extends React.Component {
if (this.props.source === 'form') {
fields = this.renderFormFields(this.props.form);
} else if (this.props.source === 'json') {
- fields = this.renderJsonFields(this.props.json, this.props.error);
+ fields = this.renderJsonFields(
+ this.props.json as JSONSettings, this.props.error);
}
return (
@@ -117,45 +127,73 @@ class SettingsComponent extends React.Component
{
);
}
- bindForm(name: string, value: any) {
- let settings = {
+ bindKeymapsForm(value: FormKeymaps) {
+ let data = new SettingData({
+ source: this.props.source,
+ form: (this.props.form as FormSettings).buildWithKeymaps(value),
+ });
+ this.props.dispatch(settingActions.set(data));
+ }
+
+ bindSearchForm(value: any) {
+ let data = new SettingData({
+ source: this.props.source,
+ form: (this.props.form as FormSettings).buildWithSearch(
+ FormSearch.valueOf(value)),
+ });
+ this.props.dispatch(settingActions.set(data));
+ }
+
+ bindBlacklistForm(value: any) {
+ let data = new SettingData({
+ source: this.props.source,
+ form: (this.props.form as FormSettings).buildWithBlacklist(
+ settings.blacklistValueOf(value)),
+ });
+ this.props.dispatch(settingActions.set(data));
+ }
+
+ bindPropertiesForm(value: any) {
+ let data = new SettingData({
source: this.props.source,
- json: this.props.json,
- form: { ...this.props.form },
- };
- settings.form[name] = value;
- this.props.dispatch(settingActions.set(settings));
+ form: (this.props.form as FormSettings).buildWithProperties(
+ settings.propertiesValueOf(value)),
+ });
+ this.props.dispatch(settingActions.set(data));
}
bindJson(_name: string, value: string) {
- let settings = {
+ let data = new SettingData({
source: this.props.source,
- json: value,
- form: this.props.form,
- };
- this.props.dispatch(settingActions.set(settings));
+ json: JSONSettings.valueOf(value),
+ });
+ this.props.dispatch(settingActions.set(data));
}
bindSource(_name: string, value: string) {
let from = this.props.source;
if (from === 'form' && value === 'json') {
- this.props.dispatch(settingActions.switchToJson(this.props.form));
+ this.props.dispatch(settingActions.switchToJson(
+ this.props.form as FormSettings));
} else if (from === 'json' && value === 'form') {
let b = window.confirm(DO_YOU_WANT_TO_CONTINUE);
if (!b) {
this.forceUpdate();
return;
}
- this.props.dispatch(settingActions.switchToForm(this.props.json));
+ this.props.dispatch(
+ settingActions.switchToForm(this.props.json as JSONSettings));
}
}
save() {
- let settings = this.props.store.getState();
- this.props.dispatch(settingActions.save(settings));
+ let { source, json, form } = this.props.store.getState();
+ this.props.dispatch(settingActions.save(
+ new SettingData({ source, json, form }),
+ ));
}
}
-const mapStateToProps = (state: any) => state;
+const mapStateToProps = (state: AppState) => ({ ...state });
export default connect(mapStateToProps)(SettingsComponent);
diff --git a/src/settings/keymaps.ts b/src/settings/keymaps.ts
index ccfc74c..38045ad 100644
--- a/src/settings/keymaps.ts
+++ b/src/settings/keymaps.ts
@@ -66,9 +66,6 @@ const fields = [
]
];
-const allowedOps = [].concat(...fields.map(group => group.map(e => e[0])));
-
export default {
fields,
- allowedOps,
};
diff --git a/src/settings/reducers/setting.ts b/src/settings/reducers/setting.ts
index 47c21bf..c4a21c7 100644
--- a/src/settings/reducers/setting.ts
+++ b/src/settings/reducers/setting.ts
@@ -1,23 +1,25 @@
import * as actions from '../actions';
+import {
+ JSONSettings, FormSettings, SettingSource,
+} from '../../shared/SettingData';
-interface State {
- source: string;
- json: string;
- form: any;
+export interface State {
+ source: SettingSource;
+ json?: JSONSettings;
+ form?: FormSettings;
error: string;
}
const defaultState: State = {
- source: '',
- json: '',
- form: null,
+ source: SettingSource.JSON,
+ json: JSONSettings.valueOf(''),
error: '',
};
export default function reducer(
state = defaultState,
action: actions.SettingAction,
-) {
+): State {
switch (action.type) {
case actions.SETTING_SET_SETTINGS:
return { ...state,
@@ -32,12 +34,12 @@ export default function reducer(
case actions.SETTING_SWITCH_TO_FORM:
return { ...state,
error: '',
- source: 'form',
+ source: SettingSource.Form,
form: action.form, };
case actions.SETTING_SWITCH_TO_JSON:
return { ...state,
error: '',
- source: 'json',
+ source: SettingSource.JSON,
json: action.json, };
default:
return state;
diff --git a/src/settings/storage.ts b/src/settings/storage.ts
new file mode 100644
index 0000000..748b9ab
--- /dev/null
+++ b/src/settings/storage.ts
@@ -0,0 +1,15 @@
+import SettingData, { DefaultSettingData } from '../shared/SettingData';
+
+export const load = async(): Promise => {
+ let { settings } = await browser.storage.local.get('settings');
+ if (!settings) {
+ return DefaultSettingData;
+ }
+ return SettingData.valueOf(settings);
+};
+
+export const save = (data: SettingData) => {
+ return browser.storage.local.set({
+ settings: data.toJSON(),
+ });
+};
diff --git a/src/shared/SettingData.ts b/src/shared/SettingData.ts
new file mode 100644
index 0000000..05e21fa
--- /dev/null
+++ b/src/shared/SettingData.ts
@@ -0,0 +1,414 @@
+import * as operations from './operations';
+import Settings, * as settings from './Settings';
+
+export class FormKeymaps {
+ private data: {[op: string]: string};
+
+ constructor(data: {[op: string]: string}) {
+ this.data = data;
+ }
+
+ toKeymaps(): settings.Keymaps {
+ let keymaps: settings.Keymaps = {};
+ for (let name of Object.keys(this.data)) {
+ let [type, argStr] = name.split('?');
+ let args = {};
+ if (argStr) {
+ args = JSON.parse(argStr);
+ }
+ let key = this.data[name];
+ keymaps[key] = operations.valueOf({ type, ...args });
+ }
+ return keymaps;
+ }
+
+ toJSON(): {[op: string]: string} {
+ return this.data;
+ }
+
+ buildWithOverride(op: string, keys: string): FormKeymaps {
+ let newData = {
+ ...this.data,
+ [op]: keys,
+ };
+ return new FormKeymaps(newData);
+ }
+
+ static valueOf(o: ReturnType): FormKeymaps {
+ let data: {[op: string]: string} = {};
+ for (let op of Object.keys(o)) {
+ data[op] = o[op] as string;
+ }
+ return new FormKeymaps(data);
+ }
+
+ static fromKeymaps(keymaps: settings.Keymaps): FormKeymaps {
+ let data: {[op: string]: string} = {};
+ for (let key of Object.keys(keymaps)) {
+ let op = keymaps[key];
+ let args = { ...op };
+ delete args.type;
+
+ let name = op.type;
+ if (Object.keys(args).length > 0) {
+ name += '?' + JSON.stringify(args);
+ }
+ data[name] = key;
+ }
+ return new FormKeymaps(data);
+ }
+}
+
+export class FormSearch {
+ private default: string;
+
+ private engines: string[][];
+
+ constructor(defaultEngine: string, engines: string[][]) {
+ this.default = defaultEngine;
+ this.engines = engines;
+ }
+
+ toSearchSettings(): settings.Search {
+ return {
+ default: this.default,
+ engines: this.engines.reduce(
+ (o: {[key: string]: string}, [name, url]) => {
+ o[name] = url;
+ return o;
+ }, {}),
+ };
+ }
+
+ toJSON(): {
+ default: string;
+ engines: string[][];
+ } {
+ return {
+ default: this.default,
+ engines: this.engines,
+ };
+ }
+
+ static valueOf(o: ReturnType): FormSearch {
+ if (!Object.prototype.hasOwnProperty.call(o, 'default')) {
+ throw new TypeError(`"default" field not set`);
+ }
+ if (!Object.prototype.hasOwnProperty.call(o, 'engines')) {
+ throw new TypeError(`"engines" field not set`);
+ }
+ return new FormSearch(o.default, o.engines);
+ }
+
+ static fromSearch(search: settings.Search): FormSearch {
+ let engines = Object.entries(search.engines).reduce(
+ (o: string[][], [name, url]) => {
+ return o.concat([[name, url]]);
+ }, []);
+ return new FormSearch(search.default, engines);
+ }
+}
+
+export class JSONSettings {
+ private json: string;
+
+ constructor(json: any) {
+ this.json = json;
+ }
+
+ toSettings(): Settings {
+ return settings.valueOf(JSON.parse(this.json));
+ }
+
+ toJSON(): string {
+ return this.json;
+ }
+
+ static valueOf(o: ReturnType): JSONSettings {
+ return new JSONSettings(o);
+ }
+
+ static fromSettings(data: Settings): JSONSettings {
+ return new JSONSettings(JSON.stringify(data, undefined, 2));
+ }
+}
+
+export class FormSettings {
+ private keymaps: FormKeymaps;
+
+ private search: FormSearch;
+
+ private properties: settings.Properties;
+
+ private blacklist: string[];
+
+ constructor(
+ keymaps: FormKeymaps,
+ search: FormSearch,
+ properties: settings.Properties,
+ blacklist: string[],
+ ) {
+ this.keymaps = keymaps;
+ this.search = search;
+ this.properties = properties;
+ this.blacklist = blacklist;
+ }
+
+ buildWithKeymaps(keymaps: FormKeymaps): FormSettings {
+ return new FormSettings(
+ keymaps,
+ this.search,
+ this.properties,
+ this.blacklist,
+ );
+ }
+
+ buildWithSearch(search: FormSearch): FormSettings {
+ return new FormSettings(
+ this.keymaps,
+ search,
+ this.properties,
+ this.blacklist,
+ );
+ }
+
+ buildWithProperties(props: settings.Properties): FormSettings {
+ return new FormSettings(
+ this.keymaps,
+ this.search,
+ props,
+ this.blacklist,
+ );
+ }
+
+ buildWithBlacklist(blacklist: string[]): FormSettings {
+ return new FormSettings(
+ this.keymaps,
+ this.search,
+ this.properties,
+ blacklist,
+ );
+ }
+
+ toSettings(): Settings {
+ return settings.valueOf({
+ keymaps: this.keymaps.toKeymaps(),
+ search: this.search.toSearchSettings(),
+ properties: this.properties,
+ blacklist: this.blacklist,
+ });
+ }
+
+ toJSON(): {
+ keymaps: ReturnType;
+ search: ReturnType;
+ properties: settings.Properties;
+ blacklist: string[];
+ } {
+ return {
+ keymaps: this.keymaps.toJSON(),
+ search: this.search.toJSON(),
+ properties: this.properties,
+ blacklist: this.blacklist,
+ };
+ }
+
+ static valueOf(o: ReturnType): FormSettings {
+ for (let name of ['keymaps', 'search', 'properties', 'blacklist']) {
+ if (!Object.prototype.hasOwnProperty.call(o, name)) {
+ throw new Error(`"${name}" field not set`);
+ }
+ }
+ return new FormSettings(
+ FormKeymaps.valueOf(o.keymaps),
+ FormSearch.valueOf(o.search),
+ settings.propertiesValueOf(o.properties),
+ settings.blacklistValueOf(o.blacklist),
+ );
+ }
+
+ static fromSettings(data: Settings): FormSettings {
+ return new FormSettings(
+ FormKeymaps.fromKeymaps(data.keymaps),
+ FormSearch.fromSearch(data.search),
+ data.properties,
+ data.blacklist);
+ }
+}
+
+export enum SettingSource {
+ JSON = 'json',
+ Form = 'form',
+}
+
+export default class SettingData {
+ private source: SettingSource;
+
+ private json?: JSONSettings;
+
+ private form?: FormSettings;
+
+ constructor({
+ source, json, form
+ }: {
+ source: SettingSource,
+ json?: JSONSettings,
+ form?: FormSettings,
+ }) {
+ this.source = source;
+ this.json = json;
+ this.form = form;
+ }
+
+ getSource(): SettingSource {
+ return this.source;
+ }
+
+ getJSON(): JSONSettings {
+ if (!this.json) {
+ throw new TypeError('json settings not set');
+ }
+ return this.json;
+ }
+
+ getForm(): FormSettings {
+ if (!this.form) {
+ throw new TypeError('form settings not set');
+ }
+ return this.form;
+ }
+
+ toJSON(): any {
+ switch (this.source) {
+ case SettingSource.JSON:
+ return {
+ source: this.source,
+ json: (this.json as JSONSettings).toJSON(),
+ };
+ case SettingSource.Form:
+ return {
+ source: this.source,
+ form: (this.form as FormSettings).toJSON(),
+ };
+ }
+ throw new Error(`unknown settings source: ${this.source}`);
+ }
+
+ toSettings(): Settings {
+ switch (this.source) {
+ case SettingSource.JSON:
+ return this.getJSON().toSettings();
+ case SettingSource.Form:
+ return this.getForm().toSettings();
+ }
+ throw new Error(`unknown settings source: ${this.source}`);
+ }
+
+ static valueOf(o: {
+ source: string;
+ json?: string;
+ form?: ReturnType;
+ }): SettingData {
+ switch (o.source) {
+ case SettingSource.JSON:
+ return new SettingData({
+ source: o.source,
+ json: JSONSettings.valueOf(
+ o.json as ReturnType),
+ });
+ case SettingSource.Form:
+ return new SettingData({
+ source: o.source,
+ form: FormSettings.valueOf(
+ o.form as ReturnType),
+ });
+ }
+ throw new Error(`unknown settings source: ${o.source}`);
+ }
+}
+
+export const DefaultSettingData: SettingData = SettingData.valueOf({
+ source: 'json',
+ json: `{
+ "keymaps": {
+ "0": { "type": "scroll.home" },
+ ":": { "type": "command.show" },
+ "o": { "type": "command.show.open", "alter": false },
+ "O": { "type": "command.show.open", "alter": true },
+ "t": { "type": "command.show.tabopen", "alter": false },
+ "T": { "type": "command.show.tabopen", "alter": true },
+ "w": { "type": "command.show.winopen", "alter": false },
+ "W": { "type": "command.show.winopen", "alter": true },
+ "b": { "type": "command.show.buffer" },
+ "a": { "type": "command.show.addbookmark", "alter": true },
+ "k": { "type": "scroll.vertically", "count": -1 },
+ "j": { "type": "scroll.vertically", "count": 1 },
+ "h": { "type": "scroll.horizonally", "count": -1 },
+ "l": { "type": "scroll.horizonally", "count": 1 },
+ "": { "type": "scroll.pages", "count": -0.5 },
+ "": { "type": "scroll.pages", "count": 0.5 },
+ "": { "type": "scroll.pages", "count": -1 },
+ "": { "type": "scroll.pages", "count": 1 },
+ "gg": { "type": "scroll.top" },
+ "G": { "type": "scroll.bottom" },
+ "$": { "type": "scroll.end" },
+ "d": { "type": "tabs.close" },
+ "D": { "type": "tabs.close.right" },
+ "!d": { "type": "tabs.close.force" },
+ "u": { "type": "tabs.reopen" },
+ "K": { "type": "tabs.prev" },
+ "J": { "type": "tabs.next" },
+ "gT": { "type": "tabs.prev" },
+ "gt": { "type": "tabs.next" },
+ "g0": { "type": "tabs.first" },
+ "g$": { "type": "tabs.last" },
+ "": { "type": "tabs.prevsel" },
+ "r": { "type": "tabs.reload", "cache": false },
+ "R": { "type": "tabs.reload", "cache": true },
+ "zp": { "type": "tabs.pin.toggle" },
+ "zd": { "type": "tabs.duplicate" },
+ "zi": { "type": "zoom.in" },
+ "zo": { "type": "zoom.out" },
+ "zz": { "type": "zoom.neutral" },
+ "f": { "type": "follow.start", "newTab": false },
+ "F": { "type": "follow.start", "newTab": true, "background": false },
+ "m": { "type": "mark.set.prefix" },
+ "'": { "type": "mark.jump.prefix" },
+ "H": { "type": "navigate.history.prev" },
+ "L": { "type": "navigate.history.next" },
+ "[[": { "type": "navigate.link.prev" },
+ "]]": { "type": "navigate.link.next" },
+ "gu": { "type": "navigate.parent" },
+ "gU": { "type": "navigate.root" },
+ "gi": { "type": "focus.input" },
+ "gf": { "type": "page.source" },
+ "gh": { "type": "page.home" },
+ "gH": { "type": "page.home", "newTab": true },
+ "y": { "type": "urls.yank" },
+ "p": { "type": "urls.paste", "newTab": false },
+ "P": { "type": "urls.paste", "newTab": true },
+ "/": { "type": "find.start" },
+ "n": { "type": "find.next" },
+ "N": { "type": "find.prev" },
+ "": { "type": "addon.toggle.enabled" }
+ },
+ "search": {
+ "default": "google",
+ "engines": {
+ "google": "https://google.com/search?q={}",
+ "yahoo": "https://search.yahoo.com/search?p={}",
+ "bing": "https://www.bing.com/search?q={}",
+ "duckduckgo": "https://duckduckgo.com/?q={}",
+ "twitter": "https://twitter.com/search?q={}",
+ "wikipedia": "https://en.wikipedia.org/w/index.php?search={}"
+ }
+ },
+ "properties": {
+ "hintchars": "abcdefghijklmnopqrstuvwxyz",
+ "smoothscroll": false,
+ "complete": "sbh"
+ },
+ "blacklist": [
+ ]
+}`,
+});
diff --git a/src/shared/Settings.ts b/src/shared/Settings.ts
new file mode 100644
index 0000000..ce6b9ee
--- /dev/null
+++ b/src/shared/Settings.ts
@@ -0,0 +1,200 @@
+import * as operations from './operations';
+import * as PropertyDefs from './property-defs';
+
+export type Keymaps = {[key: string]: operations.Operation};
+
+export interface Search {
+ default: string;
+ engines: { [key: string]: string };
+}
+
+export interface Properties {
+ hintchars: string;
+ smoothscroll: boolean;
+ complete: string;
+}
+
+export default interface Settings {
+ keymaps: Keymaps;
+ search: Search;
+ properties: Properties;
+ blacklist: string[];
+ // eslint-disable-next-line semi
+}
+
+const DefaultProperties: Properties = PropertyDefs.defs.reduce(
+ (o: {[name: string]: PropertyDefs.Type}, def) => {
+ o[def.name] = def.defaultValue;
+ return o;
+ }, {}) as Properties;
+
+
+export const keymapsValueOf = (o: any): Keymaps => {
+ return Object.keys(o).reduce((keymaps: Keymaps, key: string): Keymaps => {
+ let op = operations.valueOf(o[key]);
+ keymaps[key] = op;
+ return keymaps;
+ }, {});
+};
+
+export const searchValueOf = (o: any): Search => {
+ if (typeof o.default !== 'string') {
+ throw new TypeError('string field "default" not set"');
+ }
+ for (let name of Object.keys(o.engines)) {
+ if ((/\s/).test(name)) {
+ throw new TypeError(
+ `While space in the search engine not allowed: "${name}"`);
+ }
+ let url = o.engines[name];
+ if (typeof url !== 'string') {
+ throw new TypeError('"engines" not an object of string');
+ }
+ let matches = url.match(/{}/g);
+ if (matches === null) {
+ throw new TypeError(`No {}-placeholders in URL of "${name}"`);
+ } else if (matches.length > 1) {
+ throw new TypeError(`Multiple {}-placeholders in URL of "${name}"`);
+ }
+
+ }
+ if (!Object.prototype.hasOwnProperty.call(o.engines, o.default)) {
+ throw new TypeError(`Default engine "${o.default}" not found`);
+ }
+ return {
+ default: o.default as string,
+ engines: { ...o.engines },
+ };
+};
+
+export const propertiesValueOf = (o: any): Properties => {
+ let defNames = new Set(PropertyDefs.defs.map(def => def.name));
+ let unknownName = Object.keys(o).find(name => !defNames.has(name));
+ if (unknownName) {
+ throw new TypeError(`Unknown property name: "${unknownName}"`);
+ }
+
+ for (let def of PropertyDefs.defs) {
+ if (!Object.prototype.hasOwnProperty.call(o, def.name)) {
+ continue;
+ }
+ if (typeof o[def.name] !== def.type) {
+ throw new TypeError(`property "${def.name}" is not ${def.type}`);
+ }
+ }
+ return {
+ ...DefaultProperties,
+ ...o,
+ };
+};
+
+export const blacklistValueOf = (o: any): string[] => {
+ if (!Array.isArray(o)) {
+ throw new TypeError(`"blacklist" is not an array of string`);
+ }
+ for (let x of o) {
+ if (typeof x !== 'string') {
+ throw new TypeError(`"blacklist" is not an array of string`);
+ }
+ }
+ return o as string[];
+};
+
+export const valueOf = (o: any): Settings => {
+ let settings = { ...DefaultSetting };
+ if (Object.prototype.hasOwnProperty.call(o, 'keymaps')) {
+ settings.keymaps = keymapsValueOf(o.keymaps);
+ }
+ if (Object.prototype.hasOwnProperty.call(o, 'search')) {
+ settings.search = searchValueOf(o.search);
+ }
+ if (Object.prototype.hasOwnProperty.call(o, 'properties')) {
+ settings.properties = propertiesValueOf(o.properties);
+ }
+ if (Object.prototype.hasOwnProperty.call(o, 'blacklist')) {
+ settings.blacklist = blacklistValueOf(o.blacklist);
+ }
+ return settings;
+};
+
+const DefaultSetting: Settings = {
+ keymaps: {
+ '0': { 'type': 'scroll.home' },
+ ':': { 'type': 'command.show' },
+ 'o': { 'type': 'command.show.open', 'alter': false },
+ 'O': { 'type': 'command.show.open', 'alter': true },
+ 't': { 'type': 'command.show.tabopen', 'alter': false },
+ 'T': { 'type': 'command.show.tabopen', 'alter': true },
+ 'w': { 'type': 'command.show.winopen', 'alter': false },
+ 'W': { 'type': 'command.show.winopen', 'alter': true },
+ 'b': { 'type': 'command.show.buffer' },
+ 'a': { 'type': 'command.show.addbookmark', 'alter': true },
+ 'k': { 'type': 'scroll.vertically', 'count': -1 },
+ 'j': { 'type': 'scroll.vertically', 'count': 1 },
+ 'h': { 'type': 'scroll.horizonally', 'count': -1 },
+ 'l': { 'type': 'scroll.horizonally', 'count': 1 },
+ '': { 'type': 'scroll.pages', 'count': -0.5 },
+ '': { 'type': 'scroll.pages', 'count': 0.5 },
+ '': { 'type': 'scroll.pages', 'count': -1 },
+ '': { 'type': 'scroll.pages', 'count': 1 },
+ 'gg': { 'type': 'scroll.top' },
+ 'G': { 'type': 'scroll.bottom' },
+ '$': { 'type': 'scroll.end' },
+ 'd': { 'type': 'tabs.close' },
+ 'D': { 'type': 'tabs.close.right' },
+ '!d': { 'type': 'tabs.close.force' },
+ 'u': { 'type': 'tabs.reopen' },
+ 'K': { 'type': 'tabs.prev' },
+ 'J': { 'type': 'tabs.next' },
+ 'gT': { 'type': 'tabs.prev' },
+ 'gt': { 'type': 'tabs.next' },
+ 'g0': { 'type': 'tabs.first' },
+ 'g$': { 'type': 'tabs.last' },
+ '': { 'type': 'tabs.prevsel' },
+ 'r': { 'type': 'tabs.reload', 'cache': false },
+ 'R': { 'type': 'tabs.reload', 'cache': true },
+ 'zp': { 'type': 'tabs.pin.toggle' },
+ 'zd': { 'type': 'tabs.duplicate' },
+ 'zi': { 'type': 'zoom.in' },
+ 'zo': { 'type': 'zoom.out' },
+ 'zz': { 'type': 'zoom.neutral' },
+ 'f': { 'type': 'follow.start', 'newTab': false, 'background': false },
+ 'F': { 'type': 'follow.start', 'newTab': true, 'background': false },
+ 'm': { 'type': 'mark.set.prefix' },
+ '\'': { 'type': 'mark.jump.prefix' },
+ 'H': { 'type': 'navigate.history.prev' },
+ 'L': { 'type': 'navigate.history.next' },
+ '[[': { 'type': 'navigate.link.prev' },
+ ']]': { 'type': 'navigate.link.next' },
+ 'gu': { 'type': 'navigate.parent' },
+ 'gU': { 'type': 'navigate.root' },
+ 'gi': { 'type': 'focus.input' },
+ 'gf': { 'type': 'page.source' },
+ 'gh': { 'type': 'page.home', 'newTab': false },
+ 'gH': { 'type': 'page.home', 'newTab': true },
+ 'y': { 'type': 'urls.yank' },
+ 'p': { 'type': 'urls.paste', 'newTab': false },
+ 'P': { 'type': 'urls.paste', 'newTab': true },
+ '/': { 'type': 'find.start' },
+ 'n': { 'type': 'find.next' },
+ 'N': { 'type': 'find.prev' },
+ '': { 'type': 'addon.toggle.enabled' }
+ },
+ search: {
+ default: 'google',
+ engines: {
+ 'google': 'https://google.com/search?q={}',
+ 'yahoo': 'https://search.yahoo.com/search?p={}',
+ 'bing': 'https://www.bing.com/search?q={}',
+ 'duckduckgo': 'https://duckduckgo.com/?q={}',
+ 'twitter': 'https://twitter.com/search?q={}',
+ 'wikipedia': 'https://en.wikipedia.org/w/index.php?search={}'
+ }
+ },
+ properties: {
+ hintchars: 'abcdefghijklmnopqrstuvwxyz',
+ smoothscroll: false,
+ complete: 'sbh'
+ },
+ blacklist: []
+};
diff --git a/src/shared/operations.ts b/src/shared/operations.ts
index cc22f75..688c240 100644
--- a/src/shared/operations.ts
+++ b/src/shared/operations.ts
@@ -443,5 +443,5 @@ export const valueOf = (o: any): Operation => {
case MARK_JUMP_PREFIX:
return { type: o.type };
}
- throw new Error('unknown operation type: ' + o.type);
+ throw new TypeError('unknown operation type: ' + o.type);
};
diff --git a/src/shared/properties.ts b/src/shared/properties.ts
new file mode 100644
index 0000000..6315030
--- /dev/null
+++ b/src/shared/properties.ts
@@ -0,0 +1,50 @@
+export type Type = string | number | boolean;
+
+export class Def {
+ private name0: string;
+
+ private description0: string;
+
+ private defaultValue0: Type;
+
+ constructor(
+ name: string,
+ description: string,
+ defaultValue: Type,
+ ) {
+ this.name0 = name;
+ this.description0 = description;
+ this.defaultValue0 = defaultValue;
+ }
+
+ public get name(): string {
+ return this.name0;
+ }
+
+ public get defaultValue(): Type {
+ return this.defaultValue0;
+ }
+
+ public get description(): Type {
+ return this.description0;
+ }
+
+ public get type(): string {
+ return typeof this.defaultValue;
+ }
+}
+
+export const defs: Def[] = [
+ new Def(
+ 'hintchars',
+ 'hint characters on follow mode',
+ 'abcdefghijklmnopqrstuvwxyz'),
+ new Def(
+ 'smoothscroll',
+ 'smooth scroll',
+ false),
+ new Def(
+ 'complete',
+ 'which are completed at the open page',
+ 'sbh'),
+];
diff --git a/src/shared/property-defs.ts b/src/shared/property-defs.ts
new file mode 100644
index 0000000..6315030
--- /dev/null
+++ b/src/shared/property-defs.ts
@@ -0,0 +1,50 @@
+export type Type = string | number | boolean;
+
+export class Def {
+ private name0: string;
+
+ private description0: string;
+
+ private defaultValue0: Type;
+
+ constructor(
+ name: string,
+ description: string,
+ defaultValue: Type,
+ ) {
+ this.name0 = name;
+ this.description0 = description;
+ this.defaultValue0 = defaultValue;
+ }
+
+ public get name(): string {
+ return this.name0;
+ }
+
+ public get defaultValue(): Type {
+ return this.defaultValue0;
+ }
+
+ public get description(): Type {
+ return this.description0;
+ }
+
+ public get type(): string {
+ return typeof this.defaultValue;
+ }
+}
+
+export const defs: Def[] = [
+ new Def(
+ 'hintchars',
+ 'hint characters on follow mode',
+ 'abcdefghijklmnopqrstuvwxyz'),
+ new Def(
+ 'smoothscroll',
+ 'smooth scroll',
+ false),
+ new Def(
+ 'complete',
+ 'which are completed at the open page',
+ 'sbh'),
+];
diff --git a/src/shared/settings/default.ts b/src/shared/settings/default.ts
deleted file mode 100644
index 6523a74..0000000
--- a/src/shared/settings/default.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-export default {
- source: 'json',
- json: `{
- "keymaps": {
- "0": { "type": "scroll.home" },
- ":": { "type": "command.show" },
- "o": { "type": "command.show.open", "alter": false },
- "O": { "type": "command.show.open", "alter": true },
- "t": { "type": "command.show.tabopen", "alter": false },
- "T": { "type": "command.show.tabopen", "alter": true },
- "w": { "type": "command.show.winopen", "alter": false },
- "W": { "type": "command.show.winopen", "alter": true },
- "b": { "type": "command.show.buffer" },
- "a": { "type": "command.show.addbookmark", "alter": true },
- "k": { "type": "scroll.vertically", "count": -1 },
- "j": { "type": "scroll.vertically", "count": 1 },
- "h": { "type": "scroll.horizonally", "count": -1 },
- "l": { "type": "scroll.horizonally", "count": 1 },
- "": { "type": "scroll.pages", "count": -0.5 },
- "": { "type": "scroll.pages", "count": 0.5 },
- "": { "type": "scroll.pages", "count": -1 },
- "": { "type": "scroll.pages", "count": 1 },
- "gg": { "type": "scroll.top" },
- "G": { "type": "scroll.bottom" },
- "$": { "type": "scroll.end" },
- "d": { "type": "tabs.close" },
- "D": { "type": "tabs.close.right" },
- "!d": { "type": "tabs.close.force" },
- "u": { "type": "tabs.reopen" },
- "K": { "type": "tabs.prev", "count": 1 },
- "J": { "type": "tabs.next", "count": 1 },
- "gT": { "type": "tabs.prev", "count": 1 },
- "gt": { "type": "tabs.next", "count": 1 },
- "g0": { "type": "tabs.first" },
- "g$": { "type": "tabs.last" },
- "": { "type": "tabs.prevsel" },
- "r": { "type": "tabs.reload", "cache": false },
- "R": { "type": "tabs.reload", "cache": true },
- "zp": { "type": "tabs.pin.toggle" },
- "zd": { "type": "tabs.duplicate" },
- "zi": { "type": "zoom.in" },
- "zo": { "type": "zoom.out" },
- "zz": { "type": "zoom.neutral" },
- "f": { "type": "follow.start", "newTab": false },
- "F": { "type": "follow.start", "newTab": true, "background": false },
- "m": { "type": "mark.set.prefix" },
- "'": { "type": "mark.jump.prefix" },
- "H": { "type": "navigate.history.prev" },
- "L": { "type": "navigate.history.next" },
- "[[": { "type": "navigate.link.prev" },
- "]]": { "type": "navigate.link.next" },
- "gu": { "type": "navigate.parent" },
- "gU": { "type": "navigate.root" },
- "gi": { "type": "focus.input" },
- "gf": { "type": "page.source" },
- "gh": { "type": "page.home" },
- "gH": { "type": "page.home", "newTab": true },
- "y": { "type": "urls.yank" },
- "p": { "type": "urls.paste", "newTab": false },
- "P": { "type": "urls.paste", "newTab": true },
- "/": { "type": "find.start" },
- "n": { "type": "find.next" },
- "N": { "type": "find.prev" },
- "": { "type": "addon.toggle.enabled" }
- },
- "search": {
- "default": "google",
- "engines": {
- "google": "https://google.com/search?q={}",
- "yahoo": "https://search.yahoo.com/search?p={}",
- "bing": "https://www.bing.com/search?q={}",
- "duckduckgo": "https://duckduckgo.com/?q={}",
- "twitter": "https://twitter.com/search?q={}",
- "wikipedia": "https://en.wikipedia.org/w/index.php?search={}"
- }
- },
- "properties": {
- "hintchars": "abcdefghijklmnopqrstuvwxyz",
- "smoothscroll": false,
- "complete": "sbh"
- },
- "blacklist": [
- ]
-}`,
-};
diff --git a/src/shared/settings/properties.ts b/src/shared/settings/properties.ts
deleted file mode 100644
index 7d037df..0000000
--- a/src/shared/settings/properties.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-// describe types of a propety as:
-// mystr: 'string',
-// mynum: 'number',
-// mybool: 'boolean',
-const types: { [key: string]: string } = {
- hintchars: 'string',
- smoothscroll: 'boolean',
- complete: 'string',
-};
-
-// describe default values of a property
-const defaults: { [key: string]: string | number | boolean } = {
- hintchars: 'abcdefghijklmnopqrstuvwxyz',
- smoothscroll: false,
- complete: 'sbh',
-};
-
-const docs: { [key: string]: string } = {
- hintchars: 'hint characters on follow mode',
- smoothscroll: 'smooth scroll',
- complete: 'which are completed at the open page',
-};
-
-export { types, defaults, docs };
diff --git a/src/shared/settings/storage.ts b/src/shared/settings/storage.ts
deleted file mode 100644
index 90a3a66..0000000
--- a/src/shared/settings/storage.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import DefaultSettings from './default';
-import * as settingsValues from './values';
-
-const loadRaw = async(): Promise => {
- let { settings } = await browser.storage.local.get('settings');
- if (!settings) {
- return DefaultSettings;
- }
- return { ...DefaultSettings, ...settings as object };
-};
-
-const loadValue = async() => {
- let settings = await loadRaw();
- let value = JSON.parse(DefaultSettings.json);
- if (settings.source === 'json') {
- value = settingsValues.valueFromJson(settings.json);
- } else if (settings.source === 'form') {
- value = settingsValues.valueFromForm(settings.form);
- }
- if (!value.properties) {
- value.properties = {};
- }
- return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value };
-};
-
-const save = (settings: any): Promise => {
- return browser.storage.local.set({
- settings,
- });
-};
-
-export { loadRaw, loadValue, save };
diff --git a/src/shared/settings/validator.ts b/src/shared/settings/validator.ts
deleted file mode 100644
index 71cc466..0000000
--- a/src/shared/settings/validator.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-import * as operations from '../operations';
-import * as properties from './properties';
-
-const VALID_TOP_KEYS = ['keymaps', 'search', 'blacklist', 'properties'];
-const VALID_OPERATION_VALUES = Object.keys(operations).map((key) => {
- return operations[key];
-});
-
-const validateInvalidTopKeys = (settings: any): void => {
- let invalidKey = Object.keys(settings).find((key) => {
- return !VALID_TOP_KEYS.includes(key);
- });
- if (invalidKey) {
- throw Error(`Unknown key: "${invalidKey}"`);
- }
-};
-
-const validateKeymaps = (keymaps: any): void => {
- for (let key of Object.keys(keymaps)) {
- let value = keymaps[key];
- if (!VALID_OPERATION_VALUES.includes(value.type)) {
- throw Error(`Unknown operation: "${value.type}"`);
- }
- }
-};
-
-const validateSearch = (search: any): void => {
- let engines = search.engines;
- for (let key of Object.keys(engines)) {
- if ((/\s/).test(key)) {
- throw new Error(
- `While space in search engine name is not allowed: "${key}"`
- );
- }
- let url = engines[key];
- if (!url.match(/{}/)) {
- throw new Error(`No {}-placeholders in URL of "${key}"`);
- }
- if (url.match(/{}/g).length > 1) {
- throw new Error(`Multiple {}-placeholders in URL of "${key}"`);
- }
- }
-
- if (!search.default) {
- throw new Error(`Default engine is not set`);
- }
- if (!Object.keys(engines).includes(search.default)) {
- throw new Error(`Default engine "${search.default}" not found`);
- }
-};
-
-const validateProperties = (props: any): void => {
- for (let name of Object.keys(props)) {
- if (!properties.types[name]) {
- throw new Error(`Unknown property name: "${name}"`);
- }
- if (typeof props[name] !== properties.types[name]) {
- throw new Error(`Invalid type for property: "${name}"`);
- }
- }
-};
-
-const validate = (settings: any): void => {
- validateInvalidTopKeys(settings);
- if (settings.keymaps) {
- validateKeymaps(settings.keymaps);
- }
- if (settings.search) {
- validateSearch(settings.search);
- }
- if (settings.properties) {
- validateProperties(settings.properties);
- }
-};
-
-export { validate };
diff --git a/src/shared/settings/values.ts b/src/shared/settings/values.ts
deleted file mode 100644
index cb6a668..0000000
--- a/src/shared/settings/values.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-import * as properties from './properties';
-
-const operationFromFormName = (name: string): any => {
- let [type, argStr] = name.split('?');
- let args = {};
- if (argStr) {
- args = JSON.parse(argStr);
- }
- return { type, ...args };
-};
-
-const operationToFormName = (op: any): string => {
- let type = op.type;
- let args = { ...op };
- delete args.type;
-
- if (Object.keys(args).length === 0) {
- return type;
- }
- return op.type + '?' + JSON.stringify(args);
-};
-
-const valueFromJson = (json: string): object => {
- return JSON.parse(json);
-};
-
-const valueFromForm = (form: any): object => {
- let keymaps: any = undefined;
- if (form.keymaps) {
- keymaps = {};
- for (let name of Object.keys(form.keymaps)) {
- let keys = form.keymaps[name];
- keymaps[keys] = operationFromFormName(name);
- }
- }
-
- let search: any = undefined;
- if (form.search) {
- search = { default: form.search.default };
-
- if (form.search.engines) {
- search.engines = {};
- for (let [name, url] of form.search.engines) {
- search.engines[name] = url;
- }
- }
- }
-
- return {
- keymaps,
- search,
- blacklist: form.blacklist,
- properties: form.properties
- };
-};
-
-const jsonFromValue = (value: any): string => {
- return JSON.stringify(value, undefined, 2);
-};
-
-const formFromValue = (value: any, allowedOps: any[]): any => {
- let keymaps: any = undefined;
-
- if (value.keymaps) {
- let allowedSet = new Set(allowedOps);
-
- keymaps = {};
- for (let keys of Object.keys(value.keymaps)) {
- let op = operationToFormName(value.keymaps[keys]);
- if (allowedSet.has(op)) {
- keymaps[op] = keys;
- }
- }
- }
-
- let search: any = undefined;
- if (value.search) {
- search = { default: value.search.default };
- if (value.search.engines) {
- search.engines = Object.keys(value.search.engines).map((name) => {
- return [name, value.search.engines[name]];
- });
- }
- }
-
- let formProperties = { ...properties.defaults, ...value.properties };
-
- return {
- keymaps,
- search,
- blacklist: value.blacklist,
- properties: formProperties,
- };
-};
-
-const jsonFromForm = (form: any): string => {
- return jsonFromValue(valueFromForm(form));
-};
-
-const formFromJson = (json: string, allowedOps: any[]): any => {
- let value = valueFromJson(json);
- return formFromValue(value, allowedOps);
-};
-
-export {
- valueFromJson, valueFromForm, jsonFromValue, formFromValue,
- jsonFromForm, formFromJson
-};
diff --git a/test/background/usecases/parsers.test.ts b/test/background/usecases/parsers.test.ts
index 17b034b..f3a64eb 100644
--- a/test/background/usecases/parsers.test.ts
+++ b/test/background/usecases/parsers.test.ts
@@ -3,45 +3,32 @@ import * as parsers from 'background/usecases/parsers';
describe("shared/commands/parsers", () => {
describe("#parsers.parseSetOption", () => {
it('parse set string', () => {
- let [key, value] = parsers.parseSetOption('encoding=utf-8', { encoding: 'string' });
- expect(key).to.equal('encoding');
- expect(value).to.equal('utf-8');
+ let [key, value] = parsers.parseSetOption('hintchars=abcdefgh');
+ expect(key).to.equal('hintchars');
+ expect(value).to.equal('abcdefgh');
});
it('parse set empty string', () => {
- let [key, value] = parsers.parseSetOption('encoding=', { encoding: 'string' });
- expect(key).to.equal('encoding');
+ let [key, value] = parsers.parseSetOption('hintchars=');
+ expect(key).to.equal('hintchars');
expect(value).to.equal('');
});
- it('parse set string', () => {
- let [key, value] = parsers.parseSetOption('history=50', { history: 'number' });
- expect(key).to.equal('history');
- expect(value).to.equal(50);
- });
-
it('parse set boolean', () => {
- let [key, value] = parsers.parseSetOption('paste', { paste: 'boolean' });
- expect(key).to.equal('paste');
+ let [key, value] = parsers.parseSetOption('smoothscroll');
+ expect(key).to.equal('smoothscroll');
expect(value).to.be.true;
- [key, value] = parsers.parseSetOption('nopaste', { paste: 'boolean' });
- expect(key).to.equal('paste');
+ [key, value] = parsers.parseSetOption('nosmoothscroll');
+ expect(key).to.equal('smoothscroll');
expect(value).to.be.false;
});
it('throws error on unknown property', () => {
- expect(() => parsers.parseSetOption('charset=utf-8', {})).to.throw(Error, 'Unknown');
- expect(() => parsers.parseSetOption('smoothscroll', {})).to.throw(Error, 'Unknown');
- expect(() => parsers.parseSetOption('nosmoothscroll', {})).to.throw(Error, 'Unknown');
- })
-
- it('throws error on invalid property', () => {
- expect(() => parsers.parseSetOption('charset=utf-8', { charset: 'number' })).to.throw(Error, 'Not number');
- expect(() => parsers.parseSetOption('charset=utf-8', { charset: 'boolean' })).to.throw(Error, 'Invalid');
- expect(() => parsers.parseSetOption('charset=', { charset: 'boolean' })).to.throw(Error, 'Invalid');
- expect(() => parsers.parseSetOption('smoothscroll', { smoothscroll: 'string' })).to.throw(Error, 'Invalid');
- expect(() => parsers.parseSetOption('smoothscroll', { smoothscroll: 'number' })).to.throw(Error, 'Invalid');
- })
+ expect(() => parsers.parseSetOption('encoding=utf-8')).to.throw(Error, 'Unknown');
+ expect(() => parsers.parseSetOption('paste')).to.throw(Error, 'Unknown');
+ expect(() => parsers.parseSetOption('nopaste')).to.throw(Error, 'Unknown');
+ expect(() => parsers.parseSetOption('smoothscroll=yes')).to.throw(Error, 'Invalid argument');
+ });
});
});
diff --git a/test/content/actions/setting.test.ts b/test/content/actions/setting.test.ts
index 0721d5d..c831433 100644
--- a/test/content/actions/setting.test.ts
+++ b/test/content/actions/setting.test.ts
@@ -4,32 +4,40 @@ import * as settingActions from 'content/actions/setting';
describe("setting actions", () => {
describe("set", () => {
it('create SETTING_SET action', () => {
- let action = settingActions.set({ red: 'apple', yellow: 'banana' });
+ let action = settingActions.set({
+ keymaps: {
+ 'dd': 'remove current tab',
+ 'z': 'increment',
+ },
+ search: {
+ default: "google",
+ engines: {
+ google: 'https://google.com/search?q={}',
+ }
+ },
+ properties: {
+ hintchars: 'abcd1234',
+ },
+ blacklist: [],
+ });
expect(action.type).to.equal(actions.SETTING_SET);
- expect(action.value.red).to.equal('apple');
- expect(action.value.yellow).to.equal('banana');
- expect(action.value.keymaps).to.be.empty;
+ expect(action.settings.properties.hintchars).to.equal('abcd1234');
});
- it('converts keymaps', () => {
+ it('overrides cancel keys', () => {
let action = settingActions.set({
keymaps: {
- 'dd': 'remove current tab',
- 'z': 'increment',
+ "k": { "type": "scroll.vertically", "count": -1 },
+ "j": { "type": "scroll.vertically", "count": 1 },
}
});
- let keymaps = action.value.keymaps;
- let map = new Map(keymaps);
- expect(map).to.have.deep.all.keys(
- [
- [{ key: 'Esc', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false }],
- [{ key: '[', shiftKey: false, ctrlKey: true, altKey: false, metaKey: false }],
- [{ key: 'd', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
- { key: 'd', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false }],
- [{ key: 'z', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
- { key: 'a', shiftKey: false, ctrlKey: true, altKey: false, metaKey: false }],
- ]
- );
+ let keymaps = action.settings.keymaps;
+ expect(action.settings.keymaps).to.deep.equals({
+ "k": { type: "scroll.vertically", count: -1 },
+ "j": { type: "scroll.vertically", count: 1 },
+ '': { type: 'cancel' },
+ '': { type: 'cancel' },
+ });
});
});
});
diff --git a/test/content/reducers/setting.test.ts b/test/content/reducers/setting.test.ts
index 226fc58..fe23006 100644
--- a/test/content/reducers/setting.test.ts
+++ b/test/content/reducers/setting.test.ts
@@ -9,9 +9,24 @@ describe("content setting reducer", () => {
it('return next state for SETTING_SET', () => {
let newSettings = { red: 'apple', yellow: 'banana' };
- let action = { type: actions.SETTING_SET, value: newSettings };
+ let action = {
+ type: actions.SETTING_SET,
+ settings: {
+ keymaps: {
+ "zz": { type: "zoom.neutral" },
+ "": { "type": "addon.toggle.enabled" }
+ },
+ "blacklist": []
+ }
+ }
let state = settingReducer(undefined, action);
- expect(state).to.deep.equal(newSettings);
- expect(state).not.to.equal(newSettings); // assert deep copy
+ console.log(JSON.stringify(state.keymaps));
+ expect(state.keymaps).to.have.deep.all.members([
+ { key: [{ key: 'z', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ { key: 'z', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false }],
+ op: { type: 'zoom.neutral' }},
+ { key: [{ key: 'Esc', shiftKey: true, ctrlKey: false, altKey: false, metaKey: false }],
+ op: { type: 'addon.toggle.enabled' }},
+ ]);
});
});
diff --git a/test/settings/components/form/KeymapsForm.test.tsx b/test/settings/components/form/KeymapsForm.test.tsx
index 6ac57c9..dc2322b 100644
--- a/test/settings/components/form/KeymapsForm.test.tsx
+++ b/test/settings/components/form/KeymapsForm.test.tsx
@@ -2,15 +2,17 @@ import React from 'react';
import ReactDOM from 'react-dom';
import ReactTestRenderer from 'react-test-renderer';
import ReactTestUtils from 'react-dom/test-utils';
-import KeymapsForm from 'settings/components/form/KeymapsForm'
+import KeymapsForm from '../../../../src/settings/components/form/KeymapsForm'
+import { FormKeymaps } from 'shared/SettingData';
+import { expect } from 'chai';
describe("settings/form/KeymapsForm", () => {
describe('render', () => {
it('renders keymap fields', () => {
- let root = ReactTestRenderer.create().root
+ })} />).root
let inputj = root.findByProps({ id: 'scroll.vertically?{"count":1}' });
let inputk = root.findByProps({ id: 'scroll.vertically?{"count":-1}' });
@@ -46,12 +48,12 @@ describe("settings/form/KeymapsForm", () => {
it('invokes onChange event on edit', (done) => {
ReactTestUtils.act(() => {
ReactDOM.render( {
- expect(value['scroll.vertically?{"count":1}']).to.equal('jjj');
+ expect(value.toJSON()['scroll.vertically?{"count":1}']).to.equal('jjj');
done();
}} />, container);
});
diff --git a/test/settings/components/form/SearchEngineForm.test.tsx b/test/settings/components/form/SearchEngineForm.test.tsx
index 06822f2..0e6b17d 100644
--- a/test/settings/components/form/SearchEngineForm.test.tsx
+++ b/test/settings/components/form/SearchEngineForm.test.tsx
@@ -3,14 +3,15 @@ import ReactDOM from 'react-dom';
import ReactTestRenderer from 'react-test-renderer';
import ReactTestUtils from 'react-dom/test-utils';
import SearchForm from 'settings/components/form/SearchForm'
+import { FormSearch } from 'shared/SettingData';
describe("settings/form/SearchForm", () => {
describe('render', () => {
it('renders SearchForm', () => {
- let root = ReactTestRenderer.create().root;
+ })} />).root;
let names = root.findAllByProps({ name: 'name' });
expect(names).to.have.lengthOf(2);
@@ -22,28 +23,6 @@ describe("settings/form/SearchForm", () => {
expect(urls[0].props.value).to.equal('google.com');
expect(urls[1].props.value).to.equal('yahoo.com');
});
-
- it('renders blank value', () => {
- let root = ReactTestRenderer.create().root;
-
- let names = root.findAllByProps({ name: 'name' });
- expect(names).to.be.empty;
-
- let urls = root.findAllByProps({ name: 'url' });
- expect(urls).to.be.empty;
- });
-
- it('renders blank engines', () => {
- let root = ReactTestRenderer.create(
- ,
- ).root;
-
- let names = root.findAllByProps({ name: 'name' });
- expect(names).to.be.empty;
-
- let urls = root.findAllByProps({ name: 'url' });
- expect(urls).to.be.empty;
- });
});
describe('onChange event', () => {
@@ -62,14 +41,15 @@ describe("settings/form/SearchForm", () => {
it('invokes onChange event on edit', (done) => {
ReactTestUtils.act(() => {
ReactDOM.render( {
- expect(value.default).to.equal('louvre');
- expect(value.engines).to.have.lengthOf(2)
- expect(value.engines).to.have.deep.members(
+ let json = value.toJSON();
+ expect(json.default).to.equal('louvre');
+ expect(json.engines).to.have.lengthOf(2)
+ expect(json.engines).to.have.deep.members(
[['louvre', 'google.com'], ['yahoo', 'yahoo.com']]
);
done();
@@ -87,14 +67,15 @@ describe("settings/form/SearchForm", () => {
it('invokes onChange event on delete', (done) => {
ReactTestUtils.act(() => {
- ReactDOM.render( {
- expect(value.default).to.equal('yahoo');
- expect(value.engines).to.have.lengthOf(1)
- expect(value.engines).to.have.deep.members(
+ let json = value.toJSON();
+ expect(json.default).to.equal('yahoo');
+ expect(json.engines).to.have.lengthOf(1)
+ expect(json.engines).to.have.deep.members(
[['yahoo', 'yahoo.com']]
);
done();
@@ -107,14 +88,15 @@ describe("settings/form/SearchForm", () => {
it('invokes onChange event on add', (done) => {
ReactTestUtils.act(() => {
- ReactDOM.render( {
- expect(value.default).to.equal('yahoo');
- expect(value.engines).to.have.lengthOf(2)
- expect(value.engines).to.have.deep.members(
+ let json = value.toJSON();
+ expect(json.default).to.equal('yahoo');
+ expect(json.engines).to.have.lengthOf(2)
+ expect(json.engines).to.have.deep.members(
[['google', 'google.com'], ['', '']],
);
done();
diff --git a/test/settings/reducers/setting.test.ts b/test/settings/reducers/setting.test.ts
index 6a874e8..376d66e 100644
--- a/test/settings/reducers/setting.test.ts
+++ b/test/settings/reducers/setting.test.ts
@@ -4,8 +4,7 @@ import settingReducer from 'settings/reducers/setting';
describe("settings setting reducer", () => {
it('return the initial state', () => {
let state = settingReducer(undefined, {});
- expect(state).to.have.deep.property('json', '');
- expect(state).to.have.deep.property('form', null);
+ expect(state).to.have.deep.property('source', 'json');
expect(state).to.have.deep.property('error', '');
});
diff --git a/test/shared/SettingData.test.ts b/test/shared/SettingData.test.ts
new file mode 100644
index 0000000..8736ecb
--- /dev/null
+++ b/test/shared/SettingData.test.ts
@@ -0,0 +1,293 @@
+import SettingData, {
+ FormKeymaps, JSONSettings, FormSettings,
+} from '../../src/shared/SettingData';
+import Settings, { Keymaps } from '../../src/shared/Settings';
+import { expect } from 'chai';
+
+describe('shared/SettingData', () => {
+ describe('FormKeymaps', () => {
+ describe('#valueOF to #toKeymaps', () => {
+ it('parses form keymaps and convert to operations', () => {
+ let data = {
+ 'scroll.vertically?{"count":1}': 'j',
+ 'scroll.home': '0',
+ }
+
+ let keymaps = FormKeymaps.valueOf(data).toKeymaps();
+ expect(keymaps).to.deep.equal({
+ 'j': { type: 'scroll.vertically', count: 1 },
+ '0': { type: 'scroll.home' },
+ });
+ });
+ });
+
+ describe('#fromKeymaps to #toJSON', () => {
+ it('create from a Keymaps and create a JSON object', () => {
+ let data: Keymaps = {
+ 'j': { type: 'scroll.vertically', count: 1 },
+ '0': { type: 'scroll.home' },
+ }
+
+ let keymaps = FormKeymaps.fromKeymaps(data).toJSON();
+ expect(keymaps).to.deep.equal({
+ 'scroll.vertically?{"count":1}': 'j',
+ 'scroll.home': '0',
+ });
+ });
+ });
+ });
+
+ describe('JSONSettings', () => {
+ describe('#valueOf to #toSettings', () => {
+ it('parse object and create a Settings', () => {
+ let o = `{
+ "keymaps": {},
+ "search": {
+ "default": "google",
+ "engines": {
+ "google": "https://google.com/search?q={}"
+ }
+ },
+ "properties": {
+ "hintchars": "abcdefghijklmnopqrstuvwxyz",
+ "smoothscroll": false,
+ "complete": "sbh"
+ },
+ "blacklist": []
+ }`;
+
+ let settings = JSONSettings.valueOf(o).toSettings();
+ expect(settings).to.deep.equal(JSON.parse(o));
+ });
+ });
+
+ describe('#fromSettings to #toJSON', () => {
+ it('create from a Settings and create a JSON string', () => {
+ let o = {
+ keymaps: {},
+ search: {
+ default: "google",
+ engines: {
+ google: "https://google.com/search?q={}",
+ },
+ },
+ properties: {
+ hintchars: "abcdefghijklmnopqrstuvwxyz",
+ smoothscroll: false,
+ complete: "sbh"
+ },
+ blacklist: [],
+ };
+
+ let json = JSONSettings.fromSettings(o).toJSON();
+ expect(JSON.parse(json)).to.deep.equal(o);
+ });
+ });
+ });
+
+ describe('FormSettings', () => {
+ describe('#valueOf to #toSettings', () => {
+ it('parse object and create a Settings', () => {
+ let data = {
+ keymaps: {
+ 'scroll.vertically?{"count":1}': 'j',
+ 'scroll.home': '0',
+ },
+ search: {
+ default: "google",
+ engines: [
+ ["google", "https://google.com/search?q={}"],
+ ]
+ },
+ properties: {
+ hintchars: "abcdefghijklmnopqrstuvwxyz",
+ smoothscroll: false,
+ complete: "sbh"
+ },
+ blacklist: []
+ };
+
+ let settings = FormSettings.valueOf(data).toSettings();
+ expect(settings).to.deep.equal({
+ keymaps: {
+ 'j': { type: 'scroll.vertically', count: 1 },
+ '0': { type: 'scroll.home' },
+ },
+ search: {
+ default: "google",
+ engines: {
+ "google": "https://google.com/search?q={}"
+ }
+ },
+ properties: {
+ hintchars: "abcdefghijklmnopqrstuvwxyz",
+ smoothscroll: false,
+ complete: "sbh"
+ },
+ blacklist: []
+ });
+ });
+ });
+
+ describe('#fromSettings to #toJSON', () => {
+ it('create from a Settings and create a JSON string', () => {
+ let data: Settings = {
+ keymaps: {
+ 'j': { type: 'scroll.vertically', count: 1 },
+ '0': { type: 'scroll.home' },
+ },
+ search: {
+ default: "google",
+ engines: {
+ "google": "https://google.com/search?q={}"
+ }
+ },
+ properties: {
+ hintchars: "abcdefghijklmnopqrstuvwxyz",
+ smoothscroll: false,
+ complete: "sbh"
+ },
+ blacklist: []
+ };
+
+ let json = FormSettings.fromSettings(data).toJSON();
+ expect(json).to.deep.equal({
+ keymaps: {
+ 'scroll.vertically?{"count":1}': 'j',
+ 'scroll.home': '0',
+ },
+ search: {
+ default: "google",
+ engines: [
+ ["google", "https://google.com/search?q={}"],
+ ]
+ },
+ properties: {
+ hintchars: "abcdefghijklmnopqrstuvwxyz",
+ smoothscroll: false,
+ complete: "sbh"
+ },
+ blacklist: [],
+ });
+ });
+ });
+ });
+
+ describe('SettingData', () => {
+ describe('#valueOf to #toJSON', () => {
+ it('parse object from json source', () => {
+ let data = {
+ source: 'json',
+ json: `{
+ "keymaps": {},
+ "search": {
+ "default": "google",
+ "engines": {
+ "google": "https://google.com/search?q={}"
+ }
+ },
+ "properties": {
+ "hintchars": "abcdefghijklmnopqrstuvwxyz",
+ "smoothscroll": false,
+ "complete": "sbh"
+ },
+ "blacklist": []
+ }`,
+ };
+
+ let j = SettingData.valueOf(data).toJSON();
+ expect(j.source).to.equal('json');
+ expect(j.json).to.be.a('string');
+ });
+
+ it('parse object from form source', () => {
+ let data = {
+ source: 'form',
+ form: {
+ keymaps: {},
+ search: {
+ default: "yahoo",
+ engines: [
+ ['yahoo', 'https://yahoo.com/search?q={}'],
+ ],
+ },
+ properties: {
+ hintchars: "abcdefghijklmnopqrstuvwxyz",
+ smoothscroll: false,
+ complete: "sbh"
+ },
+ blacklist: [],
+ },
+ };
+
+ let j = SettingData.valueOf(data).toJSON();
+ expect(j.source).to.equal('form');
+ expect(j.form).to.deep.equal({
+ keymaps: {},
+ search: {
+ default: "yahoo",
+ engines: [
+ ['yahoo', 'https://yahoo.com/search?q={}'],
+ ],
+ },
+ properties: {
+ hintchars: "abcdefghijklmnopqrstuvwxyz",
+ smoothscroll: false,
+ complete: "sbh"
+ },
+ blacklist: [],
+ });
+ });
+ });
+
+ describe('#toSettings', () => {
+ it('parse object from json source', () => {
+ let data = {
+ source: 'json',
+ json: `{
+ "keymaps": {},
+ "search": {
+ "default": "google",
+ "engines": {
+ "google": "https://google.com/search?q={}"
+ }
+ },
+ "properties": {
+ "hintchars": "abcdefghijklmnopqrstuvwxyz",
+ "smoothscroll": false,
+ "complete": "sbh"
+ },
+ "blacklist": []
+ }`,
+ };
+
+ let settings = SettingData.valueOf(data).toSettings();
+ expect(settings.search.default).to.equal('google');
+ });
+
+ it('parse object from form source', () => {
+ let data = {
+ source: 'form',
+ form: {
+ keymaps: {},
+ search: {
+ default: "yahoo",
+ engines: [
+ ['yahoo', 'https://yahoo.com/search?q={}'],
+ ],
+ },
+ properties: {
+ hintchars: "abcdefghijklmnopqrstuvwxyz",
+ smoothscroll: false,
+ complete: "sbh"
+ },
+ blacklist: [],
+ },
+ };
+
+ let settings = SettingData.valueOf(data).toSettings();
+ expect(settings.search.default).to.equal('yahoo');
+ });
+ });
+ });
+});
diff --git a/test/shared/Settings.test.ts b/test/shared/Settings.test.ts
new file mode 100644
index 0000000..02cd022
--- /dev/null
+++ b/test/shared/Settings.test.ts
@@ -0,0 +1,190 @@
+import * as settings from '../../src/shared/Settings';
+import { expect } from 'chai';
+
+describe('Settings', () => {
+ describe('#keymapsValueOf', () => {
+ it('returns empty object by empty settings', () => {
+ let keymaps = settings.keymapsValueOf({});
+ expect(keymaps).to.be.empty;
+ });
+
+ it('returns keymaps by valid settings', () => {
+ let keymaps = settings.keymapsValueOf({
+ k: { type: "scroll.vertically", count: -1 },
+ j: { type: "scroll.vertically", count: 1 },
+ });
+
+ expect(keymaps['k']).to.deep.equal({ type: "scroll.vertically", count: -1 });
+ expect(keymaps['j']).to.deep.equal({ type: "scroll.vertically", count: 1 });
+ });
+
+ it('throws a TypeError by invalid settings', () => {
+ expect(() => settings.keymapsValueOf(null)).to.throw(TypeError);
+ expect(() => settings.keymapsValueOf({
+ k: { type: "invalid.operation" },
+ })).to.throw(TypeError);
+ });
+ });
+
+ describe('#searchValueOf', () => {
+ it('returns search settings by valid settings', () => {
+ let search = settings.searchValueOf({
+ default: "google",
+ engines: {
+ "google": "https://google.com/search?q={}",
+ "yahoo": "https://search.yahoo.com/search?p={}",
+ }
+ });
+
+ expect(search).to.deep.equal({
+ default: "google",
+ engines: {
+ "google": "https://google.com/search?q={}",
+ "yahoo": "https://search.yahoo.com/search?p={}",
+ }
+ });
+ });
+
+ it('throws a TypeError by invalid settings', () => {
+ expect(() => settings.searchValueOf(null)).to.throw(TypeError);
+ expect(() => settings.searchValueOf({})).to.throw(TypeError);
+ expect(() => settings.searchValueOf([])).to.throw(TypeError);
+ expect(() => settings.searchValueOf({
+ default: 123,
+ engines: {}
+ })).to.throw(TypeError);
+ expect(() => settings.searchValueOf({
+ default: "google",
+ engines: {
+ "google": 123456,
+ }
+ })).to.throw(TypeError);
+ expect(() => settings.searchValueOf({
+ default: "wikipedia",
+ engines: {
+ "google": "https://google.com/search?q={}",
+ "yahoo": "https://search.yahoo.com/search?p={}",
+ }
+ })).to.throw(TypeError);
+ expect(() => settings.searchValueOf({
+ default: "g o o g l e",
+ engines: {
+ "g o o g l e": "https://google.com/search?q={}",
+ }
+ })).to.throw(TypeError);
+ expect(() => settings.searchValueOf({
+ default: "google",
+ engines: {
+ "google": "https://google.com/search",
+ }
+ })).to.throw(TypeError);
+ expect(() => settings.searchValueOf({
+ default: "google",
+ engines: {
+ "google": "https://google.com/search?q={}&r={}",
+ }
+ })).to.throw(TypeError);
+ });
+ });
+
+ describe('#propertiesValueOf', () => {
+ it('returns with default properties by empty settings', () => {
+ let props = settings.propertiesValueOf({});
+ expect(props).to.deep.equal({
+ hintchars: "abcdefghijklmnopqrstuvwxyz",
+ smoothscroll: false,
+ complete: "sbh"
+ })
+ });
+
+ it('returns properties by valid settings', () => {
+ let props = settings.propertiesValueOf({
+ hintchars: "abcdefgh",
+ smoothscroll: false,
+ complete: "sbh"
+ });
+
+ expect(props).to.deep.equal({
+ hintchars: "abcdefgh",
+ smoothscroll: false,
+ complete: "sbh"
+ });
+ });
+
+ it('throws a TypeError by invalid settings', () => {
+ expect(() => settings.keymapsValueOf(null)).to.throw(TypeError);
+ expect(() => settings.keymapsValueOf({
+ smoothscroll: 'false',
+ })).to.throw(TypeError);
+ expect(() => settings.keymapsValueOf({
+ unknown: 'xyz'
+ })).to.throw(TypeError);
+ });
+ });
+
+ describe('#blacklistValueOf', () => {
+ it('returns empty array by empty settings', () => {
+ let blacklist = settings.blacklistValueOf([]);
+ expect(blacklist).to.be.empty;
+ });
+
+ it('returns blacklist by valid settings', () => {
+ let blacklist = settings.blacklistValueOf([
+ "github.com",
+ "circleci.com",
+ ]);
+
+ expect(blacklist).to.deep.equal([
+ "github.com",
+ "circleci.com",
+ ]);
+ });
+
+ it('throws a TypeError by invalid settings', () => {
+ expect(() => settings.blacklistValueOf(null)).to.throw(TypeError);
+ expect(() => settings.blacklistValueOf({})).to.throw(TypeError);
+ expect(() => settings.blacklistValueOf([1,2,3])).to.throw(TypeError);
+ });
+ });
+
+ describe('#valueOf', () => {
+ it('returns settings by valid settings', () => {
+ let x = settings.valueOf({
+ keymaps: {},
+ "search": {
+ "default": "google",
+ "engines": {
+ "google": "https://google.com/search?q={}",
+ }
+ },
+ "properties": {},
+ "blacklist": []
+ });
+
+ expect(x).to.deep.equal({
+ keymaps: {},
+ search: {
+ default: "google",
+ engines: {
+ google: "https://google.com/search?q={}",
+ }
+ },
+ properties: {
+ hintchars: "abcdefghijklmnopqrstuvwxyz",
+ smoothscroll: false,
+ complete: "sbh"
+ },
+ blacklist: []
+ });
+ });
+
+ it('sets default settings', () => {
+ let value = settings.valueOf({});
+ expect(value.keymaps).to.not.be.empty;
+ expect(value.properties).to.not.be.empty;
+ expect(value.search.default).to.be.a('string');
+ expect(value.search.engines).to.be.an('object');
+ expect(value.blacklist).to.be.empty;
+ });
+ });
+});
diff --git a/test/shared/properties.test.js b/test/shared/properties.test.js
new file mode 100644
index 0000000..37903d8
--- /dev/null
+++ b/test/shared/properties.test.js
@@ -0,0 +1,18 @@
+import * as settings from 'shared/settings';
+
+describe('properties', () => {
+ describe('Def class', () => {
+ it('returns property definitions', () => {
+ let def = new proerties.Def(
+ 'smoothscroll',
+ 'smooth scroll',
+ false);
+
+ expect(def.name).to.equal('smoothscroll');
+ expect(def.describe).to.equal('smooth scroll');
+ expect(def.defaultValue).to.equal(false);
+ expect(def.type).to.equal('boolean');
+ });
+ });
+});
+
diff --git a/test/shared/property-defs.test.js b/test/shared/property-defs.test.js
new file mode 100644
index 0000000..37903d8
--- /dev/null
+++ b/test/shared/property-defs.test.js
@@ -0,0 +1,18 @@
+import * as settings from 'shared/settings';
+
+describe('properties', () => {
+ describe('Def class', () => {
+ it('returns property definitions', () => {
+ let def = new proerties.Def(
+ 'smoothscroll',
+ 'smooth scroll',
+ false);
+
+ expect(def.name).to.equal('smoothscroll');
+ expect(def.describe).to.equal('smooth scroll');
+ expect(def.defaultValue).to.equal(false);
+ expect(def.type).to.equal('boolean');
+ });
+ });
+});
+
diff --git a/test/shared/settings/validator.test.ts b/test/shared/settings/validator.test.ts
deleted file mode 100644
index 9bbfa3e..0000000
--- a/test/shared/settings/validator.test.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import { validate } from 'shared/settings/validator';
-
-describe("setting validator", () => {
- describe("unknown top keys", () => {
- it('throws an error for unknown settings', () => {
- let settings = { keymaps: {}, poison: 123 };
- let fn = validate.bind(undefined, settings)
- expect(fn).to.throw(Error, 'poison');
- })
- });
-
- describe("keymaps settings", () => {
- it('throws an error for unknown operation', () => {
- let settings = {
- keymaps: {
- a: { 'type': 'scroll.home' },
- b: { 'type': 'poison.dressing' },
- }
- };
- let fn = validate.bind(undefined, settings)
- expect(fn).to.throw(Error, 'poison.dressing');
- });
- });
-
- describe("search settings", () => {
- it('throws an error for invalid search engine name', () => {
- let settings = {
- search: {
- default: 'google',
- engines: {
- 'google': 'https://google.com/search?q={}',
- 'cherry pie': 'https://cherypie.com/search?q={}',
- }
- }
- };
- let fn = validate.bind(undefined, settings)
- expect(fn).to.throw(Error, 'cherry pie');
- });
-
- it('throws an error for no {}-placeholder', () => {
- let settings = {
- search: {
- default: 'google',
- engines: {
- 'google': 'https://google.com/search?q={}',
- 'yahoo': 'https://search.yahoo.com/search',
- }
- }
- };
- let fn = validate.bind(undefined, settings)
- expect(fn).to.throw(Error, 'yahoo');
- });
-
- it('throws an error for no default engines', () => {
- let settings = {
- search: {
- engines: {
- 'google': 'https://google.com/search?q={}',
- 'yahoo': 'https://search.yahoo.com/search?q={}',
- }
- }
- };
- let fn = validate.bind(undefined, settings)
- expect(fn).to.throw(Error, 'Default engine');
- });
-
- it('throws an error for invalid default engine', () => {
- let settings = {
- search: {
- default: 'twitter',
- engines: {
- 'google': 'https://google.com/search?q={}',
- 'yahoo': 'https://search.yahoo.com/search?q={}',
- }
- }
- };
- let fn = validate.bind(undefined, settings)
- expect(fn).to.throw(Error, 'twitter');
- });
- });
-});
diff --git a/test/shared/settings/values.test.ts b/test/shared/settings/values.test.ts
deleted file mode 100644
index c72824d..0000000
--- a/test/shared/settings/values.test.ts
+++ /dev/null
@@ -1,138 +0,0 @@
-import * as values from 'shared/settings/values';
-
-describe("settings values", () => {
- describe('valueFromJson', () => {
- it('return object from json string', () => {
- let json = `{
- "keymaps": { "0": {"type": "scroll.home"}},
- "search": { "default": "google", "engines": { "google": "https://google.com/search?q={}" }},
- "blacklist": [ "*.slack.com"],
- "properties": {
- "mystr": "value",
- "mynum": 123,
- "mybool": true
- }
- }`;
- let value = values.valueFromJson(json);
-
- expect(value.keymaps).to.deep.equal({ 0: {type: "scroll.home"}});
- expect(value.search).to.deep.equal({ default: "google", engines: { google: "https://google.com/search?q={}"} });
- expect(value.blacklist).to.deep.equal(["*.slack.com"]);
- expect(value.properties).to.have.property('mystr', 'value');
- expect(value.properties).to.have.property('mynum', 123);
- expect(value.properties).to.have.property('mybool', true);
- });
- });
-
- describe('valueFromForm', () => {
- it('returns value from form', () => {
- let form = {
- keymaps: {
- 'scroll.vertically?{"count":1}': 'j',
- 'scroll.home': '0',
- },
- search: {
- default: 'google',
- engines: [['google', 'https://google.com/search?q={}']],
- },
- blacklist: ['*.slack.com'],
- "properties": {
- "mystr": "value",
- "mynum": 123,
- "mybool": true,
- }
- };
- let value = values.valueFromForm(form);
-
- expect(value.keymaps).to.have.deep.property('j', { type: "scroll.vertically", count: 1 });
- expect(value.keymaps).to.have.deep.property('0', { type: "scroll.home" });
- expect(JSON.stringify(value.search)).to.deep.equal(JSON.stringify({ default: "google", engines: { google: "https://google.com/search?q={}"} }));
- expect(value.search).to.deep.equal({ default: "google", engines: { google: "https://google.com/search?q={}"} });
- expect(value.blacklist).to.deep.equal(["*.slack.com"]);
- expect(value.properties).to.have.property('mystr', 'value');
- expect(value.properties).to.have.property('mynum', 123);
- expect(value.properties).to.have.property('mybool', true);
- });
-
- it('convert from empty form', () => {
- let form = {};
- let value = values.valueFromForm(form);
- expect(value).to.not.have.key('keymaps');
- expect(value).to.not.have.key('search');
- expect(value).to.not.have.key('blacklist');
- expect(value).to.not.have.key('properties');
- });
-
- it('override keymaps', () => {
- let form = {
- keymaps: {
- 'scroll.vertically?{"count":1}': 'j',
- 'scroll.vertically?{"count":-1}': 'j',
- }
- };
- let value = values.valueFromForm(form);
-
- expect(value.keymaps).to.have.key('j');
- });
-
- it('override search engine', () => {
- let form = {
- search: {
- default: 'google',
- engines: [
- ['google', 'https://google.com/search?q={}'],
- ['google', 'https://google.co.jp/search?q={}'],
- ]
- }
- };
- let value = values.valueFromForm(form);
-
- expect(value.search.engines).to.have.property('google', 'https://google.co.jp/search?q={}');
- });
- });
-
- describe('jsonFromValue', () => {
- });
-
- describe('formFromValue', () => {
- it('convert empty value to form', () => {
- let value = {};
- let form = values.formFromValue(value);
-
- expect(value).to.not.have.key('keymaps');
- expect(value).to.not.have.key('search');
- expect(value).to.not.have.key('blacklist');
- });
-
- it('convert value to form', () => {
- let value = {
- keymaps: {
- j: { type: 'scroll.vertically', count: 1 },
- JJ: { type: 'scroll.vertically', count: 100 },
- 0: { type: 'scroll.home' },
- },
- search: { default: 'google', engines: { google: 'https://google.com/search?q={}' }},
- blacklist: [ '*.slack.com'],
- properties: {
- "mystr": "value",
- "mynum": 123,
- "mybool": true,
- }
- };
- let allowed = ['scroll.vertically?{"count":1}', 'scroll.home' ];
- let form = values.formFromValue(value, allowed);
-
- expect(form.keymaps).to.have.property('scroll.vertically?{"count":1}', 'j');
- expect(form.keymaps).to.not.have.property('scroll.vertically?{"count":100}');
- expect(form.keymaps).to.have.property('scroll.home', '0');
- expect(Object.keys(form.keymaps)).to.have.lengthOf(2);
- expect(form.search).to.have.property('default', 'google');
- expect(form.search).to.have.deep.property('engines', [['google', 'https://google.com/search?q={}']]);
- expect(form.blacklist).to.have.lengthOf(1);
- expect(form.blacklist).to.include('*.slack.com');
- expect(form.properties).to.have.property('mystr', 'value');
- expect(form.properties).to.have.property('mynum', 123);
- expect(form.properties).to.have.property('mybool', true);
- });
- });
-});
--
cgit v1.2.3
From b002d70070a1b691b635220bc694c48df36faca5 Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka
Date: Mon, 6 May 2019 22:17:01 +0900
Subject: src/content
---
.../repositories/BrowserSettingRepository.ts | 16 +++++++
src/background/usecases/MarkUseCase.ts | 17 ++++----
src/background/usecases/VersionUseCase.ts | 2 +-
src/background/usecases/filters.ts | 6 ++-
src/console/components/Console.tsx | 51 ++++++++++------------
src/console/components/console/Input.tsx | 16 +++----
src/console/components/console/Message.tsx | 2 +-
src/console/reducers/index.ts | 2 +-
src/content/Mark.ts | 6 +++
src/content/actions/find.ts | 18 +++++++-
src/content/actions/index.ts | 3 +-
src/content/actions/input.ts | 3 +-
src/content/components/common/index.ts | 2 +-
src/content/components/common/mark.ts | 41 +++++++++--------
src/content/index.ts | 2 +-
src/content/reducers/input.ts | 3 +-
src/content/reducers/mark.ts | 6 +--
src/content/site-style.ts | 2 +-
test/content/reducers/setting.test.ts | 1 -
19 files changed, 119 insertions(+), 80 deletions(-)
create mode 100644 src/content/Mark.ts
(limited to 'src')
diff --git a/src/background/repositories/BrowserSettingRepository.ts b/src/background/repositories/BrowserSettingRepository.ts
index 48c72a5..33b35dd 100644
--- a/src/background/repositories/BrowserSettingRepository.ts
+++ b/src/background/repositories/BrowserSettingRepository.ts
@@ -1,5 +1,21 @@
import * as urls from '../../shared/urls';
+declare namespace browser.browserSettings.homepageOverride {
+
+ type BrowserSettings = {
+ value: string;
+ levelOfControl: LevelOfControlType;
+ };
+
+ type LevelOfControlType =
+ 'not_controllable' |
+ 'controlled_by_other_extensions' |
+ 'controllable_by_this_extension' |
+ 'controlled_by_this_extension';
+
+ function get(param: object): Promise;
+}
+
export default class BrowserSettingRepository {
async getHomepageUrls(): Promise {
let { value } = await browser.browserSettings.homepageOverride.get({});
diff --git a/src/background/usecases/MarkUseCase.ts b/src/background/usecases/MarkUseCase.ts
index 8b544aa..e376c55 100644
--- a/src/background/usecases/MarkUseCase.ts
+++ b/src/background/usecases/MarkUseCase.ts
@@ -21,7 +21,7 @@ export default class MarkUseCase {
async setGlobal(key: string, x: number, y: number): Promise {
let tab = await this.tabPresenter.getCurrent();
- let mark = { tabId: tab.id, url: tab.url, x, y };
+ let mark = { tabId: tab.id as number, url: tab.url as string, x, y };
return this.markRepository.setMark(key, mark);
}
@@ -33,15 +33,14 @@ export default class MarkUseCase {
return this.consoleClient.showError(
current.id as number, 'Mark is not set');
}
-
- return this.contentMessageClient.scrollTo(
- mark.tabId, mark.x, mark.y
- ).then(() => {
+ try {
+ await this.contentMessageClient.scrollTo(mark.tabId, mark.x, mark.y);
return this.tabPresenter.select(mark.tabId);
- }).catch(async() => {
+ } catch (e) {
let tab = await this.tabPresenter.create(mark.url);
- let mark2 = { tabId: tab.id, url: mark.url, x: mark.x, y: mark.y };
- return this.markRepository.setMark(key, mark2);
- });
+ return this.markRepository.setMark(key, {
+ tabId: tab.id as number, url: mark.url, x: mark.x, y: mark.y,
+ });
+ }
}
}
diff --git a/src/background/usecases/VersionUseCase.ts b/src/background/usecases/VersionUseCase.ts
index 3a3cc2e..8154eba 100644
--- a/src/background/usecases/VersionUseCase.ts
+++ b/src/background/usecases/VersionUseCase.ts
@@ -1,4 +1,3 @@
-import manifest from '../../../manifest.json';
import TabPresenter from '../presenters/TabPresenter';
import NotifyPresenter from '../presenters/NotifyPresenter';
@@ -13,6 +12,7 @@ export default class VersionUseCase {
}
notify(): Promise {
+ let manifest = browser.runtime.getManifest();
let title = `Vim Vixen ${manifest.version} has been installed`;
let message = 'Click here to see release notes';
let url = this.releaseNoteUrl(manifest.version);
diff --git a/src/background/usecases/filters.ts b/src/background/usecases/filters.ts
index 44eb56f..84a42fb 100644
--- a/src/background/usecases/filters.ts
+++ b/src/background/usecases/filters.ts
@@ -40,7 +40,8 @@ const filterByPathname = (items: Item[], min: number): Item[] => {
let pathname = url.origin + url.pathname;
if (!hash[pathname]) {
hash[pathname] = item;
- } else if (hash[pathname].url.length > item.url.length) {
+ } else if ((hash[pathname].url as string).length >
+ (item.url as string).length) {
hash[pathname] = item;
}
}
@@ -57,7 +58,8 @@ const filterByOrigin = (items: Item[], min: number): Item[] => {
let origin = new URL(item.url as string).origin;
if (!hash[origin]) {
hash[origin] = item;
- } else if (hash[origin].url.length > item.url.length) {
+ } else if ((hash[origin].url as string).length >
+ (item.url as string).length) {
hash[origin] = item;
}
}
diff --git a/src/console/components/Console.tsx b/src/console/components/Console.tsx
index 09c0f50..3274047 100644
--- a/src/console/components/Console.tsx
+++ b/src/console/components/Console.tsx
@@ -5,23 +5,23 @@ import Input from './console/Input';
import Completion from './console/Completion';
import Message from './console/Message';
import * as consoleActions from '../../console/actions/console';
+import { State as AppState } from '../reducers';
const COMPLETION_MAX_ITEMS = 33;
-interface Props {
- mode?: string;
- consoleText?: string;
- messageText?: string;
- children?: string;
+type StateProps = ReturnType;
+interface DispatchProps {
+ dispatch: (action: any) => void,
}
+type Props = StateProps & DispatchProps
class Console extends React.Component {
- private input: HTMLInputElement | null;
+ private input: React.RefObject;
constructor(props: Props) {
super(props);
- this.input = null;
+ this.input = React.createRef();
}
onBlur() {
@@ -34,7 +34,7 @@ class Console extends React.Component {
e.stopPropagation();
e.preventDefault();
- let value = e.target.value;
+ let value = (e.target as HTMLInputElement).value;
if (this.props.mode === 'command') {
return this.props.dispatch(consoleActions.enterCommand(value));
} else if (this.props.mode === 'find') {
@@ -55,15 +55,12 @@ class Console extends React.Component {
}
onKeyDown(e: React.KeyboardEvent) {
- if (e.keyCode === KeyboardEvent.DOM_VK_ESCAPE && e.ctrlKey) {
- this.props.dispatch(consoleActions.hideCommand());
- }
- switch (e.keyCode) {
- case KeyboardEvent.DOM_VK_ESCAPE:
+ switch (e.key) {
+ case 'Escape':
return this.props.dispatch(consoleActions.hideCommand());
- case KeyboardEvent.DOM_VK_RETURN:
+ case 'Enter':
return this.doEnter(e);
- case KeyboardEvent.DOM_VK_TAB:
+ case 'Tab':
if (e.shiftKey) {
this.props.dispatch(consoleActions.completionPrev());
} else {
@@ -72,22 +69,22 @@ class Console extends React.Component {
e.stopPropagation();
e.preventDefault();
break;
- case KeyboardEvent.DOM_VK_OPEN_BRACKET:
+ case '[':
if (e.ctrlKey) {
return this.props.dispatch(consoleActions.hideCommand());
}
break;
- case KeyboardEvent.DOM_VK_M:
+ case 'm':
if (e.ctrlKey) {
return this.doEnter(e);
}
break;
- case KeyboardEvent.DOM_VK_N:
+ case 'n':
if (e.ctrlKey) {
this.selectNext(e);
}
break;
- case KeyboardEvent.DOM_VK_P:
+ case 'p':
if (e.ctrlKey) {
this.selectPrev(e);
}
@@ -105,9 +102,6 @@ class Console extends React.Component {
componentDidUpdate(prevProps: Props) {
- if (!this.input) {
- return;
- }
if (prevProps.mode !== 'command' && this.props.mode === 'command') {
this.props.dispatch(
consoleActions.getCompletions(this.props.consoleText));
@@ -128,7 +122,7 @@ class Console extends React.Component {
select={this.props.select}
/>
{ this.input = c; }}
+ ref={this.input}
mode={this.props.mode}
onBlur={this.onBlur.bind(this)}
onKeyDown={this.onKeyDown.bind(this)}
@@ -148,11 +142,14 @@ class Console extends React.Component {
focus() {
window.focus();
- if (this.input) {
- this.input.focus();
+ if (this.input.current) {
+ this.input.current.focus();
}
}
}
-const mapStateToProps = (state: any) => state;
-export default connect(mapStateToProps)(Console);
+const mapStateToProps = (state: AppState) => ({ ...state });
+
+export default connect(
+ mapStateToProps,
+)(Console);
diff --git a/src/console/components/console/Input.tsx b/src/console/components/console/Input.tsx
index d0348bd..54ea251 100644
--- a/src/console/components/console/Input.tsx
+++ b/src/console/components/console/Input.tsx
@@ -3,23 +3,23 @@ import React from 'react';
interface Props {
mode: string;
value: string;
- onBlur: (e: React.FocusEvent) => void;
- onKeyDown: (e: React.KeyboardEvent) => void;
- onChange: (e: React.ChangeEvent) => void;
+ onBlur: (e: React.FocusEvent) => void;
+ onKeyDown: (e: React.KeyboardEvent) => void;
+ onChange: (e: React.ChangeEvent) => void;
}
class Input extends React.Component {
- private input: HTMLInputElement | null;
+ private input: React.RefObject;
constructor(props: Props) {
super(props);
- this.input = null;
+ this.input = React.createRef();
}
focus() {
- if (this.input) {
- this.input.focus();
+ if (this.input.current) {
+ this.input.current.focus();
}
}
@@ -38,7 +38,7 @@ class Input extends React.Component {
{ this.input = c; }}
+ ref={this.input}
onBlur={this.props.onBlur}
onKeyDown={this.props.onKeyDown}
onChange={this.props.onChange}
diff --git a/src/console/components/console/Message.tsx b/src/console/components/console/Message.tsx
index 07a929e..9fa2788 100644
--- a/src/console/components/console/Message.tsx
+++ b/src/console/components/console/Message.tsx
@@ -2,7 +2,7 @@ import React from 'react';
interface Props {
mode: string;
- children: string[];
+ children: string;
}
const Message = (props: Props) => {
diff --git a/src/console/reducers/index.ts b/src/console/reducers/index.ts
index 37ed715..b6be483 100644
--- a/src/console/reducers/index.ts
+++ b/src/console/reducers/index.ts
@@ -1,6 +1,6 @@
import * as actions from '../actions';
-interface State {
+export interface State {
mode: string;
messageText: string;
consoleText: string;
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/actions/find.ts b/src/content/actions/find.ts
index 6dd2ae6..53e03ae 100644
--- a/src/content/actions/find.ts
+++ b/src/content/actions/find.ts
@@ -9,6 +9,20 @@ 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;
@@ -18,7 +32,7 @@ const find = (str: string, backwards: boolean): boolean => {
// because of same origin policy
// eslint-disable-next-line no-extra-parens
- let found = (window).find(str, caseSensitive, backwards, wrapScan);
+ let found = window.find(str, caseSensitive, backwards, wrapScan);
if (found) {
return found;
}
@@ -28,7 +42,7 @@ const find = (str: string, backwards: boolean): boolean => {
}
// eslint-disable-next-line no-extra-parens
- return (window).find(str, caseSensitive, backwards, wrapScan);
+ return window.find(str, caseSensitive, backwards, wrapScan);
};
// eslint-disable-next-line max-statements
diff --git a/src/content/actions/index.ts b/src/content/actions/index.ts
index a259211..8aa9c23 100644
--- a/src/content/actions/index.ts
+++ b/src/content/actions/index.ts
@@ -1,5 +1,6 @@
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';
@@ -51,7 +52,7 @@ export interface SettingSetAction extends Redux.Action {
export interface InputKeyPressAction extends Redux.Action {
type: typeof INPUT_KEY_PRESS;
- key: string;
+ key: keyUtils.Key;
}
export interface InputClearKeysAction extends Redux.Action {
diff --git a/src/content/actions/input.ts b/src/content/actions/input.ts
index 21c912e..1df6452 100644
--- a/src/content/actions/input.ts
+++ b/src/content/actions/input.ts
@@ -1,6 +1,7 @@
import * as actions from './index';
+import * as keyUtils from '../../shared/utils/keys';
-const keyPress = (key: string): actions.InputAction => {
+const keyPress = (key: keyUtils.Key): actions.InputAction => {
return {
type: actions.INPUT_KEY_PRESS,
key,
diff --git a/src/content/components/common/index.ts b/src/content/components/common/index.ts
index 8bd697f..5b097b6 100644
--- a/src/content/components/common/index.ts
+++ b/src/content/components/common/index.ts
@@ -18,7 +18,7 @@ export default class Common {
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 mark = new MarkComponent(store);
const keymapper = new KeymapperComponent(store);
input.onKey((key: keys.Key) => follow.key(key));
diff --git a/src/content/components/common/mark.ts b/src/content/components/common/mark.ts
index 686116c..1237385 100644
--- a/src/content/components/common/mark.ts
+++ b/src/content/components/common/mark.ts
@@ -1,27 +1,30 @@
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): boolean => {
- return key.key === 'Esc' || key.key === '[' && key.ctrlKey;
+const cancelKey = (key: keyUtils.Key): boolean => {
+ return key.key === 'Esc' || key.key === '[' && Boolean(key.ctrlKey);
};
-const globalKey = (key) => {
+const globalKey = (key: string): boolean => {
return (/^[A-Z0-9]$/).test(key);
};
export default class MarkComponent {
- constructor(body, store) {
- this.body = body;
+ private store: any;
+
+ constructor(store: any) {
this.store = store;
}
// eslint-disable-next-line max-statements
- key(key) {
- let { mark: markStage, setting } = this.store.getState();
+ key(key: keyUtils.Key) {
+ let { mark: markState, setting } = this.store.getState();
let smoothscroll = setting.properties.smoothscroll;
- if (!markStage.setMode && !markStage.jumpMode) {
+ if (!markState.setMode && !markState.jumpMode) {
return false;
}
@@ -32,26 +35,30 @@ export default class MarkComponent {
if (key.ctrlKey || key.metaKey || key.altKey) {
consoleFrames.postError('Unknown mark');
- } else if (globalKey(key.key) && markStage.setMode) {
+ } else if (globalKey(key.key) && markState.setMode) {
this.doSetGlobal(key);
- } else if (globalKey(key.key) && markStage.jumpMode) {
+ } else if (globalKey(key.key) && markState.jumpMode) {
this.doJumpGlobal(key);
- } else if (markStage.setMode) {
+ } else if (markState.setMode) {
this.doSet(key);
- } else if (markStage.jumpMode) {
- this.doJump(markStage.marks, key, smoothscroll);
+ } else if (markState.jumpMode) {
+ this.doJump(markState.marks, key, smoothscroll);
}
this.store.dispatch(markActions.cancel());
return true;
}
- doSet(key) {
+ doSet(key: keyUtils.Key) {
let { x, y } = scrolls.getScroll();
this.store.dispatch(markActions.setLocal(key.key, x, y));
}
- doJump(marks, key, smoothscroll) {
+ doJump(
+ marks: { [key: string]: Mark },
+ key: keyUtils.Key,
+ smoothscroll: boolean,
+ ) {
if (!marks[key.key]) {
consoleFrames.postError('Mark is not set');
return;
@@ -61,12 +68,12 @@ export default class MarkComponent {
scrolls.scrollTo(x, y, smoothscroll);
}
- doSetGlobal(key) {
+ doSetGlobal(key: keyUtils.Key) {
let { x, y } = scrolls.getScroll();
this.store.dispatch(markActions.setGlobal(key.key, x, y));
}
- doJumpGlobal(key) {
+ doJumpGlobal(key: keyUtils.Key) {
this.store.dispatch(markActions.jumpGlobal(key.key));
}
}
diff --git a/src/content/index.ts b/src/content/index.ts
index 309f27f..9d791fc 100644
--- a/src/content/index.ts
+++ b/src/content/index.ts
@@ -12,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/reducers/input.ts b/src/content/reducers/input.ts
index 6257e49..35b9075 100644
--- a/src/content/reducers/input.ts
+++ b/src/content/reducers/input.ts
@@ -1,7 +1,8 @@
import * as actions from '../actions';
+import * as keyUtils from '../../shared/utils/keys';
export interface State {
- keys: string[];
+ keys: keyUtils.Key[],
}
const defaultState: State = {
diff --git a/src/content/reducers/mark.ts b/src/content/reducers/mark.ts
index e78b7b9..7409938 100644
--- a/src/content/reducers/mark.ts
+++ b/src/content/reducers/mark.ts
@@ -1,10 +1,6 @@
+import Mark from '../Mark';
import * as actions from '../actions';
-interface Mark {
- x: number;
- y: number;
-}
-
export interface State {
setMode: boolean;
jumpMode: boolean;
diff --git a/src/content/site-style.ts b/src/content/site-style.ts
index e7a82a5..0c335fc 100644
--- a/src/content/site-style.ts
+++ b/src/content/site-style.ts
@@ -1,4 +1,4 @@
-exports.default = `
+export default `
.vimvixen-console-frame {
margin: 0;
padding: 0;
diff --git a/test/content/reducers/setting.test.ts b/test/content/reducers/setting.test.ts
index fe23006..9b332aa 100644
--- a/test/content/reducers/setting.test.ts
+++ b/test/content/reducers/setting.test.ts
@@ -20,7 +20,6 @@ describe("content setting reducer", () => {
}
}
let state = settingReducer(undefined, action);
- console.log(JSON.stringify(state.keymaps));
expect(state.keymaps).to.have.deep.all.members([
{ key: [{ key: 'z', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
{ key: 'z', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false }],
--
cgit v1.2.3
From 8ae1311ef69782ad6c8a313e49946152abf9d222 Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka
Date: Mon, 6 May 2019 22:17:01 +0900
Subject: src/content
---
src/background/repositories/PersistentSettingRepository.ts | 2 +-
src/settings/storage.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
(limited to 'src')
diff --git a/src/background/repositories/PersistentSettingRepository.ts b/src/background/repositories/PersistentSettingRepository.ts
index 18476fd..ff882a5 100644
--- a/src/background/repositories/PersistentSettingRepository.ts
+++ b/src/background/repositories/PersistentSettingRepository.ts
@@ -6,7 +6,7 @@ export default class SettingRepository {
if (!settings) {
return null;
}
- return SettingData.valueOf(settings);
+ return SettingData.valueOf(settings as any);
}
}
diff --git a/src/settings/storage.ts b/src/settings/storage.ts
index 748b9ab..c0005b7 100644
--- a/src/settings/storage.ts
+++ b/src/settings/storage.ts
@@ -5,7 +5,7 @@ export const load = async(): Promise => {
if (!settings) {
return DefaultSettingData;
}
- return SettingData.valueOf(settings);
+ return SettingData.valueOf(settings as any);
};
export const save = (data: SettingData) => {
--
cgit v1.2.3
From 27d0a7f37d24a0ad68a8ccb7dee18fc1d00eea58 Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka
Date: Tue, 7 May 2019 20:50:19 +0900
Subject: Use search settings on paster
---
src/content/reducers/setting.ts | 13 +++++++------
src/content/urls.ts | 5 +++--
src/shared/Settings.ts | 2 +-
3 files changed, 11 insertions(+), 9 deletions(-)
(limited to 'src')
diff --git a/src/content/reducers/setting.ts b/src/content/reducers/setting.ts
index a3dc3aa..9ca1380 100644
--- a/src/content/reducers/setting.ts
+++ b/src/content/reducers/setting.ts
@@ -1,20 +1,20 @@
import * as actions from '../actions';
import * as keyUtils from '../../shared/utils/keys';
import * as operations from '../../shared/operations';
-import { Properties } from '../../shared/Settings';
+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: [],
- properties: {
- complete: '',
- smoothscroll: false,
- hintchars: '',
- },
+ search: DefaultSetting.search,
+ properties: DefaultSetting.properties,
};
export default function reducer(
@@ -31,6 +31,7 @@ export default function reducer(
};
}),
properties: action.settings.properties,
+ search: action.settings.search,
};
default:
return state;
diff --git a/src/content/urls.ts b/src/content/urls.ts
index 390efde..035b9bb 100644
--- a/src/content/urls.ts
+++ b/src/content/urls.ts
@@ -1,5 +1,6 @@
import * as messages from '../shared/messages';
import * as urls from '../shared/urls';
+import { Search } from '../shared/Settings';
const yank = (win: Window) => {
let input = win.document.createElement('input');
@@ -15,7 +16,7 @@ const yank = (win: Window) => {
input.remove();
};
-const paste = (win: Window, newTab: boolean, searchSettings: any) => {
+const paste = (win: Window, newTab: boolean, search: Search) => {
let textarea = win.document.createElement('textarea');
win.document.body.append(textarea);
@@ -26,7 +27,7 @@ const paste = (win: Window, newTab: boolean, searchSettings: any) => {
if (win.document.execCommand('paste')) {
let value = textarea.textContent as string;
- let url = urls.searchUrl(value, searchSettings);
+ let url = urls.searchUrl(value, search);
browser.runtime.sendMessage({
type: messages.OPEN_URL,
url,
diff --git a/src/shared/Settings.ts b/src/shared/Settings.ts
index ce6b9ee..e35094b 100644
--- a/src/shared/Settings.ts
+++ b/src/shared/Settings.ts
@@ -117,7 +117,7 @@ export const valueOf = (o: any): Settings => {
return settings;
};
-const DefaultSetting: Settings = {
+export const DefaultSetting: Settings = {
keymaps: {
'0': { 'type': 'scroll.home' },
':': { 'type': 'command.show' },
--
cgit v1.2.3