jspsych 6.2.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.
Files changed (360) hide show
  1. package/README.md +43 -29
  2. package/css/jspsych.css +39 -39
  3. package/dist/JsPsych.d.ts +112 -0
  4. package/dist/TimelineNode.d.ts +34 -0
  5. package/dist/index.browser.js +3164 -0
  6. package/dist/index.browser.js.map +1 -0
  7. package/dist/index.browser.min.js +2 -0
  8. package/dist/index.browser.min.js.map +1 -0
  9. package/dist/index.cjs +3158 -0
  10. package/dist/index.cjs.map +1 -0
  11. package/dist/index.d.ts +11 -0
  12. package/dist/index.js +3152 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/migration.d.ts +3 -0
  15. package/dist/modules/data/DataCollection.d.ts +45 -0
  16. package/dist/modules/data/DataColumn.d.ts +15 -0
  17. package/dist/modules/data/index.d.ts +25 -0
  18. package/dist/modules/data/utils.d.ts +3 -0
  19. package/dist/modules/extensions.d.ts +22 -0
  20. package/dist/modules/plugin-api/HardwareAPI.d.ts +15 -0
  21. package/dist/modules/plugin-api/KeyboardListenerAPI.d.ts +34 -0
  22. package/dist/modules/plugin-api/MediaAPI.d.ts +27 -0
  23. package/dist/modules/plugin-api/SimulationAPI.d.ts +41 -0
  24. package/dist/modules/plugin-api/TimeoutAPI.d.ts +5 -0
  25. package/dist/modules/plugin-api/index.d.ts +8 -0
  26. package/dist/modules/plugins.d.ts +129 -0
  27. package/dist/modules/randomization.d.ts +35 -0
  28. package/dist/modules/turk.d.ts +40 -0
  29. package/dist/modules/utils.d.ts +7 -0
  30. package/package.json +32 -15
  31. package/src/JsPsych.ts +884 -0
  32. package/src/TimelineNode.ts +536 -0
  33. package/src/index.ts +71 -0
  34. package/src/migration.ts +37 -0
  35. package/src/modules/data/DataCollection.ts +198 -0
  36. package/src/modules/data/DataColumn.ts +86 -0
  37. package/src/modules/data/index.ts +174 -0
  38. package/src/modules/data/utils.ts +75 -0
  39. package/src/modules/extensions.ts +23 -0
  40. package/src/modules/plugin-api/HardwareAPI.ts +32 -0
  41. package/src/modules/plugin-api/KeyboardListenerAPI.ts +165 -0
  42. package/src/modules/plugin-api/MediaAPI.ts +337 -0
  43. package/src/modules/plugin-api/SimulationAPI.ts +181 -0
  44. package/src/modules/plugin-api/TimeoutAPI.ts +16 -0
  45. package/src/modules/plugin-api/index.ts +28 -0
  46. package/src/modules/plugins.ts +158 -0
  47. package/src/modules/randomization.ts +327 -0
  48. package/src/modules/turk.ts +99 -0
  49. package/src/modules/utils.ts +30 -0
  50. package/.github/workflows/jest.yml +0 -20
  51. package/code-of-conduct.md +0 -56
  52. package/contributors.md +0 -61
  53. package/docs/CNAME +0 -1
  54. package/docs/about/about.md +0 -18
  55. package/docs/about/contributing.md +0 -43
  56. package/docs/about/license.md +0 -25
  57. package/docs/about/support.md +0 -7
  58. package/docs/core_library/jspsych-core.md +0 -661
  59. package/docs/core_library/jspsych-data.md +0 -589
  60. package/docs/core_library/jspsych-pluginAPI.md +0 -510
  61. package/docs/core_library/jspsych-randomization.md +0 -397
  62. package/docs/core_library/jspsych-turk.md +0 -102
  63. package/docs/img/blue.png +0 -0
  64. package/docs/img/folder-setup.png +0 -0
  65. package/docs/img/folder-with-html.png +0 -0
  66. package/docs/img/githubreleases.jpg +0 -0
  67. package/docs/img/jspsych-favicon.png +0 -0
  68. package/docs/img/jspsych-logo-no-text-mono.svg +0 -493
  69. package/docs/img/jspsych-logo.jpg +0 -0
  70. package/docs/img/orange.png +0 -0
  71. package/docs/img/palmer_stim.png +0 -0
  72. package/docs/img/progress_bar.png +0 -0
  73. package/docs/img/visual_search_example.jpg +0 -0
  74. package/docs/index.md +0 -9
  75. package/docs/overview/browser-device-support.md +0 -35
  76. package/docs/overview/callbacks.md +0 -140
  77. package/docs/overview/data.md +0 -281
  78. package/docs/overview/exclude-browser.md +0 -32
  79. package/docs/overview/experiment-options.md +0 -121
  80. package/docs/overview/fullscreen.md +0 -36
  81. package/docs/overview/media-preloading.md +0 -91
  82. package/docs/overview/mturk.md +0 -77
  83. package/docs/overview/progress-bar.md +0 -110
  84. package/docs/overview/record-browser-interactions.md +0 -23
  85. package/docs/overview/running-experiments.md +0 -95
  86. package/docs/overview/timeline.md +0 -387
  87. package/docs/overview/trial.md +0 -142
  88. package/docs/plugins/creating-a-plugin.md +0 -79
  89. package/docs/plugins/jspsych-animation.md +0 -40
  90. package/docs/plugins/jspsych-audio-button-response.md +0 -60
  91. package/docs/plugins/jspsych-audio-keyboard-response.md +0 -58
  92. package/docs/plugins/jspsych-audio-slider-response.md +0 -53
  93. package/docs/plugins/jspsych-call-function.md +0 -81
  94. package/docs/plugins/jspsych-canvas-button-response.md +0 -66
  95. package/docs/plugins/jspsych-canvas-keyboard-response.md +0 -68
  96. package/docs/plugins/jspsych-canvas-slider-response.md +0 -89
  97. package/docs/plugins/jspsych-categorize-animation.md +0 -60
  98. package/docs/plugins/jspsych-categorize-html.md +0 -52
  99. package/docs/plugins/jspsych-categorize-image.md +0 -53
  100. package/docs/plugins/jspsych-cloze.md +0 -45
  101. package/docs/plugins/jspsych-external-html.md +0 -70
  102. package/docs/plugins/jspsych-free-sort.md +0 -55
  103. package/docs/plugins/jspsych-fullscreen.md +0 -57
  104. package/docs/plugins/jspsych-html-button-response.md +0 -42
  105. package/docs/plugins/jspsych-html-keyboard-response.md +0 -51
  106. package/docs/plugins/jspsych-html-slider-response.md +0 -45
  107. package/docs/plugins/jspsych-iat-html.md +0 -64
  108. package/docs/plugins/jspsych-iat-image.md +0 -64
  109. package/docs/plugins/jspsych-image-button-response.md +0 -46
  110. package/docs/plugins/jspsych-image-keyboard-response.md +0 -57
  111. package/docs/plugins/jspsych-image-slider-response.md +0 -52
  112. package/docs/plugins/jspsych-instructions.md +0 -58
  113. package/docs/plugins/jspsych-maxdiff.md +0 -42
  114. package/docs/plugins/jspsych-rdk.md +0 -119
  115. package/docs/plugins/jspsych-reconstruction.md +0 -48
  116. package/docs/plugins/jspsych-resize.md +0 -39
  117. package/docs/plugins/jspsych-same-different-html.md +0 -53
  118. package/docs/plugins/jspsych-same-different-image.md +0 -66
  119. package/docs/plugins/jspsych-serial-reaction-time-mouse.md +0 -50
  120. package/docs/plugins/jspsych-serial-reaction-time.md +0 -57
  121. package/docs/plugins/jspsych-survey-html-form.md +0 -50
  122. package/docs/plugins/jspsych-survey-likert.md +0 -70
  123. package/docs/plugins/jspsych-survey-multi-choice.md +0 -48
  124. package/docs/plugins/jspsych-survey-multi-select.md +0 -53
  125. package/docs/plugins/jspsych-survey-text.md +0 -63
  126. package/docs/plugins/jspsych-video-button-response.md +0 -52
  127. package/docs/plugins/jspsych-video-keyboard-response.md +0 -48
  128. package/docs/plugins/jspsych-video-slider-response.md +0 -58
  129. package/docs/plugins/jspsych-visual-search-circle.md +0 -52
  130. package/docs/plugins/jspsych-vsl-animate-occlusion.md +0 -55
  131. package/docs/plugins/jspsych-vsl-grid-scene.md +0 -62
  132. package/docs/plugins/overview.md +0 -111
  133. package/docs/tutorials/hello-world.md +0 -144
  134. package/docs/tutorials/rt-task.md +0 -1107
  135. package/examples/add-to-end-of-timeline.html +0 -32
  136. package/examples/conditional-and-loop-functions.html +0 -63
  137. package/examples/css/jquery-ui.css +0 -1225
  138. package/examples/data-add-properties.html +0 -40
  139. package/examples/data-as-function.html +0 -36
  140. package/examples/data-from-timeline.html +0 -45
  141. package/examples/data-from-url.html +0 -21
  142. package/examples/demo-flanker.html +0 -108
  143. package/examples/demo-simple-rt-task.html +0 -104
  144. package/examples/demos/demo_1.html +0 -29
  145. package/examples/demos/demo_2.html +0 -43
  146. package/examples/demos/demo_3.html +0 -58
  147. package/examples/display-element-to-embed-experiment.html +0 -73
  148. package/examples/end-active-node.html +0 -52
  149. package/examples/end-experiment.html +0 -43
  150. package/examples/exclusions.html +0 -32
  151. package/examples/external_html/simple_consent.html +0 -4
  152. package/examples/img/1.gif +0 -0
  153. package/examples/img/10.gif +0 -0
  154. package/examples/img/11.gif +0 -0
  155. package/examples/img/12.gif +0 -0
  156. package/examples/img/2.gif +0 -0
  157. package/examples/img/3.gif +0 -0
  158. package/examples/img/4.gif +0 -0
  159. package/examples/img/5.gif +0 -0
  160. package/examples/img/6.gif +0 -0
  161. package/examples/img/7.gif +0 -0
  162. package/examples/img/8.gif +0 -0
  163. package/examples/img/9.gif +0 -0
  164. package/examples/img/age/of1.jpg +0 -0
  165. package/examples/img/age/of2.jpg +0 -0
  166. package/examples/img/age/of3.jpg +0 -0
  167. package/examples/img/age/om1.jpg +0 -0
  168. package/examples/img/age/om2.jpg +0 -0
  169. package/examples/img/age/om3.jpg +0 -0
  170. package/examples/img/age/yf1.jpg +0 -0
  171. package/examples/img/age/yf4.jpg +0 -0
  172. package/examples/img/age/yf5.jpg +0 -0
  173. package/examples/img/age/ym2.jpg +0 -0
  174. package/examples/img/age/ym3.jpg +0 -0
  175. package/examples/img/age/ym5.jpg +0 -0
  176. package/examples/img/backwardN.gif +0 -0
  177. package/examples/img/blue.png +0 -0
  178. package/examples/img/con1.png +0 -0
  179. package/examples/img/con2.png +0 -0
  180. package/examples/img/fixation.gif +0 -0
  181. package/examples/img/happy_face_1.jpg +0 -0
  182. package/examples/img/happy_face_2.jpg +0 -0
  183. package/examples/img/happy_face_3.jpg +0 -0
  184. package/examples/img/happy_face_4.jpg +0 -0
  185. package/examples/img/inc1.png +0 -0
  186. package/examples/img/inc2.png +0 -0
  187. package/examples/img/normalN.gif +0 -0
  188. package/examples/img/orange.png +0 -0
  189. package/examples/img/redX.png +0 -0
  190. package/examples/img/ribbon.jpg +0 -0
  191. package/examples/img/sad_face_1.jpg +0 -0
  192. package/examples/img/sad_face_2.jpg +0 -0
  193. package/examples/img/sad_face_3.jpg +0 -0
  194. package/examples/img/sad_face_4.jpg +0 -0
  195. package/examples/js/snap.svg-min.js +0 -21
  196. package/examples/jspsych-RDK.html +0 -58
  197. package/examples/jspsych-animation.html +0 -33
  198. package/examples/jspsych-audio-button-response.html +0 -52
  199. package/examples/jspsych-audio-keyboard-response.html +0 -62
  200. package/examples/jspsych-audio-slider-response.html +0 -55
  201. package/examples/jspsych-call-function.html +0 -32
  202. package/examples/jspsych-canvas-button-response.html +0 -95
  203. package/examples/jspsych-canvas-keyboard-response.html +0 -78
  204. package/examples/jspsych-canvas-slider-response.html +0 -67
  205. package/examples/jspsych-categorize-animation.html +0 -46
  206. package/examples/jspsych-categorize-html.html +0 -38
  207. package/examples/jspsych-categorize-image.html +0 -38
  208. package/examples/jspsych-cloze.html +0 -42
  209. package/examples/jspsych-free-sort.html +0 -97
  210. package/examples/jspsych-fullscreen.html +0 -44
  211. package/examples/jspsych-html-button-response.html +0 -46
  212. package/examples/jspsych-html-keyboard-response.html +0 -42
  213. package/examples/jspsych-html-slider-response.html +0 -53
  214. package/examples/jspsych-iat.html +0 -510
  215. package/examples/jspsych-image-button-response.html +0 -84
  216. package/examples/jspsych-image-keyboard-response.html +0 -78
  217. package/examples/jspsych-image-slider-response.html +0 -76
  218. package/examples/jspsych-instructions.html +0 -37
  219. package/examples/jspsych-maxdiff.html +0 -33
  220. package/examples/jspsych-reconstruction.html +0 -43
  221. package/examples/jspsych-resize.html +0 -34
  222. package/examples/jspsych-same-different-html.html +0 -28
  223. package/examples/jspsych-same-different-image.html +0 -33
  224. package/examples/jspsych-serial-reaction-time-mouse.html +0 -98
  225. package/examples/jspsych-serial-reaction-time.html +0 -54
  226. package/examples/jspsych-survey-html-form.html +0 -33
  227. package/examples/jspsych-survey-likert.html +0 -42
  228. package/examples/jspsych-survey-multi-choice.html +0 -40
  229. package/examples/jspsych-survey-multi-select.html +0 -42
  230. package/examples/jspsych-survey-text.html +0 -34
  231. package/examples/jspsych-video-button-response.html +0 -57
  232. package/examples/jspsych-video-keyboard-response.html +0 -53
  233. package/examples/jspsych-video-slider-response.html +0 -55
  234. package/examples/jspsych-visual-search-circle.html +0 -58
  235. package/examples/jspsych-vsl-animate-occlusion.html +0 -29
  236. package/examples/jspsych-vsl-grid-scene.html +0 -41
  237. package/examples/lexical-decision.html +0 -132
  238. package/examples/manual-preloading.html +0 -53
  239. package/examples/pause-unpause.html +0 -33
  240. package/examples/progress-bar.html +0 -62
  241. package/examples/sound/hammer.mp3 +0 -0
  242. package/examples/sound/sound.mp3 +0 -0
  243. package/examples/sound/speech_blue.mp3 +0 -0
  244. package/examples/sound/speech_green.mp3 +0 -0
  245. package/examples/sound/speech_joke.mp3 +0 -0
  246. package/examples/sound/speech_red.mp3 +0 -0
  247. package/examples/sound/tone.mp3 +0 -0
  248. package/examples/timeline-variables-sampling.html +0 -50
  249. package/examples/timeline-variables.html +0 -55
  250. package/examples/video/sample_video.mp4 +0 -0
  251. package/jspsych.js +0 -2796
  252. package/license.txt +0 -21
  253. package/mkdocs.yml +0 -104
  254. package/plugins/jspsych-animation.js +0 -189
  255. package/plugins/jspsych-audio-button-response.js +0 -247
  256. package/plugins/jspsych-audio-keyboard-response.js +0 -204
  257. package/plugins/jspsych-audio-slider-response.js +0 -262
  258. package/plugins/jspsych-call-function.js +0 -58
  259. package/plugins/jspsych-canvas-button-response.js +0 -199
  260. package/plugins/jspsych-canvas-keyboard-response.js +0 -155
  261. package/plugins/jspsych-canvas-slider-response.js +0 -207
  262. package/plugins/jspsych-categorize-animation.js +0 -266
  263. package/plugins/jspsych-categorize-html.js +0 -220
  264. package/plugins/jspsych-categorize-image.js +0 -222
  265. package/plugins/jspsych-cloze.js +0 -112
  266. package/plugins/jspsych-external-html.js +0 -112
  267. package/plugins/jspsych-free-sort.js +0 -444
  268. package/plugins/jspsych-fullscreen.js +0 -104
  269. package/plugins/jspsych-html-button-response.js +0 -188
  270. package/plugins/jspsych-html-keyboard-response.js +0 -149
  271. package/plugins/jspsych-html-slider-response.js +0 -202
  272. package/plugins/jspsych-iat-html.js +0 -284
  273. package/plugins/jspsych-iat-image.js +0 -286
  274. package/plugins/jspsych-image-button-response.js +0 -311
  275. package/plugins/jspsych-image-keyboard-response.js +0 -247
  276. package/plugins/jspsych-image-slider-response.js +0 -353
  277. package/plugins/jspsych-instructions.js +0 -237
  278. package/plugins/jspsych-maxdiff.js +0 -174
  279. package/plugins/jspsych-rdk.js +0 -1373
  280. package/plugins/jspsych-reconstruction.js +0 -134
  281. package/plugins/jspsych-resize.js +0 -166
  282. package/plugins/jspsych-same-different-html.js +0 -168
  283. package/plugins/jspsych-same-different-image.js +0 -169
  284. package/plugins/jspsych-serial-reaction-time-mouse.js +0 -213
  285. package/plugins/jspsych-serial-reaction-time.js +0 -247
  286. package/plugins/jspsych-survey-html-form.js +0 -171
  287. package/plugins/jspsych-survey-likert.js +0 -195
  288. package/plugins/jspsych-survey-multi-choice.js +0 -208
  289. package/plugins/jspsych-survey-multi-select.js +0 -232
  290. package/plugins/jspsych-survey-text.js +0 -185
  291. package/plugins/jspsych-video-button-response.js +0 -320
  292. package/plugins/jspsych-video-keyboard-response.js +0 -279
  293. package/plugins/jspsych-video-slider-response.js +0 -351
  294. package/plugins/jspsych-visual-search-circle.js +0 -259
  295. package/plugins/jspsych-vsl-animate-occlusion.js +0 -196
  296. package/plugins/jspsych-vsl-grid-scene.js +0 -103
  297. package/plugins/template/jspsych-plugin-template.js +0 -35
  298. package/tests/README.md +0 -7
  299. package/tests/jsPsych/default-iti.test.js +0 -51
  300. package/tests/jsPsych/default-parameters.test.js +0 -58
  301. package/tests/jsPsych/endexperiment.test.js +0 -49
  302. package/tests/jsPsych/events.test.js +0 -369
  303. package/tests/jsPsych/init.test.js +0 -48
  304. package/tests/jsPsych/loads.test.js +0 -7
  305. package/tests/jsPsych/min-rt.test.js +0 -58
  306. package/tests/jsPsych/progressbar.test.js +0 -202
  307. package/tests/jsPsych/timeline-variables.test.js +0 -254
  308. package/tests/jsPsych/timelines.test.js +0 -498
  309. package/tests/jsPsych.data/datacollection.test.js +0 -116
  310. package/tests/jsPsych.data/datacolumn.test.js +0 -50
  311. package/tests/jsPsych.data/datamodule.test.js +0 -152
  312. package/tests/jsPsych.data/dataparameter.test.js +0 -251
  313. package/tests/jsPsych.data/interactions.test.js +0 -109
  314. package/tests/jsPsych.pluginAPI/pluginapi.test.js +0 -144
  315. package/tests/jsPsych.randomization/randomziation.test.js +0 -27
  316. package/tests/jsPsych.utils/utils.test.js +0 -58
  317. package/tests/media/blue.png +0 -0
  318. package/tests/media/orange.png +0 -0
  319. package/tests/media/sample_video.mp4 +0 -0
  320. package/tests/media/sound.mp3 +0 -0
  321. package/tests/plugins/plugin-animation.test.js +0 -35
  322. package/tests/plugins/plugin-audio-button-response.test.js +0 -15
  323. package/tests/plugins/plugin-audio-keyboard-response.test.js +0 -15
  324. package/tests/plugins/plugin-audio-slider-response.test.js +0 -15
  325. package/tests/plugins/plugin-call-function.test.js +0 -49
  326. package/tests/plugins/plugin-categorize-animation.test.js +0 -274
  327. package/tests/plugins/plugin-categorize-html.test.js +0 -17
  328. package/tests/plugins/plugin-categorize-image.test.js +0 -17
  329. package/tests/plugins/plugin-cloze.test.js +0 -140
  330. package/tests/plugins/plugin-free-sort.test.js +0 -112
  331. package/tests/plugins/plugin-fullscreen.test.js +0 -41
  332. package/tests/plugins/plugin-html-button-response.test.js +0 -161
  333. package/tests/plugins/plugin-html-keyboard-response.test.js +0 -139
  334. package/tests/plugins/plugin-html-slider-response.test.js +0 -155
  335. package/tests/plugins/plugin-iat-html.test.js +0 -328
  336. package/tests/plugins/plugin-iat-image.test.js +0 -308
  337. package/tests/plugins/plugin-image-button-response.test.js +0 -183
  338. package/tests/plugins/plugin-image-keyboard-response.test.js +0 -154
  339. package/tests/plugins/plugin-image-slider-response.test.js +0 -183
  340. package/tests/plugins/plugin-instructions.test.js +0 -66
  341. package/tests/plugins/plugin-maxdiff.test.js +0 -39
  342. package/tests/plugins/plugin-rdk.test.js +0 -17
  343. package/tests/plugins/plugin-reconstruction.test.js +0 -16
  344. package/tests/plugins/plugin-resize.test.js +0 -16
  345. package/tests/plugins/plugin-same-different-html.test.js +0 -17
  346. package/tests/plugins/plugin-same-different-image.test.js +0 -17
  347. package/tests/plugins/plugin-serial-reaction-time-mouse.test.js +0 -42
  348. package/tests/plugins/plugin-serial-reaction-time.test.js +0 -69
  349. package/tests/plugins/plugin-survey-html-form.test.js +0 -44
  350. package/tests/plugins/plugin-survey-likert.test.js +0 -48
  351. package/tests/plugins/plugin-survey-multi-choice.test.js +0 -48
  352. package/tests/plugins/plugin-survey-multi-select.test.js +0 -72
  353. package/tests/plugins/plugin-survey-text.test.js +0 -115
  354. package/tests/plugins/plugin-video-button-response.test.js +0 -35
  355. package/tests/plugins/plugin-video-keyboard-response.test.js +0 -35
  356. package/tests/plugins/plugin-video-slider-response.test.js +0 -34
  357. package/tests/plugins/plugin-visual-search-circle.test.js +0 -16
  358. package/tests/plugins/plugin-vsl-animate-occlusion.test.js +0 -16
  359. package/tests/plugins/plugin-vsl-grid-scene.test.js +0 -16
  360. package/tests/testing-utils.js +0 -13
@@ -0,0 +1,28 @@
1
+ import autoBind from "auto-bind";
2
+
3
+ import { JsPsych } from "../../JsPsych";
4
+ import { HardwareAPI } from "./HardwareAPI";
5
+ import { KeyboardListenerAPI } from "./KeyboardListenerAPI";
6
+ import { MediaAPI } from "./MediaAPI";
7
+ import { SimulationAPI } from "./SimulationAPI";
8
+ import { TimeoutAPI } from "./TimeoutAPI";
9
+
10
+ export function createJointPluginAPIObject(jsPsych: JsPsych) {
11
+ const settings = jsPsych.getInitSettings();
12
+ return Object.assign(
13
+ {},
14
+ ...[
15
+ new KeyboardListenerAPI(
16
+ jsPsych.getDisplayContainerElement,
17
+ settings.case_sensitive_responses,
18
+ settings.minimum_valid_rt
19
+ ),
20
+ new TimeoutAPI(),
21
+ new MediaAPI(settings.use_webaudio, jsPsych.webaudio_context),
22
+ new HardwareAPI(),
23
+ new SimulationAPI(),
24
+ ].map((object) => autoBind(object))
25
+ ) as KeyboardListenerAPI & TimeoutAPI & MediaAPI & HardwareAPI & SimulationAPI;
26
+ }
27
+
28
+ export type PluginAPI = ReturnType<typeof createJointPluginAPIObject>;
@@ -0,0 +1,158 @@
1
+ /**
2
+ Flatten the type output to improve type hints shown in editors.
3
+ Borrowed from type-fest
4
+ */
5
+ type Simplify<T> = { [KeyType in keyof T]: T[KeyType] };
6
+
7
+ /**
8
+ Create a type that makes the given keys required. The remaining keys are kept as is.
9
+ Borrowed from type-fest
10
+ */
11
+ type SetRequired<BaseType, Keys extends keyof BaseType> = Simplify<
12
+ Omit<BaseType, Keys> & Required<Pick<BaseType, Keys>>
13
+ >;
14
+
15
+ /**
16
+ * Parameter types for plugins
17
+ */
18
+ export enum ParameterType {
19
+ BOOL,
20
+ STRING,
21
+ INT,
22
+ FLOAT,
23
+ FUNCTION,
24
+ KEY,
25
+ KEYS,
26
+ SELECT,
27
+ HTML_STRING,
28
+ IMAGE,
29
+ AUDIO,
30
+ VIDEO,
31
+ OBJECT,
32
+ COMPLEX,
33
+ TIMELINE,
34
+ }
35
+
36
+ type ParameterTypeMap = {
37
+ [ParameterType.BOOL]: boolean;
38
+ [ParameterType.STRING]: string;
39
+ [ParameterType.INT]: number;
40
+ [ParameterType.FLOAT]: number;
41
+ [ParameterType.FUNCTION]: (...args: any[]) => any;
42
+ [ParameterType.KEY]: string;
43
+ [ParameterType.KEYS]: string[] | "ALL_KEYS" | "NO_KEYS";
44
+ [ParameterType.SELECT]: any;
45
+ [ParameterType.HTML_STRING]: string;
46
+ [ParameterType.IMAGE]: string;
47
+ [ParameterType.AUDIO]: string;
48
+ [ParameterType.VIDEO]: string;
49
+ [ParameterType.OBJECT]: object;
50
+ [ParameterType.COMPLEX]: any;
51
+ [ParameterType.TIMELINE]: any;
52
+ };
53
+
54
+ export interface ParameterInfo {
55
+ type: ParameterType;
56
+ array?: boolean;
57
+ pretty_name?: string;
58
+ default?: any;
59
+ preload?: boolean;
60
+ }
61
+
62
+ export interface ParameterInfos {
63
+ [key: string]: ParameterInfo;
64
+ }
65
+
66
+ type InferredParameter<I extends ParameterInfo> = I["array"] extends true
67
+ ? Array<ParameterTypeMap[I["type"]]>
68
+ : ParameterTypeMap[I["type"]];
69
+
70
+ type RequiredParameterNames<I extends ParameterInfos> = {
71
+ [K in keyof I]: I[K]["default"] extends undefined ? K : never;
72
+ }[keyof I];
73
+
74
+ type InferredParameters<I extends ParameterInfos> = SetRequired<
75
+ {
76
+ [Property in keyof I]?: InferredParameter<I[Property]>;
77
+ },
78
+ RequiredParameterNames<I>
79
+ >;
80
+
81
+ export const universalPluginParameters = <const>{
82
+ /**
83
+ * Data to add to this trial (key-value pairs)
84
+ */
85
+ data: {
86
+ type: ParameterType.OBJECT,
87
+ pretty_name: "Data",
88
+ default: {},
89
+ },
90
+ /**
91
+ * Function to execute when trial begins
92
+ */
93
+ on_start: {
94
+ type: ParameterType.FUNCTION,
95
+ pretty_name: "On start",
96
+ default: function () {
97
+ return;
98
+ },
99
+ },
100
+ /**
101
+ * Function to execute when trial is finished
102
+ */
103
+ on_finish: {
104
+ type: ParameterType.FUNCTION,
105
+ pretty_name: "On finish",
106
+ default: function () {
107
+ return;
108
+ },
109
+ },
110
+ /**
111
+ * Function to execute after the trial has loaded
112
+ */
113
+ on_load: {
114
+ type: ParameterType.FUNCTION,
115
+ pretty_name: "On load",
116
+ default: function () {
117
+ return;
118
+ },
119
+ },
120
+ /**
121
+ * Length of gap between the end of this trial and the start of the next trial
122
+ */
123
+ post_trial_gap: {
124
+ type: ParameterType.INT,
125
+ pretty_name: "Post trial gap",
126
+ default: null,
127
+ },
128
+ /**
129
+ * A list of CSS classes to add to the jsPsych display element for the duration of this trial
130
+ */
131
+ css_classes: {
132
+ type: ParameterType.STRING,
133
+ pretty_name: "Custom CSS classes",
134
+ default: null,
135
+ },
136
+ };
137
+
138
+ export type UniversalPluginParameters = InferredParameters<typeof universalPluginParameters>;
139
+
140
+ export interface PluginInfo {
141
+ name: string;
142
+ parameters: {
143
+ [key: string]: ParameterInfo;
144
+ };
145
+ }
146
+
147
+ export interface JsPsychPlugin<I extends PluginInfo> {
148
+ trial(
149
+ display_element: HTMLElement,
150
+ trial: TrialType<I>,
151
+ on_load?: () => void
152
+ ): void | Promise<any>;
153
+ }
154
+
155
+ export type TrialType<I extends PluginInfo> = InferredParameters<I["parameters"]> &
156
+ UniversalPluginParameters;
157
+
158
+ export type PluginParameters<I extends PluginInfo> = InferredParameters<I["parameters"]>;
@@ -0,0 +1,327 @@
1
+ import rw from "random-words";
2
+
3
+ export function repeat(array, repetitions, unpack = false) {
4
+ const arr_isArray = Array.isArray(array);
5
+ const rep_isArray = Array.isArray(repetitions);
6
+
7
+ // if array is not an array, then we just repeat the item
8
+ if (!arr_isArray) {
9
+ if (!rep_isArray) {
10
+ array = [array];
11
+ repetitions = [repetitions];
12
+ } else {
13
+ repetitions = [repetitions[0]];
14
+ console.log(
15
+ "Unclear parameters given to randomization.repeat. Multiple set sizes specified, but only one item exists to sample. Proceeding using the first set size."
16
+ );
17
+ }
18
+ } else {
19
+ // if repetitions is not an array, but array is, then we
20
+ // repeat repetitions for each entry in array
21
+ if (!rep_isArray) {
22
+ let reps = [];
23
+ for (let i = 0; i < array.length; i++) {
24
+ reps.push(repetitions);
25
+ }
26
+ repetitions = reps;
27
+ } else {
28
+ if (array.length != repetitions.length) {
29
+ console.warn(
30
+ "Unclear parameters given to randomization.repeat. Items and repetitions are unequal lengths. Behavior may not be as expected."
31
+ );
32
+ // throw warning if repetitions is too short, use first rep ONLY.
33
+ if (repetitions.length < array.length) {
34
+ let reps = [];
35
+ for (let i = 0; i < array.length; i++) {
36
+ reps.push(repetitions);
37
+ }
38
+ repetitions = reps;
39
+ } else {
40
+ // throw warning if too long, and then use the first N
41
+ repetitions = repetitions.slice(0, array.length);
42
+ }
43
+ }
44
+ }
45
+ }
46
+
47
+ // should be clear at this point to assume that array and repetitions are arrays with == length
48
+ let allsamples = [];
49
+ for (let i = 0; i < array.length; i++) {
50
+ for (let j = 0; j < repetitions[i]; j++) {
51
+ if (array[i] == null || typeof array[i] != "object") {
52
+ allsamples.push(array[i]);
53
+ } else {
54
+ allsamples.push(Object.assign({}, array[i]));
55
+ }
56
+ }
57
+ }
58
+
59
+ let out: any = shuffle(allsamples);
60
+
61
+ if (unpack) {
62
+ out = unpackArray(out);
63
+ }
64
+
65
+ return out;
66
+ }
67
+
68
+ export function shuffle(array: Array<any>) {
69
+ if (!Array.isArray(array)) {
70
+ console.error("Argument to shuffle() must be an array.");
71
+ }
72
+
73
+ const copy_array = array.slice(0);
74
+ let m = copy_array.length,
75
+ t,
76
+ i;
77
+
78
+ // While there remain elements to shuffle…
79
+ while (m) {
80
+ // Pick a remaining element…
81
+ i = Math.floor(Math.random() * m--);
82
+
83
+ // And swap it with the current element.
84
+ t = copy_array[m];
85
+ copy_array[m] = copy_array[i];
86
+ copy_array[i] = t;
87
+ }
88
+
89
+ return copy_array;
90
+ }
91
+
92
+ export function shuffleNoRepeats(arr: Array<any>, equalityTest: (a: any, b: any) => boolean) {
93
+ if (!Array.isArray(arr)) {
94
+ console.error("First argument to shuffleNoRepeats() must be an array.");
95
+ }
96
+ if (typeof equalityTest !== "undefined" && typeof equalityTest !== "function") {
97
+ console.error("Second argument to shuffleNoRepeats() must be a function.");
98
+ }
99
+ // define a default equalityTest
100
+ if (typeof equalityTest == "undefined") {
101
+ equalityTest = function (a, b) {
102
+ if (a === b) {
103
+ return true;
104
+ } else {
105
+ return false;
106
+ }
107
+ };
108
+ }
109
+
110
+ const random_shuffle = shuffle(arr);
111
+ for (let i = 0; i < random_shuffle.length - 1; i++) {
112
+ if (equalityTest(random_shuffle[i], random_shuffle[i + 1])) {
113
+ // neighbors are equal, pick a new random neighbor to swap (not the first or last element, to avoid edge cases)
114
+ let random_pick = Math.floor(Math.random() * (random_shuffle.length - 2)) + 1;
115
+ // test to make sure the new neighbor isn't equal to the old one
116
+ while (
117
+ equalityTest(random_shuffle[i + 1], random_shuffle[random_pick]) ||
118
+ equalityTest(random_shuffle[i + 1], random_shuffle[random_pick + 1]) ||
119
+ equalityTest(random_shuffle[i + 1], random_shuffle[random_pick - 1])
120
+ ) {
121
+ random_pick = Math.floor(Math.random() * (random_shuffle.length - 2)) + 1;
122
+ }
123
+ const new_neighbor = random_shuffle[random_pick];
124
+ random_shuffle[random_pick] = random_shuffle[i + 1];
125
+ random_shuffle[i + 1] = new_neighbor;
126
+ }
127
+ }
128
+
129
+ return random_shuffle;
130
+ }
131
+
132
+ export function shuffleAlternateGroups(arr_groups, random_group_order = false) {
133
+ const n_groups = arr_groups.length;
134
+ if (n_groups == 1) {
135
+ console.warn(
136
+ "shuffleAlternateGroups() was called with only one group. Defaulting to simple shuffle."
137
+ );
138
+ return shuffle(arr_groups[0]);
139
+ }
140
+
141
+ let group_order = [];
142
+ for (let i = 0; i < n_groups; i++) {
143
+ group_order.push(i);
144
+ }
145
+ if (random_group_order) {
146
+ group_order = shuffle(group_order);
147
+ }
148
+
149
+ const randomized_groups = [];
150
+ let min_length = null;
151
+ for (let i = 0; i < n_groups; i++) {
152
+ min_length =
153
+ min_length === null ? arr_groups[i].length : Math.min(min_length, arr_groups[i].length);
154
+ randomized_groups.push(shuffle(arr_groups[i]));
155
+ }
156
+
157
+ const out = [];
158
+ for (let i = 0; i < min_length; i++) {
159
+ for (let j = 0; j < group_order.length; j++) {
160
+ out.push(randomized_groups[group_order[j]][i]);
161
+ }
162
+ }
163
+
164
+ return out;
165
+ }
166
+
167
+ export function sampleWithoutReplacement(arr, size) {
168
+ if (!Array.isArray(arr)) {
169
+ console.error("First argument to sampleWithoutReplacement() must be an array");
170
+ }
171
+
172
+ if (size > arr.length) {
173
+ console.error("Cannot take a sample larger than the size of the set of items to sample.");
174
+ }
175
+ return shuffle(arr).slice(0, size);
176
+ }
177
+
178
+ export function sampleWithReplacement(arr, size, weights?) {
179
+ if (!Array.isArray(arr)) {
180
+ console.error("First argument to sampleWithReplacement() must be an array");
181
+ }
182
+
183
+ const normalized_weights = [];
184
+ if (typeof weights !== "undefined") {
185
+ if (weights.length !== arr.length) {
186
+ console.error(
187
+ "The length of the weights array must equal the length of the array " +
188
+ "to be sampled from."
189
+ );
190
+ }
191
+ let weight_sum = 0;
192
+ for (const weight of weights) {
193
+ weight_sum += weight;
194
+ }
195
+ for (const weight of weights) {
196
+ normalized_weights.push(weight / weight_sum);
197
+ }
198
+ } else {
199
+ for (let i = 0; i < arr.length; i++) {
200
+ normalized_weights.push(1 / arr.length);
201
+ }
202
+ }
203
+
204
+ const cumulative_weights = [normalized_weights[0]];
205
+ for (let i = 1; i < normalized_weights.length; i++) {
206
+ cumulative_weights.push(normalized_weights[i] + cumulative_weights[i - 1]);
207
+ }
208
+
209
+ const samp = [];
210
+ for (let i = 0; i < size; i++) {
211
+ const rnd = Math.random();
212
+ let index = 0;
213
+ while (rnd > cumulative_weights[index]) {
214
+ index++;
215
+ }
216
+ samp.push(arr[index]);
217
+ }
218
+ return samp;
219
+ }
220
+
221
+ export function factorial(factors: Record<string, any>, repetitions = 1, unpack = false) {
222
+ let design = [{}];
223
+ for (const [factorName, factor] of Object.entries(factors)) {
224
+ const new_design = [];
225
+ for (const level of factor) {
226
+ for (const cell of design) {
227
+ new_design.push({ ...cell, [factorName]: level });
228
+ }
229
+ }
230
+ design = new_design;
231
+ }
232
+
233
+ return repeat(design, repetitions, unpack);
234
+ }
235
+
236
+ export function randomID(length = 32) {
237
+ let result = "";
238
+ const chars = "0123456789abcdefghjklmnopqrstuvwxyz";
239
+ for (let i = 0; i < length; i++) {
240
+ result += chars[Math.floor(Math.random() * chars.length)];
241
+ }
242
+ return result;
243
+ }
244
+
245
+ /**
246
+ * Generate a random integer from `lower` to `upper`, inclusive of both end points.
247
+ * @param lower The lowest value it is possible to generate
248
+ * @param upper The highest value it is possible to generate
249
+ * @returns A random integer
250
+ */
251
+ export function randomInt(lower: number, upper: number) {
252
+ if (upper < lower) {
253
+ throw new Error("Upper boundary must be less than or equal to lower boundary");
254
+ }
255
+ return lower + Math.floor(Math.random() * (upper - lower + 1));
256
+ }
257
+
258
+ /**
259
+ * Generates a random sample from a Bernoulli distribution.
260
+ * @param p The probability of sampling 1.
261
+ * @returns 0, with probability 1-p, or 1, with probability p.
262
+ */
263
+ export function sampleBernoulli(p: number) {
264
+ return Math.random() <= p ? 1 : 0;
265
+ }
266
+
267
+ export function sampleNormal(mean: number, standard_deviation: number) {
268
+ return randn_bm() * standard_deviation + mean;
269
+ }
270
+
271
+ export function sampleExponential(rate: number) {
272
+ return -Math.log(Math.random()) / rate;
273
+ }
274
+
275
+ export function sampleExGaussian(
276
+ mean: number,
277
+ standard_deviation: number,
278
+ rate: number,
279
+ positive = false
280
+ ) {
281
+ let s = sampleNormal(mean, standard_deviation) + sampleExponential(rate);
282
+ if (positive) {
283
+ while (s <= 0) {
284
+ s = sampleNormal(mean, standard_deviation) + sampleExponential(rate);
285
+ }
286
+ }
287
+ return s;
288
+ }
289
+
290
+ /**
291
+ * Generate one or more random words.
292
+ *
293
+ * This is a wrapper function for the {@link https://www.npmjs.com/package/random-words `random-words` npm package}.
294
+ *
295
+ * @param opts An object with optional properties `min`, `max`, `exactly`,
296
+ * `join`, `maxLength`, `wordsPerString`, `separator`, and `formatter`.
297
+ *
298
+ * @returns An array of words or a single string, depending on parameter choices.
299
+ */
300
+ export function randomWords(opts) {
301
+ return rw(opts);
302
+ }
303
+
304
+ // Box-Muller transformation for a random sample from normal distribution with mean = 0, std = 1
305
+ // https://stackoverflow.com/a/36481059/3726673
306
+ function randn_bm() {
307
+ var u = 0,
308
+ v = 0;
309
+ while (u === 0) u = Math.random(); //Converting [0,1) to (0,1)
310
+ while (v === 0) v = Math.random();
311
+ return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
312
+ }
313
+
314
+ function unpackArray(array) {
315
+ const out = {};
316
+
317
+ for (const x of array) {
318
+ for (const key of Object.keys(x)) {
319
+ if (typeof out[key] === "undefined") {
320
+ out[key] = [];
321
+ }
322
+ out[key].push(x[key]);
323
+ }
324
+ }
325
+
326
+ return out;
327
+ }
@@ -0,0 +1,99 @@
1
+ interface turkInformation {
2
+ /**
3
+ * Is the experiment being loaded in preview mode on Mechanical Turk?
4
+ */
5
+ previewMode: boolean;
6
+ /**
7
+ * Is the experiment being loaded outside of the Mechanical Turk environment?
8
+ */
9
+ outsideTurk: boolean;
10
+ /**
11
+ * The HIT ID.
12
+ */
13
+ hitId: string;
14
+ /**
15
+ * The Assignment ID.
16
+ */
17
+ assignmentId: string;
18
+ /**
19
+ * The worker ID.
20
+ */
21
+ workerId: string;
22
+ /**
23
+ * URL for submission of the HIT.
24
+ */
25
+ turkSubmitTo: string;
26
+ }
27
+
28
+ /**
29
+ * Gets information about the Mechanical Turk Environment, HIT, Assignment, and Worker
30
+ * by parsing the URL variables that Mechanical Turk generates.
31
+ * @returns An object containing information about the Mechanical Turk Environment, HIT, Assignment, and Worker.
32
+ */
33
+ export function turkInfo(): turkInformation {
34
+ const turk: turkInformation = {
35
+ previewMode: false,
36
+ outsideTurk: false,
37
+ hitId: "INVALID_URL_PARAMETER",
38
+ assignmentId: "INVALID_URL_PARAMETER",
39
+ workerId: "INVALID_URL_PARAMETER",
40
+ turkSubmitTo: "INVALID_URL_PARAMETER",
41
+ };
42
+
43
+ const param = function (url, name) {
44
+ name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
45
+ const regexS = "[\\?&]" + name + "=([^&#]*)";
46
+ const regex = new RegExp(regexS);
47
+ const results = regex.exec(url);
48
+ return results == null ? "" : results[1];
49
+ };
50
+
51
+ const src = param(window.location.href, "assignmentId")
52
+ ? window.location.href
53
+ : document.referrer;
54
+
55
+ const keys = ["assignmentId", "hitId", "workerId", "turkSubmitTo"];
56
+ keys.map(function (key) {
57
+ turk[key] = unescape(param(src, key));
58
+ });
59
+
60
+ turk.previewMode = turk.assignmentId == "ASSIGNMENT_ID_NOT_AVAILABLE";
61
+
62
+ turk.outsideTurk =
63
+ !turk.previewMode && turk.hitId === "" && turk.assignmentId == "" && turk.workerId == "";
64
+
65
+ return turk;
66
+ }
67
+
68
+ /**
69
+ * Send data to Mechnical Turk for storage.
70
+ * @param data An object containing `key:value` pairs to send to Mechanical Turk. Values
71
+ * cannot contain nested objects, arrays, or functions.
72
+ * @returns Nothing
73
+ */
74
+ export function submitToTurk(data) {
75
+ const turk = turkInfo();
76
+ const assignmentId = turk.assignmentId;
77
+ const turkSubmitTo = turk.turkSubmitTo;
78
+
79
+ if (!assignmentId || !turkSubmitTo) return;
80
+
81
+ const form = document.createElement("form");
82
+ form.method = "POST";
83
+ form.action = turkSubmitTo + "/mturk/externalSubmit?assignmentId=" + assignmentId;
84
+
85
+ for (const key in data) {
86
+ if (data.hasOwnProperty(key)) {
87
+ const hiddenField = document.createElement("input");
88
+ hiddenField.type = "hidden";
89
+ hiddenField.name = key;
90
+ hiddenField.id = key;
91
+ hiddenField.value = data[key];
92
+
93
+ form.appendChild(hiddenField);
94
+ }
95
+ }
96
+
97
+ document.body.appendChild(form);
98
+ form.submit();
99
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Finds all of the unique items in an array.
3
+ * @param arr The array to extract unique values from
4
+ * @returns An array with one copy of each unique item in `arr`
5
+ */
6
+ export function unique(arr: Array<any>) {
7
+ return [...new Set(arr)];
8
+ }
9
+
10
+ export function deepCopy(obj) {
11
+ if (!obj) return obj;
12
+ let out;
13
+ if (Array.isArray(obj)) {
14
+ out = [];
15
+ for (const x of obj) {
16
+ out.push(deepCopy(x));
17
+ }
18
+ return out;
19
+ } else if (typeof obj === "object" && obj !== null) {
20
+ out = {};
21
+ for (const key in obj) {
22
+ if (obj.hasOwnProperty(key)) {
23
+ out[key] = deepCopy(obj[key]);
24
+ }
25
+ }
26
+ return out;
27
+ } else {
28
+ return obj;
29
+ }
30
+ }
@@ -1,20 +0,0 @@
1
- # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2
- # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3
-
4
- name: Jest Test
5
-
6
- on: [ push, pull_request ]
7
-
8
- jobs:
9
- build:
10
-
11
- runs-on: ubuntu-latest
12
-
13
- steps:
14
- - uses: actions/checkout@v2
15
- - name: Use Node.js
16
- uses: actions/setup-node@v1
17
- with:
18
- node-version: '14.x'
19
- - run: npm ci
20
- - run: npm test