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