jspsych 7.2.3 → 7.3.1

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.
@@ -70,7 +70,7 @@ var jsPsychModule = (function (exports) {
70
70
  return self;
71
71
  };
72
72
 
73
- var version = "7.2.3";
73
+ var version = "7.3.1";
74
74
 
75
75
  class MigrationError extends Error {
76
76
  constructor(message = "The global `jsPsych` variable is no longer available in jsPsych v7.") {
@@ -842,8 +842,13 @@ var jsPsychModule = (function (exports) {
842
842
  this.img_cache = {};
843
843
  this.preloadMap = new Map();
844
844
  this.microphone_recorder = null;
845
+ this.camera_stream = null;
846
+ this.camera_recorder = null;
845
847
  }
846
848
  getVideoBuffer(videoID) {
849
+ if (videoID.startsWith("blob:")) {
850
+ this.video_buffers[videoID] = videoID;
851
+ }
847
852
  return this.video_buffers[videoID];
848
853
  }
849
854
  initAudio() {
@@ -898,15 +903,15 @@ var jsPsychModule = (function (exports) {
898
903
  callback_error({ source: source, error: e });
899
904
  });
900
905
  };
901
- request.onerror = function (e) {
906
+ request.onerror = (e) => {
902
907
  let err = e;
903
- if (this.status == 404) {
908
+ if (request.status == 404) {
904
909
  err = "404";
905
910
  }
906
911
  callback_error({ source: source, error: err });
907
912
  };
908
- request.onloadend = function (e) {
909
- if (this.status == 404) {
913
+ request.onloadend = (e) => {
914
+ if (request.status == 404) {
910
915
  callback_error({ source: source, error: "404" });
911
916
  }
912
917
  };
@@ -963,20 +968,21 @@ var jsPsychModule = (function (exports) {
963
968
  callback_complete();
964
969
  return;
965
970
  }
966
- for (var i = 0; i < images.length; i++) {
967
- var img = new Image();
968
- img.onload = function () {
971
+ for (let i = 0; i < images.length; i++) {
972
+ const img = new Image();
973
+ const src = images[i];
974
+ img.onload = () => {
969
975
  n_loaded++;
970
- callback_load(img.src);
976
+ callback_load(src);
971
977
  if (n_loaded === images.length) {
972
978
  callback_complete();
973
979
  }
974
980
  };
975
- img.onerror = function (e) {
976
- callback_error({ source: img.src, error: e });
981
+ img.onerror = (e) => {
982
+ callback_error({ source: src, error: e });
977
983
  };
978
- img.src = images[i];
979
- this.img_cache[images[i]] = img;
984
+ img.src = src;
985
+ this.img_cache[src] = img;
980
986
  this.preload_requests.push(img);
981
987
  }
982
988
  }
@@ -994,9 +1000,9 @@ var jsPsychModule = (function (exports) {
994
1000
  const request = new XMLHttpRequest();
995
1001
  request.open("GET", video, true);
996
1002
  request.responseType = "blob";
997
- request.onload = function () {
998
- if (this.status === 200 || this.status === 0) {
999
- const videoBlob = this.response;
1003
+ request.onload = () => {
1004
+ if (request.status === 200 || request.status === 0) {
1005
+ const videoBlob = request.response;
1000
1006
  video_buffers[video] = URL.createObjectURL(videoBlob); // IE10+
1001
1007
  n_loaded++;
1002
1008
  callback_load(video);
@@ -1005,15 +1011,15 @@ var jsPsychModule = (function (exports) {
1005
1011
  }
1006
1012
  }
1007
1013
  };
1008
- request.onerror = function (e) {
1014
+ request.onerror = (e) => {
1009
1015
  let err = e;
1010
- if (this.status == 404) {
1016
+ if (request.status == 404) {
1011
1017
  err = "404";
1012
1018
  }
1013
1019
  callback_error({ source: video, error: err });
1014
1020
  };
1015
- request.onloadend = function (e) {
1016
- if (this.status == 404) {
1021
+ request.onloadend = (e) => {
1022
+ if (request.status == 404) {
1017
1023
  callback_error({ source: video, error: "404" });
1018
1024
  }
1019
1025
  };
@@ -1085,6 +1091,17 @@ var jsPsychModule = (function (exports) {
1085
1091
  getMicrophoneRecorder() {
1086
1092
  return this.microphone_recorder;
1087
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
+ }
1088
1105
  }
1089
1106
 
1090
1107
  class SimulationAPI {
@@ -1859,7 +1876,8 @@ var jsPsychModule = (function (exports) {
1859
1876
  // test to make sure the new neighbor isn't equal to the old one
1860
1877
  while (equalityTest(random_shuffle[i + 1], random_shuffle[random_pick]) ||
1861
1878
  equalityTest(random_shuffle[i + 1], random_shuffle[random_pick + 1]) ||
1862
- equalityTest(random_shuffle[i + 1], random_shuffle[random_pick - 1])) {
1879
+ equalityTest(random_shuffle[i + 1], random_shuffle[random_pick - 1]) ||
1880
+ equalityTest(random_shuffle[i], random_shuffle[random_pick])) {
1863
1881
  random_pick = Math.floor(Math.random() * (random_shuffle.length - 2)) + 1;
1864
1882
  }
1865
1883
  const new_neighbor = random_shuffle[random_pick];
@@ -2409,7 +2427,11 @@ var jsPsychModule = (function (exports) {
2409
2427
  // recursive downward search for active trial to extract timeline variable
2410
2428
  timelineVariable(variable_name) {
2411
2429
  if (typeof this.timeline_parameters == "undefined") {
2412
- return this.findTimelineVariable(variable_name);
2430
+ const val = this.findTimelineVariable(variable_name);
2431
+ if (typeof val === "undefined") {
2432
+ console.warn("Timeline variable " + variable_name + " not found.");
2433
+ }
2434
+ return val;
2413
2435
  }
2414
2436
  else {
2415
2437
  // if progress.current_location is -1, then the timeline variable is being evaluated
@@ -2424,7 +2446,11 @@ var jsPsychModule = (function (exports) {
2424
2446
  loc = loc - 1;
2425
2447
  }
2426
2448
  // now find the variable
2427
- return this.timeline_parameters.timeline[loc].timelineVariable(variable_name);
2449
+ const val = this.timeline_parameters.timeline[loc].timelineVariable(variable_name);
2450
+ if (typeof val === "undefined") {
2451
+ console.warn("Timeline variable " + variable_name + " not found.");
2452
+ }
2453
+ return val;
2428
2454
  }
2429
2455
  }
2430
2456
  // recursively get all the timeline variables for this trial
@@ -2702,6 +2728,7 @@ var jsPsychModule = (function (exports) {
2702
2728
  return this.DOM_container;
2703
2729
  }
2704
2730
  finishTrial(data = {}) {
2731
+ var _a;
2705
2732
  if (this.current_trial_finished) {
2706
2733
  return;
2707
2734
  }
@@ -2742,46 +2769,58 @@ var jsPsychModule = (function (exports) {
2742
2769
  }
2743
2770
  }
2744
2771
  // handle extension callbacks
2745
- if (Array.isArray(current_trial.extensions)) {
2746
- for (const extension of current_trial.extensions) {
2747
- const ext_data_values = this.extensions[extension.type.info.name].on_finish(extension.params);
2748
- Object.assign(trial_data_values, ext_data_values);
2772
+ const extensionCallbackResults = ((_a = current_trial.extensions) !== null && _a !== void 0 ? _a : []).map((extension) => this.extensions[extension.type.info.name].on_finish(extension.params));
2773
+ const onExtensionCallbacksFinished = () => {
2774
+ // about to execute lots of callbacks, so switch context.
2775
+ this.internal.call_immediate = true;
2776
+ // handle callback at plugin level
2777
+ if (typeof current_trial.on_finish === "function") {
2778
+ current_trial.on_finish(trial_data_values);
2779
+ }
2780
+ // handle callback at whole-experiment level
2781
+ this.opts.on_trial_finish(trial_data_values);
2782
+ // after the above callbacks are complete, then the data should be finalized
2783
+ // for this trial. call the on_data_update handler, passing in the same
2784
+ // data object that just went through the trial's finish handlers.
2785
+ this.opts.on_data_update(trial_data_values);
2786
+ // done with callbacks
2787
+ this.internal.call_immediate = false;
2788
+ // wait for iti
2789
+ if (this.simulation_mode === "data-only") {
2790
+ this.nextTrial();
2749
2791
  }
2750
- }
2751
- // about to execute lots of callbacks, so switch context.
2752
- this.internal.call_immediate = true;
2753
- // handle callback at plugin level
2754
- if (typeof current_trial.on_finish === "function") {
2755
- current_trial.on_finish(trial_data_values);
2756
- }
2757
- // handle callback at whole-experiment level
2758
- this.opts.on_trial_finish(trial_data_values);
2759
- // after the above callbacks are complete, then the data should be finalized
2760
- // for this trial. call the on_data_update handler, passing in the same
2761
- // data object that just went through the trial's finish handlers.
2762
- this.opts.on_data_update(trial_data_values);
2763
- // done with callbacks
2764
- this.internal.call_immediate = false;
2765
- // wait for iti
2766
- if (this.simulation_mode === "data-only") {
2767
- this.nextTrial();
2768
- }
2769
- else if (typeof current_trial.post_trial_gap === null ||
2770
- typeof current_trial.post_trial_gap === "undefined") {
2771
- if (this.opts.default_iti > 0) {
2772
- setTimeout(this.nextTrial, this.opts.default_iti);
2792
+ else if (typeof current_trial.post_trial_gap === null ||
2793
+ typeof current_trial.post_trial_gap === "undefined") {
2794
+ if (this.opts.default_iti > 0) {
2795
+ setTimeout(this.nextTrial, this.opts.default_iti);
2796
+ }
2797
+ else {
2798
+ this.nextTrial();
2799
+ }
2773
2800
  }
2774
2801
  else {
2775
- this.nextTrial();
2802
+ if (current_trial.post_trial_gap > 0) {
2803
+ setTimeout(this.nextTrial, current_trial.post_trial_gap);
2804
+ }
2805
+ else {
2806
+ this.nextTrial();
2807
+ }
2776
2808
  }
2809
+ };
2810
+ // Strictly using Promise.resolve to turn all values into promises would be cleaner here, but it
2811
+ // would require user test code to make the event loop tick after every simulated key press even
2812
+ // if there are no async `on_finish` methods. Hence, in order to avoid a breaking change, we
2813
+ // only rely on the event loop if at least one `on_finish` method returns a promise.
2814
+ if (extensionCallbackResults.some((result) => typeof result.then === "function")) {
2815
+ Promise.all(extensionCallbackResults.map((result) => Promise.resolve(result).then((ext_data_values) => {
2816
+ Object.assign(trial_data_values, ext_data_values);
2817
+ }))).then(onExtensionCallbacksFinished);
2777
2818
  }
2778
2819
  else {
2779
- if (current_trial.post_trial_gap > 0) {
2780
- setTimeout(this.nextTrial, current_trial.post_trial_gap);
2781
- }
2782
- else {
2783
- this.nextTrial();
2820
+ for (const values of extensionCallbackResults) {
2821
+ Object.assign(trial_data_values, values);
2784
2822
  }
2823
+ onExtensionCallbacksFinished();
2785
2824
  }
2786
2825
  }
2787
2826
  endExperiment(end_message = "", data = {}) {
@@ -3069,16 +3108,16 @@ var jsPsychModule = (function (exports) {
3069
3108
  }
3070
3109
  evaluateTimelineVariables(trial) {
3071
3110
  for (const key of Object.keys(trial)) {
3072
- // timeline variables on the root level
3073
3111
  if (typeof trial[key] === "object" &&
3074
3112
  trial[key] !== null &&
3075
3113
  typeof trial[key].timelineVariablePlaceholder !== "undefined") {
3076
- /*trial[key].toString().replace(/\s/g, "") ==
3077
- "function(){returntimeline.timelineVariable(varname);}"
3078
- )*/ trial[key] = trial[key].timelineVariableFunction();
3114
+ trial[key] = trial[key].timelineVariableFunction();
3079
3115
  }
3080
3116
  // timeline variables that are nested in objects
3081
- if (typeof trial[key] === "object" && trial[key] !== null) {
3117
+ if (typeof trial[key] === "object" &&
3118
+ trial[key] !== null &&
3119
+ key !== "timeline" &&
3120
+ key !== "timeline_variables") {
3082
3121
  this.evaluateTimelineVariables(trial[key]);
3083
3122
  }
3084
3123
  }
@@ -3121,9 +3160,11 @@ var jsPsychModule = (function (exports) {
3121
3160
  else if (typeof obj === "object") {
3122
3161
  if (info === null || !info.nested) {
3123
3162
  for (const key of Object.keys(obj)) {
3124
- if (key === "type") {
3163
+ if (key === "type" || key === "timeline" || key === "timeline_variables") {
3125
3164
  // Ignore the object's `type` field because it contains a plugin and we do not want to
3126
- // call plugin functions
3165
+ // call plugin functions. Also ignore `timeline` and `timeline_variables` because they
3166
+ // are used in the `trials` parameter of the preload plugin and we don't want to actually
3167
+ // evaluate those in that context.
3127
3168
  continue;
3128
3169
  }
3129
3170
  obj[key] = this.replaceFunctionsWithValues(obj[key], null);