jspsych 7.2.3 → 7.3.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.
package/dist/index.cjs CHANGED
@@ -71,7 +71,7 @@ var autoBind = (self, {include, exclude} = {}) => {
71
71
  return self;
72
72
  };
73
73
 
74
- var version = "7.2.3";
74
+ var version = "7.3.0";
75
75
 
76
76
  class MigrationError extends Error {
77
77
  constructor(message = "The global `jsPsych` variable is no longer available in jsPsych v7.") {
@@ -843,8 +843,13 @@ class MediaAPI {
843
843
  this.img_cache = {};
844
844
  this.preloadMap = new Map();
845
845
  this.microphone_recorder = null;
846
+ this.camera_stream = null;
847
+ this.camera_recorder = null;
846
848
  }
847
849
  getVideoBuffer(videoID) {
850
+ if (videoID.startsWith("blob:")) {
851
+ this.video_buffers[videoID] = videoID;
852
+ }
848
853
  return this.video_buffers[videoID];
849
854
  }
850
855
  initAudio() {
@@ -1086,6 +1091,17 @@ class MediaAPI {
1086
1091
  getMicrophoneRecorder() {
1087
1092
  return this.microphone_recorder;
1088
1093
  }
1094
+ initializeCameraRecorder(stream, opts) {
1095
+ this.camera_stream = stream;
1096
+ const recorder = new MediaRecorder(stream, opts);
1097
+ this.camera_recorder = recorder;
1098
+ }
1099
+ getCameraStream() {
1100
+ return this.camera_stream;
1101
+ }
1102
+ getCameraRecorder() {
1103
+ return this.camera_recorder;
1104
+ }
1089
1105
  }
1090
1106
 
1091
1107
  class SimulationAPI {
@@ -2410,7 +2426,11 @@ class TimelineNode {
2410
2426
  // recursive downward search for active trial to extract timeline variable
2411
2427
  timelineVariable(variable_name) {
2412
2428
  if (typeof this.timeline_parameters == "undefined") {
2413
- return this.findTimelineVariable(variable_name);
2429
+ const val = this.findTimelineVariable(variable_name);
2430
+ if (typeof val === "undefined") {
2431
+ console.warn("Timeline variable " + variable_name + " not found.");
2432
+ }
2433
+ return val;
2414
2434
  }
2415
2435
  else {
2416
2436
  // if progress.current_location is -1, then the timeline variable is being evaluated
@@ -2425,7 +2445,11 @@ class TimelineNode {
2425
2445
  loc = loc - 1;
2426
2446
  }
2427
2447
  // now find the variable
2428
- return this.timeline_parameters.timeline[loc].timelineVariable(variable_name);
2448
+ const val = this.timeline_parameters.timeline[loc].timelineVariable(variable_name);
2449
+ if (typeof val === "undefined") {
2450
+ console.warn("Timeline variable " + variable_name + " not found.");
2451
+ }
2452
+ return val;
2429
2453
  }
2430
2454
  }
2431
2455
  // recursively get all the timeline variables for this trial
@@ -2703,6 +2727,7 @@ class JsPsych {
2703
2727
  return this.DOM_container;
2704
2728
  }
2705
2729
  finishTrial(data = {}) {
2730
+ var _a;
2706
2731
  if (this.current_trial_finished) {
2707
2732
  return;
2708
2733
  }
@@ -2743,46 +2768,58 @@ class JsPsych {
2743
2768
  }
2744
2769
  }
2745
2770
  // handle extension callbacks
2746
- if (Array.isArray(current_trial.extensions)) {
2747
- for (const extension of current_trial.extensions) {
2748
- const ext_data_values = this.extensions[extension.type.info.name].on_finish(extension.params);
2749
- Object.assign(trial_data_values, ext_data_values);
2771
+ const extensionCallbackResults = ((_a = current_trial.extensions) !== null && _a !== void 0 ? _a : []).map((extension) => this.extensions[extension.type.info.name].on_finish(extension.params));
2772
+ const onExtensionCallbacksFinished = () => {
2773
+ // about to execute lots of callbacks, so switch context.
2774
+ this.internal.call_immediate = true;
2775
+ // handle callback at plugin level
2776
+ if (typeof current_trial.on_finish === "function") {
2777
+ current_trial.on_finish(trial_data_values);
2778
+ }
2779
+ // handle callback at whole-experiment level
2780
+ this.opts.on_trial_finish(trial_data_values);
2781
+ // after the above callbacks are complete, then the data should be finalized
2782
+ // for this trial. call the on_data_update handler, passing in the same
2783
+ // data object that just went through the trial's finish handlers.
2784
+ this.opts.on_data_update(trial_data_values);
2785
+ // done with callbacks
2786
+ this.internal.call_immediate = false;
2787
+ // wait for iti
2788
+ if (this.simulation_mode === "data-only") {
2789
+ this.nextTrial();
2750
2790
  }
2751
- }
2752
- // about to execute lots of callbacks, so switch context.
2753
- this.internal.call_immediate = true;
2754
- // handle callback at plugin level
2755
- if (typeof current_trial.on_finish === "function") {
2756
- current_trial.on_finish(trial_data_values);
2757
- }
2758
- // handle callback at whole-experiment level
2759
- this.opts.on_trial_finish(trial_data_values);
2760
- // after the above callbacks are complete, then the data should be finalized
2761
- // for this trial. call the on_data_update handler, passing in the same
2762
- // data object that just went through the trial's finish handlers.
2763
- this.opts.on_data_update(trial_data_values);
2764
- // done with callbacks
2765
- this.internal.call_immediate = false;
2766
- // wait for iti
2767
- if (this.simulation_mode === "data-only") {
2768
- this.nextTrial();
2769
- }
2770
- else if (typeof current_trial.post_trial_gap === null ||
2771
- typeof current_trial.post_trial_gap === "undefined") {
2772
- if (this.opts.default_iti > 0) {
2773
- setTimeout(this.nextTrial, this.opts.default_iti);
2791
+ else if (typeof current_trial.post_trial_gap === null ||
2792
+ typeof current_trial.post_trial_gap === "undefined") {
2793
+ if (this.opts.default_iti > 0) {
2794
+ setTimeout(this.nextTrial, this.opts.default_iti);
2795
+ }
2796
+ else {
2797
+ this.nextTrial();
2798
+ }
2774
2799
  }
2775
2800
  else {
2776
- this.nextTrial();
2801
+ if (current_trial.post_trial_gap > 0) {
2802
+ setTimeout(this.nextTrial, current_trial.post_trial_gap);
2803
+ }
2804
+ else {
2805
+ this.nextTrial();
2806
+ }
2777
2807
  }
2808
+ };
2809
+ // Strictly using Promise.resolve to turn all values into promises would be cleaner here, but it
2810
+ // would require user test code to make the event loop tick after every simulated key press even
2811
+ // if there are no async `on_finish` methods. Hence, in order to avoid a breaking change, we
2812
+ // only rely on the event loop if at least one `on_finish` method returns a promise.
2813
+ if (extensionCallbackResults.some((result) => typeof result.then === "function")) {
2814
+ Promise.all(extensionCallbackResults.map((result) => Promise.resolve(result).then((ext_data_values) => {
2815
+ Object.assign(trial_data_values, ext_data_values);
2816
+ }))).then(onExtensionCallbacksFinished);
2778
2817
  }
2779
2818
  else {
2780
- if (current_trial.post_trial_gap > 0) {
2781
- setTimeout(this.nextTrial, current_trial.post_trial_gap);
2782
- }
2783
- else {
2784
- this.nextTrial();
2819
+ for (const values of extensionCallbackResults) {
2820
+ Object.assign(trial_data_values, values);
2785
2821
  }
2822
+ onExtensionCallbacksFinished();
2786
2823
  }
2787
2824
  }
2788
2825
  endExperiment(end_message = "", data = {}) {
@@ -3070,16 +3107,16 @@ class JsPsych {
3070
3107
  }
3071
3108
  evaluateTimelineVariables(trial) {
3072
3109
  for (const key of Object.keys(trial)) {
3073
- // timeline variables on the root level
3074
3110
  if (typeof trial[key] === "object" &&
3075
3111
  trial[key] !== null &&
3076
3112
  typeof trial[key].timelineVariablePlaceholder !== "undefined") {
3077
- /*trial[key].toString().replace(/\s/g, "") ==
3078
- "function(){returntimeline.timelineVariable(varname);}"
3079
- )*/ trial[key] = trial[key].timelineVariableFunction();
3113
+ trial[key] = trial[key].timelineVariableFunction();
3080
3114
  }
3081
3115
  // timeline variables that are nested in objects
3082
- if (typeof trial[key] === "object" && trial[key] !== null) {
3116
+ if (typeof trial[key] === "object" &&
3117
+ trial[key] !== null &&
3118
+ key !== "timeline" &&
3119
+ key !== "timeline_variables") {
3083
3120
  this.evaluateTimelineVariables(trial[key]);
3084
3121
  }
3085
3122
  }
@@ -3122,9 +3159,11 @@ class JsPsych {
3122
3159
  else if (typeof obj === "object") {
3123
3160
  if (info === null || !info.nested) {
3124
3161
  for (const key of Object.keys(obj)) {
3125
- if (key === "type") {
3162
+ if (key === "type" || key === "timeline" || key === "timeline_variables") {
3126
3163
  // Ignore the object's `type` field because it contains a plugin and we do not want to
3127
- // call plugin functions
3164
+ // call plugin functions. Also ignore `timeline` and `timeline_variables` because they
3165
+ // are used in the `trials` parameter of the preload plugin and we don't want to actually
3166
+ // evaluate those in that context.
3128
3167
  continue;
3129
3168
  }
3130
3169
  obj[key] = this.replaceFunctionsWithValues(obj[key], null);