rySelectorAll(selectors)) { target.removeEventListener(type, callback, opt); } } ); listener.uninit = () => lazy.EveryWindow.unregisterCallback(uuid, true); } else { for (const target of this.doc.querySelectorAll(selectors)) { target.addEventListener(type, callback, opt); } } listener.controller = controller; } else if (["timeout", "interval"].includes(type) && options.interval) { let interval; const abort = () => this.win.clearInterval(interval); const onInterval = () => { callback({ type, target: type }); if (type === "timeout") { abort(); } }; interval = this.win.setInterval(onInterval, options.interval); listener.callback = onInterval; listener.controller = { abort }; } this._listeners.set(params, listener); } /** * Removes a page event listener. * * @param {PageEventListenerParams} params */ off(params) { const listener = this._listeners.get(params); if (!listener) { return; } listener.uninit?.(); listener.controller?.abort(); this._listeners.delete(params); } /** * Adds a page event listener that is removed after the first event. * * @param {PageEventListenerParams} params * @param {Function} callback Function to call when event is triggered */ once(params, callback) { const wrappedCallback = (...args) => { this.off(params); callback(...args); }; this.on(params, wrappedCallback); } /** * Removes all page event listeners. */ clear() { for (const listener of this._listeners.values()) { listener.uninit?.(); listener.controller?.abort(); } this._listeners.clear(); } /** * Calls matching page event listeners. A way to dispatch a "fake" event. * * @param {PageEvent} event */ emit(event) { for (const [params, listener] of this._listeners) { if (params.type === event.type) { listener.callback(event); } } } } PK