import { Objects } from '@ephox/boulder';
import { Fun } from '@ephox/katamari';
import { TransformFind } from '@ephox/sugar';
import * as EventRoot from '../../alien/EventRoot';
import * as EventHandler from '../../construct/EventHandler';
import * as AlloyTriggers from './AlloyTriggers';
import * as SystemEvents from './SystemEvents';
const derive = (configs) => Objects.wrapAll(configs);
// const combine = (configs...);
const abort = (name, predicate) => {
    return {
        key: name,
        value: EventHandler.nu({
            abort: predicate
        })
    };
};
const can = (name, predicate) => {
    return {
        key: name,
        value: EventHandler.nu({
            can: predicate
        })
    };
};
const preventDefault = (name) => {
    return {
        key: name,
        value: EventHandler.nu({
            run: (component, simulatedEvent) => {
                simulatedEvent.event.prevent();
            }
        })
    };
};
const run = (name, handler) => {
    return {
        key: name,
        value: EventHandler.nu({
            run: handler
        })
    };
};
// Extra can be used when your handler needs more context, and is declared in one spot
// It's really just convenient partial application.
const runActionExtra = (name, action, extra) => {
    return {
        key: name,
        value: EventHandler.nu({
            run: (component, simulatedEvent) => {
                action.apply(undefined, [component, simulatedEvent].concat(extra));
            }
        })
    };
};
const runOnName = (name) => {
    return (handler) => run(name, handler);
};
const runOnSourceName = (name) => {
    return (handler) => ({
        key: name,
        value: EventHandler.nu({
            run: (component, simulatedEvent) => {
                if (EventRoot.isSource(component, simulatedEvent)) {
                    handler(component, simulatedEvent);
                }
            }
        })
    });
};
const redirectToUid = (name, uid) => {
    return run(name, (component, simulatedEvent) => {
        component.getSystem().getByUid(uid).each((redirectee) => {
            AlloyTriggers.dispatchEvent(redirectee, redirectee.element, name, simulatedEvent);
        });
    });
};
const redirectToPart = (name, detail, partName) => {
    const uid = detail.partUids[partName];
    return redirectToUid(name, uid);
};
const runWithTarget = (name, f) => {
    return run(name, (component, simulatedEvent) => {
        const ev = simulatedEvent.event;
        const target = component.getSystem().getByDom(ev.target).getOrThunk(
        // If we don't find an alloy component for the target, I guess we go up the tree
        // until we find an alloy component? Performance concern?
        // TODO: Write tests for this.
        () => {
            const closest = TransformFind.closest(ev.target, (el) => component.getSystem().getByDom(el).toOptional(), Fun.never);
            // If we still found nothing ... fire on component itself;
            return closest.getOr(component);
        });
        f(component, target, simulatedEvent);
    });
};
const cutter = (name) => {
    return run(name, (component, simulatedEvent) => {
        simulatedEvent.cut();
    });
};
const stopper = (name) => {
    return run(name, (component, simulatedEvent) => {
        simulatedEvent.stop();
    });
};
const runOnSource = (name, f) => {
    return runOnSourceName(name)(f);
};
const runOnAttached = runOnSourceName(SystemEvents.attachedToDom());
const runOnDetached = runOnSourceName(SystemEvents.detachedFromDom());
const runOnInit = runOnSourceName(SystemEvents.systemInit());
const runOnExecute = runOnName(SystemEvents.execute());
export { derive, run, preventDefault, runActionExtra, runOnAttached, runOnDetached, runOnSource, runOnInit, runOnExecute, redirectToUid, redirectToPart, runWithTarget, abort, can, cutter, stopper };
//# sourceMappingURL=AlloyEvents.js.map