jspsych 7.3.2 → 7.3.3

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/README.md CHANGED
@@ -48,9 +48,15 @@ See the [contributing to jsPsych](https://www.jspsych.org/latest/developers/cont
48
48
 
49
49
  ## Citation
50
50
 
51
- If you use this library in academic work, please cite the [paper that describes jsPsych](http://link.springer.com/article/10.3758%2Fs13428-014-0458-y):
51
+ If you use this library in academic work, the preferred citation is:
52
52
 
53
- de Leeuw, J.R. (2015). jsPsych: A JavaScript library for creating behavioral experiments in a Web browser. *Behavior Research Methods*, _47_(1), 1-12. doi:10.3758/s13428-014-0458-y
53
+ de Leeuw, J.R., Gilbert, R.A., & Luchterhandt, B. (2023). jsPsych: Enabling an open-source collaborative ecosystem of behavioral experiments. *Journal of Open Source Software*, *8*(85), 5351, [https://joss.theoj.org/papers/10.21105/joss.05351](https://joss.theoj.org/papers/10.21105/joss.05351).
54
+
55
+ This paper is an updated description of jsPsych and includes all current core team members. It replaces the earlier paper that described jsPsych:
56
+
57
+ de Leeuw, J.R. (2015). jsPsych: A JavaScript library for creating behavioral experiments in a Web browser. *Behavior Research Methods*, _47_(1), 1-12. doi:[10.3758/s13428-014-0458-y](http://link.springer.com/article/10.3758%2Fs13428-014-0458-y)
58
+
59
+ Citations help us demonstrate that this library is used and valued, which allows us to continue working on it.
54
60
 
55
61
  ## Contributors
56
62
 
@@ -59,4 +65,4 @@ The project is currently managed by the core team of Josh de Leeuw ([@jodeleeuw]
59
65
 
60
66
  jsPsych was created by [Josh de Leeuw](http://www.twitter.com/joshdeleeuw).
61
67
 
62
- We're also grateful for the generous support from a [Mozilla Open Source Support award](https://www.mozilla.org/en-US/moss/), which funded development of the library from 2020-2021.
68
+ We're also grateful for the generous support from a [Mozilla Open Source Support award](https://www.mozilla.org/en-US/moss/), which funded development of the library from 2020-2022.
@@ -70,7 +70,7 @@ var jsPsychModule = (function (exports) {
70
70
  return self;
71
71
  };
72
72
 
73
- var version = "7.3.2";
73
+ var version = "7.3.3";
74
74
 
75
75
  class MigrationError extends Error {
76
76
  constructor(message = "The global `jsPsych` variable is no longer available in jsPsych v7.") {
@@ -134,12 +134,45 @@ var jsPsychModule = (function (exports) {
134
134
  else {
135
135
  return obj;
136
136
  }
137
+ }
138
+ /**
139
+ * Merges two objects, recursively.
140
+ * @param obj1 Object to merge
141
+ * @param obj2 Object to merge
142
+ */
143
+ function deepMerge(obj1, obj2) {
144
+ let merged = {};
145
+ for (const key in obj1) {
146
+ if (obj1.hasOwnProperty(key)) {
147
+ if (typeof obj1[key] === "object" && obj2.hasOwnProperty(key)) {
148
+ merged[key] = deepMerge(obj1[key], obj2[key]);
149
+ }
150
+ else {
151
+ merged[key] = obj1[key];
152
+ }
153
+ }
154
+ }
155
+ for (const key in obj2) {
156
+ if (obj2.hasOwnProperty(key)) {
157
+ if (!merged.hasOwnProperty(key)) {
158
+ merged[key] = obj2[key];
159
+ }
160
+ else if (typeof obj2[key] === "object") {
161
+ merged[key] = deepMerge(merged[key], obj2[key]);
162
+ }
163
+ else {
164
+ merged[key] = obj2[key];
165
+ }
166
+ }
167
+ }
168
+ return merged;
137
169
  }
138
170
 
139
171
  var utils = /*#__PURE__*/Object.freeze({
140
172
  __proto__: null,
141
173
  unique: unique,
142
- deepCopy: deepCopy
174
+ deepCopy: deepCopy,
175
+ deepMerge: deepMerge
143
176
  });
144
177
 
145
178
  class DataColumn {
@@ -1105,8 +1138,12 @@ var jsPsychModule = (function (exports) {
1105
1138
  }
1106
1139
 
1107
1140
  class SimulationAPI {
1141
+ constructor(getDisplayContainerElement, setJsPsychTimeout) {
1142
+ this.getDisplayContainerElement = getDisplayContainerElement;
1143
+ this.setJsPsychTimeout = setJsPsychTimeout;
1144
+ }
1108
1145
  dispatchEvent(event) {
1109
- document.body.dispatchEvent(event);
1146
+ this.getDisplayContainerElement().dispatchEvent(event);
1110
1147
  }
1111
1148
  /**
1112
1149
  * Dispatches a `keydown` event for the specified key
@@ -1129,7 +1166,7 @@ var jsPsychModule = (function (exports) {
1129
1166
  */
1130
1167
  pressKey(key, delay = 0) {
1131
1168
  if (delay > 0) {
1132
- setTimeout(() => {
1169
+ this.setJsPsychTimeout(() => {
1133
1170
  this.keyDown(key);
1134
1171
  this.keyUp(key);
1135
1172
  }, delay);
@@ -1146,7 +1183,7 @@ var jsPsychModule = (function (exports) {
1146
1183
  */
1147
1184
  clickTarget(target, delay = 0) {
1148
1185
  if (delay > 0) {
1149
- setTimeout(() => {
1186
+ this.setJsPsychTimeout(() => {
1150
1187
  target.dispatchEvent(new MouseEvent("mousedown", { bubbles: true }));
1151
1188
  target.dispatchEvent(new MouseEvent("mouseup", { bubbles: true }));
1152
1189
  target.dispatchEvent(new MouseEvent("click", { bubbles: true }));
@@ -1166,7 +1203,7 @@ var jsPsychModule = (function (exports) {
1166
1203
  */
1167
1204
  fillTextInput(target, text, delay = 0) {
1168
1205
  if (delay > 0) {
1169
- setTimeout(() => {
1206
+ this.setJsPsychTimeout(() => {
1170
1207
  target.value = text;
1171
1208
  }, delay);
1172
1209
  }
@@ -1275,15 +1312,27 @@ var jsPsychModule = (function (exports) {
1275
1312
  }
1276
1313
  }
1277
1314
 
1315
+ /**
1316
+ * A class that provides a wrapper around the global setTimeout and clearTimeout functions.
1317
+ */
1278
1318
  class TimeoutAPI {
1279
1319
  constructor() {
1280
1320
  this.timeout_handlers = [];
1281
1321
  }
1322
+ /**
1323
+ * Calls a function after a specified delay, in milliseconds.
1324
+ * @param callback The function to call after the delay.
1325
+ * @param delay The number of milliseconds to wait before calling the function.
1326
+ * @returns A handle that can be used to clear the timeout with clearTimeout.
1327
+ */
1282
1328
  setTimeout(callback, delay) {
1283
1329
  const handle = window.setTimeout(callback, delay);
1284
1330
  this.timeout_handlers.push(handle);
1285
1331
  return handle;
1286
1332
  }
1333
+ /**
1334
+ * Clears all timeouts that have been created with setTimeout.
1335
+ */
1287
1336
  clearAllTimeouts() {
1288
1337
  for (const handler of this.timeout_handlers) {
1289
1338
  clearTimeout(handler);
@@ -1294,13 +1343,12 @@ var jsPsychModule = (function (exports) {
1294
1343
 
1295
1344
  function createJointPluginAPIObject(jsPsych) {
1296
1345
  const settings = jsPsych.getInitSettings();
1297
- return Object.assign({}, ...[
1298
- new KeyboardListenerAPI(jsPsych.getDisplayContainerElement, settings.case_sensitive_responses, settings.minimum_valid_rt),
1299
- new TimeoutAPI(),
1300
- new MediaAPI(settings.use_webaudio, jsPsych.webaudio_context),
1301
- new HardwareAPI(),
1302
- new SimulationAPI(),
1303
- ].map((object) => autoBind(object)));
1346
+ const keyboardListenerAPI = autoBind(new KeyboardListenerAPI(jsPsych.getDisplayContainerElement, settings.case_sensitive_responses, settings.minimum_valid_rt));
1347
+ const timeoutAPI = autoBind(new TimeoutAPI());
1348
+ const mediaAPI = autoBind(new MediaAPI(settings.use_webaudio, jsPsych.webaudio_context));
1349
+ const hardwareAPI = autoBind(new HardwareAPI());
1350
+ const simulationAPI = autoBind(new SimulationAPI(jsPsych.getDisplayContainerElement, timeoutAPI.setTimeout));
1351
+ return Object.assign({}, ...[keyboardListenerAPI, timeoutAPI, mediaAPI, hardwareAPI, simulationAPI]);
1304
1352
  }
1305
1353
 
1306
1354
  var wordList = [
@@ -3055,13 +3103,14 @@ var jsPsychModule = (function (exports) {
3055
3103
  }
3056
3104
  };
3057
3105
  let trial_complete;
3106
+ let trial_sim_opts;
3107
+ let trial_sim_opts_merged;
3058
3108
  if (!this.simulation_mode) {
3059
3109
  trial_complete = trial.type.trial(this.DOM_target, trial, load_callback);
3060
3110
  }
3061
3111
  if (this.simulation_mode) {
3062
3112
  // check if the trial supports simulation
3063
3113
  if (trial.type.simulate) {
3064
- let trial_sim_opts;
3065
3114
  if (!trial.simulation_options) {
3066
3115
  trial_sim_opts = this.simulation_options.default;
3067
3116
  }
@@ -3083,13 +3132,16 @@ var jsPsychModule = (function (exports) {
3083
3132
  trial_sim_opts = trial.simulation_options;
3084
3133
  }
3085
3134
  }
3086
- trial_sim_opts = this.utils.deepCopy(trial_sim_opts);
3087
- trial_sim_opts = this.replaceFunctionsWithValues(trial_sim_opts, null);
3088
- if ((trial_sim_opts === null || trial_sim_opts === void 0 ? void 0 : trial_sim_opts.simulate) === false) {
3135
+ // merge in default options that aren't overriden by the trial's simulation_options
3136
+ // including nested parameters in the simulation_options
3137
+ trial_sim_opts_merged = this.utils.deepMerge(this.simulation_options.default, trial_sim_opts);
3138
+ trial_sim_opts_merged = this.utils.deepCopy(trial_sim_opts_merged);
3139
+ trial_sim_opts_merged = this.replaceFunctionsWithValues(trial_sim_opts_merged, null);
3140
+ if ((trial_sim_opts_merged === null || trial_sim_opts_merged === void 0 ? void 0 : trial_sim_opts_merged.simulate) === false) {
3089
3141
  trial_complete = trial.type.trial(this.DOM_target, trial, load_callback);
3090
3142
  }
3091
3143
  else {
3092
- trial_complete = trial.type.simulate(trial, (trial_sim_opts === null || trial_sim_opts === void 0 ? void 0 : trial_sim_opts.mode) || this.simulation_mode, trial_sim_opts, load_callback);
3144
+ trial_complete = trial.type.simulate(trial, (trial_sim_opts_merged === null || trial_sim_opts_merged === void 0 ? void 0 : trial_sim_opts_merged.mode) || this.simulation_mode, trial_sim_opts_merged, load_callback);
3093
3145
  }
3094
3146
  }
3095
3147
  else {
@@ -3099,8 +3151,11 @@ var jsPsychModule = (function (exports) {
3099
3151
  }
3100
3152
  // see if trial_complete is a Promise by looking for .then() function
3101
3153
  const is_promise = trial_complete && typeof trial_complete.then == "function";
3102
- // in simulation mode we let the simulate function call the load_callback always.
3103
- if (!is_promise && !this.simulation_mode) {
3154
+ // in simulation mode we let the simulate function call the load_callback always,
3155
+ // so we don't need to call it here. however, if we are in simulation mode but not simulating
3156
+ // this particular trial we need to call it.
3157
+ if (!is_promise &&
3158
+ (!this.simulation_mode || (this.simulation_mode && (trial_sim_opts_merged === null || trial_sim_opts_merged === void 0 ? void 0 : trial_sim_opts_merged.simulate) === false))) {
3104
3159
  load_callback();
3105
3160
  }
3106
3161
  // done with callbacks