Types on src/background

jh-changes
Shin'ya Ueoka 6 years ago
parent 0cffb09e24
commit 678020a3a2
  1. 4
      src/background/controllers/AddonEnabledController.ts
  2. 14
      src/background/controllers/CommandController.ts
  3. 6
      src/background/controllers/FindController.ts
  4. 12
      src/background/controllers/LinkController.ts
  5. 10
      src/background/controllers/MarkController.ts
  6. 13
      src/background/controllers/OperationController.ts
  7. 8
      src/background/controllers/SettingController.ts
  8. 6
      src/background/controllers/VersionController.ts
  9. 13
      src/background/controllers/version.ts
  10. 3
      src/background/domains/CommandDocs.ts
  11. 17
      src/background/domains/CompletionGroup.ts
  12. 29
      src/background/domains/CompletionItem.ts
  13. 27
      src/background/domains/Completions.ts
  14. 28
      src/background/domains/GlobalMark.ts
  15. 10
      src/background/infrastructures/ConsoleClient.ts
  16. 12
      src/background/infrastructures/ContentMessageClient.ts
  17. 74
      src/background/infrastructures/ContentMessageListener.ts
  18. 6
      src/background/infrastructures/MemoryStorage.ts
  19. 4
      src/background/presenters/IndicatorPresenter.ts
  20. 8
      src/background/presenters/NotifyPresenter.ts
  21. 42
      src/background/presenters/TabPresenter.ts
  22. 2
      src/background/presenters/WindowPresenter.ts
  23. 4
      src/background/repositories/BookmarkRepository.ts
  24. 2
      src/background/repositories/BrowserSettingRepository.ts
  25. 14
      src/background/repositories/CompletionsRepository.ts
  26. 6
      src/background/repositories/FindRepository.ts
  27. 8
      src/background/repositories/MarkRepository.ts
  28. 2
      src/background/repositories/PersistentSettingRepository.ts
  29. 8
      src/background/repositories/SettingRepository.ts
  30. 10
      src/background/repositories/VersionRepository.ts
  31. 18
      src/background/usecases/AddonEnabledUseCase.ts
  32. 52
      src/background/usecases/CommandUseCase.ts
  33. 107
      src/background/usecases/CompletionsUseCase.ts
  34. 40
      src/background/usecases/ConsoleUseCase.ts
  35. 14
      src/background/usecases/FindUseCase.ts
  36. 8
      src/background/usecases/LinkUseCase.ts
  37. 20
      src/background/usecases/MarkUseCase.ts
  38. 8
      src/background/usecases/SettingUseCase.ts
  39. 24
      src/background/usecases/TabSelectUseCase.ts
  40. 36
      src/background/usecases/TabUseCase.ts
  41. 10
      src/background/usecases/VersionUseCase.ts
  42. 26
      src/background/usecases/ZoomUseCase.ts
  43. 38
      src/background/usecases/filters.ts
  44. 10
      src/background/usecases/parsers.ts
  45. 10
      src/content/scrolls.ts
  46. 11
      test/background/domains/GlobalMark.test.ts
  47. 3
      test/background/repositories/Mark.test.ts
  48. 34
      test/background/repositories/Version.ts

@ -1,11 +1,13 @@
import AddonEnabledUseCase from '../usecases/AddonEnabledUseCase'; import AddonEnabledUseCase from '../usecases/AddonEnabledUseCase';
export default class AddonEnabledController { export default class AddonEnabledController {
private addonEnabledUseCase: AddonEnabledUseCase;
constructor() { constructor() {
this.addonEnabledUseCase = new AddonEnabledUseCase(); this.addonEnabledUseCase = new AddonEnabledUseCase();
} }
indicate(enabled) { indicate(enabled: boolean): Promise<any> {
return this.addonEnabledUseCase.indicate(enabled); return this.addonEnabledUseCase.indicate(enabled);
} }
} }

@ -1,19 +1,23 @@
import CompletionsUseCase from '../usecases/CompletionsUseCase'; import CompletionsUseCase from '../usecases/CompletionsUseCase';
import CommandUseCase from '../usecases/CommandUseCase'; 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 // NOTE String.trimStart is available on Firefox 61
return str.replace(/^\s+/, ''); return str.replace(/^\s+/, '');
}; };
export default class CommandController { export default class CommandController {
private completionsUseCase: CompletionsUseCase;
private commandIndicator: CommandUseCase;
constructor() { constructor() {
this.completionsUseCase = new CompletionsUseCase(); this.completionsUseCase = new CompletionsUseCase();
this.commandIndicator = new CommandUseCase(); this.commandIndicator = new CommandUseCase();
} }
getCompletions(line) { getCompletions(line: string): Promise<CompletionGroup[]> {
let trimmed = trimStart(line); let trimmed = trimStart(line);
let words = trimmed.split(/ +/); let words = trimmed.split(/ +/);
let name = words[0]; let name = words[0];
@ -45,11 +49,11 @@ export default class CommandController {
case 'set': case 'set':
return this.completionsUseCase.querySet(name, keywords); return this.completionsUseCase.querySet(name, keywords);
} }
return Promise.resolve(Completions.empty()); return Promise.resolve([]);
} }
// eslint-disable-next-line complexity // eslint-disable-next-line complexity
exec(line) { exec(line: string): Promise<any> {
let trimmed = trimStart(line); let trimmed = trimStart(line);
let words = trimmed.split(/ +/); let words = trimmed.split(/ +/);
let name = words[0]; let name = words[0];

@ -1,15 +1,17 @@
import FindUseCase from '../usecases/FindUseCase'; import FindUseCase from '../usecases/FindUseCase';
export default class FindController { export default class FindController {
private findUseCase: FindUseCase;
constructor() { constructor() {
this.findUseCase = new FindUseCase(); this.findUseCase = new FindUseCase();
} }
getKeyword() { getKeyword(): Promise<string> {
return this.findUseCase.getKeyword(); return this.findUseCase.getKeyword();
} }
setKeyword(keyword) { setKeyword(keyword: string): Promise<any> {
return this.findUseCase.setKeyword(keyword); return this.findUseCase.setKeyword(keyword);
} }
} }

@ -1,15 +1,19 @@
import LinkUseCase from '../usecases/LinkUseCase'; import LinkUseCase from '../usecases/LinkUseCase';
export default class LinkController { export default class LinkController {
private linkUseCase: LinkUseCase;
constructor() { constructor() {
this.linkUseCase = new LinkUseCase(); this.linkUseCase = new LinkUseCase();
} }
openToTab(url, tabId) { openToTab(url: string, tabId: number): Promise<void> {
this.linkUseCase.openToTab(url, tabId); return this.linkUseCase.openToTab(url, tabId);
} }
openNewTab(url, openerId, background) { openNewTab(
this.linkUseCase.openNewTab(url, openerId, background); url: string, openerId: number, background: boolean,
): Promise<void> {
return this.linkUseCase.openNewTab(url, openerId, background);
} }
} }

@ -1,15 +1,17 @@
import MarkUseCase from '../usecases/MarkUseCase'; import MarkUseCase from '../usecases/MarkUseCase';
export default class MarkController { export default class MarkController {
private markUseCase: MarkUseCase;
constructor() { constructor() {
this.markUseCase = new MarkUseCase(); this.markUseCase = new MarkUseCase();
} }
setGlobal(key, x, y) { setGlobal(key: string, x: number, y: number): Promise<any> {
this.markUseCase.setGlobal(key, x, y); return this.markUseCase.setGlobal(key, x, y);
} }
jumpGlobal(key) { jumpGlobal(key: string): Promise<any> {
this.markUseCase.jumpGlobal(key); return this.markUseCase.jumpGlobal(key);
} }
} }

@ -6,6 +6,16 @@ import TabSelectUseCase from '../usecases/TabSelectUseCase';
import ZoomUseCase from '../usecases/ZoomUseCase'; import ZoomUseCase from '../usecases/ZoomUseCase';
export default class OperationController { export default class OperationController {
private findUseCase: FindUseCase;
private consoleUseCase: ConsoleUseCase;
private tabUseCase: TabUseCase;
private tabSelectUseCase: TabSelectUseCase;
private zoomUseCase: ZoomUseCase;
constructor() { constructor() {
this.findUseCase = new FindUseCase(); this.findUseCase = new FindUseCase();
this.consoleUseCase = new ConsoleUseCase(); this.consoleUseCase = new ConsoleUseCase();
@ -15,7 +25,7 @@ export default class OperationController {
} }
// eslint-disable-next-line complexity, max-lines-per-function // eslint-disable-next-line complexity, max-lines-per-function
exec(operation) { exec(operation: any): Promise<any> {
switch (operation.type) { switch (operation.type) {
case operations.TAB_CLOSE: case operations.TAB_CLOSE:
return this.tabUseCase.close(false); return this.tabUseCase.close(false);
@ -72,6 +82,7 @@ export default class OperationController {
case operations.CANCEL: case operations.CANCEL:
return this.consoleUseCase.hideConsole(); return this.consoleUseCase.hideConsole();
} }
throw new Error('unknown operation: ' + operation.type);
} }
} }

@ -2,16 +2,20 @@ import SettingUseCase from '../usecases/SettingUseCase';
import ContentMessageClient from '../infrastructures/ContentMessageClient'; import ContentMessageClient from '../infrastructures/ContentMessageClient';
export default class SettingController { export default class SettingController {
private settingUseCase: SettingUseCase;
private contentMessageClient: ContentMessageClient;
constructor() { constructor() {
this.settingUseCase = new SettingUseCase(); this.settingUseCase = new SettingUseCase();
this.contentMessageClient = new ContentMessageClient(); this.contentMessageClient = new ContentMessageClient();
} }
getSetting() { getSetting(): any {
return this.settingUseCase.get(); return this.settingUseCase.get();
} }
async reload() { async reload(): Promise<any> {
await this.settingUseCase.reload(); await this.settingUseCase.reload();
this.contentMessageClient.broadcastSettingsChanged(); this.contentMessageClient.broadcastSettingsChanged();
} }

@ -1,11 +1,13 @@
import VersionUseCase from '../usecases/VersionUseCase'; import VersionUseCase from '../usecases/VersionUseCase';
export default class VersionController { export default class VersionController {
private versionUseCase: VersionUseCase;
constructor() { constructor() {
this.versionUseCase = new VersionUseCase(); this.versionUseCase = new VersionUseCase();
} }
notify() { notify(): void {
this.versionUseCase.notify(); return this.versionUseCase.notify();
} }
} }

@ -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();
});
}
}

@ -8,5 +8,4 @@ export default {
bdeletes: 'Close all tabs matched by keywords', bdeletes: 'Close all tabs matched by keywords',
quit: 'Close the current tab', quit: 'Close the current tab',
quitall: 'Close all tabs', quitall: 'Close all tabs',
}; } as {[key: string]: string};

@ -1,14 +1,7 @@
export default class CompletionGroup { import CompletionItem from './CompletionItem';
constructor(name, items) {
this.name0 = name;
this.items0 = items;
}
get name() {
return this.name0;
}
get items() { export default interface CompletionGroup {
return this.items0; name: string;
} items: CompletionItem[];
// eslint-disable-next-line semi
} }

@ -1,24 +1,7 @@
export default class CompletionItem { export default interface CompletionItem {
constructor({ caption, content, url, icon }) { readonly caption?: string;
this.caption0 = caption; readonly content?: string;
this.content0 = content; readonly url?: string;
this.url0 = url; readonly icon?: string;
this.icon0 = icon; // eslint-disable-next-line semi
}
get caption() {
return this.caption0;
}
get content() {
return this.content0;
}
get url() {
return this.url0;
}
get icon() {
return this.icon0;
}
} }

@ -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([]);

@ -1,24 +1,6 @@
export default class GlobalMark { export interface GlobalMark {
constructor(tabId, url, x, y) { readonly tabId: number;
this.tabId0 = tabId; readonly url: string;
this.url0 = url; readonly x: number;
this.x0 = x; readonly y: number;
this.y0 = y;
}
get tabId() {
return this.tabId0;
}
get url() {
return this.url0;
}
get x() {
return this.x0;
}
get y() {
return this.y0;
}
} }

@ -1,34 +1,34 @@
import messages from '../../shared/messages'; import messages from '../../shared/messages';
export default class ConsoleClient { export default class ConsoleClient {
showCommand(tabId, command) { showCommand(tabId: number, command: string): Promise<any> {
return browser.tabs.sendMessage(tabId, { return browser.tabs.sendMessage(tabId, {
type: messages.CONSOLE_SHOW_COMMAND, type: messages.CONSOLE_SHOW_COMMAND,
command, command,
}); });
} }
showFind(tabId) { showFind(tabId: number): Promise<any> {
return browser.tabs.sendMessage(tabId, { return browser.tabs.sendMessage(tabId, {
type: messages.CONSOLE_SHOW_FIND type: messages.CONSOLE_SHOW_FIND
}); });
} }
showInfo(tabId, message) { showInfo(tabId: number, message: string): Promise<any> {
return browser.tabs.sendMessage(tabId, { return browser.tabs.sendMessage(tabId, {
type: messages.CONSOLE_SHOW_INFO, type: messages.CONSOLE_SHOW_INFO,
text: message, text: message,
}); });
} }
showError(tabId, message) { showError(tabId: number, message: string): Promise<any> {
return browser.tabs.sendMessage(tabId, { return browser.tabs.sendMessage(tabId, {
type: messages.CONSOLE_SHOW_ERROR, type: messages.CONSOLE_SHOW_ERROR,
text: message, text: message,
}); });
} }
hide(tabId) { hide(tabId: number): Promise<any> {
return browser.tabs.sendMessage(tabId, { return browser.tabs.sendMessage(tabId, {
type: messages.CONSOLE_HIDE, type: messages.CONSOLE_HIDE,
}); });

@ -1,10 +1,10 @@
import messages from '../../shared/messages'; import messages from '../../shared/messages';
export default class ContentMessageClient { export default class ContentMessageClient {
async broadcastSettingsChanged() { async broadcastSettingsChanged(): Promise<void> {
let tabs = await browser.tabs.query({}); let tabs = await browser.tabs.query({});
for (let tab of tabs) { for (let tab of tabs) {
if (tab.url.startsWith('about:')) { if (!tab.id || tab.url && tab.url.startsWith('about:')) {
continue; continue;
} }
browser.tabs.sendMessage(tab.id, { browser.tabs.sendMessage(tab.id, {
@ -13,20 +13,20 @@ export default class ContentMessageClient {
} }
} }
async getAddonEnabled(tabId) { async getAddonEnabled(tabId: number): Promise<boolean> {
let { enabled } = await browser.tabs.sendMessage(tabId, { let { enabled } = await browser.tabs.sendMessage(tabId, {
type: messages.ADDON_ENABLED_QUERY, type: messages.ADDON_ENABLED_QUERY,
}); }) as { enabled: boolean };
return enabled; return enabled;
} }
toggleAddonEnabled(tabId) { toggleAddonEnabled(tabId: number): Promise<void> {
return browser.tabs.sendMessage(tabId, { return browser.tabs.sendMessage(tabId, {
type: messages.ADDON_TOGGLE_ENABLED, type: messages.ADDON_TOGGLE_ENABLED,
}); });
} }
scrollTo(tabId, x, y) { scrollTo(tabId: number, x: number, y: number): Promise<void> {
return browser.tabs.sendMessage(tabId, { return browser.tabs.sendMessage(tabId, {
type: messages.TAB_SCROLL_TO, type: messages.TAB_SCROLL_TO,
x, x,

@ -1,4 +1,5 @@
import messages from '../../shared/messages'; import messages from '../../shared/messages';
import CompletionGroup from '../domains/CompletionGroup';
import CommandController from '../controllers/CommandController'; import CommandController from '../controllers/CommandController';
import SettingController from '../controllers/SettingController'; import SettingController from '../controllers/SettingController';
import FindController from '../controllers/FindController'; import FindController from '../controllers/FindController';
@ -8,6 +9,22 @@ import OperationController from '../controllers/OperationController';
import MarkController from '../controllers/MarkController'; import MarkController from '../controllers/MarkController';
export default class ContentMessageListener { 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() { constructor() {
this.settingController = new SettingController(); this.settingController = new SettingController();
this.commandController = new CommandController(); this.commandController = new CommandController();
@ -20,20 +37,28 @@ export default class ContentMessageListener {
this.consolePorts = {}; this.consolePorts = {};
} }
run() { run(): void {
browser.runtime.onMessage.addListener((message, sender) => { browser.runtime.onMessage.addListener((
message: any, sender: browser.runtime.MessageSender,
) => {
try { try {
let ret = this.onMessage(message, sender); let ret = this.onMessage(message, sender.tab as browser.tabs.Tab);
if (!(ret instanceof Promise)) { if (!(ret instanceof Promise)) {
return {}; return {};
} }
return ret.catch((e) => { return ret.catch((e) => {
if (!sender.tab || !sender.tab.id) {
return;
}
return browser.tabs.sendMessage(sender.tab.id, { return browser.tabs.sendMessage(sender.tab.id, {
type: messages.CONSOLE_SHOW_ERROR, type: messages.CONSOLE_SHOW_ERROR,
text: e.message, text: e.message,
}); });
}); });
} catch (e) { } catch (e) {
if (!sender.tab || !sender.tab.id) {
return;
}
return browser.tabs.sendMessage(sender.tab.id, { return browser.tabs.sendMessage(sender.tab.id, {
type: messages.CONSOLE_SHOW_ERROR, type: messages.CONSOLE_SHOW_ERROR,
text: e.message, text: e.message,
@ -43,7 +68,7 @@ export default class ContentMessageListener {
browser.runtime.onConnect.addListener(this.onConnected.bind(this)); browser.runtime.onConnect.addListener(this.onConnected.bind(this));
} }
onMessage(message, sender) { onMessage(message: any, senderTab: browser.tabs.Tab): Promise<any> | any {
switch (message.type) { switch (message.type) {
case messages.CONSOLE_QUERY_COMPLETIONS: case messages.CONSOLE_QUERY_COMPLETIONS:
return this.onConsoleQueryCompletions(message.text); return this.onConsoleQueryCompletions(message.text);
@ -59,7 +84,10 @@ export default class ContentMessageListener {
return this.onAddonEnabledResponse(message.enabled); return this.onAddonEnabledResponse(message.enabled);
case messages.OPEN_URL: case messages.OPEN_URL:
return this.onOpenUrl( 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: case messages.BACKGROUND_OPERATION:
return this.onBackgroundOperation(message.operation); return this.onBackgroundOperation(message.operation);
case messages.MARK_SET_GLOBAL: case messages.MARK_SET_GLOBAL:
@ -67,56 +95,60 @@ export default class ContentMessageListener {
case messages.MARK_JUMP_GLOBAL: case messages.MARK_JUMP_GLOBAL:
return this.onMarkJumpGlobal(message.key); return this.onMarkJumpGlobal(message.key);
case messages.CONSOLE_FRAME_MESSAGE: 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<CompletionGroup[]> {
let completions = await this.commandController.getCompletions(line); let completions = await this.commandController.getCompletions(line);
return Promise.resolve(completions.serialize()); return Promise.resolve(completions);
} }
onConsoleEnterCommand(text) { onConsoleEnterCommand(text: string): Promise<any> {
return this.commandController.exec(text); return this.commandController.exec(text);
} }
onSettingsQuery(): Promise<any> {
onSettingsQuery() {
return this.settingController.getSetting(); return this.settingController.getSetting();
} }
onFindGetKeyword() { onFindGetKeyword(): Promise<string> {
return this.findController.getKeyword(); return this.findController.getKeyword();
} }
onFindSetKeyword(keyword) { onFindSetKeyword(keyword: string): Promise<any> {
return this.findController.setKeyword(keyword); return this.findController.setKeyword(keyword);
} }
onAddonEnabledResponse(enabled) { onAddonEnabledResponse(enabled: boolean): Promise<any> {
return this.addonEnabledController.indicate(enabled); return this.addonEnabledController.indicate(enabled);
} }
onOpenUrl(newTab, url, openerId, background) { onOpenUrl(
newTab: boolean, url: string, openerId: number, background: boolean,
): Promise<any> {
if (newTab) { if (newTab) {
return this.linkController.openNewTab(url, openerId, background); return this.linkController.openNewTab(url, openerId, background);
} }
return this.linkController.openToTab(url, openerId); return this.linkController.openToTab(url, openerId);
} }
onBackgroundOperation(operation) { onBackgroundOperation(operation: any): Promise<any> {
return this.backgroundOperationController.exec(operation); return this.backgroundOperationController.exec(operation);
} }
onMarkSetGlobal(key, x, y) { onMarkSetGlobal(key: string, x: number, y: number): Promise<any> {
return this.markController.setGlobal(key, x, y); return this.markController.setGlobal(key, x, y);
} }
onMarkJumpGlobal(key) { onMarkJumpGlobal(key: string): Promise<any> {
return this.markController.jumpGlobal(key); return this.markController.jumpGlobal(key);
} }
onConsoleFrameMessage(tabId, message) { onConsoleFrameMessage(tabId: number, message: any): void {
let port = this.consolePorts[tabId]; let port = this.consolePorts[tabId];
if (!port) { if (!port) {
return; return;
@ -124,12 +156,14 @@ export default class ContentMessageListener {
port.postMessage(message); port.postMessage(message);
} }
onConnected(port) { onConnected(port: browser.runtime.Port): void {
if (port.name !== 'vimvixen-console') { if (port.name !== 'vimvixen-console') {
return; return;
} }
if (port.sender && port.sender.tab && port.sender.tab.id) {
let id = port.sender.tab.id; let id = port.sender.tab.id;
this.consolePorts[id] = port; this.consolePorts[id] = port;
} }
} }
}

@ -1,7 +1,7 @@
const db = {}; const db: {[key: string]: any} = {};
export default class MemoryStorage { export default class MemoryStorage {
set(name, value) { set(name: string, value: any): void {
let data = JSON.stringify(value); let data = JSON.stringify(value);
if (typeof data === 'undefined') { if (typeof data === 'undefined') {
throw new Error('value is not serializable'); throw new Error('value is not serializable');
@ -9,7 +9,7 @@ export default class MemoryStorage {
db[name] = data; db[name] = data;
} }
get(name) { get(name: string): any {
let data = db[name]; let data = db[name];
if (!data) { if (!data) {
return undefined; return undefined;

@ -1,12 +1,12 @@
export default class IndicatorPresenter { export default class IndicatorPresenter {
indicate(enabled) { indicate(enabled: boolean): Promise<void> {
let path = enabled let path = enabled
? 'resources/enabled_32x32.png' ? 'resources/enabled_32x32.png'
: 'resources/disabled_32x32.png'; : 'resources/disabled_32x32.png';
return browser.browserAction.setIcon({ path }); return browser.browserAction.setIcon({ path });
} }
onClick(listener) { onClick(listener: (arg: browser.tabs.Tab) => void): void {
browser.browserAction.onClicked.addListener(listener); browser.browserAction.onClicked.addListener(listener);
} }
} }

@ -1,8 +1,12 @@
const NOTIFICATION_ID = 'vimvixen-update'; const NOTIFICATION_ID = 'vimvixen-update';
export default class NotifyPresenter { export default class NotifyPresenter {
notify(title, message, onclick) { notify(
const listener = (id) => { title: string,
message: string,
onclick: () => void,
): Promise<string> {
const listener = (id: string) => {
if (id !== NOTIFICATION_ID) { if (id !== NOTIFICATION_ID) {
return; return;
} }

@ -3,27 +3,29 @@ import MemoryStorage from '../infrastructures/MemoryStorage';
const CURRENT_SELECTED_KEY = 'tabs.current.selected'; const CURRENT_SELECTED_KEY = 'tabs.current.selected';
const LAST_SELECTED_KEY = 'tabs.last.selected'; const LAST_SELECTED_KEY = 'tabs.last.selected';
type Tab = browser.tabs.Tab;
export default class TabPresenter { export default class TabPresenter {
open(url, tabId) { open(url: string, tabId?: number): Promise<Tab> {
return browser.tabs.update(tabId, { url }); return browser.tabs.update(tabId, { url });
} }
create(url, opts) { create(url: string, opts?: object): Promise<Tab> {
return browser.tabs.create({ url, ...opts }); return browser.tabs.create({ url, ...opts });
} }
async getCurrent() { async getCurrent(): Promise<Tab> {
let tabs = await browser.tabs.query({ let tabs = await browser.tabs.query({
active: true, currentWindow: true active: true, currentWindow: true
}); });
return tabs[0]; return tabs[0];
} }
getAll() { getAll(): Promise<Tab[]> {
return browser.tabs.query({ currentWindow: true }); return browser.tabs.query({ currentWindow: true });
} }
async getLastSelectedId() { async getLastSelectedId(): Promise<number | undefined> {
let cache = new MemoryStorage(); let cache = new MemoryStorage();
let tabId = await cache.get(LAST_SELECTED_KEY); let tabId = await cache.get(LAST_SELECTED_KEY);
if (tabId === null || typeof tabId === 'undefined') { if (tabId === null || typeof tabId === 'undefined') {
@ -32,25 +34,25 @@ export default class TabPresenter {
return tabId; return tabId;
} }
async getByKeyword(keyword, excludePinned = false) { async getByKeyword(keyword: string, excludePinned = false): Promise<Tab[]> {
let tabs = await browser.tabs.query({ currentWindow: true }); let tabs = await browser.tabs.query({ currentWindow: true });
return tabs.filter((t) => { 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()); t.title && t.title.toLowerCase().includes(keyword.toLowerCase());
}).filter((t) => { }).filter((t) => {
return !(excludePinned && t.pinned); return !(excludePinned && t.pinned);
}); });
} }
select(tabId) { select(tabId: number): Promise<Tab> {
return browser.tabs.update(tabId, { active: true }); return browser.tabs.update(tabId, { active: true });
} }
remove(ids) { remove(ids: number[]): Promise<void> {
return browser.tabs.remove(ids); return browser.tabs.remove(ids);
} }
async reopen() { async reopen(): Promise<any> {
let window = await browser.windows.getCurrent(); let window = await browser.windows.getCurrent();
let sessions = await browser.sessions.getRecentlyClosed(); let sessions = await browser.sessions.getRecentlyClosed();
let session = sessions.find((s) => { let session = sessions.find((s) => {
@ -59,39 +61,43 @@ export default class TabPresenter {
if (!session) { if (!session) {
return; return;
} }
if (session.tab) { if (session.tab && session.tab.sessionId) {
return browser.sessions.restore(session.tab.sessionId); return browser.sessions.restore(session.tab.sessionId);
} }
if (session.window && session.window.sessionId) {
return browser.sessions.restore(session.window.sessionId); return browser.sessions.restore(session.window.sessionId);
} }
}
reload(tabId, cache) { reload(tabId: number, cache: boolean): Promise<void> {
return browser.tabs.reload(tabId, { bypassCache: cache }); return browser.tabs.reload(tabId, { bypassCache: cache });
} }
setPinned(tabId, pinned) { setPinned(tabId: number, pinned: boolean): Promise<Tab> {
return browser.tabs.update(tabId, { pinned }); return browser.tabs.update(tabId, { pinned });
} }
duplicate(id) { duplicate(id: number): Promise<Tab> {
return browser.tabs.duplicate(id); return browser.tabs.duplicate(id);
} }
getZoom(tabId) { getZoom(tabId: number): Promise<number> {
return browser.tabs.getZoom(tabId); return browser.tabs.getZoom(tabId);
} }
setZoom(tabId, factor) { setZoom(tabId: number, factor: number): Promise<void> {
return browser.tabs.setZoom(tabId, factor); return browser.tabs.setZoom(tabId, factor);
} }
onSelected(listener) { onSelected(
listener: (arg: { tabId: number, windowId: number}) => void,
): void {
browser.tabs.onActivated.addListener(listener); browser.tabs.onActivated.addListener(listener);
} }
} }
let tabPresenter = new TabPresenter(); let tabPresenter = new TabPresenter();
tabPresenter.onSelected((tab) => { tabPresenter.onSelected((tab: any) => {
let cache = new MemoryStorage(); let cache = new MemoryStorage();
let lastId = cache.get(CURRENT_SELECTED_KEY); let lastId = cache.get(CURRENT_SELECTED_KEY);

@ -1,5 +1,5 @@
export default class WindowPresenter { export default class WindowPresenter {
create(url) { create(url: string): Promise<browser.windows.Window> {
return browser.windows.create({ url }); return browser.windows.create({ url });
} }
} }

@ -1,5 +1,7 @@
export default class BookmarkRepository { export default class BookmarkRepository {
async create(title, url) { async create(
title: string, url: string
): Promise<browser.bookmarks.BookmarkTreeNode> {
let item = await browser.bookmarks.create({ let item = await browser.bookmarks.create({
type: 'bookmark', type: 'bookmark',
title, title,

@ -1,7 +1,7 @@
import * as urls from '../../shared/urls'; import * as urls from '../../shared/urls';
export default class BrowserSettingRepository { export default class BrowserSettingRepository {
async getHomepageUrls() { async getHomepageUrls(): Promise<string[]> {
let { value } = await browser.browserSettings.homepageOverride.get({}); let { value } = await browser.browserSettings.homepageOverride.get({});
return value.split('|').map(urls.normalizeUrl); return value.split('|').map(urls.normalizeUrl);
} }

@ -1,7 +1,13 @@
type Tab = browser.tabs.Tab;
type BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode;
export default class CompletionsRepository { export default class CompletionsRepository {
async queryBookmarks(keywords) { async queryBookmarks(keywords: string): Promise<BookmarkTreeNode[]> {
let items = await browser.bookmarks.search({ query: keywords }); let items = await browser.bookmarks.search({ query: keywords });
return items.filter((item) => { return items.filter((item) => {
if (!item.url) {
return false;
}
let url = undefined; let url = undefined;
try { try {
url = new URL(item.url); url = new URL(item.url);
@ -12,17 +18,17 @@ export default class CompletionsRepository {
}); });
} }
queryHistories(keywords) { queryHistories(keywords: string): Promise<browser.history.HistoryItem[]> {
return browser.history.search({ return browser.history.search({
text: keywords, text: keywords,
startTime: 0, startTime: 0,
}); });
} }
async queryTabs(keywords, excludePinned) { async queryTabs(keywords: string, excludePinned: boolean): Promise<Tab[]> {
let tabs = await browser.tabs.query({ currentWindow: true }); let tabs = await browser.tabs.query({ currentWindow: true });
return tabs.filter((t) => { 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()); t.title && t.title.toLowerCase().includes(keywords.toLowerCase());
}).filter((t) => { }).filter((t) => {
return !(excludePinned && t.pinned); return !(excludePinned && t.pinned);

@ -3,15 +3,17 @@ import MemoryStorage from '../infrastructures/MemoryStorage';
const FIND_KEYWORD_KEY = 'find-keyword'; const FIND_KEYWORD_KEY = 'find-keyword';
export default class FindRepository { export default class FindRepository {
private cache: MemoryStorage;
constructor() { constructor() {
this.cache = new MemoryStorage(); this.cache = new MemoryStorage();
} }
getKeyword() { getKeyword(): Promise<string> {
return Promise.resolve(this.cache.get(FIND_KEYWORD_KEY)); return Promise.resolve(this.cache.get(FIND_KEYWORD_KEY));
} }
setKeyword(keyword) { setKeyword(keyword: string): Promise<any> {
this.cache.set(FIND_KEYWORD_KEY, keyword); this.cache.set(FIND_KEYWORD_KEY, keyword);
return Promise.resolve(); return Promise.resolve();
} }

@ -4,21 +4,23 @@ import GlobalMark from '../domains/GlobalMark';
const MARK_KEY = 'mark'; const MARK_KEY = 'mark';
export default class MarkRepository { export default class MarkRepository {
private cache: MemoryStorage;
constructor() { constructor() {
this.cache = new MemoryStorage(); this.cache = new MemoryStorage();
} }
getMark(key) { getMark(key: string): Promise<GlobalMark | undefined> {
let marks = this.getOrEmptyMarks(); let marks = this.getOrEmptyMarks();
let data = marks[key]; let data = marks[key];
if (!data) { if (!data) {
return Promise.resolve(undefined); 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); return Promise.resolve(mark);
} }
setMark(key, mark) { setMark(key: string, mark: GlobalMark): Promise<any> {
let marks = this.getOrEmptyMarks(); let marks = this.getOrEmptyMarks();
marks[key] = { tabId: mark.tabId, url: mark.url, x: mark.x, y: mark.y }; marks[key] = { tabId: mark.tabId, url: mark.url, x: mark.x, y: mark.y };
this.cache.set(MARK_KEY, marks); this.cache.set(MARK_KEY, marks);

@ -1,7 +1,7 @@
import Setting from '../domains/Setting'; import Setting from '../domains/Setting';
export default class SettingRepository { export default class SettingRepository {
async load() { async load(): Promise<any> {
let { settings } = await browser.storage.local.get('settings'); let { settings } = await browser.storage.local.get('settings');
if (!settings) { if (!settings) {
return null; return null;

@ -3,19 +3,21 @@ import MemoryStorage from '../infrastructures/MemoryStorage';
const CACHED_SETTING_KEY = 'setting'; const CACHED_SETTING_KEY = 'setting';
export default class SettingRepository { export default class SettingRepository {
private cache: MemoryStorage;
constructor() { constructor() {
this.cache = new MemoryStorage(); this.cache = new MemoryStorage();
} }
get() { get(): Promise<any> {
return Promise.resolve(this.cache.get(CACHED_SETTING_KEY)); return Promise.resolve(this.cache.get(CACHED_SETTING_KEY));
} }
update(value) { update(value: any): any {
return this.cache.set(CACHED_SETTING_KEY, value); return this.cache.set(CACHED_SETTING_KEY, value);
} }
async setProperty(name, value) { async setProperty(name: string, value: string): Promise<any> {
let current = await this.get(); let current = await this.get();
current.properties[name] = value; current.properties[name] = value;
return this.update(current); return this.update(current);

@ -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 });
}
}

@ -3,10 +3,20 @@ import TabPresenter from '../presenters/TabPresenter';
import ContentMessageClient from '../infrastructures/ContentMessageClient'; import ContentMessageClient from '../infrastructures/ContentMessageClient';
export default class AddonEnabledUseCase { export default class AddonEnabledUseCase {
private indicatorPresentor: IndicatorPresenter;
private tabPresenter: TabPresenter;
private contentMessageClient: ContentMessageClient;
constructor() { constructor() {
this.indicatorPresentor = new IndicatorPresenter(); 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 = new TabPresenter();
this.tabPresenter.onSelected(info => this.onTabSelected(info.tabId)); this.tabPresenter.onSelected(info => this.onTabSelected(info.tabId));
@ -14,15 +24,15 @@ export default class AddonEnabledUseCase {
this.contentMessageClient = new ContentMessageClient(); this.contentMessageClient = new ContentMessageClient();
} }
indicate(enabled) { indicate(enabled: boolean): Promise<void> {
return this.indicatorPresentor.indicate(enabled); return this.indicatorPresentor.indicate(enabled);
} }
onIndicatorClick(tabId) { onIndicatorClick(tabId: number): Promise<void> {
return this.contentMessageClient.toggleAddonEnabled(tabId); return this.contentMessageClient.toggleAddonEnabled(tabId);
} }
async onTabSelected(tabId) { async onTabSelected(tabId: number): Promise<void> {
let enabled = await this.contentMessageClient.getAddonEnabled(tabId); let enabled = await this.contentMessageClient.getAddonEnabled(tabId);
return this.indicatorPresentor.indicate(enabled); return this.indicatorPresentor.indicate(enabled);
} }

@ -6,9 +6,21 @@ import SettingRepository from '../repositories/SettingRepository';
import BookmarkRepository from '../repositories/BookmarkRepository'; import BookmarkRepository from '../repositories/BookmarkRepository';
import ConsoleClient from '../infrastructures/ConsoleClient'; import ConsoleClient from '../infrastructures/ConsoleClient';
import ContentMessageClient from '../infrastructures/ContentMessageClient'; import ContentMessageClient from '../infrastructures/ContentMessageClient';
import * as properties from 'shared/settings/properties'; import * as properties from '../../shared/settings/properties';
export default class CommandIndicator { export default class CommandIndicator {
private tabPresenter: TabPresenter;
private windowPresenter: WindowPresenter;
private settingRepository: SettingRepository;
private bookmarkRepository: BookmarkRepository;
private consoleClient: ConsoleClient;
private contentMessageClient: ContentMessageClient;
constructor() { constructor() {
this.tabPresenter = new TabPresenter(); this.tabPresenter = new TabPresenter();
this.windowPresenter = new WindowPresenter(); this.windowPresenter = new WindowPresenter();
@ -19,34 +31,34 @@ export default class CommandIndicator {
this.contentMessageClient = new ContentMessageClient(); this.contentMessageClient = new ContentMessageClient();
} }
async open(keywords) { async open(keywords: string): Promise<browser.tabs.Tab> {
let url = await this.urlOrSearch(keywords); let url = await this.urlOrSearch(keywords);
return this.tabPresenter.open(url); return this.tabPresenter.open(url);
} }
async tabopen(keywords) { async tabopen(keywords: string): Promise<browser.tabs.Tab> {
let url = await this.urlOrSearch(keywords); let url = await this.urlOrSearch(keywords);
return this.tabPresenter.create(url); return this.tabPresenter.create(url);
} }
async winopen(keywords) { async winopen(keywords: string): Promise<browser.windows.Window> {
let url = await this.urlOrSearch(keywords); let url = await this.urlOrSearch(keywords);
return this.windowPresenter.create(url); return this.windowPresenter.create(url);
} }
// eslint-disable-next-line max-statements // eslint-disable-next-line max-statements
async buffer(keywords) { async buffer(keywords: string): Promise<any> {
if (keywords.length === 0) { if (keywords.length === 0) {
return; return;
} }
if (!isNaN(keywords)) { if (!isNaN(Number(keywords))) {
let tabs = await this.tabPresenter.getAll(); let tabs = await this.tabPresenter.getAll();
let index = parseInt(keywords, 10) - 1; let index = parseInt(keywords, 10) - 1;
if (index < 0 || tabs.length <= index) { if (index < 0 || tabs.length <= index) {
throw new RangeError(`tab ${index + 1} does not exist`); 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() === '%') { } else if (keywords.trim() === '%') {
// Select current window // Select current window
return; return;
@ -66,13 +78,13 @@ export default class CommandIndicator {
} }
for (let tab of tabs) { for (let tab of tabs) {
if (tab.index > current.index) { 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<any> {
let excludePinned = !force; let excludePinned = !force;
let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned); let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned);
if (tabs.length === 0) { if (tabs.length === 0) {
@ -80,35 +92,35 @@ export default class CommandIndicator {
} else if (tabs.length > 1) { } else if (tabs.length > 1) {
throw new Error('More than one match for ' + keywords); 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<any> {
let excludePinned = !force; let excludePinned = !force;
let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned); 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); return this.tabPresenter.remove(ids);
} }
async quit() { async quit(): Promise<any> {
let tab = await this.tabPresenter.getCurrent(); 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<any> {
let tabs = await this.tabPresenter.getAll(); 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); this.tabPresenter.remove(ids);
} }
async addbookmark(title) { async addbookmark(title: string): Promise<any> {
let tab = await this.tabPresenter.getCurrent(); let tab = await this.tabPresenter.getCurrent();
let item = await this.bookmarkRepository.create(title, tab.url); let item = await this.bookmarkRepository.create(title, tab.url);
let message = 'Saved current page: ' + item.url; let message = 'Saved current page: ' + item.url;
return this.consoleClient.showInfo(tab.id, message); return this.consoleClient.showInfo(tab.id, message);
} }
async set(keywords) { async set(keywords: string): Promise<any> {
if (keywords.length === 0) { if (keywords.length === 0) {
return; return;
} }
@ -118,7 +130,7 @@ export default class CommandIndicator {
return this.contentMessageClient.broadcastSettingsChanged(); return this.contentMessageClient.broadcastSettingsChanged();
} }
async urlOrSearch(keywords) { async urlOrSearch(keywords: string): Promise<any> {
let settings = await this.settingRepository.get(); let settings = await this.settingRepository.get();
return urls.searchUrl(keywords, settings.search); return urls.searchUrl(keywords, settings.search);
} }

@ -1,6 +1,5 @@
import CompletionItem from '../domains/CompletionItem';
import CompletionGroup from '../domains/CompletionGroup';
import Completions from '../domains/Completions'; import Completions from '../domains/Completions';
import CompletionGroup from '../domains/CompletionGroup';
import CommandDocs from '../domains/CommandDocs'; import CommandDocs from '../domains/CommandDocs';
import CompletionsRepository from '../repositories/CompletionsRepository'; import CompletionsRepository from '../repositories/CompletionsRepository';
import * as filters from './filters'; import * as filters from './filters';
@ -10,14 +9,23 @@ import * as properties from '../../shared/settings/properties';
const COMPLETION_ITEM_LIMIT = 10; const COMPLETION_ITEM_LIMIT = 10;
type Tab = browser.tabs.Tab;
type HistoryItem = browser.history.HistoryItem;
export default class CompletionsUseCase { export default class CompletionsUseCase {
private tabPresenter: TabPresenter;
private completionsRepository: CompletionsRepository;
private settingRepository: SettingRepository;
constructor() { constructor() {
this.tabPresenter = new TabPresenter(); this.tabPresenter = new TabPresenter();
this.completionsRepository = new CompletionsRepository(); this.completionsRepository = new CompletionsRepository();
this.settingRepository = new SettingRepository(); this.settingRepository = new SettingRepository();
} }
queryConsoleCommand(prefix) { queryConsoleCommand(prefix: string): Promise<Completions> {
let keys = Object.keys(CommandDocs); let keys = Object.keys(CommandDocs);
let items = keys let items = keys
.filter(name => name.startsWith(prefix)) .filter(name => name.startsWith(prefix))
@ -28,16 +36,14 @@ export default class CompletionsUseCase {
})); }));
if (items.length === 0) { if (items.length === 0) {
return Promise.resolve(Completions.empty()); return Promise.resolve([]);
} }
return Promise.resolve( return Promise.resolve([{ name: 'Console CompletionGroup', items }]);
new Completions([new CompletionGroup('Console Command', items)])
);
} }
async queryOpen(name, keywords) { async queryOpen(name: string, keywords: string): Promise<Completions> {
let settings = await this.settingRepository.get(); let settings = await this.settingRepository.get();
let groups = []; let groups: CompletionGroup[] = [];
let complete = settings.properties.complete || properties.defaults.complete; let complete = settings.properties.complete || properties.defaults.complete;
for (let c of complete) { for (let c of complete) {
@ -45,31 +51,31 @@ export default class CompletionsUseCase {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
let engines = await this.querySearchEngineItems(name, keywords); let engines = await this.querySearchEngineItems(name, keywords);
if (engines.length > 0) { if (engines.length > 0) {
groups.push(new CompletionGroup('Search Engines', engines)); groups.push({ name: 'Search Engines', items: engines });
} }
} else if (c === 'h') { } else if (c === 'h') {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
let histories = await this.queryHistoryItems(name, keywords); let histories = await this.queryHistoryItems(name, keywords);
if (histories.length > 0) { if (histories.length > 0) {
groups.push(new CompletionGroup('History', histories)); groups.push({ name: 'History', items: histories });
} }
} else if (c === 'b') { } else if (c === 'b') {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
let bookmarks = await this.queryBookmarkItems(name, keywords); let bookmarks = await this.queryBookmarkItems(name, keywords);
if (bookmarks.length > 0) { 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 // eslint-disable-next-line max-statements
async queryBuffer(name, keywords) { async queryBuffer(name: string, keywords: string): Promise<Completions> {
let lastId = await this.tabPresenter.getLastSelectedId(); let lastId = await this.tabPresenter.getLastSelectedId();
let trimmed = keywords.trim(); let trimmed = keywords.trim();
let tabs = []; let tabs: Tab[] = [];
if (trimmed.length > 0 && !isNaN(trimmed)) { if (trimmed.length > 0 && !isNaN(Number(trimmed))) {
let all = await this.tabPresenter.getAll(); let all = await this.tabPresenter.getAll();
let index = parseInt(trimmed, 10) - 1; let index = parseInt(trimmed, 10) - 1;
if (index >= 0 && index < all.length) { if (index >= 0 && index < all.length) {
@ -77,18 +83,18 @@ export default class CompletionsUseCase {
} }
} else if (trimmed === '%') { } else if (trimmed === '%') {
let all = await this.tabPresenter.getAll(); let all = await this.tabPresenter.getAll();
let tab = all.find(t => t.active); let tab = all.find(t => t.active) as Tab;
tabs = [tab]; tabs = [tab];
} else if (trimmed === '#') { } else if (trimmed === '#') {
if (typeof lastId !== 'undefined' && lastId !== null) { if (typeof lastId !== 'undefined' && lastId !== null) {
let all = await this.tabPresenter.getAll(); 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]; tabs = [tab];
} }
} else { } else {
tabs = await this.completionsRepository.queryTabs(keywords, false); tabs = await this.completionsRepository.queryTabs(keywords, false);
} }
const flag = (tab) => { const flag = (tab: Tab) => {
if (tab.active) { if (tab.active) {
return '%'; return '%';
} else if (tab.id === lastId) { } else if (tab.id === lastId) {
@ -96,87 +102,90 @@ export default class CompletionsUseCase {
} }
return ' '; return ' ';
}; };
let items = tabs.map(tab => new CompletionItem({ let items = tabs.map(tab => ({
caption: tab.index + 1 + ': ' + flag(tab) + ' ' + tab.title, caption: tab.index + 1 + ': ' + flag(tab) + ' ' + tab.title,
content: name + ' ' + tab.title, content: name + ' ' + tab.title,
url: tab.url, url: tab.url,
icon: tab.favIconUrl icon: tab.favIconUrl,
})); }));
if (items.length === 0) { 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<CompletionGroup[]> {
return this.queryTabs(name, true, keywords); return this.queryTabs(name, true, keywords);
} }
queryBdeleteForce(name, keywords) { queryBdeleteForce(
name: string, keywords: string,
): Promise<CompletionGroup[]> {
return this.queryTabs(name, false, keywords); return this.queryTabs(name, false, keywords);
} }
querySet(name, keywords) { querySet(name: string, keywords: string): Promise<CompletionGroup[]> {
let items = Object.keys(properties.docs).map((key) => { let items = Object.keys(properties.docs).map((key) => {
if (properties.types[key] === 'boolean') { if (properties.types[key] === 'boolean') {
return [ return [
new CompletionItem({ {
caption: key, caption: key,
content: name + ' ' + key, content: name + ' ' + key,
url: 'Enable ' + properties.docs[key], url: 'Enable ' + properties.docs[key],
}), }, {
new CompletionItem({
caption: 'no' + key, caption: 'no' + key,
content: name + ' no' + key, content: name + ' no' + key,
url: 'Disable ' + properties.docs[key], url: 'Disable ' + properties.docs[key],
}), }
]; ];
} }
return [ return [
new CompletionItem({ {
caption: key, caption: key,
content: name + ' ' + key, content: name + ' ' + key,
url: 'Set ' + properties.docs[key], url: 'Set ' + properties.docs[key],
}) }
]; ];
}); });
items = items.reduce((acc, val) => acc.concat(val), []); let flatten = items.reduce((acc, val) => acc.concat(val), []);
items = items.filter((item) => { flatten = flatten.filter((item) => {
return item.caption.startsWith(keywords); return item.caption.startsWith(keywords);
}); });
if (items.length === 0) { if (flatten.length === 0) {
return Promise.resolve(Completions.empty()); return Promise.resolve([]);
} }
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<CompletionGroup[]> {
let tabs = await this.completionsRepository.queryTabs(args, excludePinned); let tabs = await this.completionsRepository.queryTabs(args, excludePinned);
let items = tabs.map(tab => new CompletionItem({ let items = tabs.map(tab => ({
caption: tab.title, caption: tab.title,
content: name + ' ' + tab.title, content: name + ' ' + tab.title,
url: tab.url, url: tab.url,
icon: tab.favIconUrl icon: tab.favIconUrl
})); }));
if (items.length === 0) { 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 settings = await this.settingRepository.get();
let engines = Object.keys(settings.search.engines) let engines = Object.keys(settings.search.engines)
.filter(key => key.startsWith(keywords)); .filter(key => key.startsWith(keywords));
return engines.map(key => new CompletionItem({ return engines.map(key => ({
caption: key, caption: key,
content: name + ' ' + key, content: name + ' ' + key,
})); }));
} }
async queryHistoryItems(name, keywords) { async queryHistoryItems(name: string, keywords: string) {
let histories = await this.completionsRepository.queryHistories(keywords); let histories = await this.completionsRepository.queryHistories(keywords);
histories = [histories] histories = [histories]
.map(filters.filterBlankTitle) .map(filters.filterBlankTitle)
@ -184,19 +193,21 @@ export default class CompletionsUseCase {
.map(filters.filterByTailingSlash) .map(filters.filterByTailingSlash)
.map(pages => filters.filterByPathname(pages, COMPLETION_ITEM_LIMIT)) .map(pages => filters.filterByPathname(pages, COMPLETION_ITEM_LIMIT))
.map(pages => filters.filterByOrigin(pages, COMPLETION_ITEM_LIMIT))[0] .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); .slice(0, COMPLETION_ITEM_LIMIT);
return histories.map(page => new CompletionItem({ return histories.map(page => ({
caption: page.title, caption: page.title,
content: name + ' ' + page.url, content: name + ' ' + page.url,
url: page.url url: page.url
})); }));
} }
async queryBookmarkItems(name, keywords) { async queryBookmarkItems(name: string, keywords: string) {
let bookmarks = await this.completionsRepository.queryBookmarks(keywords); let bookmarks = await this.completionsRepository.queryBookmarks(keywords);
return bookmarks.slice(0, COMPLETION_ITEM_LIMIT) return bookmarks.slice(0, COMPLETION_ITEM_LIMIT)
.map(page => new CompletionItem({ .map(page => ({
caption: page.title, caption: page.title,
content: name + ' ' + page.url, content: name + ' ' + page.url,
url: page.url url: page.url

@ -2,60 +2,64 @@ import TabPresenter from '../presenters/TabPresenter';
import ConsoleClient from '../infrastructures/ConsoleClient'; import ConsoleClient from '../infrastructures/ConsoleClient';
export default class ConsoleUseCase { export default class ConsoleUseCase {
private tabPresenter: TabPresenter;
private consoleClient: ConsoleClient;
constructor() { constructor() {
this.tabPresenter = new TabPresenter(); this.tabPresenter = new TabPresenter();
this.consoleClient = new ConsoleClient(); this.consoleClient = new ConsoleClient();
} }
async showCommand() { async showCommand(): Promise<any> {
let tab = await this.tabPresenter.getCurrent(); 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<any> {
let tab = await this.tabPresenter.getCurrent(); let tab = await this.tabPresenter.getCurrent();
let command = 'open '; let command = 'open ';
if (alter) { 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<any> {
let tab = await this.tabPresenter.getCurrent(); let tab = await this.tabPresenter.getCurrent();
let command = 'tabopen '; let command = 'tabopen ';
if (alter) { 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<any> {
let tab = await this.tabPresenter.getCurrent(); let tab = await this.tabPresenter.getCurrent();
let command = 'winopen '; let command = 'winopen ';
if (alter) { 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<any> {
let tab = await this.tabPresenter.getCurrent(); let tab = await this.tabPresenter.getCurrent();
let command = 'buffer '; 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<any> {
let tab = await this.tabPresenter.getCurrent(); let tab = await this.tabPresenter.getCurrent();
let command = 'addbookmark '; let command = 'addbookmark ';
if (alter) { 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<any> {
let tab = await this.tabPresenter.getCurrent(); let tab = await this.tabPresenter.getCurrent();
return this.consoleClient.hide(tab.id); return this.consoleClient.hide(tab.id as number);
} }
} }

@ -3,22 +3,28 @@ import TabPresenter from '../presenters/TabPresenter';
import ConsoleClient from '../infrastructures/ConsoleClient'; import ConsoleClient from '../infrastructures/ConsoleClient';
export default class FindUseCase { export default class FindUseCase {
private tabPresenter: TabPresenter;
private findRepository: FindRepository;
private consoleClient: ConsoleClient;
constructor() { constructor() {
this.tabPresenter = new TabPresenter(); this.tabPresenter = new TabPresenter();
this.findRepository = new FindRepository(); this.findRepository = new FindRepository();
this.consoleClient = new ConsoleClient(); this.consoleClient = new ConsoleClient();
} }
getKeyword() { getKeyword(): Promise<string> {
return this.findRepository.getKeyword(); return this.findRepository.getKeyword();
} }
setKeyword(keyword) { setKeyword(keyword: string): Promise<any> {
return this.findRepository.setKeyword(keyword); return this.findRepository.setKeyword(keyword);
} }
async findStart() { async findStart(): Promise<any> {
let tab = await this.tabPresenter.getCurrent(); let tab = await this.tabPresenter.getCurrent();
return this.consoleClient.showFind(tab.id); return this.consoleClient.showFind(tab.id as number);
} }
} }

@ -1,17 +1,17 @@
import SettingRepository from '../repositories/SettingRepository';
import TabPresenter from '../presenters/TabPresenter'; import TabPresenter from '../presenters/TabPresenter';
export default class LinkUseCase { export default class LinkUseCase {
private tabPresenter: TabPresenter;
constructor() { constructor() {
this.settingRepository = new SettingRepository();
this.tabPresenter = new TabPresenter(); this.tabPresenter = new TabPresenter();
} }
openToTab(url, tabId) { openToTab(url: string, tabId: number): Promise<any> {
return this.tabPresenter.open(url, tabId); return this.tabPresenter.open(url, tabId);
} }
openNewTab(url, openerId, background) { openNewTab(url: string, openerId: number, background: boolean): Promise<any> {
return this.tabPresenter.create(url, { return this.tabPresenter.create(url, {
openerTabId: openerId, active: !background openerTabId: openerId, active: !background
}); });

@ -1,10 +1,17 @@
import GlobalMark from '../domains/GlobalMark';
import TabPresenter from '../presenters/TabPresenter'; import TabPresenter from '../presenters/TabPresenter';
import MarkRepository from '../repositories/MarkRepository'; import MarkRepository from '../repositories/MarkRepository';
import ConsoleClient from '../infrastructures/ConsoleClient'; import ConsoleClient from '../infrastructures/ConsoleClient';
import ContentMessageClient from '../infrastructures/ContentMessageClient'; import ContentMessageClient from '../infrastructures/ContentMessageClient';
export default class MarkUseCase { export default class MarkUseCase {
private tabPresenter: TabPresenter;
private markRepository: MarkRepository;
private consoleClient: ConsoleClient;
private contentMessageClient: ContentMessageClient;
constructor() { constructor() {
this.tabPresenter = new TabPresenter(); this.tabPresenter = new TabPresenter();
this.markRepository = new MarkRepository(); this.markRepository = new MarkRepository();
@ -12,18 +19,19 @@ export default class MarkUseCase {
this.contentMessageClient = new ContentMessageClient(); this.contentMessageClient = new ContentMessageClient();
} }
async setGlobal(key, x, y) { async setGlobal(key: string, x: number, y: number): Promise<any> {
let tab = await this.tabPresenter.getCurrent(); 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); return this.markRepository.setMark(key, mark);
} }
async jumpGlobal(key) { async jumpGlobal(key: string): Promise<any> {
let current = await this.tabPresenter.getCurrent(); let current = await this.tabPresenter.getCurrent();
let mark = await this.markRepository.getMark(key); let mark = await this.markRepository.getMark(key);
if (!mark) { 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( return this.contentMessageClient.scrollTo(
@ -32,7 +40,7 @@ export default class MarkUseCase {
return this.tabPresenter.select(mark.tabId); return this.tabPresenter.select(mark.tabId);
}).catch(async() => { }).catch(async() => {
let tab = await this.tabPresenter.create(mark.url); 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); return this.markRepository.setMark(key, mark2);
}); });
} }

@ -4,16 +4,20 @@ import PersistentSettingRepository from '../repositories/PersistentSettingReposi
import SettingRepository from '../repositories/SettingRepository'; import SettingRepository from '../repositories/SettingRepository';
export default class SettingUseCase { export default class SettingUseCase {
private persistentSettingRepository: PersistentSettingRepository;
private settingRepository: SettingRepository;
constructor() { constructor() {
this.persistentSettingRepository = new PersistentSettingRepository(); this.persistentSettingRepository = new PersistentSettingRepository();
this.settingRepository = new SettingRepository(); this.settingRepository = new SettingRepository();
} }
get() { get(): Promise<any> {
return this.settingRepository.get(); return this.settingRepository.get();
} }
async reload() { async reload(): Promise<any> {
let settings = await this.persistentSettingRepository.load(); let settings = await this.persistentSettingRepository.load();
if (!settings) { if (!settings) {
settings = Setting.defaultSettings(); settings = Setting.defaultSettings();

@ -1,11 +1,13 @@
import TabPresenter from '../presenters/TabPresenter'; import TabPresenter from '../presenters/TabPresenter';
export default class TabSelectUseCase { export default class TabSelectUseCase {
private tabPresenter: TabPresenter;
constructor() { constructor() {
this.tabPresenter = new TabPresenter(); this.tabPresenter = new TabPresenter();
} }
async selectPrev(count) { async selectPrev(count: number): Promise<any> {
let tabs = await this.tabPresenter.getAll(); let tabs = await this.tabPresenter.getAll();
if (tabs.length < 2) { if (tabs.length < 2) {
return; return;
@ -15,10 +17,10 @@ export default class TabSelectUseCase {
return; return;
} }
let select = (tab.index - count + tabs.length) % tabs.length; 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<any> {
let tabs = await this.tabPresenter.getAll(); let tabs = await this.tabPresenter.getAll();
if (tabs.length < 2) { if (tabs.length < 2) {
return; return;
@ -28,24 +30,24 @@ export default class TabSelectUseCase {
return; return;
} }
let select = (tab.index + count) % tabs.length; 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<any> {
let tabs = await this.tabPresenter.getAll(); 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<any> {
let tabs = await this.tabPresenter.getAll(); 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<any> {
let tabId = await this.tabPresenter.getLastSelectedId(); let tabId = await this.tabPresenter.getLastSelectedId();
if (tabId === null || typeof tabId === 'undefined') { if (tabId === null || typeof tabId === 'undefined') {
return; return Promise.resolve();
} }
this.tabPresenter.select(tabId); return this.tabPresenter.select(tabId);
} }
} }

@ -2,20 +2,24 @@ import TabPresenter from '../presenters/TabPresenter';
import BrowserSettingRepository from '../repositories/BrowserSettingRepository'; import BrowserSettingRepository from '../repositories/BrowserSettingRepository';
export default class TabUseCase { export default class TabUseCase {
private tabPresenter: TabPresenter;
private browserSettingRepository: BrowserSettingRepository;
constructor() { constructor() {
this.tabPresenter = new TabPresenter(); this.tabPresenter = new TabPresenter();
this.browserSettingRepository = new BrowserSettingRepository(); this.browserSettingRepository = new BrowserSettingRepository();
} }
async close(force) { async close(force: boolean): Promise<any> {
let tab = await this.tabPresenter.getCurrent(); let tab = await this.tabPresenter.getCurrent();
if (!force && tab.pinned) { 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<any> {
let tabs = await this.tabPresenter.getAll(); let tabs = await this.tabPresenter.getAll();
tabs.sort((t1, t2) => t1.index - t2.index); tabs.sort((t1, t2) => t1.index - t2.index);
let index = tabs.findIndex(t => t.active); let index = tabs.findIndex(t => t.active);
@ -25,42 +29,42 @@ export default class TabUseCase {
for (let i = index + 1; i < tabs.length; ++i) { for (let i = index + 1; i < tabs.length; ++i) {
let tab = tabs[i]; let tab = tabs[i];
if (!tab.pinned) { if (!tab.pinned) {
this.tabPresenter.remove(tab.id); this.tabPresenter.remove([tab.id as number]);
} }
} }
} }
reopen() { reopen(): Promise<any> {
return this.tabPresenter.reopen(); return this.tabPresenter.reopen();
} }
async reload(cache) { async reload(cache: boolean): Promise<any> {
let tab = await this.tabPresenter.getCurrent(); 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<any> {
let tab = await this.tabPresenter.getCurrent(); 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<any> {
let tab = await this.tabPresenter.getCurrent(); 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<any> {
let tab = await this.tabPresenter.getCurrent(); 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<any> {
let tab = await this.tabPresenter.getCurrent(); let tab = await this.tabPresenter.getCurrent();
let url = 'view-source:' + tab.url; let url = 'view-source:' + tab.url;
return this.tabPresenter.create(url); return this.tabPresenter.create(url);
} }
async openHome(newTab) { async openHome(newTab: boolean): Promise<any> {
let tab = await this.tabPresenter.getCurrent(); let tab = await this.tabPresenter.getCurrent();
let urls = await this.browserSettingRepository.getHomepageUrls(); let urls = await this.browserSettingRepository.getHomepageUrls();
if (urls.length === 1 && urls[0] === 'about:home') { if (urls.length === 1 && urls[0] === 'about:home') {

@ -3,21 +3,25 @@ import TabPresenter from '../presenters/TabPresenter';
import NotifyPresenter from '../presenters/NotifyPresenter'; import NotifyPresenter from '../presenters/NotifyPresenter';
export default class VersionUseCase { export default class VersionUseCase {
private tabPresenter: TabPresenter;
private notifyPresenter: NotifyPresenter;
constructor() { constructor() {
this.tabPresenter = new TabPresenter(); this.tabPresenter = new TabPresenter();
this.notifyPresenter = new NotifyPresenter(); this.notifyPresenter = new NotifyPresenter();
} }
notify() { notify(): Promise<string> {
let title = `Vim Vixen ${manifest.version} has been installed`; let title = `Vim Vixen ${manifest.version} has been installed`;
let message = 'Click here to see release notes'; let message = 'Click here to see release notes';
let url = this.releaseNoteUrl(manifest.version); let url = this.releaseNoteUrl(manifest.version);
this.notifyPresenter.notify(title, message, () => { return this.notifyPresenter.notify(title, message, () => {
this.tabPresenter.create(url); this.tabPresenter.create(url);
}); });
} }
releaseNoteUrl(version) { releaseNoteUrl(version?: string): string {
if (version) { if (version) {
return `https://github.com/ueokande/vim-vixen/releases/tag/${version}`; return `https://github.com/ueokande/vim-vixen/releases/tag/${version}`;
} }

@ -1,35 +1,39 @@
import TabPresenter from '../presenters/TabPresenter'; 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, 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 1.10, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00
]; ];
export default class ZoomUseCase { export default class ZoomUseCase {
private tabPresenter: TabPresenter;
constructor() { constructor() {
this.tabPresenter = new TabPresenter(); this.tabPresenter = new TabPresenter();
} }
async zoomIn(tabId) { async zoomIn(): Promise<any> {
let tab = await this.tabPresenter.getCurrent(); 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); let factor = ZOOM_SETTINGS.find(f => f > current);
if (factor) { if (factor) {
return this.tabPresenter.setZoom(tabId, factor); return this.tabPresenter.setZoom(tabId as number, factor);
} }
} }
async zoomOut(tabId) { async zoomOut(): Promise<any> {
let tab = await this.tabPresenter.getCurrent(); let tab = await this.tabPresenter.getCurrent();
let current = await this.tabPresenter.getZoom(tab.id); let tabId = tab.id as number;
let factor = [].concat(ZOOM_SETTINGS).reverse().find(f => f < current); let current = await this.tabPresenter.getZoom(tabId);
let factor = ZOOM_SETTINGS.slice(0).reverse().find(f => f < current);
if (factor) { if (factor) {
return this.tabPresenter.setZoom(tabId, factor); return this.tabPresenter.setZoom(tabId as number, factor);
} }
} }
zoomNutoral(tabId) { async zoomNutoral(): Promise<any> {
return this.tabPresenter.setZoom(tabId, 1); let tab = await this.tabPresenter.getCurrent();
return this.tabPresenter.setZoom(tab.id as number, 1);
} }
} }

@ -1,40 +1,42 @@
const filterHttp = (items) => { type Item = browser.history.HistoryItem;
let httpsHosts = items.map(x => new URL(x.url))
const filterHttp = (items: Item[]): Item[] => {
let httpsHosts = items.map(x => new URL(x.url as string))
.filter(x => x.protocol === 'https:') .filter(x => x.protocol === 'https:')
.map(x => x.host); .map(x => x.host);
httpsHosts = new Set(httpsHosts); let hostsSet = new Set(httpsHosts);
return items.filter((item) => { return items.filter((item: Item) => {
let url = new URL(item.url); let url = new URL(item.url as string);
return url.protocol === 'https:' || !httpsHosts.has(url.host); return url.protocol === 'https:' || !hostsSet.has(url.host);
}); });
}; };
const filterBlankTitle = (items) => { const filterBlankTitle = (items: Item[]): Item[] => {
return items.filter(item => item.title && item.title !== ''); return items.filter(item => item.title && item.title !== '');
}; };
const filterByTailingSlash = (items) => { const filterByTailingSlash = (items: Item[]): Item[] => {
let urls = items.map(item => new URL(item.url)); let urls = items.map(item => new URL(item.url as string));
let simplePaths = urls let simplePaths = urls
.filter(url => url.hash === '' && url.search === '') .filter(url => url.hash === '' && url.search === '')
.map(url => url.origin + url.pathname); .map(url => url.origin + url.pathname);
simplePaths = new Set(simplePaths); let pathsSet = new Set(simplePaths);
return items.filter((item) => { return items.filter((item) => {
let url = new URL(item.url); let url = new URL(item.url as string);
if (url.hash !== '' || url.search !== '' || if (url.hash !== '' || url.search !== '' ||
url.pathname.slice(-1) !== '/') { url.pathname.slice(-1) !== '/') {
return true; 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) => { const filterByPathname = (items: Item[], min: number): Item[] => {
let hash = {}; let hash: {[key: string]: Item} = {};
for (let item of items) { 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; let pathname = url.origin + url.pathname;
if (!hash[pathname]) { if (!hash[pathname]) {
hash[pathname] = item; hash[pathname] = item;
@ -49,10 +51,10 @@ const filterByPathname = (items, min) => {
return filtered; return filtered;
}; };
const filterByOrigin = (items, min) => { const filterByOrigin = (items: Item[], min: number): Item[] => {
let hash = {}; let hash: {[key: string]: Item} = {};
for (let item of items) { for (let item of items) {
let origin = new URL(item.url).origin; let origin = new URL(item.url as string).origin;
if (!hash[origin]) { if (!hash[origin]) {
hash[origin] = item; hash[origin] = item;
} else if (hash[origin].url.length > item.url.length) { } else if (hash[origin].url.length > item.url.length) {

@ -1,4 +1,4 @@
const mustNumber = (v) => { const mustNumber = (v: any): number => {
let num = Number(v); let num = Number(v);
if (isNaN(num)) { if (isNaN(num)) {
throw new Error('Not number: ' + v); throw new Error('Not number: ' + v);
@ -6,8 +6,11 @@ const mustNumber = (v) => {
return num; return num;
}; };
const parseSetOption = (word, types) => { const parseSetOption = (
let [key, value] = word.split('='); word: string,
types: { [key: string]: string },
): any[] => {
let [key, value]: any[] = word.split('=');
if (value === undefined) { if (value === undefined) {
value = !key.startsWith('no'); value = !key.startsWith('no');
key = value ? key : key.slice(2); key = value ? key : key.slice(2);
@ -26,6 +29,7 @@ const parseSetOption = (word, types) => {
case 'number': return [key, mustNumber(value)]; case 'number': return [key, mustNumber(value)];
case 'boolean': return [key, value]; case 'boolean': return [key, value];
} }
throw new Error('Unknown property type: ' + type);
}; };
export { parseSetOption }; export { parseSetOption };

@ -90,16 +90,6 @@ class Scroller {
} }
} }
class RoughtScroller {
constructor(element) {
this.element = element;
}
scroll(x, y) {
this.element.scrollTo(x, y);
}
}
const getScroll = () => { const getScroll = () => {
let target = scrollTarget(); let target = scrollTarget();
return { x: target.scrollLeft, y: target.scrollTop }; return { x: target.scrollLeft, y: target.scrollTop };

@ -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);
});
});

@ -9,12 +9,11 @@ describe('background/repositories/mark', () => {
}); });
it('get and set', async() => { 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); repository.setMark('A', mark);
let got = await repository.getMark('A'); let got = await repository.getMark('A');
expect(got).to.be.a('object');
expect(got.tabId).to.equal(1); expect(got.tabId).to.equal(1);
expect(got.url).to.equal('http://example.com'); expect(got.url).to.equal('http://example.com');
expect(got.x).to.equal(10); expect(got.x).to.equal(10);

@ -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');
});
});
});