From a26d8a8a1bed48a77e062914c120a23ace7bb8cf Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sun, 24 Feb 2019 20:54:35 +0900 Subject: Capitalize background scripts --- src/background/usecases/AddonEnabledUseCase.js | 29 ++++ src/background/usecases/CommandUseCase.js | 125 +++++++++++++++ src/background/usecases/CompletionsUseCase.js | 205 ++++++++++++++++++++++++ src/background/usecases/FindUseCase.js | 15 ++ src/background/usecases/LinkUseCase.js | 19 +++ src/background/usecases/MarkUseCase.js | 39 +++++ src/background/usecases/OperationUseCase.js | 208 +++++++++++++++++++++++++ src/background/usecases/SettingUseCase.js | 32 ++++ src/background/usecases/VersionUseCase.js | 41 +++++ src/background/usecases/addon-enabled.js | 29 ---- src/background/usecases/command.js | 125 --------------- src/background/usecases/completions.js | 205 ------------------------ src/background/usecases/find.js | 15 -- src/background/usecases/link.js | 19 --- src/background/usecases/mark.js | 39 ----- src/background/usecases/operation.js | 208 ------------------------- src/background/usecases/setting.js | 31 ---- src/background/usecases/version.js | 41 ----- 18 files changed, 713 insertions(+), 712 deletions(-) create mode 100644 src/background/usecases/AddonEnabledUseCase.js create mode 100644 src/background/usecases/CommandUseCase.js create mode 100644 src/background/usecases/CompletionsUseCase.js create mode 100644 src/background/usecases/FindUseCase.js create mode 100644 src/background/usecases/LinkUseCase.js create mode 100644 src/background/usecases/MarkUseCase.js create mode 100644 src/background/usecases/OperationUseCase.js create mode 100644 src/background/usecases/SettingUseCase.js create mode 100644 src/background/usecases/VersionUseCase.js delete mode 100644 src/background/usecases/addon-enabled.js delete mode 100644 src/background/usecases/command.js delete mode 100644 src/background/usecases/completions.js delete mode 100644 src/background/usecases/find.js delete mode 100644 src/background/usecases/link.js delete mode 100644 src/background/usecases/mark.js delete mode 100644 src/background/usecases/operation.js delete mode 100644 src/background/usecases/setting.js delete mode 100644 src/background/usecases/version.js (limited to 'src/background/usecases') diff --git a/src/background/usecases/AddonEnabledUseCase.js b/src/background/usecases/AddonEnabledUseCase.js new file mode 100644 index 0000000..bb2c347 --- /dev/null +++ b/src/background/usecases/AddonEnabledUseCase.js @@ -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 new file mode 100644 index 0000000..9ec46fe --- /dev/null +++ b/src/background/usecases/CommandUseCase.js @@ -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 new file mode 100644 index 0000000..7dc30ac --- /dev/null +++ b/src/background/usecases/CompletionsUseCase.js @@ -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/FindUseCase.js b/src/background/usecases/FindUseCase.js new file mode 100644 index 0000000..d6b0323 --- /dev/null +++ b/src/background/usecases/FindUseCase.js @@ -0,0 +1,15 @@ +import FindRepository from '../repositories/FindRepository'; + +export default class FindUseCase { + constructor() { + this.findRepository = new FindRepository(); + } + + getKeyword() { + return this.findRepository.getKeyword(); + } + + setKeyword(keyword) { + return this.findRepository.setKeyword(keyword); + } +} diff --git a/src/background/usecases/LinkUseCase.js b/src/background/usecases/LinkUseCase.js new file mode 100644 index 0000000..89412c5 --- /dev/null +++ b/src/background/usecases/LinkUseCase.js @@ -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 new file mode 100644 index 0000000..39c796b --- /dev/null +++ b/src/background/usecases/MarkUseCase.js @@ -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/OperationUseCase.js b/src/background/usecases/OperationUseCase.js new file mode 100644 index 0000000..1d75d1a --- /dev/null +++ b/src/background/usecases/OperationUseCase.js @@ -0,0 +1,208 @@ +import TabPresenter from '../presenters/TabPresenter'; +import ConsoleClient from '../infrastructures/ConsoleClient'; +import * as urls from '../../shared/urls'; + +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 OperationUseCase { + constructor() { + this.tabPresenter = new TabPresenter(); + this.consoleClient = new ConsoleClient(); + } + + 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 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); + } + + 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 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); + } + + 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 findStart() { + let tab = await this.tabPresenter.getCurrent(); + return this.consoleClient.showFind(tab.id); + } + + async hideConsole() { + let tab = await this.tabPresenter.getCurrent(); + return this.consoleClient.hide(tab.id); + } + + async openHome(newTab) { + let tab = await this.tabPresenter.getCurrent(); + let result = await browser.browserSettings.homepageOverride.get({}); + let us = urls.homepageUrls(result.value); + if (us.length === 1 && us[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 (us.length === 1 && !newTab) { + return this.tabPresenter.open(us[0], tab.id); + } + for (let u of us) { + this.tabPresenter.create(u); + } + } +} + diff --git a/src/background/usecases/SettingUseCase.js b/src/background/usecases/SettingUseCase.js new file mode 100644 index 0000000..fe3ca5d --- /dev/null +++ b/src/background/usecases/SettingUseCase.js @@ -0,0 +1,32 @@ +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(); + } + + save(settings) { + this.persistentSettingRepository.save(settings); + } + + 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/VersionUseCase.js b/src/background/usecases/VersionUseCase.js new file mode 100644 index 0000000..704c808 --- /dev/null +++ b/src/background/usecases/VersionUseCase.js @@ -0,0 +1,41 @@ +import manifest from '../../../manifest.json'; +import VersionRepository from '../repositories/VersionRepository'; +import TabPresenter from '../presenters/TabPresenter'; +import Notifier from '../infrastructures/Notifier'; + +export default class VersionUseCase { + constructor() { + this.versionRepository = new VersionRepository(); + this.tabPresenter = new TabPresenter(); + this.notifier = new Notifier(); + } + + async notifyIfUpdated() { + if (!await this.checkUpdated()) { + return; + } + + let title = 'Vim Vixen ' + manifest.version + ' has been installed'; + let message = 'Click here to see release notes'; + this.notifier.notify(title, message, () => { + let url = this.releaseNoteUrl(manifest.version); + this.tabPresenter.create(url); + }); + this.versionRepository.update(manifest.version); + } + + async checkUpdated() { + let prev = await this.versionRepository.get(); + if (!prev) { + return true; + } + return manifest.version !== prev; + } + + 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/addon-enabled.js b/src/background/usecases/addon-enabled.js deleted file mode 100644 index d83192f..0000000 --- a/src/background/usecases/addon-enabled.js +++ /dev/null @@ -1,29 +0,0 @@ -import IndicatorPresenter from '../presenters/indicator'; -import TabPresenter from '../presenters/tab'; -import ContentMessageClient from '../infrastructures/content-message-client'; - -export default class AddonEnabledInteractor { - 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/command.js b/src/background/usecases/command.js deleted file mode 100644 index 0d21041..0000000 --- a/src/background/usecases/command.js +++ /dev/null @@ -1,125 +0,0 @@ -import * as parsers from './parsers'; -import * as urls from '../../shared/urls'; -import TabPresenter from '../presenters/tab'; -import WindowPresenter from '../presenters/window'; -import SettingRepository from '../repositories/setting'; -import BookmarkRepository from '../repositories/bookmark'; -import ConsolePresenter from '../presenters/console'; -import ContentMessageClient from '../infrastructures/content-message-client'; -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.consolePresenter = new ConsolePresenter(); - - 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.consolePresenter.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/completions.js b/src/background/usecases/completions.js deleted file mode 100644 index 21dc668..0000000 --- a/src/background/usecases/completions.js +++ /dev/null @@ -1,205 +0,0 @@ -import CompletionItem from '../domains/completion-item'; -import CompletionGroup from '../domains/completion-group'; -import Completions from '../domains/completions'; -import CommandDocs from '../domains/command-docs'; -import CompletionRepository from '../repositories/completions'; -import * as filters from './filters'; -import SettingRepository from '../repositories/setting'; -import TabPresenter from '../presenters/tab'; -import * as properties from '../../shared/settings/properties'; - -const COMPLETION_ITEM_LIMIT = 10; - -export default class CompletionsInteractor { - constructor() { - this.tabPresenter = new TabPresenter(); - this.completionRepository = new CompletionRepository(); - 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.completionRepository.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.completionRepository.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.completionRepository.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.completionRepository.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/find.js b/src/background/usecases/find.js deleted file mode 100644 index eae480d..0000000 --- a/src/background/usecases/find.js +++ /dev/null @@ -1,15 +0,0 @@ -import FindRepository from '../repositories/find'; - -export default class FindInteractor { - constructor() { - this.findRepository = new FindRepository(); - } - - getKeyword() { - return this.findRepository.getKeyword(); - } - - setKeyword(keyword) { - return this.findRepository.setKeyword(keyword); - } -} diff --git a/src/background/usecases/link.js b/src/background/usecases/link.js deleted file mode 100644 index b8ed719..0000000 --- a/src/background/usecases/link.js +++ /dev/null @@ -1,19 +0,0 @@ -import SettingRepository from '../repositories/setting'; -import TabPresenter from '../presenters/tab'; - -export default class LinkInteractor { - 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/mark.js b/src/background/usecases/mark.js deleted file mode 100644 index 34b8a74..0000000 --- a/src/background/usecases/mark.js +++ /dev/null @@ -1,39 +0,0 @@ -import GlobalMark from '../domains/global-mark'; -import TabPresenter from '../presenters/tab'; -import MarkRepository from '../repositories/mark'; -import ConsolePresenter from '../presenters/console'; -import ContentMessageClient from '../infrastructures/content-message-client'; - -export default class MarkInteractor { - constructor() { - this.tabPresenter = new TabPresenter(); - this.markRepository = new MarkRepository(); - this.consolePresenter = new ConsolePresenter(); - 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.consolePresenter.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/operation.js b/src/background/usecases/operation.js deleted file mode 100644 index ed64032..0000000 --- a/src/background/usecases/operation.js +++ /dev/null @@ -1,208 +0,0 @@ -import TabPresenter from '../presenters/tab'; -import ConsolePresenter from '../presenters/console'; -import * as urls from '../../shared/urls'; - -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 OperationInteractor { - constructor() { - this.tabPresenter = new TabPresenter(); - this.consolePresenter = new ConsolePresenter(); - } - - 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 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); - } - - 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 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); - } - - async showCommand() { - let tab = await this.tabPresenter.getCurrent(); - return this.consolePresenter.showCommand(tab.id, ''); - } - - async showOpenCommand(alter) { - let tab = await this.tabPresenter.getCurrent(); - let command = 'open '; - if (alter) { - command += tab.url; - } - return this.consolePresenter.showCommand(tab.id, command); - } - - async showTabopenCommand(alter) { - let tab = await this.tabPresenter.getCurrent(); - let command = 'tabopen '; - if (alter) { - command += tab.url; - } - return this.consolePresenter.showCommand(tab.id, command); - } - - async showWinopenCommand(alter) { - let tab = await this.tabPresenter.getCurrent(); - let command = 'winopen '; - if (alter) { - command += tab.url; - } - return this.consolePresenter.showCommand(tab.id, command); - } - - async showBufferCommand() { - let tab = await this.tabPresenter.getCurrent(); - let command = 'buffer '; - return this.consolePresenter.showCommand(tab.id, command); - } - - async showAddbookmarkCommand(alter) { - let tab = await this.tabPresenter.getCurrent(); - let command = 'addbookmark '; - if (alter) { - command += tab.title; - } - return this.consolePresenter.showCommand(tab.id, command); - } - - async findStart() { - let tab = await this.tabPresenter.getCurrent(); - return this.consolePresenter.showFind(tab.id); - } - - async hideConsole() { - let tab = await this.tabPresenter.getCurrent(); - return this.consolePresenter.hide(tab.id); - } - - async openHome(newTab) { - let tab = await this.tabPresenter.getCurrent(); - let result = await browser.browserSettings.homepageOverride.get({}); - let us = urls.homepageUrls(result.value); - if (us.length === 1 && us[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 (us.length === 1 && !newTab) { - return this.tabPresenter.open(us[0], tab.id); - } - for (let u of us) { - this.tabPresenter.create(u); - } - } -} - diff --git a/src/background/usecases/setting.js b/src/background/usecases/setting.js deleted file mode 100644 index 656fc3f..0000000 --- a/src/background/usecases/setting.js +++ /dev/null @@ -1,31 +0,0 @@ -import Setting from '../domains/setting'; -import PersistentSettingRepository from '../repositories/persistent-setting'; -import SettingRepository from '../repositories/setting'; - -export default class SettingInteractor { - constructor() { - this.persistentSettingRepository = new PersistentSettingRepository(); - this.settingRepository = new SettingRepository(); - } - - save(settings) { - this.persistentSettingRepository.save(settings); - } - - 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/version.js b/src/background/usecases/version.js deleted file mode 100644 index a71f90d..0000000 --- a/src/background/usecases/version.js +++ /dev/null @@ -1,41 +0,0 @@ -import manifest from '../../../manifest.json'; -import VersionRepository from '../repositories/version'; -import TabPresenter from '../presenters/tab'; -import Notifier from '../infrastructures/notifier'; - -export default class VersionInteractor { - constructor() { - this.versionRepository = new VersionRepository(); - this.tabPresenter = new TabPresenter(); - this.notifier = new Notifier(); - } - - async notifyIfUpdated() { - if (!await this.checkUpdated()) { - return; - } - - let title = 'Vim Vixen ' + manifest.version + ' has been installed'; - let message = 'Click here to see release notes'; - this.notifier.notify(title, message, () => { - let url = this.releaseNoteUrl(manifest.version); - this.tabPresenter.create(url); - }); - this.versionRepository.update(manifest.version); - } - - async checkUpdated() { - let prev = await this.versionRepository.get(); - if (!prev) { - return true; - } - return manifest.version !== prev; - } - - releaseNoteUrl(version) { - if (version) { - return 'https://github.com/ueokande/vim-vixen/releases/tag/' + version; - } - return 'https://github.com/ueokande/vim-vixen/releases/'; - } -} -- cgit v1.2.3