commit
05ef6a8ca3
205 changed files with 5117 additions and 2774 deletions
@ -1,11 +1,13 @@ |
||||
import AddonEnabledUseCase from '../usecases/AddonEnabledUseCase'; |
||||
|
||||
export default class AddonEnabledController { |
||||
private addonEnabledUseCase: AddonEnabledUseCase; |
||||
|
||||
constructor() { |
||||
this.addonEnabledUseCase = new AddonEnabledUseCase(); |
||||
} |
||||
|
||||
indicate(enabled) { |
||||
indicate(enabled: boolean): Promise<any> { |
||||
return this.addonEnabledUseCase.indicate(enabled); |
||||
} |
||||
} |
@ -1,15 +1,17 @@ |
||||
import FindUseCase from '../usecases/FindUseCase'; |
||||
|
||||
export default class FindController { |
||||
private findUseCase: FindUseCase; |
||||
|
||||
constructor() { |
||||
this.findUseCase = new FindUseCase(); |
||||
} |
||||
|
||||
getKeyword() { |
||||
getKeyword(): Promise<string> { |
||||
return this.findUseCase.getKeyword(); |
||||
} |
||||
|
||||
setKeyword(keyword) { |
||||
setKeyword(keyword: string): Promise<any> { |
||||
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 ContentMessageClient from '../infrastructures/ContentMessageClient'; |
||||
import Settings from '../../shared/Settings'; |
||||
|
||||
export default class SettingController { |
||||
private settingUseCase: SettingUseCase; |
||||
|
||||
private contentMessageClient: ContentMessageClient; |
||||
|
||||
constructor() { |
||||
this.settingUseCase = new SettingUseCase(); |
||||
this.contentMessageClient = new ContentMessageClient(); |
||||
} |
||||
|
||||
getSetting() { |
||||
getSetting(): Promise<Settings> { |
||||
return this.settingUseCase.get(); |
||||
} |
||||
|
||||
async reload() { |
||||
async reload(): Promise<any> { |
||||
await this.settingUseCase.reload(); |
||||
this.contentMessageClient.broadcastSettingsChanged(); |
||||
} |
@ -1,11 +1,13 @@ |
||||
import VersionUseCase from '../usecases/VersionUseCase'; |
||||
|
||||
export default class VersionController { |
||||
private versionUseCase: VersionUseCase; |
||||
|
||||
constructor() { |
||||
this.versionUseCase = new VersionUseCase(); |
||||
} |
||||
|
||||
notify() { |
||||
this.versionUseCase.notify(); |
||||
notify(): Promise<void> { |
||||
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 { |
||||
showCommand(tabId, command) { |
||||
showCommand(tabId: number, command: string): Promise<any> { |
||||
return browser.tabs.sendMessage(tabId, { |
||||
type: messages.CONSOLE_SHOW_COMMAND, |
||||
command, |
||||
}); |
||||
} |
||||
|
||||
showFind(tabId) { |
||||
showFind(tabId: number): Promise<any> { |
||||
return browser.tabs.sendMessage(tabId, { |
||||
type: messages.CONSOLE_SHOW_FIND |
||||
}); |
||||
} |
||||
|
||||
showInfo(tabId, message) { |
||||
showInfo(tabId: number, message: string): Promise<any> { |
||||
return browser.tabs.sendMessage(tabId, { |
||||
type: messages.CONSOLE_SHOW_INFO, |
||||
text: message, |
||||
}); |
||||
} |
||||
|
||||
showError(tabId, message) { |
||||
showError(tabId: number, message: string): Promise<any> { |
||||
return browser.tabs.sendMessage(tabId, { |
||||
type: messages.CONSOLE_SHOW_ERROR, |
||||
text: message, |
||||
}); |
||||
} |
||||
|
||||
hide(tabId) { |
||||
hide(tabId: number): Promise<any> { |
||||
return browser.tabs.sendMessage(tabId, { |
||||
type: messages.CONSOLE_HIDE, |
||||
}); |
@ -1,12 +1,12 @@ |
||||
export default class IndicatorPresenter { |
||||
indicate(enabled) { |
||||
indicate(enabled: boolean): Promise<void> { |
||||
let path = enabled |
||||
? 'resources/enabled_32x32.png' |
||||
: 'resources/disabled_32x32.png'; |
||||
return browser.browserAction.setIcon({ path }); |
||||
} |
||||
|
||||
onClick(listener) { |
||||
onClick(listener: (arg: browser.tabs.Tab) => void): void { |
||||
browser.browserAction.onClicked.addListener(listener); |
||||
} |
||||
} |
@ -1,5 +1,5 @@ |
||||
export default class WindowPresenter { |
||||
create(url) { |
||||
create(url: string): Promise<browser.windows.Window> { |
||||
return browser.windows.create({ url }); |
||||
} |
||||
} |
@ -1,5 +1,7 @@ |
||||
export default class BookmarkRepository { |
||||
async create(title, url) { |
||||
async create( |
||||
title: string, url: string |
||||
): Promise<browser.bookmarks.BookmarkTreeNode> { |
||||
let item = await browser.bookmarks.create({ |
||||
type: 'bookmark', |
||||
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 { |
||||
async load() { |
||||
async load(): Promise<SettingData | null> { |
||||
let { settings } = await browser.storage.local.get('settings'); |
||||
if (!settings) { |
||||
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'; |
||||
|
||||
export default class LinkUseCase { |
||||
private tabPresenter: TabPresenter; |
||||
|
||||
constructor() { |
||||
this.settingRepository = new SettingRepository(); |
||||
this.tabPresenter = new TabPresenter(); |
||||
} |
||||
|
||||
openToTab(url, tabId) { |
||||
openToTab(url: string, tabId: number): Promise<any> { |
||||
return this.tabPresenter.open(url, tabId); |
||||
} |
||||
|
||||
openNewTab(url, openerId, background) { |
||||
openNewTab(url: string, openerId: number, background: boolean): Promise<any> { |
||||
return this.tabPresenter.create(url, { |
||||
openerTabId: openerId, active: !background |
||||
}); |
@ -1,28 +1,31 @@ |
||||
import Setting from '../domains/Setting'; |
||||
// eslint-disable-next-line max-len
|
||||
import PersistentSettingRepository from '../repositories/PersistentSettingRepository'; |
||||
import SettingRepository from '../repositories/SettingRepository'; |
||||
import { DefaultSettingData } from '../../shared/SettingData'; |
||||
import Settings from '../../shared/Settings'; |
||||
|
||||
export default class SettingUseCase { |
||||
private persistentSettingRepository: PersistentSettingRepository; |
||||
|
||||
private settingRepository: SettingRepository; |
||||
|
||||
constructor() { |
||||
this.persistentSettingRepository = new PersistentSettingRepository(); |
||||
this.settingRepository = new SettingRepository(); |
||||
} |
||||
|
||||
get() { |
||||
get(): Promise<Settings> { |
||||
return this.settingRepository.get(); |
||||
} |
||||
|
||||
async reload() { |
||||
let settings = await this.persistentSettingRepository.load(); |
||||
if (!settings) { |
||||
settings = Setting.defaultSettings(); |
||||
async reload(): Promise<Settings> { |
||||
let data = await this.persistentSettingRepository.load(); |
||||
if (!data) { |
||||
data = DefaultSettingData; |
||||
} |
||||
|
||||
let value = settings.value(); |
||||
|
||||
let value = data.toSettings(); |
||||
this.settingRepository.update(value); |
||||
|
||||
return value; |
||||
} |
||||
} |
@ -1,23 +1,27 @@ |
||||
import manifest from '../../../manifest.json'; |
||||
import TabPresenter from '../presenters/TabPresenter'; |
||||
import NotifyPresenter from '../presenters/NotifyPresenter'; |
||||
|
||||
export default class VersionUseCase { |
||||
private tabPresenter: TabPresenter; |
||||
|
||||
private notifyPresenter: NotifyPresenter; |
||||
|
||||
constructor() { |
||||
this.tabPresenter = new TabPresenter(); |
||||
this.notifyPresenter = new NotifyPresenter(); |
||||
} |
||||
|
||||
notify() { |
||||
notify(): Promise<void> { |
||||
let manifest = browser.runtime.getManifest(); |
||||
let title = `Vim Vixen ${manifest.version} has been installed`; |
||||
let message = 'Click here to see release notes'; |
||||
let url = this.releaseNoteUrl(manifest.version); |
||||
this.notifyPresenter.notify(title, message, () => { |
||||
return this.notifyPresenter.notify(title, message, () => { |
||||
this.tabPresenter.create(url); |
||||
}); |
||||
} |
||||
|
||||
releaseNoteUrl(version) { |
||||
releaseNoteUrl(version?: string): string { |
||||
if (version) { |
||||
return `https://github.com/ueokande/vim-vixen/releases/tag/${version}`; |
||||
} |
@ -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 PropTypes from 'prop-types'; |
||||
|
||||
const CompletionItem = (props) => { |
||||
interface Props { |
||||
highlight: boolean; |
||||
caption?: string; |
||||
url?: string; |
||||
icon?: string; |
||||
} |
||||
|
||||
const CompletionItem = (props: Props) => { |
||||
let className = 'vimvixen-console-completion-item'; |
||||
if (props.highlight) { |
||||
className += ' vimvixen-completion-selected'; |
@ -1,14 +1,13 @@ |
||||
import React from 'react'; |
||||
import PropTypes from 'prop-types'; |
||||
|
||||
const CompletionTitle = (props) => { |
||||
interface Props { |
||||
title: string; |
||||
} |
||||
|
||||
const CompletionTitle = (props: Props) => { |
||||
return <li className='vimvixen-console-completion-title'> |
||||
{props.title} |
||||
</li>; |
||||
}; |
||||
|
||||
CompletionTitle.propTypes = { |
||||
title: PropTypes.string, |
||||
}; |
||||
|
||||
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