import { SetRequired, Class } from 'type-fest';

/**
 * Parameter types for plugins
 */
declare enum ParameterType {
    BOOL = 0,
    STRING = 1,
    INT = 2,
    FLOAT = 3,
    FUNCTION = 4,
    KEY = 5,
    KEYS = 6,
    SELECT = 7,
    HTML_STRING = 8,
    IMAGE = 9,
    AUDIO = 10,
    VIDEO = 11,
    OBJECT = 12,
    COMPLEX = 13,
    TIMELINE = 14
}
type ParameterTypeMap = {
    [ParameterType.BOOL]: boolean;
    [ParameterType.STRING]: string;
    [ParameterType.INT]: number;
    [ParameterType.FLOAT]: number;
    [ParameterType.FUNCTION]: (...args: any[]) => any;
    [ParameterType.KEY]: string;
    [ParameterType.KEYS]: string[] | "ALL_KEYS" | "NO_KEYS";
    [ParameterType.SELECT]: any;
    [ParameterType.HTML_STRING]: string;
    [ParameterType.IMAGE]: string;
    [ParameterType.AUDIO]: string;
    [ParameterType.VIDEO]: string;
    [ParameterType.OBJECT]: object;
    [ParameterType.COMPLEX]: any;
    [ParameterType.TIMELINE]: any;
};
type PreloadParameterType = ParameterType.AUDIO | ParameterType.VIDEO | ParameterType.IMAGE;
type ParameterInfo = ({
    type: Exclude<ParameterType, ParameterType.COMPLEX | PreloadParameterType>;
} | {
    type: ParameterType.COMPLEX;
    nested?: ParameterInfos;
} | {
    type: PreloadParameterType;
    preload?: boolean;
}) & {
    array?: boolean;
    pretty_name?: string;
    default?: any;
};
type ParameterInfos = Record<string, ParameterInfo>;
type InferredParameter<I extends ParameterInfo> = I["array"] extends true ? Array<ParameterTypeMap[I["type"]]> : ParameterTypeMap[I["type"]];
type RequiredParameterNames<I extends ParameterInfos> = {
    [K in keyof I]: I[K]["default"] extends undefined ? K : never;
}[keyof I];
type InferredParameters<I extends ParameterInfos> = SetRequired<{
    [Property in keyof I]?: InferredParameter<I[Property]>;
}, RequiredParameterNames<I>>;
interface PluginInfo {
    name: string;
    version?: string;
    parameters: ParameterInfos;
    data?: ParameterInfos;
    citations?: Record<string, string> | string;
}
interface JsPsychPlugin<I extends PluginInfo> {
    trial(display_element: HTMLElement, trial: TrialType<I>, on_load?: () => void): void | Promise<TrialResult | void>;
    simulate?(trial: TrialType<I>, simulation_mode: SimulationMode, simulation_options: SimulationOptions, on_load?: () => void): void | Promise<TrialResult | void>;
}
type TrialType<I extends PluginInfo> = InferredParameters<I["parameters"]> & TrialDescription;

interface JsPsychExtensionInfo {
    name: string;
    version?: string;
    data?: ParameterInfos;
    citations?: Record<string, string> | string;
}
interface JsPsychExtension {
    /**
     * Called once at the start of the experiment to initialize the extension
     */
    initialize(params?: Record<string, any>): Promise<void>;
    /**
     * Called at the start of a trial, prior to invoking the plugin's trial method.
     */
    on_start(params?: Record<string, any>): void;
    /**
     * Called during a trial, after the plugin makes initial changes to the DOM.
     */
    on_load(params?: Record<string, any>): void;
    /**
     * Called at the end of the trial.
     * @returns Data to append to the trial's data object.
     */
    on_finish(params?: Record<string, any>): Record<string, any> | Promise<Record<string, any>>;
}

type GetParameterValueOptions = {
    /**
     * If true, and the retrieved parameter value is a function, invoke the function and return its
     * return value (defaults to `true`)
     */
    evaluateFunctions?: boolean;
    /**
     * Whether to fall back to parent timeline node parameters (defaults to `true`)
     */
    recursive?: boolean;
    /**
     * Whether the timeline node should cache the parameter lookup result for successive lookups,
     * including those of nested properties or array elements (defaults to `true`)
     */
    cacheResult?: boolean;
    /**
     * A function that will be invoked with the original result of the parameter value lookup.
     * Whatever it returns will subsequently be used instead of the original result. This allows to
     * modify results before they are cached.
     */
    replaceResult?: (originalResult: any) => any;
};
declare abstract class TimelineNode {
    protected readonly dependencies: TimelineNodeDependencies;
    abstract readonly description: TimelineDescription | TrialDescription | TimelineArray;
    /**
     * The globally unique trial index of this node. It is set when the node is run. Timeline nodes
     * have the same trial index as their first trial.
     */
    index?: number;
    abstract readonly parent?: Timeline;
    abstract run(): Promise<void>;
    /**
     * Returns a flat array of all currently available results of this node
     */
    abstract getResults(): TrialResult[];
    /**
     * Recursively evaluates the given timeline variable, starting at the current timeline node.
     * Returns the result, or `undefined` if the variable is neither specified in the timeline
     * description of this node, nor in the description of any parent node.
     */
    abstract evaluateTimelineVariable(variable: TimelineVariable): any;
    /**
     * Returns the most recent (child) TimelineNode. For trial nodes, this is always the trial node
     * itself since trial nodes do not have child nodes. For timeline nodes, the return value is a
     * Trial object most of the time, but it may also be a Timeline object when a timeline hasn't yet
     * instantiated its children (e.g. during initial timeline callback functions).
     */
    abstract getLatestNode(): TimelineNode;
    /**
     * Returns an active child timeline (or itself) that matches the given name, or `undefined` if no such child exists.
     */
    abstract getActiveTimelineByName(name: string): Timeline | undefined;
    protected status: TimelineNodeStatus;
    constructor(dependencies: TimelineNodeDependencies);
    getStatus(): TimelineNodeStatus;
    private parameterValueCache;
    /**
     * Initializes the parameter value cache with `this.description`. To be called by subclass
     * constructors after setting `this.description`.
     */
    protected initializeParameterValueCache(): void;
    /**
     * Resets all cached parameter values in this timeline node and all of its parents. This is
     * necessary to re-evaluate function parameters and timeline variables at each new trial.
     */
    protected resetParameterValueCache(): void;
    /**
     * Retrieves a parameter value from the description of this timeline node, recursively falling
     * back to the description of each parent timeline node unless `recursive` is set to `false`. If
     * the parameter...
     *
     * * is a timeline variable, evaluates the variable and returns the result.
     * * is not specified, returns `undefined`.
     * * is a function and `evaluateFunctions` is not set to `false`, invokes the function and returns
     *   its return value
     * * has previously been looked up, return the cached result of the previous lookup
     *
     * @param parameterPath The path of the respective parameter in the timeline node description. If
     * the path is an array, nested object properties or array items will be looked up.
     * @param options See {@link GetParameterValueOptions}
     */
    getParameterValue(parameterPath: string | string[], options?: GetParameterValueOptions): any;
    /**
     * Retrieves and evaluates the `data` parameter. It is different from other parameters in that
     * it's properties may be functions that have to be evaluated, and parent nodes' data parameter
     * properties are merged into the result.
     */
    getDataParameter(): Record<string, any> | undefined;
}

declare class Timeline extends TimelineNode {
    readonly parent?: Timeline;
    readonly children: TimelineNode[];
    readonly description: TimelineDescription;
    constructor(dependencies: TimelineNodeDependencies, description: TimelineDescription | TimelineArray, parent?: Timeline);
    private currentChild?;
    private shouldAbort;
    run(): Promise<void>;
    private onStart;
    private onFinish;
    pause(): void;
    private resumePromise;
    resume(): void;
    /**
     * If the timeline is running or paused, aborts the timeline after the current trial has completed
     */
    abort(): void;
    private instantiateChildNode;
    private currentTimelineVariables;
    private setCurrentTimelineVariablesByIndex;
    /**
     * If the timeline has timeline variables, returns the order of `timeline_variables` array indices
     * to be used, according to the timeline's `sample` setting. If the timeline has no timeline
     * variables, returns `[null]`.
     */
    private generateTimelineVariableOrder;
    /**
     * Returns the current values of all timeline variables, including those from parent timelines
     */
    getAllTimelineVariables(): Record<string, any>;
    evaluateTimelineVariable(variable: TimelineVariable): any;
    getResults(): TrialResult[];
    /**
     * Returns the naive progress of the timeline (as a fraction), without considering conditional or
     * loop functions.
     */
    getNaiveProgress(): number;
    /**
     * Recursively computes the naive number of trials in the timeline, without considering
     * conditional or loop functions.
     */
    getNaiveTrialCount(): any;
    getLatestNode(): any;
    getActiveTimelineByName(name: string): Timeline;
}

declare class Trial extends TimelineNode {
    readonly description: TrialDescription;
    readonly parent: Timeline;
    readonly pluginClass: Class<JsPsychPlugin<any>>;
    pluginInstance: JsPsychPlugin<any>;
    trialObject?: TrialDescription;
    index?: number;
    private result;
    private readonly pluginInfo;
    constructor(dependencies: TimelineNodeDependencies, description: TrialDescription, parent: Timeline);
    run(): Promise<void>;
    private executeTrial;
    private invokeTrialMethod;
    /**
     * Cleanup the trial by removing the display element and removing event listeners
     */
    private cleanupTrial;
    /**
     * Add the CSS classes from the `css_classes` parameter to the display element
     */
    private addCssClasses;
    /**
     * Removes the provided css classes from the display element
     */
    private removeCssClasses;
    private processResult;
    /**
     * Runs a callback function retrieved from a parameter value and returns its result.
     *
     * @param parameterName The name of the parameter to retrieve the callback function from.
     * @param callbackParameters The parameters (if any) to be passed to the callback function
     */
    private runParameterCallback;
    private onStart;
    private onLoad;
    private onFinish;
    evaluateTimelineVariable(variable: TimelineVariable): any;
    getParameterValue(parameterPath: string | string[], options?: GetParameterValueOptions): any;
    /**
     * Retrieves and evaluates the `simulation_options` parameter, considering nested properties and
     * global simulation options.
     */
    getSimulationOptions(): SimulationOptions;
    /**
     * Returns the result object of this trial or `undefined` if the result is not yet known or the
     * `record_data` trial parameter is `false`.
     */
    getResult(): TrialResult;
    getResults(): TrialResult[];
    /**
     * Checks that the parameters provided in the trial description align with the plugin's info
     * object, resolves missing parameter values from the parent timeline, resolves timeline variable
     * parameters, evaluates parameter functions if the expected parameter type is not `FUNCTION`, and
     * sets default values for optional parameters.
     */
    private processParameters;
    getLatestNode(): this;
    getActiveTimelineByName(name: string): Timeline | undefined;
}

/**
 * Maintains a promise and offers a function to resolve it. Whenever the promise is resolved, it is
 * replaced with a new one.
 */
declare class PromiseWrapper<ResolveType = void> {
    constructor();
    private promise;
    private resolvePromise;
    reset(): void;
    get(): Promise<ResolveType>;
    resolve(value: ResolveType): void;
}

declare class TimelineVariable {
    readonly name: string;
    constructor(name: string);
}
type Parameter<T> = T | (() => T) | TimelineVariable;
type TrialExtensionsConfiguration = Array<{
    type: Class<JsPsychExtension>;
    params?: Record<string, any>;
}>;
type SimulationMode = "visual" | "data-only";
type SimulationOptions = {
    data?: Record<string, any>;
    mode?: SimulationMode;
    simulate?: boolean;
};
type SimulationOptionsParameter = Parameter<{
    data?: Parameter<Record<string, Parameter<any>>>;
    mode?: Parameter<SimulationMode>;
    simulate?: Parameter<boolean>;
}>;
interface TrialDescription extends Record<string, any> {
    type: Parameter<Class<JsPsychPlugin<any>>>;
    /** https://www.jspsych.org/latest/overview/plugins/#the-post_trial_gap-iti-parameter */
    post_trial_gap?: Parameter<number>;
    /** https://www.jspsych.org/latest/overview/plugins/#the-save_trial_parameters-parameter */
    save_trial_parameters?: Parameter<Record<string, boolean>>;
    /**
     * Whether to include the values of timeline variables under a `timeline_variables` key. Can be
     * `true` to save the values of all timeline variables, or an array of timeline variable names to
     * only save specific timeline variables. Defaults to `false`.
     */
    save_timeline_variables?: Parameter<boolean | string[]>;
    /** https://www.jspsych.org/latest/overview/style/#using-the-css_classes-trial-parameter */
    css_classes?: Parameter<string | string[]>;
    /** https://www.jspsych.org/latest/overview/simulation/#controlling-simulation-mode-with-simulation_options */
    simulation_options?: SimulationOptionsParameter | string;
    /** https://www.jspsych.org/latest/overview/extensions/ */
    extensions?: Parameter<TrialExtensionsConfiguration>;
    /**
     * Whether to record the data of this trial. Defaults to `true`.
     */
    record_data?: Parameter<boolean>;
    /** https://www.jspsych.org/latest/overview/events/#on_start-trial */
    on_start?: (trial: any) => void;
    /** https://www.jspsych.org/latest/overview/events/#on_load */
    on_load?: () => void;
    /** https://www.jspsych.org/latest/overview/events/#on_finish-trial */
    on_finish?: (data: any) => void;
}
/** https://www.jspsych.org/latest/overview/timeline/#sampling-methods */
type SampleOptions = {
    type: "with-replacement";
    size: number;
    weights?: number[];
} | {
    type: "without-replacement";
    size: number;
} | {
    type: "fixed-repetitions";
    size: number;
} | {
    type: "alternate-groups";
    groups: number[][];
    randomize_group_order?: boolean;
} | {
    type: "custom";
    fn: (ids: number[]) => number[];
};
type TimelineArray = Array<TimelineDescription | TrialDescription | TimelineArray>;
interface TimelineDescription extends Record<string, any> {
    timeline: TimelineArray;
    timeline_variables?: Record<string, any>[];
    name?: string;
    /** https://www.jspsych.org/latest/overview/timeline/#repeating-a-set-of-trials */
    repetitions?: number;
    /** https://www.jspsych.org/latest/overview/timeline/#looping-timelines */
    loop_function?: (data: any) => boolean;
    /** https://www.jspsych.org/latest/overview/timeline/#conditional-timelines */
    conditional_function?: () => boolean;
    /** https://www.jspsych.org/latest/overview/timeline/#random-orders-of-trials */
    randomize_order?: boolean;
    /** https://www.jspsych.org/latest/overview/timeline/#sampling-methods */
    sample?: SampleOptions;
    /** https://www.jspsych.org/latest/overview/events/#on_timeline_start */
    on_timeline_start?: () => void;
    /** https://www.jspsych.org/latest/overview/events/#on_timeline_finish */
    on_timeline_finish?: () => void;
}
declare enum TimelineNodeStatus {
    PENDING = 0,
    RUNNING = 1,
    PAUSED = 2,
    COMPLETED = 3,
    ABORTED = 4
}
/**
 * Functions and options needed by `TimelineNode`s, provided by the `JsPsych` instance. This
 * approach allows to keep the public `JsPsych` API slim and decouples the `JsPsych` and timeline
 * node classes, simplifying unit testing.
 */
interface TimelineNodeDependencies {
    /**
     * Called at the start of a trial, prior to invoking the plugin's trial method.
     */
    onTrialStart: (trial: Trial) => void;
    /**
     * Called when a trial's result data is available, before invoking `onTrialFinished()`.
     */
    onTrialResultAvailable: (trial: Trial) => void;
    /**
     * Called after a trial has finished.
     */
    onTrialFinished: (trial: Trial) => void;
    /**
     * Invoke `on_start` extension callbacks according to `extensionsConfiguration`
     */
    runOnStartExtensionCallbacks(extensionsConfiguration: TrialExtensionsConfiguration): void;
    /**
     * Invoke `on_load` extension callbacks according to `extensionsConfiguration`
     */
    runOnLoadExtensionCallbacks(extensionsConfiguration: TrialExtensionsConfiguration): void;
    /**
     * Invoke `on_finish` extension callbacks according to `extensionsConfiguration` and return a
     * joint extensions result object
     */
    runOnFinishExtensionCallbacks(extensionsConfiguration: TrialExtensionsConfiguration): Promise<Record<string, any>>;
    /**
     * Returns the simulation mode or `undefined`, if the experiment is not running in simulation
     * mode.
     */
    getSimulationMode(): SimulationMode | undefined;
    /**
     * Returns the global simulation options as passed to `jsPsych.simulate()`
     */
    getGlobalSimulationOptions(): Record<string, SimulationOptionsParameter>;
    /**
     * Given a plugin class, create a new instance of it and return it.
     */
    instantiatePlugin: <Info extends PluginInfo>(pluginClass: Class<JsPsychPlugin<Info>>) => JsPsychPlugin<Info>;
    /**
     * Return JsPsych's display element so it can be provided to plugins
     */
    getDisplayElement: () => HTMLElement;
    /**
     * Return the default inter-trial interval as provided to `initJsPsych()`
     */
    getDefaultIti: () => number;
    /**
     * A `PromiseWrapper` whose promise is resolved with result data whenever `jsPsych.finishTrial()`
     * is called.
     */
    finishTrialPromise: PromiseWrapper<TrialResult | void>;
    /**
     * Clear all of the timeouts
     */
    clearAllTimeouts: () => void;
}
type TrialResult = Record<string, any>;

declare class DataColumn {
    values: any[];
    constructor(values?: any[]);
    sum(): number;
    mean(): number;
    median(): any;
    min(): any;
    max(): any;
    count(): number;
    variance(): number;
    sd(): number;
    frequencies(): {};
    all(eval_fn: any): boolean;
    subset(eval_fn: any): DataColumn;
}

declare class DataCollection {
    private trials;
    constructor(data?: any[]);
    push(new_data: any): this;
    join(other_data_collection: DataCollection): this;
    top(): DataCollection;
    /**
     * Queries the first n elements in a collection of trials.
     *
     * @param n A positive integer of elements to return. A value of
     *          n that is less than 1 will throw an error.
     *
     * @return First n objects of a collection of trials. If fewer than
     *         n trials are available, the trials.length elements will
     *         be returned.
     *
     */
    first(n?: number): DataCollection;
    /**
     * Queries the last n elements in a collection of trials.
     *
     * @param n A positive integer of elements to return. A value of
     *          n that is less than 1 will throw an error.
     *
     * @return Last n objects of a collection of trials. If fewer than
     *         n trials are available, the trials.length elements will
     *         be returned.
     *
     */
    last(n?: number): DataCollection;
    values(): any[];
    count(): number;
    readOnly(): DataCollection;
    addToAll(properties: any): this;
    addToLast(properties: any): this;
    filter(filters: any): DataCollection;
    filterCustom(fn: any): DataCollection;
    filterColumns(columns: Array<string>): DataCollection;
    select(column: any): DataColumn;
    ignore(columns: any): DataCollection;
    uniqueNames(): any[];
    csv(): string;
    json(pretty?: boolean): string;
    localSave(format: any, filename: any): void;
}

type InteractionEvent = "blur" | "focus" | "fullscreenenter" | "fullscreenexit";
interface InteractionRecord {
    event: InteractionEvent;
    trial: number;
    time: number;
}
/**
 * Functions and options needed by the `JsPsychData` module
 */
interface JsPsychDataDependencies {
    /**
     * Returns progress information for interaction records.
     */
    getProgress: () => {
        trial: number;
        time: number;
    };
    onInteractionRecordAdded: (record: InteractionRecord) => void;
    getDisplayElement: () => HTMLElement;
}
declare class JsPsychData {
    private dependencies;
    private results;
    private resultToTrialMap;
    /** Browser interaction event data */
    private interactionRecords;
    /** Data properties for all trials */
    private dataProperties;
    private query_string;
    constructor(dependencies: JsPsychDataDependencies);
    reset(): void;
    get(): DataCollection;
    getInteractionData(): DataCollection;
    write(trial: Trial): void;
    addProperties(properties: any): void;
    addDataToLastTrial(data: any): void;
    getLastTrialData(): DataCollection;
    getLastTimelineData(): DataCollection;
    displayData(format?: string): void;
    urlVariables(): any;
    getURLVariable(whichvar: any): any;
    private addInteractionRecord;
    private interactionListeners;
    createInteractionListeners(): void;
    removeInteractionListeners(): void;
}

type KeyboardListener = (e: KeyboardEvent) => void;
type ValidResponses = string[] | "ALL_KEYS" | "NO_KEYS";
interface GetKeyboardResponseOptions {
    callback_function: any;
    valid_responses?: ValidResponses;
    rt_method?: "performance" | "audio";
    persist?: boolean;
    audio_context?: AudioContext;
    audio_context_start_time?: number;
    allow_held_key?: boolean;
    minimum_valid_rt?: number;
}
declare class KeyboardListenerAPI {
    private getRootElement;
    private areResponsesCaseSensitive;
    private minimumValidRt;
    constructor(getRootElement: () => Element | undefined, areResponsesCaseSensitive?: boolean, minimumValidRt?: number);
    private listeners;
    private heldKeys;
    private areRootListenersRegistered;
    /**
     * If not previously done and `this.getRootElement()` returns an element, adds the root key
     * listeners to that element.
     */
    private registerRootListeners;
    private rootKeydownListener;
    private toLowerCaseIfInsensitive;
    private rootKeyupListener;
    private isResponseValid;
    getKeyboardResponse({ callback_function, valid_responses, rt_method, persist, audio_context, audio_context_start_time, allow_held_key, minimum_valid_rt, }: GetKeyboardResponseOptions): KeyboardListener;
    cancelKeyboardResponse(listener: KeyboardListener): void;
    cancelAllKeyboardResponses(): void;
    compareKeys(key1: string | null, key2: string | null): boolean;
}

interface AudioPlayerOptions {
    useWebAudio: boolean;
    audioContext?: AudioContext;
}
interface AudioPlayerInterface {
    load(): Promise<void>;
    play(): void;
    stop(): void;
    addEventListener(eventName: string, callback: EventListenerOrEventListenerObject): void;
    removeEventListener(eventName: string, callback: EventListenerOrEventListenerObject): void;
}
declare class AudioPlayer implements AudioPlayerInterface {
    private audio;
    private webAudioBuffer;
    private audioContext;
    private useWebAudio;
    private src;
    constructor(src: string, options?: AudioPlayerOptions);
    load(): Promise<void>;
    play(): void;
    stop(): void;
    addEventListener(eventName: string, callback: EventListenerOrEventListenerObject): void;
    removeEventListener(eventName: string, callback: EventListenerOrEventListenerObject): void;
    private getAudioSourceNode;
    private preloadWebAudio;
    private preloadHTMLAudio;
}

declare class MediaAPI {
    useWebaudio: boolean;
    constructor(useWebaudio: boolean);
    private video_buffers;
    getVideoBuffer(videoID: string): any;
    private context;
    private audio_buffers;
    audioContext(): AudioContext;
    getAudioPlayer(audioID: string): Promise<AudioPlayer>;
    private preload_requests;
    private img_cache;
    preloadAudio(files: any, callback_complete?: () => void, callback_load?: (filepath: string) => void, callback_error?: (error: any) => void): void;
    preloadImages(images: any, callback_complete?: () => void, callback_load?: (filepath: any) => void, callback_error?: (error_msg: any) => void): void;
    preloadVideo(videos: any, callback_complete?: () => void, callback_load?: (filepath: any) => void, callback_error?: (error_msg: any) => void): void;
    private preloadMap;
    getAutoPreloadList(timeline_description: any[]): {
        images: string[];
        audio: string[];
        video: string[];
    };
    cancelPreloads(): void;
    private microphone_recorder;
    initializeMicrophoneRecorder(stream: MediaStream): void;
    getMicrophoneRecorder(): MediaRecorder;
    private camera_stream;
    private camera_recorder;
    initializeCameraRecorder(stream: MediaStream, opts?: MediaRecorderOptions): void;
    getCameraStream(): MediaStream;
    getCameraRecorder(): MediaRecorder;
}

declare class SimulationAPI {
    private getDisplayContainerElement;
    private setJsPsychTimeout;
    constructor(getDisplayContainerElement: () => HTMLElement, setJsPsychTimeout: (callback: () => void, delay: number) => number);
    dispatchEvent(event: Event): void;
    /**
     * Dispatches a `keydown` event for the specified key
     * @param key Character code (`.key` property) for the key to press.
     */
    keyDown(key: string): void;
    /**
     * Dispatches a `keyup` event for the specified key
     * @param key Character code (`.key` property) for the key to press.
     */
    keyUp(key: string): void;
    /**
     * Dispatches a `keydown` and `keyup` event in sequence to simulate pressing a key.
     * @param key Character code (`.key` property) for the key to press.
     * @param delay Length of time to wait (ms) before executing action
     */
    pressKey(key: string, delay?: number): void;
    /**
     * Dispatches `mousedown`, `mouseup`, and `click` events on the target element
     * @param target The element to click
     * @param delay Length of time to wait (ms) before executing action
     */
    clickTarget(target: Element, delay?: number): void;
    /**
     * Sets the value of a target text input
     * @param target A text input element to fill in
     * @param text Text to input
     * @param delay Length of time to wait (ms) before executing action
     */
    fillTextInput(target: HTMLInputElement, text: string, delay?: number): void;
    /**
     * Picks a valid key from `choices`, taking into account jsPsych-specific
     * identifiers like "NO_KEYS" and "ALL_KEYS".
     * @param choices Which keys are valid.
     * @returns A key selected at random from the valid keys.
     */
    getValidKey(choices: "NO_KEYS" | "ALL_KEYS" | Array<string> | Array<Array<string>>): any;
    mergeSimulationData(default_data: any, simulation_options: any): any;
    ensureSimulationDataConsistency(trial: any, data: any): void;
}

/**
 * A class that provides a wrapper around the global setTimeout and clearTimeout functions.
 */
declare class TimeoutAPI {
    private timeout_handlers;
    /**
     * Calls a function after a specified delay, in milliseconds.
     * @param callback The function to call after the delay.
     * @param delay The number of milliseconds to wait before calling the function.
     * @returns A handle that can be used to clear the timeout with clearTimeout.
     */
    setTimeout(callback: () => void, delay: number): number;
    /**
     * Clears all timeouts that have been created with setTimeout.
     */
    clearAllTimeouts(): void;
}

declare function createJointPluginAPIObject(jsPsych: JsPsych): KeyboardListenerAPI & TimeoutAPI & MediaAPI & SimulationAPI;
type PluginAPI = ReturnType<typeof createJointPluginAPIObject>;

/**
 * Uses the `seedrandom` package to replace Math.random() with a seedable PRNG.
 *
 * @param seed An optional seed. If none is given, a random seed will be generated.
 * @returns The seed value.
 */
declare function setSeed(seed?: string): string;
declare function repeat(array: any, repetitions: any, unpack?: boolean): any;
declare function shuffle<T>(array: Array<T>): T[];
declare function shuffleNoRepeats<T>(arr: Array<T>, equalityTest: (a: T, b: T) => boolean): T[];
declare function shuffleAlternateGroups<T extends any[]>(arr_groups: Array<T>, random_group_order?: boolean): any[];
declare function sampleWithoutReplacement<T>(arr: Array<T>, size: number): T[];
declare function sampleWithReplacement<T>(arr: Array<T>, size: number, weights?: number[]): any[];
declare function factorial(factors: Record<string, any>, repetitions?: number, unpack?: boolean): any;
declare function randomID(length?: number): string;
/**
 * Generate a random integer from `lower` to `upper`, inclusive of both end points.
 * @param lower The lowest value it is possible to generate
 * @param upper The highest value it is possible to generate
 * @returns A random integer
 */
declare function randomInt(lower: number, upper: number): number;
/**
 * Generates a random sample from a Bernoulli distribution.
 * @param p The probability of sampling 1.
 * @returns 0, with probability 1-p, or 1, with probability p.
 */
declare function sampleBernoulli(p: number): 0 | 1;
declare function sampleNormal(mean: number, standard_deviation: number): number;
declare function sampleExponential(rate: number): number;
declare function sampleExGaussian(mean: number, standard_deviation: number, rate: number, positive?: boolean): number;
type RandomWordsOptions = {
    min?: number;
    max?: number;
    exactly?: number;
    maxLength?: number;
    wordsPerString?: number;
    seperator?: string;
    formatter?: (word: string, index: number) => string;
    join?: string;
};
type RandomWordsResult<T extends RandomWordsOptions> = T extends {
    join: string;
} ? string : string[];
/**
 * Generate one or more random words.
 *
 * This is a wrapper function for the {@link https://www.npmjs.com/package/random-words `random-words` npm package}.
 *
 * @param opts An object with optional properties `min`, `max`, `exactly`,
 * `join`, `maxLength`, `wordsPerString`, `separator`, and `formatter`.
 *
 * @returns An array of words or a single string, depending on parameter choices.
 */
declare function randomWords<T extends RandomWordsOptions>(opts: T): RandomWordsResult<T>;

declare const randomization_factorial: typeof factorial;
declare const randomization_randomID: typeof randomID;
declare const randomization_randomInt: typeof randomInt;
declare const randomization_randomWords: typeof randomWords;
declare const randomization_repeat: typeof repeat;
declare const randomization_sampleBernoulli: typeof sampleBernoulli;
declare const randomization_sampleExGaussian: typeof sampleExGaussian;
declare const randomization_sampleExponential: typeof sampleExponential;
declare const randomization_sampleNormal: typeof sampleNormal;
declare const randomization_sampleWithReplacement: typeof sampleWithReplacement;
declare const randomization_sampleWithoutReplacement: typeof sampleWithoutReplacement;
declare const randomization_setSeed: typeof setSeed;
declare const randomization_shuffle: typeof shuffle;
declare const randomization_shuffleAlternateGroups: typeof shuffleAlternateGroups;
declare const randomization_shuffleNoRepeats: typeof shuffleNoRepeats;
declare namespace randomization {
  export { randomization_factorial as factorial, randomization_randomID as randomID, randomization_randomInt as randomInt, randomization_randomWords as randomWords, randomization_repeat as repeat, randomization_sampleBernoulli as sampleBernoulli, randomization_sampleExGaussian as sampleExGaussian, randomization_sampleExponential as sampleExponential, randomization_sampleNormal as sampleNormal, randomization_sampleWithReplacement as sampleWithReplacement, randomization_sampleWithoutReplacement as sampleWithoutReplacement, randomization_setSeed as setSeed, randomization_shuffle as shuffle, randomization_shuffleAlternateGroups as shuffleAlternateGroups, randomization_shuffleNoRepeats as shuffleNoRepeats };
}

interface turkInformation {
    /**
     * Is the experiment being loaded in preview mode on Mechanical Turk?
     */
    previewMode: boolean;
    /**
     * Is the experiment being loaded outside of the Mechanical Turk environment?
     */
    outsideTurk: boolean;
    /**
     * The HIT ID.
     */
    hitId: string;
    /**
     * The Assignment ID.
     */
    assignmentId: string;
    /**
     * The worker ID.
     */
    workerId: string;
    /**
     * URL for submission of the HIT.
     */
    turkSubmitTo: string;
}
/**
 * Gets information about the Mechanical Turk Environment, HIT, Assignment, and Worker
 * by parsing the URL variables that Mechanical Turk generates.
 * @returns An object containing information about the Mechanical Turk Environment, HIT, Assignment, and Worker.
 */
declare function turkInfo(): turkInformation;
/**
 * Send data to Mechnical Turk for storage.
 * @param data An object containing `key:value` pairs to send to Mechanical Turk. Values
 * cannot contain nested objects, arrays, or functions.
 * @returns Nothing
 */
declare function submitToTurk(data: any): void;

declare const turk_submitToTurk: typeof submitToTurk;
declare const turk_turkInfo: typeof turkInfo;
declare namespace turk {
  export { turk_submitToTurk as submitToTurk, turk_turkInfo as turkInfo };
}

/**
 * Finds all of the unique items in an array.
 * @param arr The array to extract unique values from
 * @returns An array with one copy of each unique item in `arr`
 */
declare function unique(arr: Array<any>): any[];
declare function deepCopy(obj: any): any;
/**
 * Merges two objects, recursively.
 * @param obj1 Object to merge
 * @param obj2 Object to merge
 */
declare function deepMerge(obj1: any, obj2: any): any;

declare const utils_deepCopy: typeof deepCopy;
declare const utils_deepMerge: typeof deepMerge;
declare const utils_unique: typeof unique;
declare namespace utils {
  export { utils_deepCopy as deepCopy, utils_deepMerge as deepMerge, utils_unique as unique };
}

/**
 * Maintains a visual progress bar using HTML and CSS
 */
declare class ProgressBar {
    private readonly containerElement;
    private readonly message;
    constructor(containerElement: HTMLDivElement, message: string | ((progress: number) => string));
    private _progress;
    private innerDiv;
    private messageSpan;
    /** Adds the progress bar HTML code into `this.containerElement` */
    private setupElements;
    /** Updates the progress bar according to `this.progress` */
    private update;
    /**
     * The bar's current position as a number in the closed interval [0, 1]. Set this to update the
     * progress bar accordingly.
     */
    set progress(progress: number);
    get progress(): number;
}

declare class JsPsych {
    turk: typeof turk;
    randomization: typeof randomization;
    utils: typeof utils;
    data: JsPsychData;
    pluginAPI: PluginAPI;
    version(): string;
    private citation;
    /** Options */
    private options;
    /** Experiment timeline */
    private timeline?;
    /** Target DOM element */
    private displayContainerElement;
    private displayElement;
    /** Time that the experiment began */
    private experimentStartTime;
    /**
     * Whether the page is retrieved directly via the `file://` protocol (true) or hosted on a web
     * server (false)
     */
    private isFileProtocolUsed;
    /** The simulation mode (if the experiment is being simulated) */
    private simulationMode?;
    /** Simulation options passed in via `simulate()` */
    private simulationOptions;
    private extensionManager;
    constructor(options?: any);
    private endMessage?;
    /**
     * Starts an experiment using the provided timeline and returns a promise that is resolved when
     * the experiment is finished.
     *
     * @param timeline The timeline to be run
     */
    run(timeline: TimelineDescription | TimelineArray): Promise<void>;
    simulate(timeline: any[], simulation_mode?: "data-only" | "visual", simulation_options?: {}): Promise<void>;
    progressBar?: ProgressBar;
    getProgress(): {
        total_trials: any;
        current_trial_global: any;
        percent_complete: number;
    };
    getStartTime(): Date;
    getTotalTime(): number;
    getDisplayElement(): HTMLElement;
    getDisplayContainerElement(): HTMLElement;
    abortExperiment(endMessage?: string, data?: {}): void;
    abortCurrentTimeline(): void;
    /**
     * Aborts a named timeline. The timeline must be currently running in order to abort it.
     *
     * @param name The name of the timeline to abort. Timelines can be given names by setting the `name` parameter in the description of the timeline.
     */
    abortTimelineByName(name: string): void;
    getCurrentTrial(): TrialDescription;
    getInitSettings(): any;
    timelineVariable(variableName: string): TimelineVariable;
    evaluateTimelineVariable(variableName: string): any;
    pauseExperiment(): void;
    resumeExperiment(): void;
    getSafeModeStatus(): boolean;
    getTimeline(): TimelineArray;
    /**
     * Prints out a string containing citations for the jsPsych library and all input plugins/extensions in the specified format.
     * If called without input, prints citation for jsPsych library.
     *
     * @param plugins The plugins/extensions to generate citations for. Always prints the citation for the jsPsych library at the top.
     * @param format The desired output citation format. Currently supports "apa" and "bibtex".
     * @returns String containing citations separated with newline character.
     */
    getCitations(plugins?: Array<Class<JsPsychPlugin<any>> | Class<JsPsychExtension>>, format?: "apa" | "bibtex"): string;
    get extensions(): Record<string, JsPsychExtension>;
    private prepareDom;
    private finishTrialPromise;
    finishTrial(data?: TrialResult): void;
    private timelineDependencies;
    private extensionManagerDependencies;
    private dataDependencies;
}

/**
 * Creates a new JsPsych instance using the provided options.
 *
 * @param options The options to pass to the JsPsych constructor
 * @returns A new JsPsych instance
 */
declare function initJsPsych(options?: any): JsPsych;

export { DataCollection, JsPsych, type JsPsychExtension, type JsPsychExtensionInfo, type JsPsychPlugin, ParameterType, type PluginInfo, type TrialType, initJsPsych };
