import { Adt, Arr, type Cell, Fun, type Optional } from '@ephox/katamari'; import { type SugarElement, Traverse } from '@ephox/sugar'; import type { DebuggerLogger } from '../debugging/Debugging'; import * as DescribedHandler from './DescribedHandler'; import type { ElementAndHandler, UidAndHandler } from './EventRegistry'; import * as EventSource from './EventSource'; import { type EventFormat, fromExternal, fromSource } from './SimulatedEvent'; type LookupEvent = (eventName: string, target: SugarElement) => Optional; export interface TriggerAdt { fold: ( stopped: () => T, resume: (elem: SugarElement) => T, complete: () => T ) => T; match: (branches: { stopped: () => T; resume: (elem: SugarElement) => T; complete: () => T; }) => T; log: (label: string) => void; } const adt: { stopped: () => TriggerAdt; resume: (elem: SugarElement) => TriggerAdt; complete: () => TriggerAdt; } = Adt.generate([ { stopped: [ ] }, { resume: [ 'element' ] }, { complete: [ ] } ]); const doTriggerHandler = (lookup: LookupEvent, eventType: string, rawEvent: EventFormat, target: SugarElement, source: Cell>, logger: DebuggerLogger): TriggerAdt => { const handler = lookup(eventType, target); const simulatedEvent = fromSource(rawEvent, source); return handler.fold(() => { // No handler, so complete. logger.logEventNoHandlers(eventType, target); return adt.complete(); }, (handlerInfo) => { const descHandler = handlerInfo.descHandler; const eventHandler = DescribedHandler.getCurried(descHandler); eventHandler(simulatedEvent); // Now, check if the event was stopped. if (simulatedEvent.isStopped()) { logger.logEventStopped(eventType, handlerInfo.element, descHandler.purpose); return adt.stopped(); } else if (simulatedEvent.isCut()) { logger.logEventCut(eventType, handlerInfo.element, descHandler.purpose); return adt.complete(); } else { return Traverse.parent(handlerInfo.element).fold(() => { logger.logNoParent(eventType, handlerInfo.element, descHandler.purpose); // No parent, so complete. return adt.complete(); }, (parent) => { logger.logEventResponse(eventType, handlerInfo.element, descHandler.purpose); // Resume at parent return adt.resume(parent); }); } }); }; const doTriggerOnUntilStopped = (lookup: LookupEvent, eventType: string, rawEvent: EventFormat, rawTarget: SugarElement, source: Cell>, logger: DebuggerLogger): boolean => doTriggerHandler(lookup, eventType, rawEvent, rawTarget, source, logger).fold( // stopped. Fun.always, // Go again. (parent) => doTriggerOnUntilStopped(lookup, eventType, rawEvent, parent, source, logger), // completed Fun.never ); const triggerHandler = (lookup: LookupEvent, eventType: string, rawEvent: T, target: SugarElement, logger: DebuggerLogger): TriggerAdt => { const source = EventSource.derive(rawEvent, target); return doTriggerHandler(lookup, eventType, rawEvent, target, source, logger); }; const broadcast = (listeners: UidAndHandler[], rawEvent: EventFormat, _logger?: DebuggerLogger): boolean => { const simulatedEvent = fromExternal(rawEvent); Arr.each(listeners, (listener) => { const descHandler = listener.descHandler; const handler = DescribedHandler.getCurried(descHandler); handler(simulatedEvent); }); return simulatedEvent.isStopped(); }; const triggerUntilStopped = (lookup: LookupEvent, eventType: string, rawEvent: EventFormat, logger: DebuggerLogger): boolean => triggerOnUntilStopped(lookup, eventType, rawEvent, rawEvent.target, logger); const triggerOnUntilStopped = (lookup: LookupEvent, eventType: string, rawEvent: EventFormat, rawTarget: SugarElement, logger: DebuggerLogger): boolean => { const source = EventSource.derive(rawEvent, rawTarget); return doTriggerOnUntilStopped(lookup, eventType, rawEvent, rawTarget, source, logger); }; export { triggerHandler, triggerUntilStopped, triggerOnUntilStopped, broadcast };