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