Path: blob/main/core/injected-scripts/MouseEvents.ts
1028 views
// eslint-disable-next-line max-classes-per-file1import IMouseUpResult from '@secret-agent/interfaces/IMouseUpResult';23// eslint-disable-next-line @typescript-eslint/no-unused-vars4class MouseEvents {5private static pendingMouseover?: EventResolvable<MouseEvent, boolean>;6private static pendingMouseup?: EventResolvable<MouseEvent, IMouseUpResult>;78public static listenFor(mouseEvent: 'mouseup' | 'mouseover', nodeId: number) {9this.clearEvent(mouseEvent);1011const node = NodeTracker.getWatchedNodeWithId(nodeId);12if (!node) throw new Error('Node not found');13if (!node.isConnected) {14throw new Error(15`Target node for "${mouseEvent}" is not connected to the DOM, and won't receive mouse events.`,16);17}1819if (mouseEvent === 'mouseover') {20this.pendingMouseover = new EventResolvable(nodeId, () => {21this.pendingMouseover?.resolve(true);22});2324node.addEventListener('mouseover', this.pendingMouseover.onEventFn, {25once: true,26});27} else {28this.pendingMouseup = new EventResolvable(nodeId, event => {29const targetNodeId = event.target ? NodeTracker.watchNode(event.target as Node) : undefined;30const relatedTargetNodeId = event.relatedTarget31? NodeTracker.watchNode(event.relatedTarget as Node)32: undefined;3334const result: IMouseUpResult = {35pageX: event.pageX - window.scrollX,36pageY: event.pageY - window.scrollY,37targetNodeId,38relatedTargetNodeId,39didClickLocation: node.contains(event.target as Node) || node === event.target,40};4142if (!result.didClickLocation) {43// @ts-ignore44result.targetNodePreview = generateNodePreview(event.target);45// @ts-ignore46result.expectedNodePreview = generateNodePreview(node);47const expectedNode = new ObjectAtPath();48expectedNode.objectAtPath = node;49result.expectedNodeVisibility = expectedNode.getComputedVisibility();50}5152this.pendingMouseup?.resolve(result);53});5455window.addEventListener('mouseup', this.pendingMouseup.onEventFn, {56once: true,57});58}59}6061public static didTrigger(mouseEvent: 'mouseup' | 'mouseover', nodeId: number) {62try {63const pendingEvent = mouseEvent === 'mouseup' ? this.pendingMouseup : this.pendingMouseover;64if (pendingEvent?.nodeId !== nodeId) {65throw new Error(`${mouseEvent.toUpperCase()} listener not found`);66}6768return pendingEvent.trigger;69} finally {70this.clearEvent(mouseEvent);71}72}7374private static clearEvent(mouseEvent: 'mouseup' | 'mouseover') {75if (mouseEvent === 'mouseover') this.clearPendingMouseover();76if (mouseEvent === 'mouseup') this.clearPendingMouseup();77}7879private static clearPendingMouseover() {80if (this.pendingMouseover) {81const node = NodeTracker.getWatchedNodeWithId(this.pendingMouseover.nodeId);82node?.removeEventListener('mouseover', this.pendingMouseover.onEventFn);83this.pendingMouseover = null;84}85}8687private static clearPendingMouseup() {88if (this.pendingMouseup) {89window.removeEventListener('mouseup', this.pendingMouseup.onEventFn);90this.pendingMouseup = null;91}92}93}9495class EventResolvable<EventType, T> {96trigger: T;9798constructor(readonly nodeId, readonly onEventFn: (ev: EventType) => void) {}99100resolve(result: T) {101this.trigger = result;102}103}104105106