import { injectable, inject } from 'tsyringe'; import FollowKeyRepository from '../repositories/FollowKeyRepository'; import FollowMasterRepository from '../repositories/FollowMasterRepository'; import FollowSlaveClient from '../client/FollowSlaveClient'; import FollowSlaveClientFactory from '../client/FollowSlaveClientFactory'; import SettingRepository from '../repositories/SettingRepository'; import HintKeyProducer from './HintKeyProducer'; @injectable() export default class FollowMasterUseCase { // TODO Make repository private producer: HintKeyProducer | null; constructor( @inject('FollowKeyRepository') private followKeyRepository: FollowKeyRepository, @inject('FollowMasterRepository') private followMasterRepository: FollowMasterRepository, @inject('SettingRepository') private settingRepository: SettingRepository, @inject('FollowSlaveClientFactory') private followSlaveClientFactory: FollowSlaveClientFactory, ) { this.producer = null; } startFollow(newTab: boolean, background: boolean): void { const hintchars = this.settingRepository.get().properties.hintchars; this.producer = new HintKeyProducer(hintchars); this.followKeyRepository.clearKeys(); this.followMasterRepository.setCurrentFollowMode(newTab, background); const viewWidth = window.top.innerWidth; const viewHeight = window.top.innerHeight; this.followSlaveClientFactory.create(window.top).requestHintCount( { width: viewWidth, height: viewHeight }, { x: 0, y: 0 }, ); const frameElements = window.document.querySelectorAll('iframe'); for (let i = 0; i < frameElements.length; ++i) { const ele = frameElements[i] as HTMLFrameElement | HTMLIFrameElement; const { left: frameX, top: frameY } = ele.getBoundingClientRect(); const client = this.followSlaveClientFactory.create(ele.contentWindow!!); client.requestHintCount( { width: viewWidth, height: viewHeight }, { x: frameX, y: frameY }, ); } } // eslint-disable-next-line max-statements createSlaveHints(count: number, sender: Window): void { const produced = []; for (let i = 0; i < count; ++i) { const tag = this.producer!!.produce(); produced.push(tag); this.followMasterRepository.addTag(tag); } const doc = window.document; const viewWidth = window.innerWidth || doc.documentElement.clientWidth; const viewHeight = window.innerHeight || doc.documentElement.clientHeight; let pos = { x: 0, y: 0 }; if (sender !== window) { const frameElements = window.document.querySelectorAll('iframe'); const ele = Array.from(frameElements).find(e => e.contentWindow === sender); if (!ele) { // elements of the sender is gone return; } const { left: frameX, top: frameY } = ele.getBoundingClientRect(); pos = { x: frameX, y: frameY }; } const client = this.followSlaveClientFactory.create(sender); client.createHints( { width: viewWidth, height: viewHeight }, pos, produced, ); } cancelFollow(): void { this.followMasterRepository.clearTags(); this.broadcastToSlaves((client) => { client.clearHints(); }); } filter(prefix: string): void { this.broadcastToSlaves((client) => { client.filterHints(prefix); }); } activate(tag: string): void { this.followMasterRepository.clearTags(); const newTab = this.followMasterRepository.getCurrentNewTabMode(); const background = this.followMasterRepository.getCurrentBackgroundMode(); this.broadcastToSlaves((client) => { client.activateIfExists(tag, newTab, background); client.clearHints(); }); } enqueue(key: string): void { switch (key) { case 'Enter': this.activate(this.getCurrentTag()); return; case 'Esc': this.cancelFollow(); return; case 'Backspace': case 'Delete': this.followKeyRepository.popKey(); this.filter(this.getCurrentTag()); return; } this.followKeyRepository.pushKey(key); const tag = this.getCurrentTag(); const matched = this.followMasterRepository.getTagsByPrefix(tag); if (matched.length === 0) { this.cancelFollow(); } else if (matched.length === 1) { this.activate(tag); } else { this.filter(tag); } } private broadcastToSlaves(handler: (client: FollowSlaveClient) => void) { const allFrames = [window.self].concat(Array.from(window.frames as any)); const clients = allFrames.map(w => this.followSlaveClientFactory.create(w)); for (const client of clients) { handler(client); } } private getCurrentTag(): string { return this.followKeyRepository.getKeys().join(''); } }