jspsych 7.3.3 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +1 -1
  2. package/css/jspsych.css +19 -11
  3. package/dist/index.browser.js +3082 -3399
  4. package/dist/index.browser.js.map +1 -1
  5. package/dist/index.browser.min.js +6 -2
  6. package/dist/index.browser.min.js.map +1 -1
  7. package/dist/index.cjs +2464 -3327
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +990 -12
  10. package/dist/index.js +2463 -3325
  11. package/dist/index.js.map +1 -1
  12. package/package.json +6 -5
  13. package/src/ExtensionManager.spec.ts +123 -0
  14. package/src/ExtensionManager.ts +81 -0
  15. package/src/JsPsych.ts +195 -690
  16. package/src/ProgressBar.spec.ts +60 -0
  17. package/src/ProgressBar.ts +60 -0
  18. package/src/index.scss +29 -8
  19. package/src/index.ts +4 -9
  20. package/src/modules/data/DataCollection.ts +1 -1
  21. package/src/modules/data/DataColumn.ts +12 -1
  22. package/src/modules/data/index.ts +92 -103
  23. package/src/modules/extensions.ts +4 -0
  24. package/src/modules/plugin-api/AudioPlayer.ts +101 -0
  25. package/src/modules/plugin-api/KeyboardListenerAPI.ts +1 -1
  26. package/src/modules/plugin-api/MediaAPI.ts +48 -106
  27. package/src/modules/plugin-api/__mocks__/AudioPlayer.ts +38 -0
  28. package/src/modules/plugin-api/index.ts +11 -14
  29. package/src/modules/plugins.ts +26 -27
  30. package/src/modules/randomization.ts +1 -1
  31. package/src/timeline/Timeline.spec.ts +921 -0
  32. package/src/timeline/Timeline.ts +342 -0
  33. package/src/timeline/TimelineNode.ts +174 -0
  34. package/src/timeline/Trial.spec.ts +897 -0
  35. package/src/timeline/Trial.ts +419 -0
  36. package/src/timeline/index.ts +232 -0
  37. package/src/timeline/util.spec.ts +124 -0
  38. package/src/timeline/util.ts +146 -0
  39. package/dist/JsPsych.d.ts +0 -112
  40. package/dist/TimelineNode.d.ts +0 -34
  41. package/dist/migration.d.ts +0 -3
  42. package/dist/modules/data/DataCollection.d.ts +0 -46
  43. package/dist/modules/data/DataColumn.d.ts +0 -15
  44. package/dist/modules/data/index.d.ts +0 -25
  45. package/dist/modules/data/utils.d.ts +0 -3
  46. package/dist/modules/extensions.d.ts +0 -22
  47. package/dist/modules/plugin-api/HardwareAPI.d.ts +0 -15
  48. package/dist/modules/plugin-api/KeyboardListenerAPI.d.ts +0 -34
  49. package/dist/modules/plugin-api/MediaAPI.d.ts +0 -32
  50. package/dist/modules/plugin-api/SimulationAPI.d.ts +0 -44
  51. package/dist/modules/plugin-api/TimeoutAPI.d.ts +0 -17
  52. package/dist/modules/plugin-api/index.d.ts +0 -8
  53. package/dist/modules/plugins.d.ts +0 -136
  54. package/dist/modules/randomization.d.ts +0 -42
  55. package/dist/modules/turk.d.ts +0 -40
  56. package/dist/modules/utils.d.ts +0 -13
  57. package/src/TimelineNode.ts +0 -544
  58. package/src/modules/plugin-api/HardwareAPI.ts +0 -32
package/dist/index.d.ts CHANGED
@@ -1,12 +1,990 @@
1
- import "regenerator-runtime/runtime.js";
2
- import { JsPsych } from "./JsPsych";
3
- /**
4
- * Creates a new JsPsych instance using the provided options.
5
- *
6
- * @param options The options to pass to the JsPsych constructor
7
- * @returns A new JsPsych instance
8
- */
9
- export declare function initJsPsych(options?: any): JsPsych;
10
- export { JsPsych } from "./JsPsych";
11
- export { JsPsychPlugin, PluginInfo, TrialType, ParameterType, universalPluginParameters, UniversalPluginParameters, } from "./modules/plugins";
12
- export { JsPsychExtension, JsPsychExtensionInfo } from "./modules/extensions";
1
+ import { SetRequired, Class } from 'type-fest';
2
+
3
+ /**
4
+ * Parameter types for plugins
5
+ */
6
+ declare enum ParameterType {
7
+ BOOL = 0,
8
+ STRING = 1,
9
+ INT = 2,
10
+ FLOAT = 3,
11
+ FUNCTION = 4,
12
+ KEY = 5,
13
+ KEYS = 6,
14
+ SELECT = 7,
15
+ HTML_STRING = 8,
16
+ IMAGE = 9,
17
+ AUDIO = 10,
18
+ VIDEO = 11,
19
+ OBJECT = 12,
20
+ COMPLEX = 13,
21
+ TIMELINE = 14
22
+ }
23
+ type ParameterTypeMap = {
24
+ [ParameterType.BOOL]: boolean;
25
+ [ParameterType.STRING]: string;
26
+ [ParameterType.INT]: number;
27
+ [ParameterType.FLOAT]: number;
28
+ [ParameterType.FUNCTION]: (...args: any[]) => any;
29
+ [ParameterType.KEY]: string;
30
+ [ParameterType.KEYS]: string[] | "ALL_KEYS" | "NO_KEYS";
31
+ [ParameterType.SELECT]: any;
32
+ [ParameterType.HTML_STRING]: string;
33
+ [ParameterType.IMAGE]: string;
34
+ [ParameterType.AUDIO]: string;
35
+ [ParameterType.VIDEO]: string;
36
+ [ParameterType.OBJECT]: object;
37
+ [ParameterType.COMPLEX]: any;
38
+ [ParameterType.TIMELINE]: any;
39
+ };
40
+ type PreloadParameterType = ParameterType.AUDIO | ParameterType.VIDEO | ParameterType.IMAGE;
41
+ type ParameterInfo = ({
42
+ type: Exclude<ParameterType, ParameterType.COMPLEX | PreloadParameterType>;
43
+ } | {
44
+ type: ParameterType.COMPLEX;
45
+ nested?: ParameterInfos;
46
+ } | {
47
+ type: PreloadParameterType;
48
+ preload?: boolean;
49
+ }) & {
50
+ array?: boolean;
51
+ pretty_name?: string;
52
+ default?: any;
53
+ };
54
+ type ParameterInfos = Record<string, ParameterInfo>;
55
+ type InferredParameter<I extends ParameterInfo> = I["array"] extends true ? Array<ParameterTypeMap[I["type"]]> : ParameterTypeMap[I["type"]];
56
+ type RequiredParameterNames<I extends ParameterInfos> = {
57
+ [K in keyof I]: I[K]["default"] extends undefined ? K : never;
58
+ }[keyof I];
59
+ type InferredParameters<I extends ParameterInfos> = SetRequired<{
60
+ [Property in keyof I]?: InferredParameter<I[Property]>;
61
+ }, RequiredParameterNames<I>>;
62
+ interface PluginInfo {
63
+ name: string;
64
+ version?: string;
65
+ parameters: ParameterInfos;
66
+ data?: ParameterInfos;
67
+ }
68
+ interface JsPsychPlugin<I extends PluginInfo> {
69
+ trial(display_element: HTMLElement, trial: TrialType<I>, on_load?: () => void): void | Promise<TrialResult | void>;
70
+ simulate?(trial: TrialType<I>, simulation_mode: SimulationMode, simulation_options: SimulationOptions, on_load?: () => void): void | Promise<TrialResult | void>;
71
+ }
72
+ type TrialType<I extends PluginInfo> = InferredParameters<I["parameters"]> & TrialDescription;
73
+
74
+ interface JsPsychExtensionInfo {
75
+ name: string;
76
+ version?: string;
77
+ data?: ParameterInfos;
78
+ }
79
+ interface JsPsychExtension {
80
+ /**
81
+ * Called once at the start of the experiment to initialize the extension
82
+ */
83
+ initialize(params?: Record<string, any>): Promise<void>;
84
+ /**
85
+ * Called at the start of a trial, prior to invoking the plugin's trial method.
86
+ */
87
+ on_start(params?: Record<string, any>): void;
88
+ /**
89
+ * Called during a trial, after the plugin makes initial changes to the DOM.
90
+ */
91
+ on_load(params?: Record<string, any>): void;
92
+ /**
93
+ * Called at the end of the trial.
94
+ * @returns Data to append to the trial's data object.
95
+ */
96
+ on_finish(params?: Record<string, any>): Record<string, any> | Promise<Record<string, any>>;
97
+ }
98
+
99
+ type GetParameterValueOptions = {
100
+ /**
101
+ * If true, and the retrieved parameter value is a function, invoke the function and return its
102
+ * return value (defaults to `true`)
103
+ */
104
+ evaluateFunctions?: boolean;
105
+ /**
106
+ * Whether to fall back to parent timeline node parameters (defaults to `true`)
107
+ */
108
+ recursive?: boolean;
109
+ /**
110
+ * Whether the timeline node should cache the parameter lookup result for successive lookups,
111
+ * including those of nested properties or array elements (defaults to `true`)
112
+ */
113
+ cacheResult?: boolean;
114
+ /**
115
+ * A function that will be invoked with the original result of the parameter value lookup.
116
+ * Whatever it returns will subsequently be used instead of the original result. This allows to
117
+ * modify results before they are cached.
118
+ */
119
+ replaceResult?: (originalResult: any) => any;
120
+ };
121
+ declare abstract class TimelineNode {
122
+ protected readonly dependencies: TimelineNodeDependencies;
123
+ abstract readonly description: TimelineDescription | TrialDescription | TimelineArray;
124
+ /**
125
+ * The globally unique trial index of this node. It is set when the node is run. Timeline nodes
126
+ * have the same trial index as their first trial.
127
+ */
128
+ index?: number;
129
+ abstract readonly parent?: Timeline;
130
+ abstract run(): Promise<void>;
131
+ /**
132
+ * Returns a flat array of all currently available results of this node
133
+ */
134
+ abstract getResults(): TrialResult[];
135
+ /**
136
+ * Recursively evaluates the given timeline variable, starting at the current timeline node.
137
+ * Returns the result, or `undefined` if the variable is neither specified in the timeline
138
+ * description of this node, nor in the description of any parent node.
139
+ */
140
+ abstract evaluateTimelineVariable(variable: TimelineVariable): any;
141
+ /**
142
+ * Returns the most recent (child) TimelineNode. For trial nodes, this is always the trial node
143
+ * itself since trial nodes do not have child nodes. For timeline nodes, the return value is a
144
+ * Trial object most of the time, but it may also be a Timeline object when a timeline hasn't yet
145
+ * instantiated its children (e.g. during initial timeline callback functions).
146
+ */
147
+ abstract getLatestNode(): TimelineNode;
148
+ /**
149
+ * Returns an active child timeline (or itself) that matches the given name, or `undefined` if no such child exists.
150
+ */
151
+ abstract getActiveTimelineByName(name: string): Timeline | undefined;
152
+ protected status: TimelineNodeStatus;
153
+ constructor(dependencies: TimelineNodeDependencies);
154
+ getStatus(): TimelineNodeStatus;
155
+ private parameterValueCache;
156
+ /**
157
+ * Initializes the parameter value cache with `this.description`. To be called by subclass
158
+ * constructors after setting `this.description`.
159
+ */
160
+ protected initializeParameterValueCache(): void;
161
+ /**
162
+ * Resets all cached parameter values in this timeline node and all of its parents. This is
163
+ * necessary to re-evaluate function parameters and timeline variables at each new trial.
164
+ */
165
+ protected resetParameterValueCache(): void;
166
+ /**
167
+ * Retrieves a parameter value from the description of this timeline node, recursively falling
168
+ * back to the description of each parent timeline node unless `recursive` is set to `false`. If
169
+ * the parameter...
170
+ *
171
+ * * is a timeline variable, evaluates the variable and returns the result.
172
+ * * is not specified, returns `undefined`.
173
+ * * is a function and `evaluateFunctions` is not set to `false`, invokes the function and returns
174
+ * its return value
175
+ * * has previously been looked up, return the cached result of the previous lookup
176
+ *
177
+ * @param parameterPath The path of the respective parameter in the timeline node description. If
178
+ * the path is an array, nested object properties or array items will be looked up.
179
+ * @param options See {@link GetParameterValueOptions}
180
+ */
181
+ getParameterValue(parameterPath: string | string[], options?: GetParameterValueOptions): any;
182
+ /**
183
+ * Retrieves and evaluates the `data` parameter. It is different from other parameters in that
184
+ * it's properties may be functions that have to be evaluated, and parent nodes' data parameter
185
+ * properties are merged into the result.
186
+ */
187
+ getDataParameter(): Record<string, any> | undefined;
188
+ }
189
+
190
+ declare class Timeline extends TimelineNode {
191
+ readonly parent?: Timeline;
192
+ readonly children: TimelineNode[];
193
+ readonly description: TimelineDescription;
194
+ constructor(dependencies: TimelineNodeDependencies, description: TimelineDescription | TimelineArray, parent?: Timeline);
195
+ private currentChild?;
196
+ private shouldAbort;
197
+ run(): Promise<void>;
198
+ private onStart;
199
+ private onFinish;
200
+ pause(): void;
201
+ private resumePromise;
202
+ resume(): void;
203
+ /**
204
+ * If the timeline is running or paused, aborts the timeline after the current trial has completed
205
+ */
206
+ abort(): void;
207
+ private instantiateChildNodes;
208
+ private currentTimelineVariables;
209
+ private setCurrentTimelineVariablesByIndex;
210
+ /**
211
+ * If the timeline has timeline variables, returns the order of `timeline_variables` array indices
212
+ * to be used, according to the timeline's `sample` setting. If the timeline has no timeline
213
+ * variables, returns `[null]`.
214
+ */
215
+ private generateTimelineVariableOrder;
216
+ /**
217
+ * Returns the current values of all timeline variables, including those from parent timelines
218
+ */
219
+ getAllTimelineVariables(): Record<string, any>;
220
+ evaluateTimelineVariable(variable: TimelineVariable): any;
221
+ getResults(): TrialResult[];
222
+ /**
223
+ * Returns the naive progress of the timeline (as a fraction), without considering conditional or
224
+ * loop functions.
225
+ */
226
+ getNaiveProgress(): number;
227
+ /**
228
+ * Recursively computes the naive number of trials in the timeline, without considering
229
+ * conditional or loop functions.
230
+ */
231
+ getNaiveTrialCount(): any;
232
+ getLatestNode(): any;
233
+ getActiveTimelineByName(name: string): Timeline;
234
+ }
235
+
236
+ declare class Trial extends TimelineNode {
237
+ readonly description: TrialDescription;
238
+ readonly parent: Timeline;
239
+ readonly pluginClass: Class<JsPsychPlugin<any>>;
240
+ pluginInstance: JsPsychPlugin<any>;
241
+ trialObject?: TrialDescription;
242
+ index?: number;
243
+ private result;
244
+ private readonly pluginInfo;
245
+ constructor(dependencies: TimelineNodeDependencies, description: TrialDescription, parent: Timeline);
246
+ run(): Promise<void>;
247
+ private executeTrial;
248
+ private invokeTrialMethod;
249
+ /**
250
+ * Cleanup the trial by removing the display element and removing event listeners
251
+ */
252
+ private cleanupTrial;
253
+ /**
254
+ * Add the CSS classes from the `css_classes` parameter to the display element
255
+ */
256
+ private addCssClasses;
257
+ /**
258
+ * Removes the provided css classes from the display element
259
+ */
260
+ private removeCssClasses;
261
+ private processResult;
262
+ /**
263
+ * Runs a callback function retrieved from a parameter value and returns its result.
264
+ *
265
+ * @param parameterName The name of the parameter to retrieve the callback function from.
266
+ * @param callbackParameters The parameters (if any) to be passed to the callback function
267
+ */
268
+ private runParameterCallback;
269
+ private onStart;
270
+ private onLoad;
271
+ private onFinish;
272
+ evaluateTimelineVariable(variable: TimelineVariable): any;
273
+ getParameterValue(parameterPath: string | string[], options?: GetParameterValueOptions): any;
274
+ /**
275
+ * Retrieves and evaluates the `simulation_options` parameter, considering nested properties and
276
+ * global simulation options.
277
+ */
278
+ getSimulationOptions(): SimulationOptions;
279
+ /**
280
+ * Returns the result object of this trial or `undefined` if the result is not yet known or the
281
+ * `record_data` trial parameter is `false`.
282
+ */
283
+ getResult(): TrialResult;
284
+ getResults(): TrialResult[];
285
+ /**
286
+ * Checks that the parameters provided in the trial description align with the plugin's info
287
+ * object, resolves missing parameter values from the parent timeline, resolves timeline variable
288
+ * parameters, evaluates parameter functions if the expected parameter type is not `FUNCTION`, and
289
+ * sets default values for optional parameters.
290
+ */
291
+ private processParameters;
292
+ getLatestNode(): this;
293
+ getActiveTimelineByName(name: string): Timeline | undefined;
294
+ }
295
+
296
+ /**
297
+ * Maintains a promise and offers a function to resolve it. Whenever the promise is resolved, it is
298
+ * replaced with a new one.
299
+ */
300
+ declare class PromiseWrapper<ResolveType = void> {
301
+ constructor();
302
+ private promise;
303
+ private resolvePromise;
304
+ reset(): void;
305
+ get(): Promise<ResolveType>;
306
+ resolve(value: ResolveType): void;
307
+ }
308
+
309
+ declare class TimelineVariable {
310
+ readonly name: string;
311
+ constructor(name: string);
312
+ }
313
+ type Parameter<T> = T | (() => T) | TimelineVariable;
314
+ type TrialExtensionsConfiguration = Array<{
315
+ type: Class<JsPsychExtension>;
316
+ params?: Record<string, any>;
317
+ }>;
318
+ type SimulationMode = "visual" | "data-only";
319
+ type SimulationOptions = {
320
+ data?: Record<string, any>;
321
+ mode?: SimulationMode;
322
+ simulate?: boolean;
323
+ };
324
+ type SimulationOptionsParameter = Parameter<{
325
+ data?: Parameter<Record<string, Parameter<any>>>;
326
+ mode?: Parameter<SimulationMode>;
327
+ simulate?: Parameter<boolean>;
328
+ }>;
329
+ interface TrialDescription extends Record<string, any> {
330
+ type: Parameter<Class<JsPsychPlugin<any>>>;
331
+ /** https://www.jspsych.org/latest/overview/plugins/#the-post_trial_gap-iti-parameter */
332
+ post_trial_gap?: Parameter<number>;
333
+ /** https://www.jspsych.org/latest/overview/plugins/#the-save_trial_parameters-parameter */
334
+ save_trial_parameters?: Parameter<Record<string, boolean>>;
335
+ /**
336
+ * Whether to include the values of timeline variables under a `timeline_variables` key. Can be
337
+ * `true` to save the values of all timeline variables, or an array of timeline variable names to
338
+ * only save specific timeline variables. Defaults to `false`.
339
+ */
340
+ save_timeline_variables?: Parameter<boolean | string[]>;
341
+ /** https://www.jspsych.org/latest/overview/style/#using-the-css_classes-trial-parameter */
342
+ css_classes?: Parameter<string | string[]>;
343
+ /** https://www.jspsych.org/latest/overview/simulation/#controlling-simulation-mode-with-simulation_options */
344
+ simulation_options?: SimulationOptionsParameter | string;
345
+ /** https://www.jspsych.org/latest/overview/extensions/ */
346
+ extensions?: Parameter<TrialExtensionsConfiguration>;
347
+ /**
348
+ * Whether to record the data of this trial. Defaults to `true`.
349
+ */
350
+ record_data?: Parameter<boolean>;
351
+ /** https://www.jspsych.org/latest/overview/events/#on_start-trial */
352
+ on_start?: (trial: any) => void;
353
+ /** https://www.jspsych.org/latest/overview/events/#on_load */
354
+ on_load?: () => void;
355
+ /** https://www.jspsych.org/latest/overview/events/#on_finish-trial */
356
+ on_finish?: (data: any) => void;
357
+ }
358
+ /** https://www.jspsych.org/latest/overview/timeline/#sampling-methods */
359
+ type SampleOptions = {
360
+ type: "with-replacement";
361
+ size: number;
362
+ weights?: number[];
363
+ } | {
364
+ type: "without-replacement";
365
+ size: number;
366
+ } | {
367
+ type: "fixed-repetitions";
368
+ size: number;
369
+ } | {
370
+ type: "alternate-groups";
371
+ groups: number[][];
372
+ randomize_group_order?: boolean;
373
+ } | {
374
+ type: "custom";
375
+ fn: (ids: number[]) => number[];
376
+ };
377
+ type TimelineArray = Array<TimelineDescription | TrialDescription | TimelineArray>;
378
+ interface TimelineDescription extends Record<string, any> {
379
+ timeline: TimelineArray;
380
+ timeline_variables?: Record<string, any>[];
381
+ name?: string;
382
+ /** https://www.jspsych.org/latest/overview/timeline/#repeating-a-set-of-trials */
383
+ repetitions?: number;
384
+ /** https://www.jspsych.org/latest/overview/timeline/#looping-timelines */
385
+ loop_function?: (data: any) => boolean;
386
+ /** https://www.jspsych.org/latest/overview/timeline/#conditional-timelines */
387
+ conditional_function?: () => boolean;
388
+ /** https://www.jspsych.org/latest/overview/timeline/#random-orders-of-trials */
389
+ randomize_order?: boolean;
390
+ /** https://www.jspsych.org/latest/overview/timeline/#sampling-methods */
391
+ sample?: SampleOptions;
392
+ /** https://www.jspsych.org/latest/overview/events/#on_timeline_start */
393
+ on_timeline_start?: () => void;
394
+ /** https://www.jspsych.org/latest/overview/events/#on_timeline_finish */
395
+ on_timeline_finish?: () => void;
396
+ }
397
+ declare enum TimelineNodeStatus {
398
+ PENDING = 0,
399
+ RUNNING = 1,
400
+ PAUSED = 2,
401
+ COMPLETED = 3,
402
+ ABORTED = 4
403
+ }
404
+ /**
405
+ * Functions and options needed by `TimelineNode`s, provided by the `JsPsych` instance. This
406
+ * approach allows to keep the public `JsPsych` API slim and decouples the `JsPsych` and timeline
407
+ * node classes, simplifying unit testing.
408
+ */
409
+ interface TimelineNodeDependencies {
410
+ /**
411
+ * Called at the start of a trial, prior to invoking the plugin's trial method.
412
+ */
413
+ onTrialStart: (trial: Trial) => void;
414
+ /**
415
+ * Called when a trial's result data is available, before invoking `onTrialFinished()`.
416
+ */
417
+ onTrialResultAvailable: (trial: Trial) => void;
418
+ /**
419
+ * Called after a trial has finished.
420
+ */
421
+ onTrialFinished: (trial: Trial) => void;
422
+ /**
423
+ * Invoke `on_start` extension callbacks according to `extensionsConfiguration`
424
+ */
425
+ runOnStartExtensionCallbacks(extensionsConfiguration: TrialExtensionsConfiguration): void;
426
+ /**
427
+ * Invoke `on_load` extension callbacks according to `extensionsConfiguration`
428
+ */
429
+ runOnLoadExtensionCallbacks(extensionsConfiguration: TrialExtensionsConfiguration): void;
430
+ /**
431
+ * Invoke `on_finish` extension callbacks according to `extensionsConfiguration` and return a
432
+ * joint extensions result object
433
+ */
434
+ runOnFinishExtensionCallbacks(extensionsConfiguration: TrialExtensionsConfiguration): Promise<Record<string, any>>;
435
+ /**
436
+ * Returns the simulation mode or `undefined`, if the experiment is not running in simulation
437
+ * mode.
438
+ */
439
+ getSimulationMode(): SimulationMode | undefined;
440
+ /**
441
+ * Returns the global simulation options as passed to `jsPsych.simulate()`
442
+ */
443
+ getGlobalSimulationOptions(): Record<string, SimulationOptionsParameter>;
444
+ /**
445
+ * Given a plugin class, create a new instance of it and return it.
446
+ */
447
+ instantiatePlugin: <Info extends PluginInfo>(pluginClass: Class<JsPsychPlugin<Info>>) => JsPsychPlugin<Info>;
448
+ /**
449
+ * Return JsPsych's display element so it can be provided to plugins
450
+ */
451
+ getDisplayElement: () => HTMLElement;
452
+ /**
453
+ * Return the default inter-trial interval as provided to `initJsPsych()`
454
+ */
455
+ getDefaultIti: () => number;
456
+ /**
457
+ * A `PromiseWrapper` whose promise is resolved with result data whenever `jsPsych.finishTrial()`
458
+ * is called.
459
+ */
460
+ finishTrialPromise: PromiseWrapper<TrialResult | void>;
461
+ /**
462
+ * Clear all of the timeouts
463
+ */
464
+ clearAllTimeouts: () => void;
465
+ }
466
+ type TrialResult = Record<string, any>;
467
+
468
+ declare class DataColumn {
469
+ values: any[];
470
+ constructor(values?: any[]);
471
+ sum(): number;
472
+ mean(): number;
473
+ median(): any;
474
+ min(): any;
475
+ max(): any;
476
+ count(): number;
477
+ variance(): number;
478
+ sd(): number;
479
+ frequencies(): {};
480
+ all(eval_fn: any): boolean;
481
+ subset(eval_fn: any): DataColumn;
482
+ }
483
+
484
+ declare class DataCollection {
485
+ private trials;
486
+ constructor(data?: any[]);
487
+ push(new_data: any): this;
488
+ join(other_data_collection: DataCollection): this;
489
+ top(): DataCollection;
490
+ /**
491
+ * Queries the first n elements in a collection of trials.
492
+ *
493
+ * @param n A positive integer of elements to return. A value of
494
+ * n that is less than 1 will throw an error.
495
+ *
496
+ * @return First n objects of a collection of trials. If fewer than
497
+ * n trials are available, the trials.length elements will
498
+ * be returned.
499
+ *
500
+ */
501
+ first(n?: number): DataCollection;
502
+ /**
503
+ * Queries the last n elements in a collection of trials.
504
+ *
505
+ * @param n A positive integer of elements to return. A value of
506
+ * n that is less than 1 will throw an error.
507
+ *
508
+ * @return Last n objects of a collection of trials. If fewer than
509
+ * n trials are available, the trials.length elements will
510
+ * be returned.
511
+ *
512
+ */
513
+ last(n?: number): DataCollection;
514
+ values(): any[];
515
+ count(): number;
516
+ readOnly(): DataCollection;
517
+ addToAll(properties: any): this;
518
+ addToLast(properties: any): this;
519
+ filter(filters: any): DataCollection;
520
+ filterCustom(fn: any): DataCollection;
521
+ filterColumns(columns: Array<string>): DataCollection;
522
+ select(column: any): DataColumn;
523
+ ignore(columns: any): DataCollection;
524
+ uniqueNames(): any[];
525
+ csv(): string;
526
+ json(pretty?: boolean): string;
527
+ localSave(format: any, filename: any): void;
528
+ }
529
+
530
+ type InteractionEvent = "blur" | "focus" | "fullscreenenter" | "fullscreenexit";
531
+ interface InteractionRecord {
532
+ event: InteractionEvent;
533
+ trial: number;
534
+ time: number;
535
+ }
536
+ /**
537
+ * Functions and options needed by the `JsPsychData` module
538
+ */
539
+ interface JsPsychDataDependencies {
540
+ /**
541
+ * Returns progress information for interaction records.
542
+ */
543
+ getProgress: () => {
544
+ trial: number;
545
+ time: number;
546
+ };
547
+ onInteractionRecordAdded: (record: InteractionRecord) => void;
548
+ getDisplayElement: () => HTMLElement;
549
+ }
550
+ declare class JsPsychData {
551
+ private dependencies;
552
+ private results;
553
+ private resultToTrialMap;
554
+ /** Browser interaction event data */
555
+ private interactionRecords;
556
+ /** Data properties for all trials */
557
+ private dataProperties;
558
+ private query_string;
559
+ constructor(dependencies: JsPsychDataDependencies);
560
+ reset(): void;
561
+ get(): DataCollection;
562
+ getInteractionData(): DataCollection;
563
+ write(trial: Trial): void;
564
+ addProperties(properties: any): void;
565
+ addDataToLastTrial(data: any): void;
566
+ getLastTrialData(): DataCollection;
567
+ getLastTimelineData(): DataCollection;
568
+ displayData(format?: string): void;
569
+ urlVariables(): any;
570
+ getURLVariable(whichvar: any): any;
571
+ private addInteractionRecord;
572
+ private interactionListeners;
573
+ createInteractionListeners(): void;
574
+ removeInteractionListeners(): void;
575
+ }
576
+
577
+ type KeyboardListener = (e: KeyboardEvent) => void;
578
+ type ValidResponses = string[] | "ALL_KEYS" | "NO_KEYS";
579
+ interface GetKeyboardResponseOptions {
580
+ callback_function: any;
581
+ valid_responses?: ValidResponses;
582
+ rt_method?: "performance" | "audio";
583
+ persist?: boolean;
584
+ audio_context?: AudioContext;
585
+ audio_context_start_time?: number;
586
+ allow_held_key?: boolean;
587
+ minimum_valid_rt?: number;
588
+ }
589
+ declare class KeyboardListenerAPI {
590
+ private getRootElement;
591
+ private areResponsesCaseSensitive;
592
+ private minimumValidRt;
593
+ constructor(getRootElement: () => Element | undefined, areResponsesCaseSensitive?: boolean, minimumValidRt?: number);
594
+ private listeners;
595
+ private heldKeys;
596
+ private areRootListenersRegistered;
597
+ /**
598
+ * If not previously done and `this.getRootElement()` returns an element, adds the root key
599
+ * listeners to that element.
600
+ */
601
+ private registerRootListeners;
602
+ private rootKeydownListener;
603
+ private toLowerCaseIfInsensitive;
604
+ private rootKeyupListener;
605
+ private isResponseValid;
606
+ getKeyboardResponse({ callback_function, valid_responses, rt_method, persist, audio_context, audio_context_start_time, allow_held_key, minimum_valid_rt, }: GetKeyboardResponseOptions): KeyboardListener;
607
+ cancelKeyboardResponse(listener: KeyboardListener): void;
608
+ cancelAllKeyboardResponses(): void;
609
+ compareKeys(key1: string | null, key2: string | null): boolean;
610
+ }
611
+
612
+ interface AudioPlayerOptions {
613
+ useWebAudio: boolean;
614
+ audioContext?: AudioContext;
615
+ }
616
+ interface AudioPlayerInterface {
617
+ load(): Promise<void>;
618
+ play(): void;
619
+ stop(): void;
620
+ addEventListener(eventName: string, callback: EventListenerOrEventListenerObject): void;
621
+ removeEventListener(eventName: string, callback: EventListenerOrEventListenerObject): void;
622
+ }
623
+ declare class AudioPlayer implements AudioPlayerInterface {
624
+ private audio;
625
+ private webAudioBuffer;
626
+ private audioContext;
627
+ private useWebAudio;
628
+ private src;
629
+ constructor(src: string, options?: AudioPlayerOptions);
630
+ load(): Promise<void>;
631
+ play(): void;
632
+ stop(): void;
633
+ addEventListener(eventName: string, callback: EventListenerOrEventListenerObject): void;
634
+ removeEventListener(eventName: string, callback: EventListenerOrEventListenerObject): void;
635
+ private getAudioSourceNode;
636
+ private preloadWebAudio;
637
+ private preloadHTMLAudio;
638
+ }
639
+
640
+ declare class MediaAPI {
641
+ useWebaudio: boolean;
642
+ constructor(useWebaudio: boolean);
643
+ private video_buffers;
644
+ getVideoBuffer(videoID: string): any;
645
+ private context;
646
+ private audio_buffers;
647
+ audioContext(): AudioContext;
648
+ getAudioPlayer(audioID: string): Promise<AudioPlayer>;
649
+ private preload_requests;
650
+ private img_cache;
651
+ preloadAudio(files: any, callback_complete?: () => void, callback_load?: (filepath: string) => void, callback_error?: (error: any) => void): void;
652
+ preloadImages(images: any, callback_complete?: () => void, callback_load?: (filepath: any) => void, callback_error?: (error_msg: any) => void): void;
653
+ preloadVideo(videos: any, callback_complete?: () => void, callback_load?: (filepath: any) => void, callback_error?: (error_msg: any) => void): void;
654
+ private preloadMap;
655
+ getAutoPreloadList(timeline_description: any[]): {
656
+ images: string[];
657
+ audio: string[];
658
+ video: string[];
659
+ };
660
+ cancelPreloads(): void;
661
+ private microphone_recorder;
662
+ initializeMicrophoneRecorder(stream: MediaStream): void;
663
+ getMicrophoneRecorder(): MediaRecorder;
664
+ private camera_stream;
665
+ private camera_recorder;
666
+ initializeCameraRecorder(stream: MediaStream, opts?: MediaRecorderOptions): void;
667
+ getCameraStream(): MediaStream;
668
+ getCameraRecorder(): MediaRecorder;
669
+ }
670
+
671
+ declare class SimulationAPI {
672
+ private getDisplayContainerElement;
673
+ private setJsPsychTimeout;
674
+ constructor(getDisplayContainerElement: () => HTMLElement, setJsPsychTimeout: (callback: () => void, delay: number) => number);
675
+ dispatchEvent(event: Event): void;
676
+ /**
677
+ * Dispatches a `keydown` event for the specified key
678
+ * @param key Character code (`.key` property) for the key to press.
679
+ */
680
+ keyDown(key: string): void;
681
+ /**
682
+ * Dispatches a `keyup` event for the specified key
683
+ * @param key Character code (`.key` property) for the key to press.
684
+ */
685
+ keyUp(key: string): void;
686
+ /**
687
+ * Dispatches a `keydown` and `keyup` event in sequence to simulate pressing a key.
688
+ * @param key Character code (`.key` property) for the key to press.
689
+ * @param delay Length of time to wait (ms) before executing action
690
+ */
691
+ pressKey(key: string, delay?: number): void;
692
+ /**
693
+ * Dispatches `mousedown`, `mouseup`, and `click` events on the target element
694
+ * @param target The element to click
695
+ * @param delay Length of time to wait (ms) before executing action
696
+ */
697
+ clickTarget(target: Element, delay?: number): void;
698
+ /**
699
+ * Sets the value of a target text input
700
+ * @param target A text input element to fill in
701
+ * @param text Text to input
702
+ * @param delay Length of time to wait (ms) before executing action
703
+ */
704
+ fillTextInput(target: HTMLInputElement, text: string, delay?: number): void;
705
+ /**
706
+ * Picks a valid key from `choices`, taking into account jsPsych-specific
707
+ * identifiers like "NO_KEYS" and "ALL_KEYS".
708
+ * @param choices Which keys are valid.
709
+ * @returns A key selected at random from the valid keys.
710
+ */
711
+ getValidKey(choices: "NO_KEYS" | "ALL_KEYS" | Array<string> | Array<Array<string>>): any;
712
+ mergeSimulationData(default_data: any, simulation_options: any): any;
713
+ ensureSimulationDataConsistency(trial: any, data: any): void;
714
+ }
715
+
716
+ /**
717
+ * A class that provides a wrapper around the global setTimeout and clearTimeout functions.
718
+ */
719
+ declare class TimeoutAPI {
720
+ private timeout_handlers;
721
+ /**
722
+ * Calls a function after a specified delay, in milliseconds.
723
+ * @param callback The function to call after the delay.
724
+ * @param delay The number of milliseconds to wait before calling the function.
725
+ * @returns A handle that can be used to clear the timeout with clearTimeout.
726
+ */
727
+ setTimeout(callback: () => void, delay: number): number;
728
+ /**
729
+ * Clears all timeouts that have been created with setTimeout.
730
+ */
731
+ clearAllTimeouts(): void;
732
+ }
733
+
734
+ declare function createJointPluginAPIObject(jsPsych: JsPsych): KeyboardListenerAPI & TimeoutAPI & MediaAPI & SimulationAPI;
735
+ type PluginAPI = ReturnType<typeof createJointPluginAPIObject>;
736
+
737
+ /**
738
+ * Uses the `seedrandom` package to replace Math.random() with a seedable PRNG.
739
+ *
740
+ * @param seed An optional seed. If none is given, a random seed will be generated.
741
+ * @returns The seed value.
742
+ */
743
+ declare function setSeed(seed?: string): string;
744
+ declare function repeat(array: any, repetitions: any, unpack?: boolean): any;
745
+ declare function shuffle(array: Array<any>): any[];
746
+ declare function shuffleNoRepeats(arr: Array<any>, equalityTest: (a: any, b: any) => boolean): any[];
747
+ declare function shuffleAlternateGroups(arr_groups: any, random_group_order?: boolean): any[];
748
+ declare function sampleWithoutReplacement(arr: any, size: any): any[];
749
+ declare function sampleWithReplacement(arr: any, size: any, weights?: any): any[];
750
+ declare function factorial(factors: Record<string, any>, repetitions?: number, unpack?: boolean): any;
751
+ declare function randomID(length?: number): string;
752
+ /**
753
+ * Generate a random integer from `lower` to `upper`, inclusive of both end points.
754
+ * @param lower The lowest value it is possible to generate
755
+ * @param upper The highest value it is possible to generate
756
+ * @returns A random integer
757
+ */
758
+ declare function randomInt(lower: number, upper: number): number;
759
+ /**
760
+ * Generates a random sample from a Bernoulli distribution.
761
+ * @param p The probability of sampling 1.
762
+ * @returns 0, with probability 1-p, or 1, with probability p.
763
+ */
764
+ declare function sampleBernoulli(p: number): 0 | 1;
765
+ declare function sampleNormal(mean: number, standard_deviation: number): number;
766
+ declare function sampleExponential(rate: number): number;
767
+ declare function sampleExGaussian(mean: number, standard_deviation: number, rate: number, positive?: boolean): number;
768
+ /**
769
+ * Generate one or more random words.
770
+ *
771
+ * This is a wrapper function for the {@link https://www.npmjs.com/package/random-words `random-words` npm package}.
772
+ *
773
+ * @param opts An object with optional properties `min`, `max`, `exactly`,
774
+ * `join`, `maxLength`, `wordsPerString`, `separator`, and `formatter`.
775
+ *
776
+ * @returns An array of words or a single string, depending on parameter choices.
777
+ */
778
+ declare function randomWords(opts: any): string[];
779
+
780
+ declare const randomization_setSeed: typeof setSeed;
781
+ declare const randomization_repeat: typeof repeat;
782
+ declare const randomization_shuffle: typeof shuffle;
783
+ declare const randomization_shuffleNoRepeats: typeof shuffleNoRepeats;
784
+ declare const randomization_shuffleAlternateGroups: typeof shuffleAlternateGroups;
785
+ declare const randomization_sampleWithoutReplacement: typeof sampleWithoutReplacement;
786
+ declare const randomization_sampleWithReplacement: typeof sampleWithReplacement;
787
+ declare const randomization_factorial: typeof factorial;
788
+ declare const randomization_randomID: typeof randomID;
789
+ declare const randomization_randomInt: typeof randomInt;
790
+ declare const randomization_sampleBernoulli: typeof sampleBernoulli;
791
+ declare const randomization_sampleNormal: typeof sampleNormal;
792
+ declare const randomization_sampleExponential: typeof sampleExponential;
793
+ declare const randomization_sampleExGaussian: typeof sampleExGaussian;
794
+ declare const randomization_randomWords: typeof randomWords;
795
+ declare namespace randomization {
796
+ export {
797
+ randomization_setSeed as setSeed,
798
+ randomization_repeat as repeat,
799
+ randomization_shuffle as shuffle,
800
+ randomization_shuffleNoRepeats as shuffleNoRepeats,
801
+ randomization_shuffleAlternateGroups as shuffleAlternateGroups,
802
+ randomization_sampleWithoutReplacement as sampleWithoutReplacement,
803
+ randomization_sampleWithReplacement as sampleWithReplacement,
804
+ randomization_factorial as factorial,
805
+ randomization_randomID as randomID,
806
+ randomization_randomInt as randomInt,
807
+ randomization_sampleBernoulli as sampleBernoulli,
808
+ randomization_sampleNormal as sampleNormal,
809
+ randomization_sampleExponential as sampleExponential,
810
+ randomization_sampleExGaussian as sampleExGaussian,
811
+ randomization_randomWords as randomWords,
812
+ };
813
+ }
814
+
815
+ interface turkInformation {
816
+ /**
817
+ * Is the experiment being loaded in preview mode on Mechanical Turk?
818
+ */
819
+ previewMode: boolean;
820
+ /**
821
+ * Is the experiment being loaded outside of the Mechanical Turk environment?
822
+ */
823
+ outsideTurk: boolean;
824
+ /**
825
+ * The HIT ID.
826
+ */
827
+ hitId: string;
828
+ /**
829
+ * The Assignment ID.
830
+ */
831
+ assignmentId: string;
832
+ /**
833
+ * The worker ID.
834
+ */
835
+ workerId: string;
836
+ /**
837
+ * URL for submission of the HIT.
838
+ */
839
+ turkSubmitTo: string;
840
+ }
841
+ /**
842
+ * Gets information about the Mechanical Turk Environment, HIT, Assignment, and Worker
843
+ * by parsing the URL variables that Mechanical Turk generates.
844
+ * @returns An object containing information about the Mechanical Turk Environment, HIT, Assignment, and Worker.
845
+ */
846
+ declare function turkInfo(): turkInformation;
847
+ /**
848
+ * Send data to Mechnical Turk for storage.
849
+ * @param data An object containing `key:value` pairs to send to Mechanical Turk. Values
850
+ * cannot contain nested objects, arrays, or functions.
851
+ * @returns Nothing
852
+ */
853
+ declare function submitToTurk(data: any): void;
854
+
855
+ declare const turk_turkInfo: typeof turkInfo;
856
+ declare const turk_submitToTurk: typeof submitToTurk;
857
+ declare namespace turk {
858
+ export {
859
+ turk_turkInfo as turkInfo,
860
+ turk_submitToTurk as submitToTurk,
861
+ };
862
+ }
863
+
864
+ /**
865
+ * Finds all of the unique items in an array.
866
+ * @param arr The array to extract unique values from
867
+ * @returns An array with one copy of each unique item in `arr`
868
+ */
869
+ declare function unique(arr: Array<any>): any[];
870
+ declare function deepCopy(obj: any): any;
871
+ /**
872
+ * Merges two objects, recursively.
873
+ * @param obj1 Object to merge
874
+ * @param obj2 Object to merge
875
+ */
876
+ declare function deepMerge(obj1: any, obj2: any): any;
877
+
878
+ declare const utils_unique: typeof unique;
879
+ declare const utils_deepCopy: typeof deepCopy;
880
+ declare const utils_deepMerge: typeof deepMerge;
881
+ declare namespace utils {
882
+ export {
883
+ utils_unique as unique,
884
+ utils_deepCopy as deepCopy,
885
+ utils_deepMerge as deepMerge,
886
+ };
887
+ }
888
+
889
+ /**
890
+ * Maintains a visual progress bar using HTML and CSS
891
+ */
892
+ declare class ProgressBar {
893
+ private readonly containerElement;
894
+ private readonly message;
895
+ constructor(containerElement: HTMLDivElement, message: string | ((progress: number) => string));
896
+ private _progress;
897
+ private innerDiv;
898
+ private messageSpan;
899
+ /** Adds the progress bar HTML code into `this.containerElement` */
900
+ private setupElements;
901
+ /** Updates the progress bar according to `this.progress` */
902
+ private update;
903
+ /**
904
+ * The bar's current position as a number in the closed interval [0, 1]. Set this to update the
905
+ * progress bar accordingly.
906
+ */
907
+ set progress(progress: number);
908
+ get progress(): number;
909
+ }
910
+
911
+ declare class JsPsych {
912
+ turk: typeof turk;
913
+ randomization: typeof randomization;
914
+ utils: typeof utils;
915
+ data: JsPsychData;
916
+ pluginAPI: PluginAPI;
917
+ version(): string;
918
+ /** Options */
919
+ private options;
920
+ /** Experiment timeline */
921
+ private timeline?;
922
+ /** Target DOM element */
923
+ private displayContainerElement;
924
+ private displayElement;
925
+ /** Time that the experiment began */
926
+ private experimentStartTime;
927
+ /**
928
+ * Whether the page is retrieved directly via the `file://` protocol (true) or hosted on a web
929
+ * server (false)
930
+ */
931
+ private isFileProtocolUsed;
932
+ /** The simulation mode (if the experiment is being simulated) */
933
+ private simulationMode?;
934
+ /** Simulation options passed in via `simulate()` */
935
+ private simulationOptions;
936
+ private extensionManager;
937
+ constructor(options?: any);
938
+ private endMessage?;
939
+ /**
940
+ * Starts an experiment using the provided timeline and returns a promise that is resolved when
941
+ * the experiment is finished.
942
+ *
943
+ * @param timeline The timeline to be run
944
+ */
945
+ run(timeline: TimelineDescription | TimelineArray): Promise<void>;
946
+ simulate(timeline: any[], simulation_mode?: "data-only" | "visual", simulation_options?: {}): Promise<void>;
947
+ progressBar?: ProgressBar;
948
+ getProgress(): {
949
+ total_trials: any;
950
+ current_trial_global: any;
951
+ percent_complete: number;
952
+ };
953
+ getStartTime(): Date;
954
+ getTotalTime(): number;
955
+ getDisplayElement(): HTMLElement;
956
+ getDisplayContainerElement(): HTMLElement;
957
+ abortExperiment(endMessage?: string, data?: {}): void;
958
+ abortCurrentTimeline(): void;
959
+ /**
960
+ * Aborts a named timeline. The timeline must be currently running in order to abort it.
961
+ *
962
+ * @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.
963
+ */
964
+ abortTimelineByName(name: string): void;
965
+ getCurrentTrial(): TrialDescription;
966
+ getInitSettings(): any;
967
+ timelineVariable(variableName: string): TimelineVariable;
968
+ evaluateTimelineVariable(variableName: string): any;
969
+ pauseExperiment(): void;
970
+ resumeExperiment(): void;
971
+ getSafeModeStatus(): boolean;
972
+ getTimeline(): TimelineArray;
973
+ get extensions(): Record<string, JsPsychExtension>;
974
+ private prepareDom;
975
+ private finishTrialPromise;
976
+ finishTrial(data?: TrialResult): void;
977
+ private timelineDependencies;
978
+ private extensionManagerDependencies;
979
+ private dataDependencies;
980
+ }
981
+
982
+ /**
983
+ * Creates a new JsPsych instance using the provided options.
984
+ *
985
+ * @param options The options to pass to the JsPsych constructor
986
+ * @returns A new JsPsych instance
987
+ */
988
+ declare function initJsPsych(options?: any): JsPsych;
989
+
990
+ export { DataCollection, JsPsych, JsPsychExtension, JsPsychExtensionInfo, JsPsychPlugin, ParameterType, PluginInfo, TrialType, initJsPsych };