jspsych 7.0.0 → 7.1.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
@@ -69,7 +69,38 @@ var autoBind = (self, {include, exclude} = {}) => {
69
69
  return self;
70
70
  };
71
71
 
72
- var version = "7.0.0";
72
+ var version = "7.1.0";
73
+
74
+ class MigrationError extends Error {
75
+ constructor(message = "The global `jsPsych` variable is no longer available in jsPsych v7.") {
76
+ super(`${message} Please follow the migration guide at https://www.jspsych.org/7.0/support/migration-v7/ to update your experiment.`);
77
+ this.name = "MigrationError";
78
+ }
79
+ }
80
+ // Define a global jsPsych object to handle invocations on it with migration errors
81
+ window.jsPsych = {
82
+ get init() {
83
+ throw new MigrationError("`jsPsych.init()` was replaced by `initJsPsych()` in jsPsych v7.");
84
+ },
85
+ get data() {
86
+ throw new MigrationError();
87
+ },
88
+ get randomization() {
89
+ throw new MigrationError();
90
+ },
91
+ get turk() {
92
+ throw new MigrationError();
93
+ },
94
+ get pluginAPI() {
95
+ throw new MigrationError();
96
+ },
97
+ get ALL_KEYS() {
98
+ throw new MigrationError('jsPsych.ALL_KEYS was replaced by the "ALL_KEYS" string in jsPsych v7.');
99
+ },
100
+ get NO_KEYS() {
101
+ throw new MigrationError('jsPsych.NO_KEYS was replaced by the "NO_KEYS" string in jsPsych v7.');
102
+ },
103
+ };
73
104
 
74
105
  /**
75
106
  * Finds all of the unique items in an array.
@@ -799,6 +830,7 @@ class MediaAPI {
799
830
  this.preload_requests = [];
800
831
  this.img_cache = {};
801
832
  this.preloadMap = new Map();
833
+ this.microphone_recorder = null;
802
834
  }
803
835
  getVideoBuffer(videoID) {
804
836
  return this.video_buffers[videoID];
@@ -1035,6 +1067,184 @@ class MediaAPI {
1035
1067
  }
1036
1068
  this.preload_requests = [];
1037
1069
  }
1070
+ initializeMicrophoneRecorder(stream) {
1071
+ const recorder = new MediaRecorder(stream);
1072
+ this.microphone_recorder = recorder;
1073
+ }
1074
+ getMicrophoneRecorder() {
1075
+ return this.microphone_recorder;
1076
+ }
1077
+ }
1078
+
1079
+ class SimulationAPI {
1080
+ dispatchEvent(event) {
1081
+ document.body.dispatchEvent(event);
1082
+ }
1083
+ /**
1084
+ * Dispatches a `keydown` event for the specified key
1085
+ * @param key Character code (`.key` property) for the key to press.
1086
+ */
1087
+ keyDown(key) {
1088
+ this.dispatchEvent(new KeyboardEvent("keydown", { key }));
1089
+ }
1090
+ /**
1091
+ * Dispatches a `keyup` event for the specified key
1092
+ * @param key Character code (`.key` property) for the key to press.
1093
+ */
1094
+ keyUp(key) {
1095
+ this.dispatchEvent(new KeyboardEvent("keyup", { key }));
1096
+ }
1097
+ /**
1098
+ * Dispatches a `keydown` and `keyup` event in sequence to simulate pressing a key.
1099
+ * @param key Character code (`.key` property) for the key to press.
1100
+ * @param delay Length of time to wait (ms) before executing action
1101
+ */
1102
+ pressKey(key, delay = 0) {
1103
+ if (delay > 0) {
1104
+ setTimeout(() => {
1105
+ this.keyDown(key);
1106
+ this.keyUp(key);
1107
+ }, delay);
1108
+ }
1109
+ else {
1110
+ this.keyDown(key);
1111
+ this.keyUp(key);
1112
+ }
1113
+ }
1114
+ /**
1115
+ * Dispatches `mousedown`, `mouseup`, and `click` events on the target element
1116
+ * @param target The element to click
1117
+ * @param delay Length of time to wait (ms) before executing action
1118
+ */
1119
+ clickTarget(target, delay = 0) {
1120
+ if (delay > 0) {
1121
+ setTimeout(() => {
1122
+ target.dispatchEvent(new MouseEvent("mousedown", { bubbles: true }));
1123
+ target.dispatchEvent(new MouseEvent("mouseup", { bubbles: true }));
1124
+ target.dispatchEvent(new MouseEvent("click", { bubbles: true }));
1125
+ }, delay);
1126
+ }
1127
+ else {
1128
+ target.dispatchEvent(new MouseEvent("mousedown", { bubbles: true }));
1129
+ target.dispatchEvent(new MouseEvent("mouseup", { bubbles: true }));
1130
+ target.dispatchEvent(new MouseEvent("click", { bubbles: true }));
1131
+ }
1132
+ }
1133
+ /**
1134
+ * Sets the value of a target text input
1135
+ * @param target A text input element to fill in
1136
+ * @param text Text to input
1137
+ * @param delay Length of time to wait (ms) before executing action
1138
+ */
1139
+ fillTextInput(target, text, delay = 0) {
1140
+ if (delay > 0) {
1141
+ setTimeout(() => {
1142
+ target.value = text;
1143
+ }, delay);
1144
+ }
1145
+ else {
1146
+ target.value = text;
1147
+ }
1148
+ }
1149
+ /**
1150
+ * Picks a valid key from `choices`, taking into account jsPsych-specific
1151
+ * identifiers like "NO_KEYS" and "ALL_KEYS".
1152
+ * @param choices Which keys are valid.
1153
+ * @returns A key selected at random from the valid keys.
1154
+ */
1155
+ getValidKey(choices) {
1156
+ const possible_keys = [
1157
+ "a",
1158
+ "b",
1159
+ "c",
1160
+ "d",
1161
+ "e",
1162
+ "f",
1163
+ "g",
1164
+ "h",
1165
+ "i",
1166
+ "j",
1167
+ "k",
1168
+ "l",
1169
+ "m",
1170
+ "n",
1171
+ "o",
1172
+ "p",
1173
+ "q",
1174
+ "r",
1175
+ "s",
1176
+ "t",
1177
+ "u",
1178
+ "v",
1179
+ "w",
1180
+ "x",
1181
+ "y",
1182
+ "z",
1183
+ "0",
1184
+ "1",
1185
+ "2",
1186
+ "3",
1187
+ "4",
1188
+ "5",
1189
+ "6",
1190
+ "7",
1191
+ "8",
1192
+ "9",
1193
+ " ",
1194
+ ];
1195
+ let key;
1196
+ if (choices == "NO_KEYS") {
1197
+ key = null;
1198
+ }
1199
+ else if (choices == "ALL_KEYS") {
1200
+ key = possible_keys[Math.floor(Math.random() * possible_keys.length)];
1201
+ }
1202
+ else {
1203
+ const flat_choices = choices.flat();
1204
+ key = flat_choices[Math.floor(Math.random() * flat_choices.length)];
1205
+ }
1206
+ return key;
1207
+ }
1208
+ mergeSimulationData(default_data, simulation_options) {
1209
+ // override any data with data from simulation object
1210
+ return Object.assign(Object.assign({}, default_data), simulation_options === null || simulation_options === void 0 ? void 0 : simulation_options.data);
1211
+ }
1212
+ ensureSimulationDataConsistency(trial, data) {
1213
+ // All RTs must be rounded
1214
+ if (data.rt) {
1215
+ data.rt = Math.round(data.rt);
1216
+ }
1217
+ // If a trial_duration and rt exist, make sure that the RT is not longer than the trial.
1218
+ if (trial.trial_duration && data.rt && data.rt > trial.trial_duration) {
1219
+ data.rt = null;
1220
+ if (data.response) {
1221
+ data.response = null;
1222
+ }
1223
+ if (data.correct) {
1224
+ data.correct = false;
1225
+ }
1226
+ }
1227
+ // If trial.choices is NO_KEYS make sure that response and RT are null
1228
+ if (trial.choices && trial.choices == "NO_KEYS") {
1229
+ if (data.rt) {
1230
+ data.rt = null;
1231
+ }
1232
+ if (data.response) {
1233
+ data.response = null;
1234
+ }
1235
+ }
1236
+ // If response is not allowed before stimulus display complete, ensure RT
1237
+ // is longer than display time.
1238
+ if (trial.allow_response_before_complete) {
1239
+ if (trial.sequence_reps && trial.frame_time) {
1240
+ const min_time = trial.sequence_reps * trial.frame_time * trial.stimuli.length;
1241
+ if (data.rt < min_time) {
1242
+ data.rt = null;
1243
+ data.response = null;
1244
+ }
1245
+ }
1246
+ }
1247
+ }
1038
1248
  }
1039
1249
 
1040
1250
  class TimeoutAPI {
@@ -1061,9 +1271,351 @@ function createJointPluginAPIObject(jsPsych) {
1061
1271
  new TimeoutAPI(),
1062
1272
  new MediaAPI(settings.use_webaudio, jsPsych.webaudio_context),
1063
1273
  new HardwareAPI(),
1274
+ new SimulationAPI(),
1064
1275
  ].map((object) => autoBind(object)));
1065
1276
  }
1066
1277
 
1278
+ var wordList = [
1279
+ // Borrowed from xkcd password generator which borrowed it from wherever
1280
+ "ability","able","aboard","about","above","accept","accident","according",
1281
+ "account","accurate","acres","across","act","action","active","activity",
1282
+ "actual","actually","add","addition","additional","adjective","adult","adventure",
1283
+ "advice","affect","afraid","after","afternoon","again","against","age",
1284
+ "ago","agree","ahead","aid","air","airplane","alike","alive",
1285
+ "all","allow","almost","alone","along","aloud","alphabet","already",
1286
+ "also","although","am","among","amount","ancient","angle","angry",
1287
+ "animal","announced","another","answer","ants","any","anybody","anyone",
1288
+ "anything","anyway","anywhere","apart","apartment","appearance","apple","applied",
1289
+ "appropriate","are","area","arm","army","around","arrange","arrangement",
1290
+ "arrive","arrow","art","article","as","aside","ask","asleep",
1291
+ "at","ate","atmosphere","atom","atomic","attached","attack","attempt",
1292
+ "attention","audience","author","automobile","available","average","avoid","aware",
1293
+ "away","baby","back","bad","badly","bag","balance","ball",
1294
+ "balloon","band","bank","bar","bare","bark","barn","base",
1295
+ "baseball","basic","basis","basket","bat","battle","be","bean",
1296
+ "bear","beat","beautiful","beauty","became","because","become","becoming",
1297
+ "bee","been","before","began","beginning","begun","behavior","behind",
1298
+ "being","believed","bell","belong","below","belt","bend","beneath",
1299
+ "bent","beside","best","bet","better","between","beyond","bicycle",
1300
+ "bigger","biggest","bill","birds","birth","birthday","bit","bite",
1301
+ "black","blank","blanket","blew","blind","block","blood","blow",
1302
+ "blue","board","boat","body","bone","book","border","born",
1303
+ "both","bottle","bottom","bound","bow","bowl","box","boy",
1304
+ "brain","branch","brass","brave","bread","break","breakfast","breath",
1305
+ "breathe","breathing","breeze","brick","bridge","brief","bright","bring",
1306
+ "broad","broke","broken","brother","brought","brown","brush","buffalo",
1307
+ "build","building","built","buried","burn","burst","bus","bush",
1308
+ "business","busy","but","butter","buy","by","cabin","cage",
1309
+ "cake","call","calm","came","camera","camp","can","canal",
1310
+ "cannot","cap","capital","captain","captured","car","carbon","card",
1311
+ "care","careful","carefully","carried","carry","case","cast","castle",
1312
+ "cat","catch","cattle","caught","cause","cave","cell","cent",
1313
+ "center","central","century","certain","certainly","chain","chair","chamber",
1314
+ "chance","change","changing","chapter","character","characteristic","charge","chart",
1315
+ "check","cheese","chemical","chest","chicken","chief","child","children",
1316
+ "choice","choose","chose","chosen","church","circle","circus","citizen",
1317
+ "city","class","classroom","claws","clay","clean","clear","clearly",
1318
+ "climate","climb","clock","close","closely","closer","cloth","clothes",
1319
+ "clothing","cloud","club","coach","coal","coast","coat","coffee",
1320
+ "cold","collect","college","colony","color","column","combination","combine",
1321
+ "come","comfortable","coming","command","common","community","company","compare",
1322
+ "compass","complete","completely","complex","composed","composition","compound","concerned",
1323
+ "condition","congress","connected","consider","consist","consonant","constantly","construction",
1324
+ "contain","continent","continued","contrast","control","conversation","cook","cookies",
1325
+ "cool","copper","copy","corn","corner","correct","correctly","cost",
1326
+ "cotton","could","count","country","couple","courage","course","court",
1327
+ "cover","cow","cowboy","crack","cream","create","creature","crew",
1328
+ "crop","cross","crowd","cry","cup","curious","current","curve",
1329
+ "customs","cut","cutting","daily","damage","dance","danger","dangerous",
1330
+ "dark","darkness","date","daughter","dawn","day","dead","deal",
1331
+ "dear","death","decide","declared","deep","deeply","deer","definition",
1332
+ "degree","depend","depth","describe","desert","design","desk","detail",
1333
+ "determine","develop","development","diagram","diameter","did","die","differ",
1334
+ "difference","different","difficult","difficulty","dig","dinner","direct","direction",
1335
+ "directly","dirt","dirty","disappear","discover","discovery","discuss","discussion",
1336
+ "disease","dish","distance","distant","divide","division","do","doctor",
1337
+ "does","dog","doing","doll","dollar","done","donkey","door",
1338
+ "dot","double","doubt","down","dozen","draw","drawn","dream",
1339
+ "dress","drew","dried","drink","drive","driven","driver","driving",
1340
+ "drop","dropped","drove","dry","duck","due","dug","dull",
1341
+ "during","dust","duty","each","eager","ear","earlier","early",
1342
+ "earn","earth","easier","easily","east","easy","eat","eaten",
1343
+ "edge","education","effect","effort","egg","eight","either","electric",
1344
+ "electricity","element","elephant","eleven","else","empty","end","enemy",
1345
+ "energy","engine","engineer","enjoy","enough","enter","entire","entirely",
1346
+ "environment","equal","equally","equator","equipment","escape","especially","essential",
1347
+ "establish","even","evening","event","eventually","ever","every","everybody",
1348
+ "everyone","everything","everywhere","evidence","exact","exactly","examine","example",
1349
+ "excellent","except","exchange","excited","excitement","exciting","exclaimed","exercise",
1350
+ "exist","expect","experience","experiment","explain","explanation","explore","express",
1351
+ "expression","extra","eye","face","facing","fact","factor","factory",
1352
+ "failed","fair","fairly","fall","fallen","familiar","family","famous",
1353
+ "far","farm","farmer","farther","fast","fastened","faster","fat",
1354
+ "father","favorite","fear","feathers","feature","fed","feed","feel",
1355
+ "feet","fell","fellow","felt","fence","few","fewer","field",
1356
+ "fierce","fifteen","fifth","fifty","fight","fighting","figure","fill",
1357
+ "film","final","finally","find","fine","finest","finger","finish",
1358
+ "fire","fireplace","firm","first","fish","five","fix","flag",
1359
+ "flame","flat","flew","flies","flight","floating","floor","flow",
1360
+ "flower","fly","fog","folks","follow","food","foot","football",
1361
+ "for","force","foreign","forest","forget","forgot","forgotten","form",
1362
+ "former","fort","forth","forty","forward","fought","found","four",
1363
+ "fourth","fox","frame","free","freedom","frequently","fresh","friend",
1364
+ "friendly","frighten","frog","from","front","frozen","fruit","fuel",
1365
+ "full","fully","fun","function","funny","fur","furniture","further",
1366
+ "future","gain","game","garage","garden","gas","gasoline","gate",
1367
+ "gather","gave","general","generally","gentle","gently","get","getting",
1368
+ "giant","gift","girl","give","given","giving","glad","glass",
1369
+ "globe","go","goes","gold","golden","gone","good","goose",
1370
+ "got","government","grabbed","grade","gradually","grain","grandfather","grandmother",
1371
+ "graph","grass","gravity","gray","great","greater","greatest","greatly",
1372
+ "green","grew","ground","group","grow","grown","growth","guard",
1373
+ "guess","guide","gulf","gun","habit","had","hair","half",
1374
+ "halfway","hall","hand","handle","handsome","hang","happen","happened",
1375
+ "happily","happy","harbor","hard","harder","hardly","has","hat",
1376
+ "have","having","hay","he","headed","heading","health","heard",
1377
+ "hearing","heart","heat","heavy","height","held","hello","help",
1378
+ "helpful","her","herd","here","herself","hidden","hide","high",
1379
+ "higher","highest","highway","hill","him","himself","his","history",
1380
+ "hit","hold","hole","hollow","home","honor","hope","horn",
1381
+ "horse","hospital","hot","hour","house","how","however","huge",
1382
+ "human","hundred","hung","hungry","hunt","hunter","hurried","hurry",
1383
+ "hurt","husband","ice","idea","identity","if","ill","image",
1384
+ "imagine","immediately","importance","important","impossible","improve","in","inch",
1385
+ "include","including","income","increase","indeed","independent","indicate","individual",
1386
+ "industrial","industry","influence","information","inside","instance","instant","instead",
1387
+ "instrument","interest","interior","into","introduced","invented","involved","iron",
1388
+ "is","island","it","its","itself","jack","jar","jet",
1389
+ "job","join","joined","journey","joy","judge","jump","jungle",
1390
+ "just","keep","kept","key","kids","kill","kind","kitchen",
1391
+ "knew","knife","know","knowledge","known","label","labor","lack",
1392
+ "lady","laid","lake","lamp","land","language","large","larger",
1393
+ "largest","last","late","later","laugh","law","lay","layers",
1394
+ "lead","leader","leaf","learn","least","leather","leave","leaving",
1395
+ "led","left","leg","length","lesson","let","letter","level",
1396
+ "library","lie","life","lift","light","like","likely","limited",
1397
+ "line","lion","lips","liquid","list","listen","little","live",
1398
+ "living","load","local","locate","location","log","lonely","long",
1399
+ "longer","look","loose","lose","loss","lost","lot","loud",
1400
+ "love","lovely","low","lower","luck","lucky","lunch","lungs",
1401
+ "lying","machine","machinery","mad","made","magic","magnet","mail",
1402
+ "main","mainly","major","make","making","man","managed","manner",
1403
+ "manufacturing","many","map","mark","market","married","mass","massage",
1404
+ "master","material","mathematics","matter","may","maybe","me","meal",
1405
+ "mean","means","meant","measure","meat","medicine","meet","melted",
1406
+ "member","memory","men","mental","merely","met","metal","method",
1407
+ "mice","middle","might","mighty","mile","military","milk","mill",
1408
+ "mind","mine","minerals","minute","mirror","missing","mission","mistake",
1409
+ "mix","mixture","model","modern","molecular","moment","money","monkey",
1410
+ "month","mood","moon","more","morning","most","mostly","mother",
1411
+ "motion","motor","mountain","mouse","mouth","move","movement","movie",
1412
+ "moving","mud","muscle","music","musical","must","my","myself",
1413
+ "mysterious","nails","name","nation","national","native","natural","naturally",
1414
+ "nature","near","nearby","nearer","nearest","nearly","necessary","neck",
1415
+ "needed","needle","needs","negative","neighbor","neighborhood","nervous","nest",
1416
+ "never","new","news","newspaper","next","nice","night","nine",
1417
+ "no","nobody","nodded","noise","none","noon","nor","north",
1418
+ "nose","not","note","noted","nothing","notice","noun","now",
1419
+ "number","numeral","nuts","object","observe","obtain","occasionally","occur",
1420
+ "ocean","of","off","offer","office","officer","official","oil",
1421
+ "old","older","oldest","on","once","one","only","onto",
1422
+ "open","operation","opinion","opportunity","opposite","or","orange","orbit",
1423
+ "order","ordinary","organization","organized","origin","original","other","ought",
1424
+ "our","ourselves","out","outer","outline","outside","over","own",
1425
+ "owner","oxygen","pack","package","page","paid","pain","paint",
1426
+ "pair","palace","pale","pan","paper","paragraph","parallel","parent",
1427
+ "park","part","particles","particular","particularly","partly","parts","party",
1428
+ "pass","passage","past","path","pattern","pay","peace","pen",
1429
+ "pencil","people","per","percent","perfect","perfectly","perhaps","period",
1430
+ "person","personal","pet","phrase","physical","piano","pick","picture",
1431
+ "pictured","pie","piece","pig","pile","pilot","pine","pink",
1432
+ "pipe","pitch","place","plain","plan","plane","planet","planned",
1433
+ "planning","plant","plastic","plate","plates","play","pleasant","please",
1434
+ "pleasure","plenty","plural","plus","pocket","poem","poet","poetry",
1435
+ "point","pole","police","policeman","political","pond","pony","pool",
1436
+ "poor","popular","population","porch","port","position","positive","possible",
1437
+ "possibly","post","pot","potatoes","pound","pour","powder","power",
1438
+ "powerful","practical","practice","prepare","present","president","press","pressure",
1439
+ "pretty","prevent","previous","price","pride","primitive","principal","principle",
1440
+ "printed","private","prize","probably","problem","process","produce","product",
1441
+ "production","program","progress","promised","proper","properly","property","protection",
1442
+ "proud","prove","provide","public","pull","pupil","pure","purple",
1443
+ "purpose","push","put","putting","quarter","queen","question","quick",
1444
+ "quickly","quiet","quietly","quite","rabbit","race","radio","railroad",
1445
+ "rain","raise","ran","ranch","range","rapidly","rate","rather",
1446
+ "raw","rays","reach","read","reader","ready","real","realize",
1447
+ "rear","reason","recall","receive","recent","recently","recognize","record",
1448
+ "red","refer","refused","region","regular","related","relationship","religious",
1449
+ "remain","remarkable","remember","remove","repeat","replace","replied","report",
1450
+ "represent","require","research","respect","rest","result","return","review",
1451
+ "rhyme","rhythm","rice","rich","ride","riding","right","ring",
1452
+ "rise","rising","river","road","roar","rock","rocket","rocky",
1453
+ "rod","roll","roof","room","root","rope","rose","rough",
1454
+ "round","route","row","rubbed","rubber","rule","ruler","run",
1455
+ "running","rush","sad","saddle","safe","safety","said","sail",
1456
+ "sale","salmon","salt","same","sand","sang","sat","satellites",
1457
+ "satisfied","save","saved","saw","say","scale","scared","scene",
1458
+ "school","science","scientific","scientist","score","screen","sea","search",
1459
+ "season","seat","second","secret","section","see","seed","seeing",
1460
+ "seems","seen","seldom","select","selection","sell","send","sense",
1461
+ "sent","sentence","separate","series","serious","serve","service","sets",
1462
+ "setting","settle","settlers","seven","several","shade","shadow","shake",
1463
+ "shaking","shall","shallow","shape","share","sharp","she","sheep",
1464
+ "sheet","shelf","shells","shelter","shine","shinning","ship","shirt",
1465
+ "shoe","shoot","shop","shore","short","shorter","shot","should",
1466
+ "shoulder","shout","show","shown","shut","sick","sides","sight",
1467
+ "sign","signal","silence","silent","silk","silly","silver","similar",
1468
+ "simple","simplest","simply","since","sing","single","sink","sister",
1469
+ "sit","sitting","situation","six","size","skill","skin","sky",
1470
+ "slabs","slave","sleep","slept","slide","slight","slightly","slip",
1471
+ "slipped","slope","slow","slowly","small","smaller","smallest","smell",
1472
+ "smile","smoke","smooth","snake","snow","so","soap","social",
1473
+ "society","soft","softly","soil","solar","sold","soldier","solid",
1474
+ "solution","solve","some","somebody","somehow","someone","something","sometime",
1475
+ "somewhere","son","song","soon","sort","sound","source","south",
1476
+ "southern","space","speak","special","species","specific","speech","speed",
1477
+ "spell","spend","spent","spider","spin","spirit","spite","split",
1478
+ "spoken","sport","spread","spring","square","stage","stairs","stand",
1479
+ "standard","star","stared","start","state","statement","station","stay",
1480
+ "steady","steam","steel","steep","stems","step","stepped","stick",
1481
+ "stiff","still","stock","stomach","stone","stood","stop","stopped",
1482
+ "store","storm","story","stove","straight","strange","stranger","straw",
1483
+ "stream","street","strength","stretch","strike","string","strip","strong",
1484
+ "stronger","struck","structure","struggle","stuck","student","studied","studying",
1485
+ "subject","substance","success","successful","such","sudden","suddenly","sugar",
1486
+ "suggest","suit","sum","summer","sun","sunlight","supper","supply",
1487
+ "support","suppose","sure","surface","surprise","surrounded","swam","sweet",
1488
+ "swept","swim","swimming","swing","swung","syllable","symbol","system",
1489
+ "table","tail","take","taken","tales","talk","tall","tank",
1490
+ "tape","task","taste","taught","tax","tea","teach","teacher",
1491
+ "team","tears","teeth","telephone","television","tell","temperature","ten",
1492
+ "tent","term","terrible","test","than","thank","that","thee",
1493
+ "them","themselves","then","theory","there","therefore","these","they",
1494
+ "thick","thin","thing","think","third","thirty","this","those",
1495
+ "thou","though","thought","thousand","thread","three","threw","throat",
1496
+ "through","throughout","throw","thrown","thumb","thus","thy","tide",
1497
+ "tie","tight","tightly","till","time","tin","tiny","tip",
1498
+ "tired","title","to","tobacco","today","together","told","tomorrow",
1499
+ "tone","tongue","tonight","too","took","tool","top","topic",
1500
+ "torn","total","touch","toward","tower","town","toy","trace",
1501
+ "track","trade","traffic","trail","train","transportation","trap","travel",
1502
+ "treated","tree","triangle","tribe","trick","tried","trip","troops",
1503
+ "tropical","trouble","truck","trunk","truth","try","tube","tune",
1504
+ "turn","twelve","twenty","twice","two","type","typical","uncle",
1505
+ "under","underline","understanding","unhappy","union","unit","universe","unknown",
1506
+ "unless","until","unusual","up","upon","upper","upward","us",
1507
+ "use","useful","using","usual","usually","valley","valuable","value",
1508
+ "vapor","variety","various","vast","vegetable","verb","vertical","very",
1509
+ "vessels","victory","view","village","visit","visitor","voice","volume",
1510
+ "vote","vowel","voyage","wagon","wait","walk","wall","want",
1511
+ "war","warm","warn","was","wash","waste","watch","water",
1512
+ "wave","way","we","weak","wealth","wear","weather","week",
1513
+ "weigh","weight","welcome","well","went","were","west","western",
1514
+ "wet","whale","what","whatever","wheat","wheel","when","whenever",
1515
+ "where","wherever","whether","which","while","whispered","whistle","white",
1516
+ "who","whole","whom","whose","why","wide","widely","wife",
1517
+ "wild","will","willing","win","wind","window","wing","winter",
1518
+ "wire","wise","wish","with","within","without","wolf","women",
1519
+ "won","wonder","wonderful","wood","wooden","wool","word","wore",
1520
+ "work","worker","world","worried","worry","worse","worth","would",
1521
+ "wrapped","write","writer","writing","written","wrong","wrote","yard",
1522
+ "year","yellow","yes","yesterday","yet","you","young","younger",
1523
+ "your","yourself","youth","zero","zebra","zipper","zoo","zulu"
1524
+ ];
1525
+
1526
+ function words(options) {
1527
+
1528
+ function word() {
1529
+ if (options && options.maxLength > 1) {
1530
+ return generateWordWithMaxLength();
1531
+ } else {
1532
+ return generateRandomWord();
1533
+ }
1534
+ }
1535
+
1536
+ function generateWordWithMaxLength() {
1537
+ var rightSize = false;
1538
+ var wordUsed;
1539
+ while (!rightSize) {
1540
+ wordUsed = generateRandomWord();
1541
+ if(wordUsed.length <= options.maxLength) {
1542
+ rightSize = true;
1543
+ }
1544
+
1545
+ }
1546
+ return wordUsed;
1547
+ }
1548
+
1549
+ function generateRandomWord() {
1550
+ return wordList[randInt(wordList.length)];
1551
+ }
1552
+
1553
+ function randInt(lessThan) {
1554
+ return Math.floor(Math.random() * lessThan);
1555
+ }
1556
+
1557
+ // No arguments = generate one word
1558
+ if (typeof(options) === 'undefined') {
1559
+ return word();
1560
+ }
1561
+
1562
+ // Just a number = return that many words
1563
+ if (typeof(options) === 'number') {
1564
+ options = { exactly: options };
1565
+ }
1566
+
1567
+ // options supported: exactly, min, max, join
1568
+ if (options.exactly) {
1569
+ options.min = options.exactly;
1570
+ options.max = options.exactly;
1571
+ }
1572
+
1573
+ // not a number = one word par string
1574
+ if (typeof(options.wordsPerString) !== 'number') {
1575
+ options.wordsPerString = 1;
1576
+ }
1577
+
1578
+ //not a function = returns the raw word
1579
+ if (typeof(options.formatter) !== 'function') {
1580
+ options.formatter = (word) => word;
1581
+ }
1582
+
1583
+ //not a string = separator is a space
1584
+ if (typeof(options.separator) !== 'string') {
1585
+ options.separator = ' ';
1586
+ }
1587
+
1588
+ var total = options.min + randInt(options.max + 1 - options.min);
1589
+ var results = [];
1590
+ var token = '';
1591
+ var relativeIndex = 0;
1592
+
1593
+ for (var i = 0; (i < total * options.wordsPerString); i++) {
1594
+ if (relativeIndex === options.wordsPerString - 1) {
1595
+ token += options.formatter(word(), relativeIndex);
1596
+ }
1597
+ else {
1598
+ token += options.formatter(word(), relativeIndex) + options.separator;
1599
+ }
1600
+ relativeIndex++;
1601
+ if ((i + 1) % options.wordsPerString === 0) {
1602
+ results.push(token);
1603
+ token = '';
1604
+ relativeIndex = 0;
1605
+ }
1606
+
1607
+ }
1608
+ if (typeof options.join === 'string') {
1609
+ results = results.join(options.join);
1610
+ }
1611
+
1612
+ return results;
1613
+ }
1614
+
1615
+ var randomWords$1 = words;
1616
+ // Export the word list as it is often useful
1617
+ words.wordList = wordList;
1618
+
1067
1619
  function repeat(array, repetitions, unpack = false) {
1068
1620
  const arr_isArray = Array.isArray(array);
1069
1621
  const rep_isArray = Array.isArray(repetitions);
@@ -1273,6 +1825,64 @@ function randomID(length = 32) {
1273
1825
  }
1274
1826
  return result;
1275
1827
  }
1828
+ /**
1829
+ * Generate a random integer from `lower` to `upper`, inclusive of both end points.
1830
+ * @param lower The lowest value it is possible to generate
1831
+ * @param upper The highest value it is possible to generate
1832
+ * @returns A random integer
1833
+ */
1834
+ function randomInt(lower, upper) {
1835
+ if (upper < lower) {
1836
+ throw new Error("Upper boundary must be less than or equal to lower boundary");
1837
+ }
1838
+ return lower + Math.floor(Math.random() * (upper - lower + 1));
1839
+ }
1840
+ /**
1841
+ * Generates a random sample from a Bernoulli distribution.
1842
+ * @param p The probability of sampling 1.
1843
+ * @returns 0, with probability 1-p, or 1, with probability p.
1844
+ */
1845
+ function sampleBernoulli(p) {
1846
+ return Math.random() <= p ? 1 : 0;
1847
+ }
1848
+ function sampleNormal(mean, standard_deviation) {
1849
+ return randn_bm() * standard_deviation + mean;
1850
+ }
1851
+ function sampleExponential(rate) {
1852
+ return -Math.log(Math.random()) / rate;
1853
+ }
1854
+ function sampleExGaussian(mean, standard_deviation, rate, positive = false) {
1855
+ let s = sampleNormal(mean, standard_deviation) + sampleExponential(rate);
1856
+ if (positive) {
1857
+ while (s <= 0) {
1858
+ s = sampleNormal(mean, standard_deviation) + sampleExponential(rate);
1859
+ }
1860
+ }
1861
+ return s;
1862
+ }
1863
+ /**
1864
+ * Generate one or more random words.
1865
+ *
1866
+ * This is a wrapper function for the {@link https://www.npmjs.com/package/random-words `random-words` npm package}.
1867
+ *
1868
+ * @param opts An object with optional properties `min`, `max`, `exactly`,
1869
+ * `join`, `maxLength`, `wordsPerString`, `separator`, and `formatter`.
1870
+ *
1871
+ * @returns An array of words or a single string, depending on parameter choices.
1872
+ */
1873
+ function randomWords(opts) {
1874
+ return randomWords$1(opts);
1875
+ }
1876
+ // Box-Muller transformation for a random sample from normal distribution with mean = 0, std = 1
1877
+ // https://stackoverflow.com/a/36481059/3726673
1878
+ function randn_bm() {
1879
+ var u = 0, v = 0;
1880
+ while (u === 0)
1881
+ u = Math.random(); //Converting [0,1) to (0,1)
1882
+ while (v === 0)
1883
+ v = Math.random();
1884
+ return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
1885
+ }
1276
1886
  function unpackArray(array) {
1277
1887
  const out = {};
1278
1888
  for (const x of array) {
@@ -1295,7 +1905,13 @@ var randomization = /*#__PURE__*/Object.freeze({
1295
1905
  sampleWithoutReplacement: sampleWithoutReplacement,
1296
1906
  sampleWithReplacement: sampleWithReplacement,
1297
1907
  factorial: factorial,
1298
- randomID: randomID
1908
+ randomID: randomID,
1909
+ randomInt: randomInt,
1910
+ sampleBernoulli: sampleBernoulli,
1911
+ sampleNormal: sampleNormal,
1912
+ sampleExponential: sampleExponential,
1913
+ sampleExGaussian: sampleExGaussian,
1914
+ randomWords: randomWords
1299
1915
  });
1300
1916
 
1301
1917
  /**
@@ -1842,6 +2458,10 @@ class JsPsych {
1842
2458
  * is the page retrieved directly via file:// protocol (true) or hosted on a server (false)?
1843
2459
  */
1844
2460
  this.file_protocol = false;
2461
+ /**
2462
+ * is the experiment running in `simulate()` mode
2463
+ */
2464
+ this.simulation_mode = null;
1845
2465
  // storing a single webaudio context to prevent problems with multiple inits
1846
2466
  // of jsPsych
1847
2467
  this.webaudio_context = null;
@@ -1911,6 +2531,13 @@ class JsPsych {
1911
2531
  yield this.finished;
1912
2532
  });
1913
2533
  }
2534
+ simulate(timeline, simulation_mode, simulation_options = {}) {
2535
+ return __awaiter(this, void 0, void 0, function* () {
2536
+ this.simulation_mode = simulation_mode;
2537
+ this.simulation_options = simulation_options;
2538
+ yield this.run(timeline);
2539
+ });
2540
+ }
1914
2541
  getProgress() {
1915
2542
  return {
1916
2543
  total_trials: typeof this.timeline === "undefined" ? undefined : this.timeline.length(),
@@ -2013,12 +2640,12 @@ class JsPsych {
2013
2640
  }
2014
2641
  }
2015
2642
  }
2016
- endExperiment(end_message) {
2643
+ endExperiment(end_message = "", data = {}) {
2017
2644
  this.timeline.end_message = end_message;
2018
2645
  this.timeline.end();
2019
2646
  this.pluginAPI.cancelAllKeyboardResponses();
2020
2647
  this.pluginAPI.clearAllTimeouts();
2021
- this.finishTrial();
2648
+ this.finishTrial(data);
2022
2649
  }
2023
2650
  endCurrentTimeline() {
2024
2651
  this.timeline.endActiveNode();
@@ -2196,6 +2823,9 @@ class JsPsych {
2196
2823
  this.current_trial_finished = false;
2197
2824
  // process all timeline variables for this trial
2198
2825
  this.evaluateTimelineVariables(trial);
2826
+ if (typeof trial.type === "string") {
2827
+ throw new MigrationError("A string was provided as the trial's `type` parameter. Since jsPsych v7, the `type` parameter needs to be a plugin object.");
2828
+ }
2199
2829
  // instantiate the plugin for this trial
2200
2830
  trial.type = Object.assign(Object.assign({}, autoBind(new trial.type(this))), { info: trial.type.info });
2201
2831
  // evaluate variables that are functions
@@ -2241,10 +2871,53 @@ class JsPsych {
2241
2871
  }
2242
2872
  }
2243
2873
  };
2244
- const trial_complete = trial.type.trial(this.DOM_target, trial, load_callback);
2874
+ let trial_complete;
2875
+ if (!this.simulation_mode) {
2876
+ trial_complete = trial.type.trial(this.DOM_target, trial, load_callback);
2877
+ }
2878
+ if (this.simulation_mode) {
2879
+ // check if the trial supports simulation
2880
+ if (trial.type.simulate) {
2881
+ let trial_sim_opts;
2882
+ if (!trial.simulation_options) {
2883
+ trial_sim_opts = this.simulation_options.default;
2884
+ }
2885
+ if (trial.simulation_options) {
2886
+ if (typeof trial.simulation_options == "string") {
2887
+ if (this.simulation_options[trial.simulation_options]) {
2888
+ trial_sim_opts = this.simulation_options[trial.simulation_options];
2889
+ }
2890
+ else if (this.simulation_options.default) {
2891
+ console.log(`No matching simulation options found for "${trial.simulation_options}". Using "default" options.`);
2892
+ trial_sim_opts = this.simulation_options.default;
2893
+ }
2894
+ else {
2895
+ console.log(`No matching simulation options found for "${trial.simulation_options}" and no "default" options provided. Using the default values provided by the plugin.`);
2896
+ trial_sim_opts = {};
2897
+ }
2898
+ }
2899
+ else {
2900
+ trial_sim_opts = trial.simulation_options;
2901
+ }
2902
+ }
2903
+ trial_sim_opts = this.utils.deepCopy(trial_sim_opts);
2904
+ trial_sim_opts = this.replaceFunctionsWithValues(trial_sim_opts, null);
2905
+ if ((trial_sim_opts === null || trial_sim_opts === void 0 ? void 0 : trial_sim_opts.simulate) === false) {
2906
+ trial_complete = trial.type.trial(this.DOM_target, trial, load_callback);
2907
+ }
2908
+ else {
2909
+ 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);
2910
+ }
2911
+ }
2912
+ else {
2913
+ // trial doesn't have a simulate method, so just run as usual
2914
+ trial_complete = trial.type.trial(this.DOM_target, trial, load_callback);
2915
+ }
2916
+ }
2245
2917
  // see if trial_complete is a Promise by looking for .then() function
2246
2918
  const is_promise = trial_complete && typeof trial_complete.then == "function";
2247
- if (!is_promise) {
2919
+ // in simulation mode we let the simulate function call the load_callback always.
2920
+ if (!is_promise && !this.simulation_mode) {
2248
2921
  load_callback();
2249
2922
  }
2250
2923
  // done with callbacks
@@ -2370,6 +3043,9 @@ class JsPsych {
2370
3043
  }
2371
3044
  checkExclusions(exclusions) {
2372
3045
  return __awaiter(this, void 0, void 0, function* () {
3046
+ if (exclusions.min_width || exclusions.min_height || exclusions.audio) {
3047
+ console.warn("The exclusions option in `initJsPsych()` is deprecated and will be removed in a future version. We recommend using the browser-check plugin instead. See https://www.jspsych.org/latest/plugins/browser-check/.");
3048
+ }
2373
3049
  // MINIMUM SIZE
2374
3050
  if (exclusions.min_width || exclusions.min_height) {
2375
3051
  const mw = exclusions.min_width || 0;
@@ -2450,7 +3126,30 @@ if (typeof window !== "undefined" &&
2450
3126
  * @returns A new JsPsych instance
2451
3127
  */
2452
3128
  function initJsPsych(options) {
2453
- return new JsPsych(options);
3129
+ const jsPsych = new JsPsych(options);
3130
+ // Handle invocations of non-existent v6 methods with migration errors
3131
+ const migrationMessages = {
3132
+ init: "`jsPsych.init()` was replaced by `initJsPsych()` in jsPsych v7.",
3133
+ ALL_KEYS: 'jsPsych.ALL_KEYS was replaced by the "ALL_KEYS" string in jsPsych v7.',
3134
+ NO_KEYS: 'jsPsych.NO_KEYS was replaced by the "NO_KEYS" string in jsPsych v7.',
3135
+ // Getter functions that were renamed
3136
+ currentTimelineNodeID: "`currentTimelineNodeID()` was renamed to `getCurrentTimelineNodeID()` in jsPsych v7.",
3137
+ progress: "`progress()` was renamed to `getProgress()` in jsPsych v7.",
3138
+ startTime: "`startTime()` was renamed to `getStartTime()` in jsPsych v7.",
3139
+ totalTime: "`totalTime()` was renamed to `getTotalTime()` in jsPsych v7.",
3140
+ currentTrial: "`currentTrial()` was renamed to `getCurrentTrial()` in jsPsych v7.",
3141
+ initSettings: "`initSettings()` was renamed to `getInitSettings()` in jsPsych v7.",
3142
+ allTimelineVariables: "`allTimelineVariables()` was renamed to `getAllTimelineVariables()` in jsPsych v7.",
3143
+ };
3144
+ Object.defineProperties(jsPsych, Object.fromEntries(Object.entries(migrationMessages).map(([key, message]) => [
3145
+ key,
3146
+ {
3147
+ get() {
3148
+ throw new MigrationError(message);
3149
+ },
3150
+ },
3151
+ ])));
3152
+ return jsPsych;
2454
3153
  }
2455
3154
 
2456
3155
  exports.JsPsych = JsPsych;