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