export const throttle = (delay, callback, options) => {
    const {
        noTrailing = false,
        noLeading = false,
        debounceMode = undefined
    } = options || {};
    let timeoutID;
    let cancelled = false;
    let lastExec = 0;
    function clearExistingTimeout() {
        if (timeoutID) {
            clearTimeout(timeoutID);
        }
    }
    function cancel(options) {
        const { upcomingOnly = false } = options || {};
        clearExistingTimeout();
        cancelled = !upcomingOnly;
    }
    function wrapper(...arguments_) {
        let self = this;
        let elapsed = Date.now() - lastExec;
        if (cancelled) {
            return;
        }
        function exec() {
            lastExec = Date.now();
            callback.apply(self, arguments_);
        }
        function clear() {
            timeoutID = undefined;
        }
        if (!noLeading && debounceMode && !timeoutID) {

            exec();
        }
        clearExistingTimeout();
        if (debounceMode === undefined && elapsed > delay) {
            if (noLeading) {

                lastExec = Date.now();
                if (!noTrailing) {
                    timeoutID = setTimeout(debounceMode ? clear : exec, delay);
                }
            } else {

                exec();
            }
        } else if (noTrailing !== true) {

            timeoutID = setTimeout(
                debounceMode ? clear : exec,
                debounceMode === undefined ? delay - elapsed : delay
            );
        }
    }
    wrapper.cancel = cancel;
    return wrapper;
}
export const debounce = (delay, callback, options) => {
    const { atBegin = false } = options || {};
    return throttle(delay, callback, { debounceMode: atBegin !== false });
}