aboutsummaryrefslogtreecommitdiff
path: root/src/content/usecases/FollowMasterUseCase.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/content/usecases/FollowMasterUseCase.ts')
-rw-r--r--src/content/usecases/FollowMasterUseCase.ts150
1 files changed, 150 insertions, 0 deletions
diff --git a/src/content/usecases/FollowMasterUseCase.ts b/src/content/usecases/FollowMasterUseCase.ts
new file mode 100644
index 0000000..c2c6835
--- /dev/null
+++ b/src/content/usecases/FollowMasterUseCase.ts
@@ -0,0 +1,150 @@
+import FollowKeyRepository, { FollowKeyRepositoryImpl }
+ from '../repositories/FollowKeyRepository';
+import FollowMasterRepository, { FollowMasterRepositoryImpl }
+ from '../repositories/FollowMasterRepository';
+import FollowSlaveClient, { FollowSlaveClientImpl }
+ from '../client/FollowSlaveClient';
+import HintKeyProducer from '../hint-key-producer';
+import SettingRepository, { SettingRepositoryImpl }
+ from '../repositories/SettingRepository';
+
+export default class FollowMasterUseCase {
+ private followKeyRepository: FollowKeyRepository;
+
+ private followMasterRepository: FollowMasterRepository;
+
+ private settingRepository: SettingRepository;
+
+ // TODO Make repository
+ private producer: HintKeyProducer | null;
+
+ constructor({
+ followKeyRepository = new FollowKeyRepositoryImpl(),
+ followMasterRepository = new FollowMasterRepositoryImpl(),
+ settingRepository = new SettingRepositoryImpl(),
+ } = {}) {
+ this.followKeyRepository = followKeyRepository;
+ this.followMasterRepository = followMasterRepository;
+ this.settingRepository = settingRepository;
+ this.producer = null;
+ }
+
+ startFollow(newTab: boolean, background: boolean): void {
+ let hintchars = this.settingRepository.get().properties.hintchars;
+ this.producer = new HintKeyProducer(hintchars);
+
+ this.followKeyRepository.clearKeys();
+ this.followMasterRepository.setCurrentFollowMode(newTab, background);
+
+ let viewWidth = window.top.innerWidth;
+ let viewHeight = window.top.innerHeight;
+ new FollowSlaveClientImpl(window.top).requestHintCount(
+ { width: viewWidth, height: viewHeight },
+ { x: 0, y: 0 },
+ );
+
+ let frameElements = window.document.querySelectorAll('iframe');
+ for (let i = 0; i < frameElements.length; ++i) {
+ let ele = frameElements[i] as HTMLFrameElement | HTMLIFrameElement;
+ let { left: frameX, top: frameY } = ele.getBoundingClientRect();
+ new FollowSlaveClientImpl(ele.contentWindow!!).requestHintCount(
+ { width: viewWidth, height: viewHeight },
+ { x: frameX, y: frameY },
+ );
+ }
+ }
+
+ // eslint-disable-next-line max-statements
+ createSlaveHints(count: number, sender: Window): void {
+ let produced = [];
+ for (let i = 0; i < count; ++i) {
+ let tag = this.producer!!.produce();
+ produced.push(tag);
+ this.followMasterRepository.addTag(tag);
+ }
+
+ let doc = window.document;
+ let viewWidth = window.innerWidth || doc.documentElement.clientWidth;
+ let viewHeight = window.innerHeight || doc.documentElement.clientHeight;
+ let pos = { x: 0, y: 0 };
+ if (sender !== window) {
+ let frameElements = window.document.querySelectorAll('iframe');
+ let ele = Array.from(frameElements).find(e => e.contentWindow === sender);
+ if (!ele) {
+ // elements of the sender is gone
+ return;
+ }
+ let { left: frameX, top: frameY } = ele.getBoundingClientRect();
+ pos = { x: frameX, y: frameY };
+ }
+ new FollowSlaveClientImpl(sender).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();
+
+ let newTab = this.followMasterRepository.getCurrentNewTabMode();
+ let 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);
+
+ let tag = this.getCurrentTag();
+ let 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) {
+ let allFrames = [window.self].concat(Array.from(window.frames as any));
+ let clients = allFrames.map(frame => new FollowSlaveClientImpl(frame));
+ for (let client of clients) {
+ handler(client);
+ }
+ }
+
+ private getCurrentTag(): string {
+ return this.followKeyRepository.getKeys().join('');
+ }
+}