commit
05ef6a8ca3
205 changed files with 5117 additions and 2774 deletions
@ -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,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 +0,0 @@ |
|||||||
import LinkUseCase from '../usecases/LinkUseCase'; |
|
||||||
|
|
||||||
export default class LinkController { |
|
||||||
constructor() { |
|
||||||
this.linkUseCase = new LinkUseCase(); |
|
||||||
} |
|
||||||
|
|
||||||
openToTab(url, tabId) { |
|
||||||
this.linkUseCase.openToTab(url, tabId); |
|
||||||
} |
|
||||||
|
|
||||||
openNewTab(url, openerId, background) { |
|
||||||
this.linkUseCase.openNewTab(url, openerId, background); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,19 @@ |
|||||||
|
import LinkUseCase from '../usecases/LinkUseCase'; |
||||||
|
|
||||||
|
export default class LinkController { |
||||||
|
private linkUseCase: LinkUseCase; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.linkUseCase = new LinkUseCase(); |
||||||
|
} |
||||||
|
|
||||||
|
openToTab(url: string, tabId: number): Promise<void> { |
||||||
|
return this.linkUseCase.openToTab(url, tabId); |
||||||
|
} |
||||||
|
|
||||||
|
openNewTab( |
||||||
|
url: string, openerId: number, background: boolean, |
||||||
|
): Promise<void> { |
||||||
|
return this.linkUseCase.openNewTab(url, openerId, background); |
||||||
|
} |
||||||
|
} |
@ -1,15 +0,0 @@ |
|||||||
import MarkUseCase from '../usecases/MarkUseCase'; |
|
||||||
|
|
||||||
export default class MarkController { |
|
||||||
constructor() { |
|
||||||
this.markUseCase = new MarkUseCase(); |
|
||||||
} |
|
||||||
|
|
||||||
setGlobal(key, x, y) { |
|
||||||
this.markUseCase.setGlobal(key, x, y); |
|
||||||
} |
|
||||||
|
|
||||||
jumpGlobal(key) { |
|
||||||
this.markUseCase.jumpGlobal(key); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,17 @@ |
|||||||
|
import MarkUseCase from '../usecases/MarkUseCase'; |
||||||
|
|
||||||
|
export default class MarkController { |
||||||
|
private markUseCase: MarkUseCase; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.markUseCase = new MarkUseCase(); |
||||||
|
} |
||||||
|
|
||||||
|
setGlobal(key: string, x: number, y: number): Promise<any> { |
||||||
|
return this.markUseCase.setGlobal(key, x, y); |
||||||
|
} |
||||||
|
|
||||||
|
jumpGlobal(key: string): Promise<any> { |
||||||
|
return this.markUseCase.jumpGlobal(key); |
||||||
|
} |
||||||
|
} |
@ -1,17 +1,22 @@ |
|||||||
import SettingUseCase from '../usecases/SettingUseCase'; |
import SettingUseCase from '../usecases/SettingUseCase'; |
||||||
import ContentMessageClient from '../infrastructures/ContentMessageClient'; |
import ContentMessageClient from '../infrastructures/ContentMessageClient'; |
||||||
|
import Settings from '../../shared/Settings'; |
||||||
|
|
||||||
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(): Promise<Settings> { |
||||||
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(): Promise<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(); |
|
||||||
}); |
|
||||||
} |
|
||||||
} |
|
@ -1,14 +0,0 @@ |
|||||||
export default class CompletionGroup { |
|
||||||
constructor(name, items) { |
|
||||||
this.name0 = name; |
|
||||||
this.items0 = items; |
|
||||||
} |
|
||||||
|
|
||||||
get name() { |
|
||||||
return this.name0; |
|
||||||
} |
|
||||||
|
|
||||||
get items() { |
|
||||||
return this.items0; |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,7 @@ |
|||||||
|
import CompletionItem from './CompletionItem'; |
||||||
|
|
||||||
|
export default interface CompletionGroup { |
||||||
|
name: string; |
||||||
|
items: CompletionItem[]; |
||||||
|
// eslint-disable-next-line semi
|
||||||
|
} |
@ -1,24 +0,0 @@ |
|||||||
export default class CompletionItem { |
|
||||||
constructor({ caption, content, url, icon }) { |
|
||||||
this.caption0 = caption; |
|
||||||
this.content0 = content; |
|
||||||
this.url0 = url; |
|
||||||
this.icon0 = icon; |
|
||||||
} |
|
||||||
|
|
||||||
get caption() { |
|
||||||
return this.caption0; |
|
||||||
} |
|
||||||
|
|
||||||
get content() { |
|
||||||
return this.content0; |
|
||||||
} |
|
||||||
|
|
||||||
get url() { |
|
||||||
return this.url0; |
|
||||||
} |
|
||||||
|
|
||||||
get icon() { |
|
||||||
return this.icon0; |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,7 @@ |
|||||||
|
export default interface CompletionItem { |
||||||
|
readonly caption?: string; |
||||||
|
readonly content?: string; |
||||||
|
readonly url?: string; |
||||||
|
readonly icon?: string; |
||||||
|
// eslint-disable-next-line semi
|
||||||
|
} |
@ -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 +0,0 @@ |
|||||||
export default class GlobalMark { |
|
||||||
constructor(tabId, url, x, y) { |
|
||||||
this.tabId0 = tabId; |
|
||||||
this.url0 = url; |
|
||||||
this.x0 = x; |
|
||||||
this.y0 = y; |
|
||||||
} |
|
||||||
|
|
||||||
get tabId() { |
|
||||||
return this.tabId0; |
|
||||||
} |
|
||||||
|
|
||||||
get url() { |
|
||||||
return this.url0; |
|
||||||
} |
|
||||||
|
|
||||||
get x() { |
|
||||||
return this.x0; |
|
||||||
} |
|
||||||
|
|
||||||
get y() { |
|
||||||
return this.y0; |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,7 @@ |
|||||||
|
export default interface GlobalMark { |
||||||
|
readonly tabId: number; |
||||||
|
readonly url: string; |
||||||
|
readonly x: number; |
||||||
|
readonly y: number; |
||||||
|
// eslint-disable-next-line semi
|
||||||
|
} |
@ -1,51 +0,0 @@ |
|||||||
import DefaultSettings from '../../shared/settings/default'; |
|
||||||
import * as settingsValues from '../../shared/settings/values'; |
|
||||||
|
|
||||||
export default class Setting { |
|
||||||
constructor({ source, json, form }) { |
|
||||||
this.obj = { |
|
||||||
source, json, form |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
get source() { |
|
||||||
return this.obj.source; |
|
||||||
} |
|
||||||
|
|
||||||
get json() { |
|
||||||
return this.obj.json; |
|
||||||
} |
|
||||||
|
|
||||||
get form() { |
|
||||||
return this.obj.form; |
|
||||||
} |
|
||||||
|
|
||||||
value() { |
|
||||||
let value = JSON.parse(DefaultSettings.json); |
|
||||||
if (this.obj.source === 'json') { |
|
||||||
value = settingsValues.valueFromJson(this.obj.json); |
|
||||||
} else if (this.obj.source === 'form') { |
|
||||||
value = settingsValues.valueFromForm(this.obj.form); |
|
||||||
} |
|
||||||
if (!value.properties) { |
|
||||||
value.properties = {}; |
|
||||||
} |
|
||||||
return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value }; |
|
||||||
} |
|
||||||
|
|
||||||
serialize() { |
|
||||||
return this.obj; |
|
||||||
} |
|
||||||
|
|
||||||
static deserialize(obj) { |
|
||||||
return new Setting({ source: obj.source, json: obj.json, form: obj.form }); |
|
||||||
} |
|
||||||
|
|
||||||
static defaultSettings() { |
|
||||||
return new Setting({ |
|
||||||
source: DefaultSettings.source, |
|
||||||
json: DefaultSettings.json, |
|
||||||
form: {}, |
|
||||||
}); |
|
||||||
} |
|
||||||
} |
|
@ -1,34 +1,34 @@ |
|||||||
import messages from '../../shared/messages'; |
import * as 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,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,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,8 +0,0 @@ |
|||||||
import * as urls from '../../shared/urls'; |
|
||||||
|
|
||||||
export default class BrowserSettingRepository { |
|
||||||
async getHomepageUrls() { |
|
||||||
let { value } = await browser.browserSettings.homepageOverride.get({}); |
|
||||||
return value.split('|').map(urls.normalizeUrl); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,24 @@ |
|||||||
|
import * as urls from '../../shared/urls'; |
||||||
|
|
||||||
|
declare namespace browser.browserSettings.homepageOverride { |
||||||
|
|
||||||
|
type BrowserSettings = { |
||||||
|
value: string; |
||||||
|
levelOfControl: LevelOfControlType; |
||||||
|
}; |
||||||
|
|
||||||
|
type LevelOfControlType = |
||||||
|
'not_controllable' | |
||||||
|
'controlled_by_other_extensions' | |
||||||
|
'controllable_by_this_extension' | |
||||||
|
'controlled_by_this_extension'; |
||||||
|
|
||||||
|
function get(param: object): Promise<BrowserSettings>; |
||||||
|
} |
||||||
|
|
||||||
|
export default class BrowserSettingRepository { |
||||||
|
async getHomepageUrls(): Promise<string[]> { |
||||||
|
let { value } = await browser.browserSettings.homepageOverride.get({}); |
||||||
|
return value.split('|').map(urls.normalizeUrl); |
||||||
|
} |
||||||
|
} |
@ -1,12 +1,12 @@ |
|||||||
import Setting from '../domains/Setting'; |
import SettingData from '../../shared/SettingData'; |
||||||
|
|
||||||
export default class SettingRepository { |
export default class SettingRepository { |
||||||
async load() { |
async load(): Promise<SettingData | null> { |
||||||
let { settings } = await browser.storage.local.get('settings'); |
let { settings } = await browser.storage.local.get('settings'); |
||||||
if (!settings) { |
if (!settings) { |
||||||
return null; |
return null; |
||||||
} |
} |
||||||
return Setting.deserialize(settings); |
return SettingData.valueOf(settings as any); |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
@ -1,23 +0,0 @@ |
|||||||
import MemoryStorage from '../infrastructures/MemoryStorage'; |
|
||||||
|
|
||||||
const CACHED_SETTING_KEY = 'setting'; |
|
||||||
|
|
||||||
export default class SettingRepository { |
|
||||||
constructor() { |
|
||||||
this.cache = new MemoryStorage(); |
|
||||||
} |
|
||||||
|
|
||||||
get() { |
|
||||||
return Promise.resolve(this.cache.get(CACHED_SETTING_KEY)); |
|
||||||
} |
|
||||||
|
|
||||||
update(value) { |
|
||||||
return this.cache.set(CACHED_SETTING_KEY, value); |
|
||||||
} |
|
||||||
|
|
||||||
async setProperty(name, value) { |
|
||||||
let current = await this.get(); |
|
||||||
current.properties[name] = value; |
|
||||||
return this.update(current); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,51 @@ |
|||||||
|
import MemoryStorage from '../infrastructures/MemoryStorage'; |
||||||
|
import Settings from '../../shared/Settings'; |
||||||
|
import * as PropertyDefs from '../../shared/property-defs'; |
||||||
|
|
||||||
|
const CACHED_SETTING_KEY = 'setting'; |
||||||
|
|
||||||
|
export default class SettingRepository { |
||||||
|
private cache: MemoryStorage; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.cache = new MemoryStorage(); |
||||||
|
} |
||||||
|
|
||||||
|
get(): Promise<Settings> { |
||||||
|
return Promise.resolve(this.cache.get(CACHED_SETTING_KEY)); |
||||||
|
} |
||||||
|
|
||||||
|
update(value: Settings): void { |
||||||
|
return this.cache.set(CACHED_SETTING_KEY, value); |
||||||
|
} |
||||||
|
|
||||||
|
async setProperty( |
||||||
|
name: string, value: string | number | boolean, |
||||||
|
): Promise<void> { |
||||||
|
let def = PropertyDefs.defs.find(d => name === d.name); |
||||||
|
if (!def) { |
||||||
|
throw new Error('unknown property: ' + name); |
||||||
|
} |
||||||
|
if (typeof value !== def.type) { |
||||||
|
throw new TypeError(`property type of ${name} mismatch: ${typeof value}`); |
||||||
|
} |
||||||
|
let newValue = value; |
||||||
|
if (typeof value === 'string' && value === '') { |
||||||
|
newValue = def.defaultValue; |
||||||
|
} |
||||||
|
|
||||||
|
let current = await this.get(); |
||||||
|
switch (name) { |
||||||
|
case 'hintchars': |
||||||
|
current.properties.hintchars = newValue as string; |
||||||
|
break; |
||||||
|
case 'smoothscroll': |
||||||
|
current.properties.smoothscroll = newValue as boolean; |
||||||
|
break; |
||||||
|
case 'complete': |
||||||
|
current.properties.complete = newValue as string; |
||||||
|
break; |
||||||
|
} |
||||||
|
return this.update(current); |
||||||
|
} |
||||||
|
} |
@ -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 }); |
|
||||||
} |
|
||||||
} |
|
@ -1,61 +0,0 @@ |
|||||||
import TabPresenter from '../presenters/TabPresenter'; |
|
||||||
import ConsoleClient from '../infrastructures/ConsoleClient'; |
|
||||||
|
|
||||||
export default class ConsoleUseCase { |
|
||||||
constructor() { |
|
||||||
this.tabPresenter = new TabPresenter(); |
|
||||||
this.consoleClient = new ConsoleClient(); |
|
||||||
} |
|
||||||
|
|
||||||
async showCommand() { |
|
||||||
let tab = await this.tabPresenter.getCurrent(); |
|
||||||
return this.consoleClient.showCommand(tab.id, ''); |
|
||||||
} |
|
||||||
|
|
||||||
async showOpenCommand(alter) { |
|
||||||
let tab = await this.tabPresenter.getCurrent(); |
|
||||||
let command = 'open '; |
|
||||||
if (alter) { |
|
||||||
command += tab.url; |
|
||||||
} |
|
||||||
return this.consoleClient.showCommand(tab.id, command); |
|
||||||
} |
|
||||||
|
|
||||||
async showTabopenCommand(alter) { |
|
||||||
let tab = await this.tabPresenter.getCurrent(); |
|
||||||
let command = 'tabopen '; |
|
||||||
if (alter) { |
|
||||||
command += tab.url; |
|
||||||
} |
|
||||||
return this.consoleClient.showCommand(tab.id, command); |
|
||||||
} |
|
||||||
|
|
||||||
async showWinopenCommand(alter) { |
|
||||||
let tab = await this.tabPresenter.getCurrent(); |
|
||||||
let command = 'winopen '; |
|
||||||
if (alter) { |
|
||||||
command += tab.url; |
|
||||||
} |
|
||||||
return this.consoleClient.showCommand(tab.id, command); |
|
||||||
} |
|
||||||
|
|
||||||
async showBufferCommand() { |
|
||||||
let tab = await this.tabPresenter.getCurrent(); |
|
||||||
let command = 'buffer '; |
|
||||||
return this.consoleClient.showCommand(tab.id, command); |
|
||||||
} |
|
||||||
|
|
||||||
async showAddbookmarkCommand(alter) { |
|
||||||
let tab = await this.tabPresenter.getCurrent(); |
|
||||||
let command = 'addbookmark '; |
|
||||||
if (alter) { |
|
||||||
command += tab.title; |
|
||||||
} |
|
||||||
return this.consoleClient.showCommand(tab.id, command); |
|
||||||
} |
|
||||||
|
|
||||||
async hideConsole() { |
|
||||||
let tab = await this.tabPresenter.getCurrent(); |
|
||||||
return this.consoleClient.hide(tab.id); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,65 @@ |
|||||||
|
import TabPresenter from '../presenters/TabPresenter'; |
||||||
|
import ConsoleClient from '../infrastructures/ConsoleClient'; |
||||||
|
|
||||||
|
export default class ConsoleUseCase { |
||||||
|
private tabPresenter: TabPresenter; |
||||||
|
|
||||||
|
private consoleClient: ConsoleClient; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.tabPresenter = new TabPresenter(); |
||||||
|
this.consoleClient = new ConsoleClient(); |
||||||
|
} |
||||||
|
|
||||||
|
async showCommand(): Promise<any> { |
||||||
|
let tab = await this.tabPresenter.getCurrent(); |
||||||
|
return this.consoleClient.showCommand(tab.id as number, ''); |
||||||
|
} |
||||||
|
|
||||||
|
async showOpenCommand(alter: boolean): Promise<any> { |
||||||
|
let tab = await this.tabPresenter.getCurrent(); |
||||||
|
let command = 'open '; |
||||||
|
if (alter) { |
||||||
|
command += tab.url || ''; |
||||||
|
} |
||||||
|
return this.consoleClient.showCommand(tab.id as number, command); |
||||||
|
} |
||||||
|
|
||||||
|
async showTabopenCommand(alter: boolean): Promise<any> { |
||||||
|
let tab = await this.tabPresenter.getCurrent(); |
||||||
|
let command = 'tabopen '; |
||||||
|
if (alter) { |
||||||
|
command += tab.url || ''; |
||||||
|
} |
||||||
|
return this.consoleClient.showCommand(tab.id as number, command); |
||||||
|
} |
||||||
|
|
||||||
|
async showWinopenCommand(alter: boolean): Promise<any> { |
||||||
|
let tab = await this.tabPresenter.getCurrent(); |
||||||
|
let command = 'winopen '; |
||||||
|
if (alter) { |
||||||
|
command += tab.url || ''; |
||||||
|
} |
||||||
|
return this.consoleClient.showCommand(tab.id as number, command); |
||||||
|
} |
||||||
|
|
||||||
|
async showBufferCommand(): Promise<any> { |
||||||
|
let tab = await this.tabPresenter.getCurrent(); |
||||||
|
let command = 'buffer '; |
||||||
|
return this.consoleClient.showCommand(tab.id as number, command); |
||||||
|
} |
||||||
|
|
||||||
|
async showAddbookmarkCommand(alter: boolean): Promise<any> { |
||||||
|
let tab = await this.tabPresenter.getCurrent(); |
||||||
|
let command = 'addbookmark '; |
||||||
|
if (alter) { |
||||||
|
command += tab.title || ''; |
||||||
|
} |
||||||
|
return this.consoleClient.showCommand(tab.id as number, command); |
||||||
|
} |
||||||
|
|
||||||
|
async hideConsole(): Promise<any> { |
||||||
|
let tab = await this.tabPresenter.getCurrent(); |
||||||
|
return this.consoleClient.hide(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,28 +1,31 @@ |
|||||||
import Setting from '../domains/Setting'; |
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
import PersistentSettingRepository from '../repositories/PersistentSettingRepository'; |
import PersistentSettingRepository from '../repositories/PersistentSettingRepository'; |
||||||
import SettingRepository from '../repositories/SettingRepository'; |
import SettingRepository from '../repositories/SettingRepository'; |
||||||
|
import { DefaultSettingData } from '../../shared/SettingData'; |
||||||
|
import Settings from '../../shared/Settings'; |
||||||
|
|
||||||
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<Settings> { |
||||||
return this.settingRepository.get(); |
return this.settingRepository.get(); |
||||||
} |
} |
||||||
|
|
||||||
async reload() { |
async reload(): Promise<Settings> { |
||||||
let settings = await this.persistentSettingRepository.load(); |
let data = await this.persistentSettingRepository.load(); |
||||||
if (!settings) { |
if (!data) { |
||||||
settings = Setting.defaultSettings(); |
data = DefaultSettingData; |
||||||
} |
} |
||||||
|
|
||||||
let value = settings.value(); |
let value = data.toSettings(); |
||||||
|
|
||||||
this.settingRepository.update(value); |
this.settingRepository.update(value); |
||||||
|
|
||||||
return value; |
return value; |
||||||
} |
} |
||||||
} |
} |
@ -1,23 +1,27 @@ |
|||||||
import manifest from '../../../manifest.json'; |
|
||||||
import TabPresenter from '../presenters/TabPresenter'; |
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<void> { |
||||||
|
let manifest = browser.runtime.getManifest(); |
||||||
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 +0,0 @@ |
|||||||
import TabPresenter from '../presenters/TabPresenter'; |
|
||||||
|
|
||||||
const ZOOM_SETTINGS = [ |
|
||||||
0.33, 0.50, 0.66, 0.75, 0.80, 0.90, 1.00, |
|
||||||
1.10, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00 |
|
||||||
]; |
|
||||||
|
|
||||||
export default class ZoomUseCase { |
|
||||||
constructor() { |
|
||||||
this.tabPresenter = new TabPresenter(); |
|
||||||
} |
|
||||||
|
|
||||||
async zoomIn(tabId) { |
|
||||||
let tab = await this.tabPresenter.getCurrent(); |
|
||||||
let current = await this.tabPresenter.getZoom(tab.id); |
|
||||||
let factor = ZOOM_SETTINGS.find(f => f > current); |
|
||||||
if (factor) { |
|
||||||
return this.tabPresenter.setZoom(tabId, factor); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
async zoomOut(tabId) { |
|
||||||
let tab = await this.tabPresenter.getCurrent(); |
|
||||||
let current = await this.tabPresenter.getZoom(tab.id); |
|
||||||
let factor = [].concat(ZOOM_SETTINGS).reverse().find(f => f < current); |
|
||||||
if (factor) { |
|
||||||
return this.tabPresenter.setZoom(tabId, factor); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
zoomNutoral(tabId) { |
|
||||||
return this.tabPresenter.setZoom(tabId, 1); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,39 @@ |
|||||||
|
import TabPresenter from '../presenters/TabPresenter'; |
||||||
|
|
||||||
|
const ZOOM_SETTINGS: number[] = [ |
||||||
|
0.33, 0.50, 0.66, 0.75, 0.80, 0.90, 1.00, |
||||||
|
1.10, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00 |
||||||
|
]; |
||||||
|
|
||||||
|
export default class ZoomUseCase { |
||||||
|
private tabPresenter: TabPresenter; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.tabPresenter = new TabPresenter(); |
||||||
|
} |
||||||
|
|
||||||
|
async zoomIn(): Promise<any> { |
||||||
|
let tab = await this.tabPresenter.getCurrent(); |
||||||
|
let tabId = tab.id as number; |
||||||
|
let current = await this.tabPresenter.getZoom(tabId); |
||||||
|
let factor = ZOOM_SETTINGS.find(f => f > current); |
||||||
|
if (factor) { |
||||||
|
return this.tabPresenter.setZoom(tabId as number, factor); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
async zoomOut(): Promise<any> { |
||||||
|
let tab = await this.tabPresenter.getCurrent(); |
||||||
|
let tabId = tab.id as number; |
||||||
|
let current = await this.tabPresenter.getZoom(tabId); |
||||||
|
let factor = ZOOM_SETTINGS.slice(0).reverse().find(f => f < current); |
||||||
|
if (factor) { |
||||||
|
return this.tabPresenter.setZoom(tabId as number, factor); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
async zoomNutoral(): Promise<any> { |
||||||
|
let tab = await this.tabPresenter.getCurrent(); |
||||||
|
return this.tabPresenter.setZoom(tab.id as number, 1); |
||||||
|
} |
||||||
|
} |
@ -1,72 +0,0 @@ |
|||||||
const filterHttp = (items) => { |
|
||||||
let httpsHosts = items.map(x => new URL(x.url)) |
|
||||||
.filter(x => x.protocol === 'https:') |
|
||||||
.map(x => x.host); |
|
||||||
httpsHosts = new Set(httpsHosts); |
|
||||||
|
|
||||||
return items.filter((item) => { |
|
||||||
let url = new URL(item.url); |
|
||||||
return url.protocol === 'https:' || !httpsHosts.has(url.host); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
const filterBlankTitle = (items) => { |
|
||||||
return items.filter(item => item.title && item.title !== ''); |
|
||||||
}; |
|
||||||
|
|
||||||
const filterByTailingSlash = (items) => { |
|
||||||
let urls = items.map(item => new URL(item.url)); |
|
||||||
let simplePaths = urls |
|
||||||
.filter(url => url.hash === '' && url.search === '') |
|
||||||
.map(url => url.origin + url.pathname); |
|
||||||
simplePaths = new Set(simplePaths); |
|
||||||
|
|
||||||
return items.filter((item) => { |
|
||||||
let url = new URL(item.url); |
|
||||||
if (url.hash !== '' || url.search !== '' || |
|
||||||
url.pathname.slice(-1) !== '/') { |
|
||||||
return true; |
|
||||||
} |
|
||||||
return !simplePaths.has(url.origin + url.pathname.slice(0, -1)); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
const filterByPathname = (items, min) => { |
|
||||||
let hash = {}; |
|
||||||
for (let item of items) { |
|
||||||
let url = new URL(item.url); |
|
||||||
let pathname = url.origin + url.pathname; |
|
||||||
if (!hash[pathname]) { |
|
||||||
hash[pathname] = item; |
|
||||||
} else if (hash[pathname].url.length > item.url.length) { |
|
||||||
hash[pathname] = item; |
|
||||||
} |
|
||||||
} |
|
||||||
let filtered = Object.values(hash); |
|
||||||
if (filtered.length < min) { |
|
||||||
return items; |
|
||||||
} |
|
||||||
return filtered; |
|
||||||
}; |
|
||||||
|
|
||||||
const filterByOrigin = (items, min) => { |
|
||||||
let hash = {}; |
|
||||||
for (let item of items) { |
|
||||||
let origin = new URL(item.url).origin; |
|
||||||
if (!hash[origin]) { |
|
||||||
hash[origin] = item; |
|
||||||
} else if (hash[origin].url.length > item.url.length) { |
|
||||||
hash[origin] = item; |
|
||||||
} |
|
||||||
} |
|
||||||
let filtered = Object.values(hash); |
|
||||||
if (filtered.length < min) { |
|
||||||
return items; |
|
||||||
} |
|
||||||
return filtered; |
|
||||||
}; |
|
||||||
|
|
||||||
export { |
|
||||||
filterHttp, filterBlankTitle, filterByTailingSlash, |
|
||||||
filterByPathname, filterByOrigin |
|
||||||
}; |
|
@ -0,0 +1,76 @@ |
|||||||
|
type Item = browser.history.HistoryItem; |
||||||
|
|
||||||
|
const filterHttp = (items: Item[]): Item[] => { |
||||||
|
let httpsHosts = items.map(x => new URL(x.url as string)) |
||||||
|
.filter(x => x.protocol === 'https:') |
||||||
|
.map(x => x.host); |
||||||
|
let hostsSet = new Set(httpsHosts); |
||||||
|
|
||||||
|
return items.filter((item: Item) => { |
||||||
|
let url = new URL(item.url as string); |
||||||
|
return url.protocol === 'https:' || !hostsSet.has(url.host); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
const filterBlankTitle = (items: Item[]): Item[] => { |
||||||
|
return items.filter(item => item.title && item.title !== ''); |
||||||
|
}; |
||||||
|
|
||||||
|
const filterByTailingSlash = (items: Item[]): Item[] => { |
||||||
|
let urls = items.map(item => new URL(item.url as string)); |
||||||
|
let simplePaths = urls |
||||||
|
.filter(url => url.hash === '' && url.search === '') |
||||||
|
.map(url => url.origin + url.pathname); |
||||||
|
let pathsSet = new Set(simplePaths); |
||||||
|
|
||||||
|
return items.filter((item) => { |
||||||
|
let url = new URL(item.url as string); |
||||||
|
if (url.hash !== '' || url.search !== '' || |
||||||
|
url.pathname.slice(-1) !== '/') { |
||||||
|
return true; |
||||||
|
} |
||||||
|
return !pathsSet.has(url.origin + url.pathname.slice(0, -1)); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
const filterByPathname = (items: Item[], min: number): Item[] => { |
||||||
|
let hash: {[key: string]: Item} = {}; |
||||||
|
for (let item of items) { |
||||||
|
let url = new URL(item.url as string); |
||||||
|
let pathname = url.origin + url.pathname; |
||||||
|
if (!hash[pathname]) { |
||||||
|
hash[pathname] = item; |
||||||
|
} else if ((hash[pathname].url as string).length > |
||||||
|
(item.url as string).length) { |
||||||
|
hash[pathname] = item; |
||||||
|
} |
||||||
|
} |
||||||
|
let filtered = Object.values(hash); |
||||||
|
if (filtered.length < min) { |
||||||
|
return items; |
||||||
|
} |
||||||
|
return filtered; |
||||||
|
}; |
||||||
|
|
||||||
|
const filterByOrigin = (items: Item[], min: number): Item[] => { |
||||||
|
let hash: {[key: string]: Item} = {}; |
||||||
|
for (let item of items) { |
||||||
|
let origin = new URL(item.url as string).origin; |
||||||
|
if (!hash[origin]) { |
||||||
|
hash[origin] = item; |
||||||
|
} else if ((hash[origin].url as string).length > |
||||||
|
(item.url as string).length) { |
||||||
|
hash[origin] = item; |
||||||
|
} |
||||||
|
} |
||||||
|
let filtered = Object.values(hash); |
||||||
|
if (filtered.length < min) { |
||||||
|
return items; |
||||||
|
} |
||||||
|
return filtered; |
||||||
|
}; |
||||||
|
|
||||||
|
export { |
||||||
|
filterHttp, filterBlankTitle, filterByTailingSlash, |
||||||
|
filterByPathname, filterByOrigin |
||||||
|
}; |
@ -1,31 +0,0 @@ |
|||||||
const mustNumber = (v) => { |
|
||||||
let num = Number(v); |
|
||||||
if (isNaN(num)) { |
|
||||||
throw new Error('Not number: ' + v); |
|
||||||
} |
|
||||||
return num; |
|
||||||
}; |
|
||||||
|
|
||||||
const parseSetOption = (word, types) => { |
|
||||||
let [key, value] = word.split('='); |
|
||||||
if (value === undefined) { |
|
||||||
value = !key.startsWith('no'); |
|
||||||
key = value ? key : key.slice(2); |
|
||||||
} |
|
||||||
let type = types[key]; |
|
||||||
if (!type) { |
|
||||||
throw new Error('Unknown property: ' + key); |
|
||||||
} |
|
||||||
if (type === 'boolean' && typeof value !== 'boolean' || |
|
||||||
type !== 'boolean' && typeof value === 'boolean') { |
|
||||||
throw new Error('Invalid argument: ' + word); |
|
||||||
} |
|
||||||
|
|
||||||
switch (type) { |
|
||||||
case 'string': return [key, value]; |
|
||||||
case 'number': return [key, mustNumber(value)]; |
|
||||||
case 'boolean': return [key, value]; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
export { parseSetOption }; |
|
@ -0,0 +1,36 @@ |
|||||||
|
import * as PropertyDefs from '../../shared//property-defs'; |
||||||
|
|
||||||
|
const mustNumber = (v: any): number => { |
||||||
|
let num = Number(v); |
||||||
|
if (isNaN(num)) { |
||||||
|
throw new Error('Not number: ' + v); |
||||||
|
} |
||||||
|
return num; |
||||||
|
}; |
||||||
|
|
||||||
|
const parseSetOption = ( |
||||||
|
args: string, |
||||||
|
): any[] => { |
||||||
|
let [key, value]: any[] = args.split('='); |
||||||
|
if (value === undefined) { |
||||||
|
value = !key.startsWith('no'); |
||||||
|
key = value ? key : key.slice(2); |
||||||
|
} |
||||||
|
let def = PropertyDefs.defs.find(d => d.name === key); |
||||||
|
if (!def) { |
||||||
|
throw new Error('Unknown property: ' + key); |
||||||
|
} |
||||||
|
if (def.type === 'boolean' && typeof value !== 'boolean' || |
||||||
|
def.type !== 'boolean' && typeof value === 'boolean') { |
||||||
|
throw new Error('Invalid argument: ' + args); |
||||||
|
} |
||||||
|
|
||||||
|
switch (def.type) { |
||||||
|
case 'string': return [key, value]; |
||||||
|
case 'number': return [key, mustNumber(value)]; |
||||||
|
case 'boolean': return [key, value]; |
||||||
|
} |
||||||
|
throw new Error('Unknown property type: ' + def.type); |
||||||
|
}; |
||||||
|
|
||||||
|
export { parseSetOption }; |
@ -1,13 +0,0 @@ |
|||||||
export default { |
|
||||||
// console commands
|
|
||||||
CONSOLE_HIDE: 'console.hide', |
|
||||||
CONSOLE_SHOW_COMMAND: 'console.show.command', |
|
||||||
CONSOLE_SHOW_ERROR: 'console.show.error', |
|
||||||
CONSOLE_SHOW_INFO: 'console.show.info', |
|
||||||
CONSOLE_HIDE_COMMAND: 'console.hide.command', |
|
||||||
CONSOLE_SET_CONSOLE_TEXT: 'console.set.command', |
|
||||||
CONSOLE_SET_COMPLETIONS: 'console.set.completions', |
|
||||||
CONSOLE_COMPLETION_NEXT: 'console.completion.next', |
|
||||||
CONSOLE_COMPLETION_PREV: 'console.completion.prev', |
|
||||||
CONSOLE_SHOW_FIND: 'console.show.find', |
|
||||||
}; |
|
@ -0,0 +1,63 @@ |
|||||||
|
// console commands
|
||||||
|
export const CONSOLE_HIDE = 'console.hide'; |
||||||
|
export const CONSOLE_SHOW_COMMAND = 'console.show.command'; |
||||||
|
export const CONSOLE_SHOW_ERROR = 'console.show.error'; |
||||||
|
export const CONSOLE_SHOW_INFO = 'console.show.info'; |
||||||
|
export const CONSOLE_HIDE_COMMAND = 'console.hide.command'; |
||||||
|
export const CONSOLE_SET_CONSOLE_TEXT = 'console.set.command'; |
||||||
|
export const CONSOLE_SET_COMPLETIONS = 'console.set.completions'; |
||||||
|
export const CONSOLE_COMPLETION_NEXT = 'console.completion.next'; |
||||||
|
export const CONSOLE_COMPLETION_PREV = 'console.completion.prev'; |
||||||
|
export const CONSOLE_SHOW_FIND = 'console.show.find'; |
||||||
|
|
||||||
|
interface HideAction { |
||||||
|
type: typeof CONSOLE_HIDE; |
||||||
|
} |
||||||
|
|
||||||
|
interface ShowCommand { |
||||||
|
type: typeof CONSOLE_SHOW_COMMAND; |
||||||
|
text: string; |
||||||
|
} |
||||||
|
|
||||||
|
interface ShowFindAction { |
||||||
|
type: typeof CONSOLE_SHOW_FIND; |
||||||
|
} |
||||||
|
|
||||||
|
interface ShowErrorAction { |
||||||
|
type: typeof CONSOLE_SHOW_ERROR; |
||||||
|
text: string; |
||||||
|
} |
||||||
|
|
||||||
|
interface ShowInfoAction { |
||||||
|
type: typeof CONSOLE_SHOW_INFO; |
||||||
|
text: string; |
||||||
|
} |
||||||
|
|
||||||
|
interface HideCommandAction { |
||||||
|
type: typeof CONSOLE_HIDE_COMMAND; |
||||||
|
} |
||||||
|
|
||||||
|
interface SetConsoleTextAction { |
||||||
|
type: typeof CONSOLE_SET_CONSOLE_TEXT; |
||||||
|
consoleText: string; |
||||||
|
} |
||||||
|
|
||||||
|
interface SetCompletionsAction { |
||||||
|
type: typeof CONSOLE_SET_COMPLETIONS; |
||||||
|
completions: any[]; |
||||||
|
completionSource: string; |
||||||
|
} |
||||||
|
|
||||||
|
interface CompletionNextAction { |
||||||
|
type: typeof CONSOLE_COMPLETION_NEXT; |
||||||
|
} |
||||||
|
|
||||||
|
interface CompletionPrevAction { |
||||||
|
type: typeof CONSOLE_COMPLETION_PREV; |
||||||
|
} |
||||||
|
|
||||||
|
export type ConsoleAction = |
||||||
|
HideAction | ShowCommand | ShowFindAction | ShowErrorAction | |
||||||
|
ShowInfoAction | HideCommandAction | SetConsoleTextAction | |
||||||
|
SetCompletionsAction | CompletionNextAction | CompletionPrevAction; |
||||||
|
|
@ -1,7 +1,14 @@ |
|||||||
import React from 'react'; |
import React from 'react'; |
||||||
import PropTypes from 'prop-types'; |
import PropTypes from 'prop-types'; |
||||||
|
|
||||||
const CompletionItem = (props) => { |
interface Props { |
||||||
|
highlight: boolean; |
||||||
|
caption?: string; |
||||||
|
url?: string; |
||||||
|
icon?: string; |
||||||
|
} |
||||||
|
|
||||||
|
const CompletionItem = (props: Props) => { |
||||||
let className = 'vimvixen-console-completion-item'; |
let className = 'vimvixen-console-completion-item'; |
||||||
if (props.highlight) { |
if (props.highlight) { |
||||||
className += ' vimvixen-completion-selected'; |
className += ' vimvixen-completion-selected'; |
@ -1,14 +1,13 @@ |
|||||||
import React from 'react'; |
import React from 'react'; |
||||||
import PropTypes from 'prop-types'; |
|
||||||
|
|
||||||
const CompletionTitle = (props) => { |
interface Props { |
||||||
|
title: string; |
||||||
|
} |
||||||
|
|
||||||
|
const CompletionTitle = (props: Props) => { |
||||||
return <li className='vimvixen-console-completion-title'> |
return <li className='vimvixen-console-completion-title'> |
||||||
{props.title} |
{props.title} |
||||||
</li>; |
</li>; |
||||||
}; |
}; |
||||||
|
|
||||||
CompletionTitle.propTypes = { |
|
||||||
title: PropTypes.string, |
|
||||||
}; |
|
||||||
|
|
||||||
export default CompletionTitle; |
export default CompletionTitle; |
@ -0,0 +1,6 @@ |
|||||||
|
export default interface Mark { |
||||||
|
x: number; |
||||||
|
y: number; |
||||||
|
// eslint-disable-next-line semi
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,32 @@ |
|||||||
|
import { Message, valueOf } from '../shared/messages'; |
||||||
|
|
||||||
|
export type WebMessageSender = Window | MessagePort | ServiceWorker | null; |
||||||
|
export type WebExtMessageSender = browser.runtime.MessageSender; |
||||||
|
|
||||||
|
export default class MessageListener { |
||||||
|
onWebMessage( |
||||||
|
listener: (msg: Message, sender: WebMessageSender) => void, |
||||||
|
) { |
||||||
|
window.addEventListener('message', (event: MessageEvent) => { |
||||||
|
let sender = event.source; |
||||||
|
let message = null; |
||||||
|
try { |
||||||
|
message = JSON.parse(event.data); |
||||||
|
} catch (e) { |
||||||
|
// ignore unexpected message
|
||||||
|
return; |
||||||
|
} |
||||||
|
listener(message, sender); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
onBackgroundMessage( |
||||||
|
listener: (msg: Message, sender: WebExtMessageSender) => any, |
||||||
|
) { |
||||||
|
browser.runtime.onMessage.addListener( |
||||||
|
(msg: any, sender: WebExtMessageSender) => { |
||||||
|
listener(valueOf(msg), sender); |
||||||
|
}, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -1,19 +0,0 @@ |
|||||||
import messages from 'shared/messages'; |
|
||||||
import actions from 'content/actions'; |
|
||||||
|
|
||||||
const enable = () => setEnabled(true); |
|
||||||
|
|
||||||
const disable = () => setEnabled(false); |
|
||||||
|
|
||||||
const setEnabled = async(enabled) => { |
|
||||||
await browser.runtime.sendMessage({ |
|
||||||
type: messages.ADDON_ENABLED_RESPONSE, |
|
||||||
enabled, |
|
||||||
}); |
|
||||||
return { |
|
||||||
type: actions.ADDON_SET_ENABLED, |
|
||||||
enabled, |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
|
||||||
export { enable, disable, setEnabled }; |
|
@ -0,0 +1,19 @@ |
|||||||
|
import * as messages from '../../shared/messages'; |
||||||
|
import * as actions from './index'; |
||||||
|
|
||||||
|
const enable = (): Promise<actions.AddonAction> => setEnabled(true); |
||||||
|
|
||||||
|
const disable = (): Promise<actions.AddonAction> => setEnabled(false); |
||||||
|
|
||||||
|
const setEnabled = async(enabled: boolean): Promise<actions.AddonAction> => { |
||||||
|
await browser.runtime.sendMessage({ |
||||||
|
type: messages.ADDON_ENABLED_RESPONSE, |
||||||
|
enabled, |
||||||
|
}); |
||||||
|
return { |
||||||
|
type: actions.ADDON_SET_ENABLED, |
||||||
|
enabled, |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
export { enable, disable, setEnabled }; |
@ -1,68 +0,0 @@ |
|||||||
//
|
|
||||||
// window.find(aString, aCaseSensitive, aBackwards, aWrapAround,
|
|
||||||
// aWholeWord, aSearchInFrames);
|
|
||||||
//
|
|
||||||
// NOTE: window.find is not standard API
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/Window/find
|
|
||||||
|
|
||||||
import messages from 'shared/messages'; |
|
||||||
import actions from 'content/actions'; |
|
||||||
import * as consoleFrames from '../console-frames'; |
|
||||||
|
|
||||||
const find = (string, backwards) => { |
|
||||||
let caseSensitive = false; |
|
||||||
let wrapScan = true; |
|
||||||
|
|
||||||
|
|
||||||
// NOTE: aWholeWord dows not implemented, and aSearchInFrames does not work
|
|
||||||
// because of same origin policy
|
|
||||||
let found = window.find(string, caseSensitive, backwards, wrapScan); |
|
||||||
if (found) { |
|
||||||
return found; |
|
||||||
} |
|
||||||
window.getSelection().removeAllRanges(); |
|
||||||
return window.find(string, caseSensitive, backwards, wrapScan); |
|
||||||
}; |
|
||||||
|
|
||||||
const findNext = async(currentKeyword, reset, backwards) => { |
|
||||||
if (reset) { |
|
||||||
window.getSelection().removeAllRanges(); |
|
||||||
} |
|
||||||
|
|
||||||
let keyword = currentKeyword; |
|
||||||
if (currentKeyword) { |
|
||||||
browser.runtime.sendMessage({ |
|
||||||
type: messages.FIND_SET_KEYWORD, |
|
||||||
keyword: currentKeyword, |
|
||||||
}); |
|
||||||
} else { |
|
||||||
keyword = await browser.runtime.sendMessage({ |
|
||||||
type: messages.FIND_GET_KEYWORD, |
|
||||||
}); |
|
||||||
} |
|
||||||
if (!keyword) { |
|
||||||
return consoleFrames.postError('No previous search keywords'); |
|
||||||
} |
|
||||||
let found = find(keyword, backwards); |
|
||||||
if (found) { |
|
||||||
consoleFrames.postInfo('Pattern found: ' + keyword); |
|
||||||
} else { |
|
||||||
consoleFrames.postError('Pattern not found: ' + keyword); |
|
||||||
} |
|
||||||
|
|
||||||
return { |
|
||||||
type: actions.FIND_SET_KEYWORD, |
|
||||||
keyword, |
|
||||||
found, |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
|
||||||
const next = (currentKeyword, reset) => { |
|
||||||
return findNext(currentKeyword, reset, false); |
|
||||||
}; |
|
||||||
|
|
||||||
const prev = (currentKeyword, reset) => { |
|
||||||
return findNext(currentKeyword, reset, true); |
|
||||||
}; |
|
||||||
|
|
||||||
export { next, prev }; |
|
@ -0,0 +1,100 @@ |
|||||||
|
//
|
||||||
|
// window.find(aString, aCaseSensitive, aBackwards, aWrapAround,
|
||||||
|
// aWholeWord, aSearchInFrames);
|
||||||
|
//
|
||||||
|
// NOTE: window.find is not standard API
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/Window/find
|
||||||
|
|
||||||
|
import * as messages from '../../shared/messages'; |
||||||
|
import * as actions from './index'; |
||||||
|
import * as consoleFrames from '../console-frames'; |
||||||
|
|
||||||
|
interface MyWindow extends Window { |
||||||
|
find( |
||||||
|
aString: string, |
||||||
|
aCaseSensitive?: boolean, |
||||||
|
aBackwards?: boolean, |
||||||
|
aWrapAround?: boolean, |
||||||
|
aWholeWord?: boolean, |
||||||
|
aSearchInFrames?: boolean, |
||||||
|
aShowDialog?: boolean): boolean; |
||||||
|
} |
||||||
|
|
||||||
|
// eslint-disable-next-line no-var, vars-on-top, init-declarations
|
||||||
|
declare var window: MyWindow; |
||||||
|
|
||||||
|
const find = (str: string, backwards: boolean): boolean => { |
||||||
|
let caseSensitive = false; |
||||||
|
let wrapScan = true; |
||||||
|
|
||||||
|
|
||||||
|
// NOTE: aWholeWord dows not implemented, and aSearchInFrames does not work
|
||||||
|
// because of same origin policy
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-extra-parens
|
||||||
|
let found = window.find(str, caseSensitive, backwards, wrapScan); |
||||||
|
if (found) { |
||||||
|
return found; |
||||||
|
} |
||||||
|
let sel = window.getSelection(); |
||||||
|
if (sel) { |
||||||
|
sel.removeAllRanges(); |
||||||
|
} |
||||||
|
|
||||||
|
// eslint-disable-next-line no-extra-parens
|
||||||
|
return window.find(str, caseSensitive, backwards, wrapScan); |
||||||
|
}; |
||||||
|
|
||||||
|
// eslint-disable-next-line max-statements
|
||||||
|
const findNext = async( |
||||||
|
currentKeyword: string, reset: boolean, backwards: boolean, |
||||||
|
): Promise<actions.FindAction> => { |
||||||
|
if (reset) { |
||||||
|
let sel = window.getSelection(); |
||||||
|
if (sel) { |
||||||
|
sel.removeAllRanges(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let keyword = currentKeyword; |
||||||
|
if (currentKeyword) { |
||||||
|
browser.runtime.sendMessage({ |
||||||
|
type: messages.FIND_SET_KEYWORD, |
||||||
|
keyword: currentKeyword, |
||||||
|
}); |
||||||
|
} else { |
||||||
|
keyword = await browser.runtime.sendMessage({ |
||||||
|
type: messages.FIND_GET_KEYWORD, |
||||||
|
}); |
||||||
|
} |
||||||
|
if (!keyword) { |
||||||
|
await consoleFrames.postError('No previous search keywords'); |
||||||
|
return { type: actions.NOOP }; |
||||||
|
} |
||||||
|
let found = find(keyword, backwards); |
||||||
|
if (found) { |
||||||
|
consoleFrames.postInfo('Pattern found: ' + keyword); |
||||||
|
} else { |
||||||
|
consoleFrames.postError('Pattern not found: ' + keyword); |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
type: actions.FIND_SET_KEYWORD, |
||||||
|
keyword, |
||||||
|
found, |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
const next = ( |
||||||
|
currentKeyword: string, reset: boolean, |
||||||
|
): Promise<actions.FindAction> => { |
||||||
|
return findNext(currentKeyword, reset, false); |
||||||
|
}; |
||||||
|
|
||||||
|
const prev = ( |
||||||
|
currentKeyword: string, reset: boolean, |
||||||
|
): Promise<actions.FindAction> => { |
||||||
|
return findNext(currentKeyword, reset, true); |
||||||
|
}; |
||||||
|
|
||||||
|
export { next, prev }; |
@ -1,31 +0,0 @@ |
|||||||
export default { |
|
||||||
// Enable/disable
|
|
||||||
ADDON_SET_ENABLED: 'addon.set.enabled', |
|
||||||
|
|
||||||
// Settings
|
|
||||||
SETTING_SET: 'setting.set', |
|
||||||
|
|
||||||
// User input
|
|
||||||
INPUT_KEY_PRESS: 'input.key.press', |
|
||||||
INPUT_CLEAR_KEYS: 'input.clear.keys', |
|
||||||
|
|
||||||
// Completion
|
|
||||||
COMPLETION_SET_ITEMS: 'completion.set.items', |
|
||||||
COMPLETION_SELECT_NEXT: 'completions.select.next', |
|
||||||
COMPLETION_SELECT_PREV: 'completions.select.prev', |
|
||||||
|
|
||||||
// Follow
|
|
||||||
FOLLOW_CONTROLLER_ENABLE: 'follow.controller.enable', |
|
||||||
FOLLOW_CONTROLLER_DISABLE: 'follow.controller.disable', |
|
||||||
FOLLOW_CONTROLLER_KEY_PRESS: 'follow.controller.key.press', |
|
||||||
FOLLOW_CONTROLLER_BACKSPACE: 'follow.controller.backspace', |
|
||||||
|
|
||||||
// Find
|
|
||||||
FIND_SET_KEYWORD: 'find.set.keyword', |
|
||||||
|
|
||||||
// Mark
|
|
||||||
MARK_START_SET: 'mark.start.set', |
|
||||||
MARK_START_JUMP: 'mark.start.jump', |
|
||||||
MARK_CANCEL: 'mark.cancel', |
|
||||||
MARK_SET_LOCAL: 'mark.set.local', |
|
||||||
}; |
|
@ -0,0 +1,122 @@ |
|||||||
|
import Redux from 'redux'; |
||||||
|
import Settings from '../../shared/Settings'; |
||||||
|
import * as keyUtils from '../../shared/utils/keys'; |
||||||
|
|
||||||
|
// Enable/disable
|
||||||
|
export const ADDON_SET_ENABLED = 'addon.set.enabled'; |
||||||
|
|
||||||
|
// Find
|
||||||
|
export const FIND_SET_KEYWORD = 'find.set.keyword'; |
||||||
|
|
||||||
|
// Settings
|
||||||
|
export const SETTING_SET = 'setting.set'; |
||||||
|
|
||||||
|
// User input
|
||||||
|
export const INPUT_KEY_PRESS = 'input.key.press'; |
||||||
|
export const INPUT_CLEAR_KEYS = 'input.clear.keys'; |
||||||
|
|
||||||
|
// Completion
|
||||||
|
export const COMPLETION_SET_ITEMS = 'completion.set.items'; |
||||||
|
export const COMPLETION_SELECT_NEXT = 'completions.select.next'; |
||||||
|
export const COMPLETION_SELECT_PREV = 'completions.select.prev'; |
||||||
|
|
||||||
|
// Follow
|
||||||
|
export const FOLLOW_CONTROLLER_ENABLE = 'follow.controller.enable'; |
||||||
|
export const FOLLOW_CONTROLLER_DISABLE = 'follow.controller.disable'; |
||||||
|
export const FOLLOW_CONTROLLER_KEY_PRESS = 'follow.controller.key.press'; |
||||||
|
export const FOLLOW_CONTROLLER_BACKSPACE = 'follow.controller.backspace'; |
||||||
|
|
||||||
|
// Mark
|
||||||
|
export const MARK_START_SET = 'mark.start.set'; |
||||||
|
export const MARK_START_JUMP = 'mark.start.jump'; |
||||||
|
export const MARK_CANCEL = 'mark.cancel'; |
||||||
|
export const MARK_SET_LOCAL = 'mark.set.local'; |
||||||
|
|
||||||
|
export const NOOP = 'noop'; |
||||||
|
|
||||||
|
export interface AddonSetEnabledAction extends Redux.Action { |
||||||
|
type: typeof ADDON_SET_ENABLED; |
||||||
|
enabled: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
export interface FindSetKeywordAction extends Redux.Action { |
||||||
|
type: typeof FIND_SET_KEYWORD; |
||||||
|
keyword: string; |
||||||
|
found: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
export interface SettingSetAction extends Redux.Action { |
||||||
|
type: typeof SETTING_SET; |
||||||
|
settings: Settings, |
||||||
|
} |
||||||
|
|
||||||
|
export interface InputKeyPressAction extends Redux.Action { |
||||||
|
type: typeof INPUT_KEY_PRESS; |
||||||
|
key: keyUtils.Key; |
||||||
|
} |
||||||
|
|
||||||
|
export interface InputClearKeysAction extends Redux.Action { |
||||||
|
type: typeof INPUT_CLEAR_KEYS; |
||||||
|
} |
||||||
|
|
||||||
|
export interface FollowControllerEnableAction extends Redux.Action { |
||||||
|
type: typeof FOLLOW_CONTROLLER_ENABLE; |
||||||
|
newTab: boolean; |
||||||
|
background: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
export interface FollowControllerDisableAction extends Redux.Action { |
||||||
|
type: typeof FOLLOW_CONTROLLER_DISABLE; |
||||||
|
} |
||||||
|
|
||||||
|
export interface FollowControllerKeyPressAction extends Redux.Action { |
||||||
|
type: typeof FOLLOW_CONTROLLER_KEY_PRESS; |
||||||
|
key: string; |
||||||
|
} |
||||||
|
|
||||||
|
export interface FollowControllerBackspaceAction extends Redux.Action { |
||||||
|
type: typeof FOLLOW_CONTROLLER_BACKSPACE; |
||||||
|
} |
||||||
|
|
||||||
|
export interface MarkStartSetAction extends Redux.Action { |
||||||
|
type: typeof MARK_START_SET; |
||||||
|
} |
||||||
|
|
||||||
|
export interface MarkStartJumpAction extends Redux.Action { |
||||||
|
type: typeof MARK_START_JUMP; |
||||||
|
} |
||||||
|
|
||||||
|
export interface MarkCancelAction extends Redux.Action { |
||||||
|
type: typeof MARK_CANCEL; |
||||||
|
} |
||||||
|
|
||||||
|
export interface MarkSetLocalAction extends Redux.Action { |
||||||
|
type: typeof MARK_SET_LOCAL; |
||||||
|
key: string; |
||||||
|
x: number; |
||||||
|
y: number; |
||||||
|
} |
||||||
|
|
||||||
|
export interface NoopAction extends Redux.Action { |
||||||
|
type: typeof NOOP; |
||||||
|
} |
||||||
|
|
||||||
|
export type AddonAction = AddonSetEnabledAction; |
||||||
|
export type FindAction = FindSetKeywordAction | NoopAction; |
||||||
|
export type SettingAction = SettingSetAction; |
||||||
|
export type InputAction = InputKeyPressAction | InputClearKeysAction; |
||||||
|
export type FollowAction = |
||||||
|
FollowControllerEnableAction | FollowControllerDisableAction | |
||||||
|
FollowControllerKeyPressAction | FollowControllerBackspaceAction; |
||||||
|
export type MarkAction = |
||||||
|
MarkStartSetAction | MarkStartJumpAction | |
||||||
|
MarkCancelAction | MarkSetLocalAction | NoopAction; |
||||||
|
|
||||||
|
export type Action = |
||||||
|
AddonAction | |
||||||
|
FindAction | |
||||||
|
SettingAction | |
||||||
|
InputAction | |
||||||
|
FollowAction | |
||||||
|
MarkAction | |
||||||
|
NoopAction; |
@ -1,16 +0,0 @@ |
|||||||
import actions from 'content/actions'; |
|
||||||
|
|
||||||
const keyPress = (key) => { |
|
||||||
return { |
|
||||||
type: actions.INPUT_KEY_PRESS, |
|
||||||
key, |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
|
||||||
const clearKeys = () => { |
|
||||||
return { |
|
||||||
type: actions.INPUT_CLEAR_KEYS |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
|
||||||
export { keyPress, clearKeys }; |
|
@ -0,0 +1,17 @@ |
|||||||
|
import * as actions from './index'; |
||||||
|
import * as keyUtils from '../../shared/utils/keys'; |
||||||
|
|
||||||
|
const keyPress = (key: keyUtils.Key): actions.InputAction => { |
||||||
|
return { |
||||||
|
type: actions.INPUT_KEY_PRESS, |
||||||
|
key, |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
const clearKeys = (): actions.InputAction => { |
||||||
|
return { |
||||||
|
type: actions.INPUT_CLEAR_KEYS |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
export { keyPress, clearKeys }; |
@ -1,46 +0,0 @@ |
|||||||
import actions from 'content/actions'; |
|
||||||
import messages from 'shared/messages'; |
|
||||||
|
|
||||||
const startSet = () => { |
|
||||||
return { type: actions.MARK_START_SET }; |
|
||||||
}; |
|
||||||
|
|
||||||
const startJump = () => { |
|
||||||
return { type: actions.MARK_START_JUMP }; |
|
||||||
}; |
|
||||||
|
|
||||||
const cancel = () => { |
|
||||||
return { type: actions.MARK_CANCEL }; |
|
||||||
}; |
|
||||||
|
|
||||||
const setLocal = (key, x, y) => { |
|
||||||
return { |
|
||||||
type: actions.MARK_SET_LOCAL, |
|
||||||
key, |
|
||||||
x, |
|
||||||
y, |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
|
||||||
const setGlobal = (key, x, y) => { |
|
||||||
browser.runtime.sendMessage({ |
|
||||||
type: messages.MARK_SET_GLOBAL, |
|
||||||
key, |
|
||||||
x, |
|
||||||
y, |
|
||||||
}); |
|
||||||
return { type: '' }; |
|
||||||
}; |
|
||||||
|
|
||||||
const jumpGlobal = (key) => { |
|
||||||
browser.runtime.sendMessage({ |
|
||||||
type: messages.MARK_JUMP_GLOBAL, |
|
||||||
key, |
|
||||||
}); |
|
||||||
return { type: '' }; |
|
||||||
}; |
|
||||||
|
|
||||||
export { |
|
||||||
startSet, startJump, cancel, setLocal, |
|
||||||
setGlobal, jumpGlobal, |
|
||||||
}; |
|
@ -0,0 +1,46 @@ |
|||||||
|
import * as actions from './index'; |
||||||
|
import * as messages from '../../shared/messages'; |
||||||
|
|
||||||
|
const startSet = (): actions.MarkAction => { |
||||||
|
return { type: actions.MARK_START_SET }; |
||||||
|
}; |
||||||
|
|
||||||
|
const startJump = (): actions.MarkAction => { |
||||||
|
return { type: actions.MARK_START_JUMP }; |
||||||
|
}; |
||||||
|
|
||||||
|
const cancel = (): actions.MarkAction => { |
||||||
|
return { type: actions.MARK_CANCEL }; |
||||||
|
}; |
||||||
|
|
||||||
|
const setLocal = (key: string, x: number, y: number): actions.MarkAction => { |
||||||
|
return { |
||||||
|
type: actions.MARK_SET_LOCAL, |
||||||
|
key, |
||||||
|
x, |
||||||
|
y, |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
const setGlobal = (key: string, x: number, y: number): actions.MarkAction => { |
||||||
|
browser.runtime.sendMessage({ |
||||||
|
type: messages.MARK_SET_GLOBAL, |
||||||
|
key, |
||||||
|
x, |
||||||
|
y, |
||||||
|
}); |
||||||
|
return { type: actions.NOOP }; |
||||||
|
}; |
||||||
|
|
||||||
|
const jumpGlobal = (key: string): actions.MarkAction => { |
||||||
|
browser.runtime.sendMessage({ |
||||||
|
type: messages.MARK_JUMP_GLOBAL, |
||||||
|
key, |
||||||
|
}); |
||||||
|
return { type: actions.NOOP }; |
||||||
|
}; |
||||||
|
|
||||||
|
export { |
||||||
|
startSet, startJump, cancel, setLocal, |
||||||
|
setGlobal, jumpGlobal, |
||||||
|
}; |
@ -1,37 +0,0 @@ |
|||||||
import actions from 'content/actions'; |
|
||||||
import * as keyUtils from 'shared/utils/keys'; |
|
||||||
import operations from 'shared/operations'; |
|
||||||
import messages from 'shared/messages'; |
|
||||||
|
|
||||||
const reservedKeymaps = { |
|
||||||
'<Esc>': { type: operations.CANCEL }, |
|
||||||
'<C-[>': { type: operations.CANCEL }, |
|
||||||
}; |
|
||||||
|
|
||||||
const set = (value) => { |
|
||||||
let entries = []; |
|
||||||
if (value.keymaps) { |
|
||||||
let keymaps = { ...value.keymaps, ...reservedKeymaps }; |
|
||||||
entries = Object.entries(keymaps).map((entry) => { |
|
||||||
return [ |
|
||||||
keyUtils.fromMapKeys(entry[0]), |
|
||||||
entry[1], |
|
||||||
]; |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
return { |
|
||||||
type: actions.SETTING_SET, |
|
||||||
value: { ...value, |
|
||||||
keymaps: entries, } |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
|
||||||
const load = async() => { |
|
||||||
let settings = await browser.runtime.sendMessage({ |
|
||||||
type: messages.SETTINGS_QUERY, |
|
||||||
}); |
|
||||||
return set(settings); |
|
||||||
}; |
|
||||||
|
|
||||||
export { set, load }; |
|
@ -0,0 +1,28 @@ |
|||||||
|
import * as actions from './index'; |
||||||
|
import * as operations from '../../shared/operations'; |
||||||
|
import * as messages from '../../shared/messages'; |
||||||
|
import Settings, { Keymaps } from '../../shared/Settings'; |
||||||
|
|
||||||
|
const reservedKeymaps: Keymaps = { |
||||||
|
'<Esc>': { type: operations.CANCEL }, |
||||||
|
'<C-[>': { type: operations.CANCEL }, |
||||||
|
}; |
||||||
|
|
||||||
|
const set = (settings: Settings): actions.SettingAction => { |
||||||
|
return { |
||||||
|
type: actions.SETTING_SET, |
||||||
|
settings: { |
||||||
|
...settings, |
||||||
|
keymaps: { ...settings.keymaps, ...reservedKeymaps }, |
||||||
|
} |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
const load = async(): Promise<actions.SettingAction> => { |
||||||
|
let settings = await browser.runtime.sendMessage({ |
||||||
|
type: messages.SETTINGS_QUERY, |
||||||
|
}); |
||||||
|
return set(settings); |
||||||
|
}; |
||||||
|
|
||||||
|
export { set, load }; |
@ -1,55 +0,0 @@ |
|||||||
import InputComponent from './input'; |
|
||||||
import FollowComponent from './follow'; |
|
||||||
import MarkComponent from './mark'; |
|
||||||
import KeymapperComponent from './keymapper'; |
|
||||||
import * as settingActions from 'content/actions/setting'; |
|
||||||
import messages from 'shared/messages'; |
|
||||||
import * as addonActions from '../../actions/addon'; |
|
||||||
import * as blacklists from 'shared/blacklists'; |
|
||||||
|
|
||||||
export default class Common { |
|
||||||
constructor(win, store) { |
|
||||||
const input = new InputComponent(win.document.body, store); |
|
||||||
const follow = new FollowComponent(win, store); |
|
||||||
const mark = new MarkComponent(win.document.body, store); |
|
||||||
const keymapper = new KeymapperComponent(store); |
|
||||||
|
|
||||||
input.onKey(key => follow.key(key)); |
|
||||||
input.onKey(key => mark.key(key)); |
|
||||||
input.onKey(key => keymapper.key(key)); |
|
||||||
|
|
||||||
this.win = win; |
|
||||||
this.store = store; |
|
||||||
this.prevEnabled = undefined; |
|
||||||
this.prevBlacklist = undefined; |
|
||||||
|
|
||||||
this.reloadSettings(); |
|
||||||
|
|
||||||
messages.onMessage(this.onMessage.bind(this)); |
|
||||||
} |
|
||||||
|
|
||||||
onMessage(message) { |
|
||||||
let { enabled } = this.store.getState().addon; |
|
||||||
switch (message.type) { |
|
||||||
case messages.SETTINGS_CHANGED: |
|
||||||
return this.reloadSettings(); |
|
||||||
case messages.ADDON_TOGGLE_ENABLED: |
|
||||||
this.store.dispatch(addonActions.setEnabled(!enabled)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
reloadSettings() { |
|
||||||
try { |
|
||||||
this.store.dispatch(settingActions.load()).then(({ value: settings }) => { |
|
||||||
let enabled = !blacklists.includes( |
|
||||||
settings.blacklist, this.win.location.href |
|
||||||
); |
|
||||||
this.store.dispatch(addonActions.setEnabled(enabled)); |
|
||||||
}); |
|
||||||
} catch (e) { |
|
||||||
// Sometime sendMessage fails when background script is not ready.
|
|
||||||
console.warn(e); |
|
||||||
setTimeout(() => this.reloadSettings(), 500); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,61 @@ |
|||||||
|
import InputComponent from './input'; |
||||||
|
import FollowComponent from './follow'; |
||||||
|
import MarkComponent from './mark'; |
||||||
|
import KeymapperComponent from './keymapper'; |
||||||
|
import * as settingActions from '../../actions/setting'; |
||||||
|
import * as messages from '../../../shared/messages'; |
||||||
|
import MessageListener from '../../MessageListener'; |
||||||
|
import * as addonActions from '../../actions/addon'; |
||||||
|
import * as blacklists from '../../../shared/blacklists'; |
||||||
|
import * as keys from '../../../shared/utils/keys'; |
||||||
|
import * as actions from '../../actions'; |
||||||
|
|
||||||
|
export default class Common { |
||||||
|
private win: Window; |
||||||
|
|
||||||
|
private store: any; |
||||||
|
|
||||||
|
constructor(win: Window, store: any) { |
||||||
|
const input = new InputComponent(win.document.body); |
||||||
|
const follow = new FollowComponent(win); |
||||||
|
const mark = new MarkComponent(store); |
||||||
|
const keymapper = new KeymapperComponent(store); |
||||||
|
|
||||||
|
input.onKey((key: keys.Key) => follow.key(key)); |
||||||
|
input.onKey((key: keys.Key) => mark.key(key)); |
||||||
|
input.onKey((key: keys.Key) => keymapper.key(key)); |
||||||
|
|
||||||
|
this.win = win; |
||||||
|
this.store = store; |
||||||
|
|
||||||
|
this.reloadSettings(); |
||||||
|
|
||||||
|
new MessageListener().onBackgroundMessage(this.onMessage.bind(this)); |
||||||
|
} |
||||||
|
|
||||||
|
onMessage(message: messages.Message) { |
||||||
|
let { enabled } = this.store.getState().addon; |
||||||
|
switch (message.type) { |
||||||
|
case messages.SETTINGS_CHANGED: |
||||||
|
return this.reloadSettings(); |
||||||
|
case messages.ADDON_TOGGLE_ENABLED: |
||||||
|
this.store.dispatch(addonActions.setEnabled(!enabled)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
reloadSettings() { |
||||||
|
try { |
||||||
|
this.store.dispatch(settingActions.load()) |
||||||
|
.then((action: actions.SettingAction) => { |
||||||
|
let enabled = !blacklists.includes( |
||||||
|
action.settings.blacklist, this.win.location.href |
||||||
|
); |
||||||
|
this.store.dispatch(addonActions.setEnabled(enabled)); |
||||||
|
}); |
||||||
|
} catch (e) { |
||||||
|
// Sometime sendMessage fails when background script is not ready.
|
||||||
|
console.warn(e); |
||||||
|
setTimeout(() => this.reloadSettings(), 500); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,74 +0,0 @@ |
|||||||
import * as markActions from 'content/actions/mark'; |
|
||||||
import * as scrolls from 'content/scrolls'; |
|
||||||
import * as consoleFrames from 'content/console-frames'; |
|
||||||
import * as properties from 'shared/settings/properties'; |
|
||||||
|
|
||||||
const cancelKey = (key) => { |
|
||||||
return key.key === 'Esc' || key.key === '[' && key.ctrlKey; |
|
||||||
}; |
|
||||||
|
|
||||||
const globalKey = (key) => { |
|
||||||
return (/^[A-Z0-9]$/).test(key); |
|
||||||
}; |
|
||||||
|
|
||||||
export default class MarkComponent { |
|
||||||
constructor(body, store) { |
|
||||||
this.body = body; |
|
||||||
this.store = store; |
|
||||||
} |
|
||||||
|
|
||||||
// eslint-disable-next-line max-statements
|
|
||||||
key(key) { |
|
||||||
let { mark: markStage, setting } = this.store.getState(); |
|
||||||
let smoothscroll = setting.properties.smoothscroll || |
|
||||||
properties.defaults.smoothscroll; |
|
||||||
|
|
||||||
if (!markStage.setMode && !markStage.jumpMode) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
if (cancelKey(key)) { |
|
||||||
this.store.dispatch(markActions.cancel()); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
if (key.ctrlKey || key.metaKey || key.altKey) { |
|
||||||
consoleFrames.postError('Unknown mark'); |
|
||||||
} else if (globalKey(key.key) && markStage.setMode) { |
|
||||||
this.doSetGlobal(key); |
|
||||||
} else if (globalKey(key.key) && markStage.jumpMode) { |
|
||||||
this.doJumpGlobal(key); |
|
||||||
} else if (markStage.setMode) { |
|
||||||
this.doSet(key); |
|
||||||
} else if (markStage.jumpMode) { |
|
||||||
this.doJump(markStage.marks, key, smoothscroll); |
|
||||||
} |
|
||||||
|
|
||||||
this.store.dispatch(markActions.cancel()); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
doSet(key) { |
|
||||||
let { x, y } = scrolls.getScroll(); |
|
||||||
this.store.dispatch(markActions.setLocal(key.key, x, y)); |
|
||||||
} |
|
||||||
|
|
||||||
doJump(marks, key, smoothscroll) { |
|
||||||
if (!marks[key.key]) { |
|
||||||
consoleFrames.postError('Mark is not set'); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
let { x, y } = marks[key.key]; |
|
||||||
scrolls.scrollTo(x, y, smoothscroll); |
|
||||||
} |
|
||||||
|
|
||||||
doSetGlobal(key) { |
|
||||||
let { x, y } = scrolls.getScroll(); |
|
||||||
this.store.dispatch(markActions.setGlobal(key.key, x, y)); |
|
||||||
} |
|
||||||
|
|
||||||
doJumpGlobal(key) { |
|
||||||
this.store.dispatch(markActions.jumpGlobal(key.key)); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,79 @@ |
|||||||
|
import * as markActions from '../../actions/mark'; |
||||||
|
import * as scrolls from '../..//scrolls'; |
||||||
|
import * as consoleFrames from '../..//console-frames'; |
||||||
|
import * as keyUtils from '../../../shared/utils/keys'; |
||||||
|
import Mark from '../../Mark'; |
||||||
|
|
||||||
|
const cancelKey = (key: keyUtils.Key): boolean => { |
||||||
|
return key.key === 'Esc' || key.key === '[' && Boolean(key.ctrlKey); |
||||||
|
}; |
||||||
|
|
||||||
|
const globalKey = (key: string): boolean => { |
||||||
|
return (/^[A-Z0-9]$/).test(key); |
||||||
|
}; |
||||||
|
|
||||||
|
export default class MarkComponent { |
||||||
|
private store: any; |
||||||
|
|
||||||
|
constructor(store: any) { |
||||||
|
this.store = store; |
||||||
|
} |
||||||
|
|
||||||
|
// eslint-disable-next-line max-statements
|
||||||
|
key(key: keyUtils.Key) { |
||||||
|
let { mark: markState, setting } = this.store.getState(); |
||||||
|
let smoothscroll = setting.properties.smoothscroll; |
||||||
|
|
||||||
|
if (!markState.setMode && !markState.jumpMode) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (cancelKey(key)) { |
||||||
|
this.store.dispatch(markActions.cancel()); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (key.ctrlKey || key.metaKey || key.altKey) { |
||||||
|
consoleFrames.postError('Unknown mark'); |
||||||
|
} else if (globalKey(key.key) && markState.setMode) { |
||||||
|
this.doSetGlobal(key); |
||||||
|
} else if (globalKey(key.key) && markState.jumpMode) { |
||||||
|
this.doJumpGlobal(key); |
||||||
|
} else if (markState.setMode) { |
||||||
|
this.doSet(key); |
||||||
|
} else if (markState.jumpMode) { |
||||||
|
this.doJump(markState.marks, key, smoothscroll); |
||||||
|
} |
||||||
|
|
||||||
|
this.store.dispatch(markActions.cancel()); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
doSet(key: keyUtils.Key) { |
||||||
|
let { x, y } = scrolls.getScroll(); |
||||||
|
this.store.dispatch(markActions.setLocal(key.key, x, y)); |
||||||
|
} |
||||||
|
|
||||||
|
doJump( |
||||||
|
marks: { [key: string]: Mark }, |
||||||
|
key: keyUtils.Key, |
||||||
|
smoothscroll: boolean, |
||||||
|
) { |
||||||
|
if (!marks[key.key]) { |
||||||
|
consoleFrames.postError('Mark is not set'); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
let { x, y } = marks[key.key]; |
||||||
|
scrolls.scrollTo(x, y, smoothscroll); |
||||||
|
} |
||||||
|
|
||||||
|
doSetGlobal(key: keyUtils.Key) { |
||||||
|
let { x, y } = scrolls.getScroll(); |
||||||
|
this.store.dispatch(markActions.setGlobal(key.key, x, y)); |
||||||
|
} |
||||||
|
|
||||||
|
doJumpGlobal(key: keyUtils.Key) { |
||||||
|
this.store.dispatch(markActions.jumpGlobal(key.key)); |
||||||
|
} |
||||||
|
} |
@ -1,41 +0,0 @@ |
|||||||
import * as findActions from 'content/actions/find'; |
|
||||||
import messages from 'shared/messages'; |
|
||||||
|
|
||||||
export default class FindComponent { |
|
||||||
constructor(win, store) { |
|
||||||
this.win = win; |
|
||||||
this.store = store; |
|
||||||
|
|
||||||
messages.onMessage(this.onMessage.bind(this)); |
|
||||||
} |
|
||||||
|
|
||||||
onMessage(message) { |
|
||||||
switch (message.type) { |
|
||||||
case messages.CONSOLE_ENTER_FIND: |
|
||||||
return this.start(message.text); |
|
||||||
case messages.FIND_NEXT: |
|
||||||
return this.next(); |
|
||||||
case messages.FIND_PREV: |
|
||||||
return this.prev(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
start(text) { |
|
||||||
let state = this.store.getState().find; |
|
||||||
|
|
||||||
if (text.length === 0) { |
|
||||||
return this.store.dispatch(findActions.next(state.keyword, true)); |
|
||||||
} |
|
||||||
return this.store.dispatch(findActions.next(text, true)); |
|
||||||
} |
|
||||||
|
|
||||||
next() { |
|
||||||
let state = this.store.getState().find; |
|
||||||
return this.store.dispatch(findActions.next(state.keyword, false)); |
|
||||||
} |
|
||||||
|
|
||||||
prev() { |
|
||||||
let state = this.store.getState().find; |
|
||||||
return this.store.dispatch(findActions.prev(state.keyword, false)); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,46 @@ |
|||||||
|
import * as findActions from '../../actions/find'; |
||||||
|
import * as messages from '../../../shared/messages'; |
||||||
|
import MessageListener from '../../MessageListener'; |
||||||
|
|
||||||
|
export default class FindComponent { |
||||||
|
private store: any; |
||||||
|
|
||||||
|
constructor(store: any) { |
||||||
|
this.store = store; |
||||||
|
|
||||||
|
new MessageListener().onWebMessage(this.onMessage.bind(this)); |
||||||
|
} |
||||||
|
|
||||||
|
onMessage(message: messages.Message) { |
||||||
|
switch (message.type) { |
||||||
|
case messages.CONSOLE_ENTER_FIND: |
||||||
|
return this.start(message.text); |
||||||
|
case messages.FIND_NEXT: |
||||||
|
return this.next(); |
||||||
|
case messages.FIND_PREV: |
||||||
|
return this.prev(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
start(text: string) { |
||||||
|
let state = this.store.getState().find; |
||||||
|
|
||||||
|
if (text.length === 0) { |
||||||
|
return this.store.dispatch( |
||||||
|
findActions.next(state.keyword as string, true)); |
||||||
|
} |
||||||
|
return this.store.dispatch(findActions.next(text, true)); |
||||||
|
} |
||||||
|
|
||||||
|
next() { |
||||||
|
let state = this.store.getState().find; |
||||||
|
return this.store.dispatch( |
||||||
|
findActions.next(state.keyword as string, false)); |
||||||
|
} |
||||||
|
|
||||||
|
prev() { |
||||||
|
let state = this.store.getState().find; |
||||||
|
return this.store.dispatch( |
||||||
|
findActions.prev(state.keyword as string, false)); |
||||||
|
} |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Reference in new issue