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