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
@@ -1,1373 +0,0 @@
1
- /*
2
-
3
- RDK plugin for JsPsych
4
- ----------------------
5
-
6
- This code was created in the Consciousness and Metacognition Lab at UCLA,
7
- under the supervision of Brian Odegaard and Hakwan Lau
8
-
9
- We would appreciate it if you cited this paper when you use the RDK:
10
- Rajananda, S., Lau, H. & Odegaard, B., (2018). A Random-Dot Kinematogram for Web-Based Vision Research. Journal of Open Research Software. 6(1), p.6. DOI: [http://doi.org/10.5334/jors.194]
11
-
12
- ----------------------
13
-
14
- Copyright (C) 2017 Sivananda Rajananda
15
-
16
- This program is free software: you can redistribute it and/or modify
17
- it under the terms of the GNU General Public License as published by
18
- the Free Software Foundation, either version 3 of the License, or
19
- (at your option) any later version.
20
-
21
- This program is distributed in the hope that it will be useful,
22
- but WITHOUT ANY WARRANTY; without even the implied warranty of
23
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
- GNU General Public License for more details.
25
-
26
- You should have received a copy of the GNU General Public License
27
- along with this program. If not, see <http://www.gnu.org/licenses/>.
28
-
29
- */
30
-
31
-
32
- jsPsych.plugins["rdk"] = (function() {
33
-
34
- var plugin = {};
35
-
36
- plugin.info = {
37
- name: "rdk",
38
- parameters: {
39
- choices: {
40
- type: jsPsych.plugins.parameterType.INT,
41
- pretty_name: "Choices",
42
- default: [],
43
- array: true,
44
- description: "The valid keys that the subject can press to indicate a response"
45
- },
46
- correct_choice: {
47
- type: jsPsych.plugins.parameterType.STRING,
48
- pretty_name: "Correct choice",
49
- default: undefined,
50
- array: true,
51
- description: "The correct keys for that trial"
52
- },
53
- trial_duration: {
54
- type: jsPsych.plugins.parameterType.INT,
55
- pretty_name: "Trial duration",
56
- default: 500,
57
- description: "The length of stimulus presentation"
58
- },
59
- response_ends_trial: {
60
- type: jsPsych.plugins.parameterType.BOOL,
61
- pretty_name: "Response ends trial",
62
- default: true,
63
- description: "If true, then any valid key will end the trial"
64
- },
65
- number_of_apertures: {
66
- type: jsPsych.plugins.parameterType.INT,
67
- pretty_name: "Number of apertures",
68
- default: 1,
69
- description: "The number of RDK apertures (If more than one, make sure to separate them by setting aperture_center_x and aperture_center_y for each RDK)"
70
- },
71
- number_of_dots: {
72
- type: jsPsych.plugins.parameterType.INT,
73
- pretty_name: "Number of dots",
74
- default: 300,
75
- description: "The number of dots per set in the stimulus"
76
- },
77
- number_of_sets: {
78
- type: jsPsych.plugins.parameterType.INT,
79
- pretty_name: "Number of sets",
80
- default: 1,
81
- description: "The number of sets of dots to cycle through"
82
- },
83
- coherent_direction: {
84
- type: jsPsych.plugins.parameterType.INT,
85
- pretty_name: "Coherent direction",
86
- default: 0,
87
- description: "The direction of coherent motion in degrees"
88
- },
89
- coherence: {
90
- type: jsPsych.plugins.parameterType.FLOAT,
91
- pretty_name: "Coherence",
92
- default: 0.5,
93
- description: "The percentage of dots moving in the coherent direction"
94
- },
95
- opposite_coherence: {
96
- type: jsPsych.plugins.parameterType.FLOAT,
97
- pretty_name: "Opposite coherence",
98
- default: 0,
99
- description: "The percentage of dots moving in the direction opposite of the coherent direction"
100
- },
101
- dot_radius: {
102
- type: jsPsych.plugins.parameterType.INT,
103
- pretty_name: "Dot radius",
104
- default: 2,
105
- description: "The radius of the dots in pixels"
106
- },
107
- dot_life: {
108
- type: jsPsych.plugins.parameterType.INT,
109
- pretty_name: "Dot life",
110
- default: -1,
111
- description: "The number of frames that pass before each dot disappears and reappears somewhere else"
112
- },
113
- move_distance: {
114
- type: jsPsych.plugins.parameterType.INT,
115
- pretty_name: "Move distance",
116
- default: 1,
117
- description: "The distance in pixels each dot moves per frame"
118
- },
119
- aperture_width: {
120
- type: jsPsych.plugins.parameterType.INT,
121
- pretty_name: "Aperture width",
122
- default: 600,
123
- description: "The width of the aperture in pixels"
124
- },
125
- aperture_height: {
126
- type: jsPsych.plugins.parameterType.INT,
127
- pretty_name: "Aperture height",
128
- default: 400,
129
- description: "The height of the aperture in pixels"
130
- },
131
- dot_color: {
132
- type: jsPsych.plugins.parameterType.STRING,
133
- pretty_name: "Dot color",
134
- default: "white",
135
- description: "The color of the dots"
136
- },
137
- background_color: {
138
- type: jsPsych.plugins.parameterType.STRING,
139
- pretty_name: "Background color",
140
- default: "gray",
141
- description: "The background of the stimulus"
142
- },
143
- RDK_type: {
144
- type: jsPsych.plugins.parameterType.INT,
145
- pretty_name: "RDK type",
146
- default: 3,
147
- description: "The Type of RDK (refer to documentation for details)"
148
- },
149
- aperture_type: {
150
- type: jsPsych.plugins.parameterType.INT,
151
- pretty_name: "Aperture Type",
152
- default: 2,
153
- description: "The shape of the aperture"
154
- },
155
- reinsert_type: {
156
- type: jsPsych.plugins.parameterType.INT,
157
- pretty_name: "Reinsert type",
158
- default: 2,
159
- description: "The reinsertion rule for dots that move out of the aperture"
160
- },
161
- aperture_center_x: {
162
- type: jsPsych.plugins.parameterType.INT,
163
- pretty_name: "Aperture center X",
164
- default: window.innerWidth/2,
165
- description: "The x-coordinate of the center of the aperture"
166
- },
167
- aperture_center_y: {
168
- type: jsPsych.plugins.parameterType.INT,
169
- pretty_name: "Aperture center Y",
170
- default: window.innerHeight/2,
171
- description: "The y-coordinate of the center of the aperture"
172
- },
173
- fixation_cross: {
174
- type: jsPsych.plugins.parameterType.INT, //boolean
175
- pretty_name: "Fixation cross",
176
- default: false,
177
- description: "If true, then a fixation cross will be present in the middle of the screen"
178
- },
179
- fixation_cross_width: {
180
- type: jsPsych.plugins.parameterType.INT,
181
- pretty_name: "Fixation cross width",
182
- default: 20,
183
- description: "The width of the fixation cross in pixels"
184
- },
185
- fixation_cross_height: {
186
- type: jsPsych.plugins.parameterType.INT,
187
- pretty_name: "Fixation cross height",
188
- default: 20,
189
- description: "The height of the fixation cross in pixels"
190
- },
191
- fixation_cross_color: {
192
- type: jsPsych.plugins.parameterType.STRING,
193
- pretty_name: "Fixation cross color",
194
- default: "black",
195
- description: "The color of the fixation cross"
196
- },
197
- fixation_cross_thickness: {
198
- type: jsPsych.plugins.parameterType.INT,
199
- pretty_name: "Fixation cross thickness",
200
- default: 1,
201
- description: "The thickness of the fixation cross"
202
- },
203
- border: {
204
- type: jsPsych.plugins.parameterType.BOOL,
205
- pretty_name: "Border",
206
- default: false,
207
- description: "The presence of a border around the aperture"
208
- },
209
- border_thickness: {
210
- type: jsPsych.plugins.parameterType.INT,
211
- pretty_name: "Border width",
212
- default: 1,
213
- description: "The thickness of the border in pixels"
214
- },
215
- border_color: {
216
- type: jsPsych.plugins.parameterType.STRING,
217
- pretty_name: "Border Color",
218
- default: 1,
219
- description: "The color of the border"
220
- }
221
- }
222
- }
223
-
224
-
225
- //BEGINNING OF TRIAL
226
- plugin.trial = function(display_element, trial) {
227
-
228
- //--------------------------------------
229
- //---------SET PARAMETERS BEGIN---------
230
- //--------------------------------------
231
-
232
-
233
- //Note on '||' logical operator: If the first option is 'undefined', it evalutes to 'false' and the second option is returned as the assignment
234
- trial.choices = assignParameterValue(trial.choices, []);
235
- trial.correct_choice = assignParameterValue(trial.correct_choice, undefined);
236
- trial.trial_duration = assignParameterValue(trial.trial_duration, 500);
237
- trial.response_ends_trial = assignParameterValue(trial.response_ends_trial, true);
238
- trial.number_of_apertures = assignParameterValue(trial.number_of_apertures, 1);
239
- trial.number_of_dots = assignParameterValue(trial.number_of_dots, 300);
240
- trial.number_of_sets = assignParameterValue(trial.number_of_sets, 1);
241
- trial.coherent_direction = assignParameterValue(trial.coherent_direction, 0);
242
- trial.coherence = assignParameterValue(trial.coherence, 0.5);
243
- trial.opposite_coherence = assignParameterValue(trial.opposite_coherence, 0);
244
- trial.dot_radius = assignParameterValue(trial.dot_radius, 2);
245
- trial.dot_life = assignParameterValue(trial.dot_life, -1);
246
- trial.move_distance = assignParameterValue(trial.move_distance, 1);
247
- trial.aperture_width = assignParameterValue(trial.aperture_width, 600);
248
- trial.aperture_height = assignParameterValue(trial.aperture_height, 400);
249
- trial.dot_color = assignParameterValue(trial.dot_color, "white");
250
- trial.background_color = assignParameterValue(trial.background_color, "gray");
251
- trial.RDK_type = assignParameterValue(trial.RDK_type, 3);
252
- trial.aperture_type = assignParameterValue(trial.aperture_type, 2);
253
- trial.reinsert_type = assignParameterValue(trial.reinsert_type, 2);
254
- trial.aperture_center_x = assignParameterValue(trial.aperture_center_x, window.innerWidth/2);
255
- trial.aperture_center_y = assignParameterValue(trial.aperture_center_y, window.innerHeight/2);
256
- trial.fixation_cross = assignParameterValue(trial.fixation_cross, false);
257
- trial.fixation_cross_width = assignParameterValue(trial.fixation_cross_width, 20);
258
- trial.fixation_cross_height = assignParameterValue(trial.fixation_cross_height, 20);
259
- trial.fixation_cross_color = assignParameterValue(trial.fixation_cross_color, "black");
260
- trial.fixation_cross_thickness = assignParameterValue(trial.fixation_cross_thickness, 1);
261
- trial.border = assignParameterValue(trial.border, false);
262
- trial.border_thickness = assignParameterValue(trial.border_thickness, 1);
263
- trial.border_color = assignParameterValue(trial.border_color, "black");
264
-
265
-
266
- //For square and circle, set the aperture height == aperture width
267
- if (apertureType == 1 || apertureType == 3) {
268
- trial.aperture_height = trial.aperture_width;
269
- }
270
-
271
- //Convert the parameter variables to those that the code below can use
272
-
273
- var nApertures = trial.number_of_apertures; //The number of apertures
274
- var nDots = trial.number_of_dots; //Number of dots per set (equivalent to number of dots per frame)
275
- var nSets = trial.number_of_sets; //Number of sets to cycle through per frame
276
- var coherentDirection = trial.coherent_direction; //The direction of the coherentDots in degrees. Starts at 3 o'clock and goes counterclockwise (0 == rightwards, 90 == upwards, 180 == leftwards, 270 == downwards), range 0 - 360
277
- var coherence = trial.coherence; //Proportion of dots to move together, range from 0 to 1
278
- var oppositeCoherence = trial.opposite_coherence; // The coherence for the dots going the opposite direction as the coherent dots
279
- var dotRadius = trial.dot_radius; //Radius of each dot in pixels
280
- var dotLife = trial.dot_life; //How many frames a dot will keep following its trajectory before it is redrawn at a random location. -1 denotes infinite life (the dot will only be redrawn if it reaches the end of the aperture).
281
- var moveDistance = trial.move_distance; //How many pixels the dots move per frame
282
- var apertureWidth = trial.aperture_width; // How many pixels wide the aperture is. For square aperture this will be the both height and width. For circle, this will be the diameter.
283
- var apertureHeight = trial.aperture_height; //How many pixels high the aperture is. Only relevant for ellipse and rectangle apertures. For circle and square, this is ignored.
284
- var dotColor = trial.dot_color; //Color of the dots
285
- var backgroundColor = trial.background_color; //Color of the background
286
- var apertureCenterX = trial.aperture_center_x; // The x-coordinate of center of the aperture on the screen, in pixels
287
- var apertureCenterY = trial.aperture_center_y; // The y-coordinate of center of the aperture on the screen, in pixels
288
-
289
-
290
- /* RDK type parameter
291
- ** See Fig. 1 in Scase, Braddick, and Raymond (1996) for a visual depiction of these different signal selection rules and noise types
292
-
293
- -------------------
294
- SUMMARY:
295
-
296
- Signal Selection rule:
297
- -Same: Each dot is designated to be either a coherent dot (signal) or incoherent dot (noise) and will remain so throughout all frames in the display. Coherent dots will always move in the direction of coherent motion in all frames.
298
- -Different: Each dot can be either a coherent dot (signal) or incoherent dot (noise) and will be designated randomly (weighted based on the coherence level) at each frame. Only the dots that are designated to be coherent dots will move in the direction of coherent motion, but only in that frame. In the next frame, each dot will be designated randomly again on whether it is a coherent or incoherent dot.
299
-
300
- Noise Type:
301
- -Random position: The incoherent dots appear in a random location in the aperture in each frame
302
- -Random walk: The incoherent dots will move in a random direction (designated randomly in each frame) in each frame.
303
- -Random direction: Each incoherent dot has its own alternative direction of motion (designated randomly at the beginning of the trial), and moves in that direction in each frame.
304
-
305
- -------------------
306
-
307
- 1 - same && random position
308
- 2 - same && random walk
309
- 3 - same && random direction
310
- 4 - different && random position
311
- 5 - different && random walk
312
- 6 - different && random direction */
313
-
314
- var RDK = trial.RDK_type;
315
-
316
-
317
- /*
318
- Shape of aperture
319
- 1 - Circle
320
- 2 - Ellipse
321
- 3 - Square
322
- 4 - Rectangle
323
- */
324
- var apertureType = trial.aperture_type;
325
-
326
- /*
327
- Out of Bounds Decision
328
- How we reinsert a dot that has moved outside the edges of the aperture:
329
- 1 - Randomly appear anywhere in the aperture
330
- 2 - Appear on the opposite edge of the aperture (Random if square or rectangle, reflected about origin in circle and ellipse)
331
- */
332
- var reinsertType = trial.reinsert_type;
333
-
334
- //Fixation Cross Parameters
335
- var fixationCross = trial.fixation_cross; //To display or not to display the cross
336
- var fixationCrossWidth = trial.fixation_cross_width; //The width of the fixation cross in pixels
337
- var fixationCrossHeight = trial.fixation_cross_height; //The height of the fixation cross in pixels
338
- var fixationCrossColor = trial.fixation_cross_color; //The color of the fixation cross
339
- var fixationCrossThickness = trial.fixation_cross_thickness; //The thickness of the fixation cross, must be positive number above 1
340
-
341
- //Border Parameters
342
- var border = trial.border; //To display or not to display the border
343
- var borderThickness = trial.border_thickness; //The width of the border in pixels
344
- var borderColor = trial.border_color; //The color of the border
345
-
346
-
347
-
348
- //--------------------------------------
349
- //----------SET PARAMETERS END----------
350
- //--------------------------------------
351
-
352
- //--------Set up Canvas begin-------
353
-
354
- //Create a canvas element and append it to the DOM
355
- var canvas = document.createElement("canvas");
356
- display_element.appendChild(canvas);
357
-
358
-
359
- //The document body IS 'display_element' (i.e. <body class="jspsych-display-element"> .... </body> )
360
- var body = document.getElementsByClassName("jspsych-display-element")[0];
361
-
362
- //Save the current settings to be restored later
363
- var originalMargin = body.style.margin;
364
- var originalPadding = body.style.padding;
365
- var originalBackgroundColor = body.style.backgroundColor;
366
-
367
- //Remove the margins and paddings of the display_element
368
- body.style.margin = 0;
369
- body.style.padding = 0;
370
- body.style.backgroundColor = backgroundColor; //Match the background of the display element to the background color of the canvas so that the removal of the canvas at the end of the trial is not noticed
371
-
372
- //Remove the margins and padding of the canvas
373
- canvas.style.margin = 0;
374
- canvas.style.padding = 0;
375
- // use absolute positioning in top left corner to get rid of scroll bars
376
- canvas.style.position = 'absolute';
377
- canvas.style.top = 0;
378
- canvas.style.left = 0;
379
-
380
- //Get the context of the canvas so that it can be painted on.
381
- var ctx = canvas.getContext("2d");
382
-
383
- //Declare variables for width and height, and also set the canvas width and height to the window width and height
384
- var canvasWidth = canvas.width = window.innerWidth;
385
- var canvasHeight = canvas.height = window.innerHeight;
386
-
387
- //Set the canvas background color
388
- canvas.style.backgroundColor = backgroundColor;
389
-
390
- //--------Set up Canvas end-------
391
-
392
-
393
-
394
- //--------RDK variables and function calls begin--------
395
-
396
- //This is the main part of the trial that makes everything run
397
-
398
- //Global variable for the current aperture number
399
- var currentApertureNumber;
400
-
401
- //3D Array to hold the dots (1st D is Apertures, 2nd D is Sets, 3rd D is Dots)
402
- var dotArray3d = [];
403
-
404
- //Variables for different apertures (initialized in setUpMultipleApertures function below)
405
- var nDotsArray;
406
- var nSetsArray;
407
- var coherentDirectionArray;
408
- var coherenceArray;
409
- var oppositeCoherenceArray;
410
- var dotRadiusArray;
411
- var dotLifeArray;
412
- var moveDistanceArray;
413
- var apertureWidthArray;
414
- var apertureHeightArray;
415
- var dotColorArray;
416
- var apertureCenterXArray;
417
- var apertureCenterYArray;
418
-
419
- // Set up multiple apertures
420
- setUpMultipleApertures();
421
-
422
- //Declare aperture parameters for initialization based on shape (used in initializeApertureDimensions function below)
423
- var horizontalAxis;
424
- var verticalAxis;
425
-
426
- //Calculate the x and y jump sizes for coherent dots
427
- var coherentJumpSizeX;
428
- var coherentJumpSizeY;
429
-
430
- //Calculate the number of coherent, opposite coherent, and incoherent dots
431
- var nCoherentDots;
432
- var nOppositeCoherentDots;
433
- var nIncoherentDots;
434
-
435
- //Make the array of arrays containing dot objects
436
- var dotArray2d;
437
-
438
- var dotArray; //Declare a global variable to hold the current array
439
- var currentSetArray; //Declare and initialize a global variable to cycle through the dot arrays
440
-
441
-
442
- //Initialize stopping condition for animateDotMotion function that runs in a loop
443
- var stopDotMotion = false;
444
-
445
- //Variable to control the frame rate, to ensure that the first frame is skipped because it follows a different timing
446
- var firstFrame = true; //Used to skip the first frame in animate function below (in animateDotMotion function)
447
-
448
- //Variable to start the timer when the time comes
449
- var timerHasStarted = false;
450
-
451
- //Initialize object to store the response data. Default values of -1 are used if the trial times out and the subject has not pressed a valid key
452
- var response = {
453
- rt: -1,
454
- key: -1
455
- }
456
-
457
- //Declare a global timeout ID to be initialized below in animateDotMotion function and to be used in after_response function
458
- var timeoutID;
459
-
460
- //Declare global variable to be defined in startKeyboardListener function and to be used in end_trial function
461
- var keyboardListener;
462
-
463
- //Declare global variable to store the frame rate of the trial
464
- var frameRate = []; //How often the monitor refreshes, in ms. Currently an array to store all the intervals. Will be converted into a single number (the average) in end_trial function.
465
-
466
- //variable to store how many frames were presented.
467
- var numberOfFrames = 0;
468
-
469
- //This runs the dot motion simulation, updating it according to the frame refresh rate of the screen.
470
- animateDotMotion();
471
-
472
-
473
- //--------RDK variables and function calls end--------
474
-
475
-
476
-
477
- //-------------------------------------
478
- //-----------FUNCTIONS BEGIN-----------
479
- //-------------------------------------
480
-
481
- //----JsPsych Functions Begin----
482
-
483
-
484
- //Function to start the keyboard listener
485
- function startKeyboardListener(){
486
- //Start the response listener if there are choices for keys
487
- if (trial.choices != jsPsych.NO_KEYS) {
488
- //Create the keyboard listener to listen for subjects' key response
489
- keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
490
- callback_function: after_response, //Function to call once the subject presses a valid key
491
- valid_responses: trial.choices, //The keys that will be considered a valid response and cause the callback function to be called
492
- rt_method: 'performance', //The type of method to record timing information.
493
- persist: false, //If set to false, keyboard listener will only trigger the first time a valid key is pressed. If set to true, it has to be explicitly cancelled by the cancelKeyboardResponse plugin API.
494
- allow_held_key: false //Only register the key once, after this getKeyboardResponse function is called. (Check JsPsych docs for better info under 'jsPsych.pluginAPI.getKeyboardResponse').
495
- });
496
- }
497
- }
498
-
499
- //Function to end the trial proper
500
- function end_trial() {
501
-
502
- //Stop the dot motion animation
503
- stopDotMotion = true;
504
-
505
- //Store the number of frames
506
- numberOfFrames = frameRate.length;
507
-
508
- //Variable to store the frame rate array
509
- var frameRateArray = frameRate;
510
-
511
- //Calculate the average frame rate
512
- if(frameRate.length > 0){//Check to make sure that the array is not empty
513
- frameRate = frameRate.reduce((total,current) => total + current)/frameRate.length; //Sum up all the elements in the array
514
- }else{
515
- frameRate = 0; //Set to zero if the subject presses an answer before a frame is shown (i.e. if frameRate is an empty array)
516
- }
517
-
518
- //Kill the keyboard listener if keyboardListener has been defined
519
- if (typeof keyboardListener !== 'undefined') {
520
- jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
521
- }
522
-
523
- //Place all the data to be saved from this trial in one data object
524
- var trial_data = {
525
- "rt": response.rt, //The response time
526
- "key_press": response.key, //The key that the subject pressed
527
- "correct": correctOrNot(), //If the subject response was correct
528
- "choices": trial.choices, //The set of valid keys
529
- "correct_choice": trial.correct_choice, //The correct choice
530
- "trial_duration": trial.trial_duration, //The trial duration
531
- "response_ends_trial": trial.response_ends_trial, //If the response ends the trial
532
- "number_of_apertures": trial.number_of_apertures,
533
- "number_of_dots": trial.number_of_dots,
534
- "number_of_sets": trial.number_of_sets,
535
- "coherent_direction": trial.coherent_direction,
536
- "coherence": trial.coherence,
537
- "opposite_coherence": trial.opposite_coherence,
538
- "dot_radius": trial.dot_radius,
539
- "dot_life": trial.dot_life,
540
- "move_distance": trial.move_distance,
541
- "aperture_width": trial.aperture_width,
542
- "aperture_height": trial.aperture_height,
543
- "dot_color": trial.dot_color,
544
- "background_color": trial.background_color,
545
- "RDK_type": trial.RDK_type,
546
- "aperture_type": trial.aperture_type,
547
- "reinsert_type": trial.reinsert_type,
548
- "frame_rate": frameRate, //The average frame rate for the trial
549
- "frame_rate_array": JSON.stringify(frameRateArray), //The array of ms per frame in this trial, in the form of a JSON string
550
- "number_of_frames": numberOfFrames, //The number of frames in this trial
551
- "aperture_center_x": trial.aperture_center_x,
552
- "aperture_center_y": trial.aperture_center_y,
553
- "fixation_cross": trial.fixation_cross,
554
- "fixation_cross_width": trial.fixation_cross_width,
555
- "fixation_cross_height": trial.fixation_cross_height,
556
- "fixation_cross_color": trial.fixation_cross_color,
557
- "fixation_cross_thickness": trial.fixation_cross_thickness,
558
- "border": trial.border,
559
- "border_thickness": trial.border_thickness,
560
- "border_color": trial.border_color,
561
- "canvas_width": canvasWidth,
562
- "canvas_height": canvasHeight
563
-
564
- }
565
-
566
- //Remove the canvas as the child of the display_element element
567
- display_element.innerHTML='';
568
-
569
- //Restore the settings to JsPsych defaults
570
- body.style.margin = originalMargin;
571
- body.style.padding = originalPadding;
572
- body.style.backgroundColor = originalBackgroundColor
573
-
574
- //End this trial and move on to the next trial
575
- jsPsych.finishTrial(trial_data);
576
-
577
- } //End of end_trial
578
-
579
- //Function to record the first response by the subject
580
- function after_response(info) {
581
-
582
- //If the response has not been recorded, record it
583
- if (response.key == -1) {
584
- response = info; //Replace the response object created above
585
- }
586
-
587
- //If the parameter is set such that the response ends the trial, then kill the timeout and end the trial
588
- if (trial.response_ends_trial) {
589
- window.clearTimeout(timeoutID);
590
- end_trial();
591
- }
592
-
593
- } //End of after_response
594
-
595
- //Function that determines if the response is correct
596
- function correctOrNot(){
597
-
598
- //Check that the correct_choice has been defined
599
- if(typeof trial.correct_choice !== 'undefined'){
600
- //If the correct_choice variable holds an array
601
- if(trial.correct_choice.constructor === Array){ //If it is an array
602
- //If the elements are characters
603
- if(typeof trial.correct_choice[0] === 'string' || trial.correct_choice[0] instanceof String){
604
- trial.correct_choice = trial.correct_choice.map(function(x){return x.toUpperCase();}); //Convert all the values to upper case
605
- return trial.correct_choice.includes(String.fromCharCode(response.key)); //If the response is included in the correct_choice array, return true. Else, return false.
606
- }
607
- //Else if the elements are numbers (javascript character codes)
608
- else if (typeof trial.correct_choice[0] === 'number'){
609
- return trial.correct_choice.includes(response.key); //If the response is included in the correct_choice array, return true. Else, return false.
610
- }
611
- }
612
- //Else compare the char with the response key
613
- else{
614
- //If the element is a character
615
- if(typeof trial.correct_choice === 'string' || trial.correct_choice instanceof String){
616
- //Return true if the user's response matches the correct answer. Return false otherwise.
617
- return response.key == trial.correct_choice.toUpperCase().charCodeAt(0);
618
- }
619
- //Else if the element is a number (javascript character codes)
620
- else if (typeof trial.correct_choice === 'number'){
621
- console.log(response.key == trial.correct_choice);
622
- return response.key == trial.correct_choice;
623
- }
624
- }
625
- }
626
- }
627
-
628
- //----JsPsych Functions End----
629
-
630
- //----RDK Functions Begin----
631
-
632
- //Set up the variables for the apertures
633
- function setUpMultipleApertures(){
634
- nDotsArray = setParameter(nDots);
635
- nSetsArray = setParameter(nSets);
636
- coherentDirectionArray = setParameter(coherentDirection);
637
- coherenceArray = setParameter(coherence);
638
- oppositeCoherenceArray = setParameter(oppositeCoherence);
639
- dotRadiusArray = setParameter(dotRadius);
640
- dotLifeArray = setParameter(dotLife);
641
- moveDistanceArray = setParameter(moveDistance);
642
- apertureWidthArray = setParameter(apertureWidth);
643
- apertureHeightArray = setParameter(apertureHeight);
644
- dotColorArray = setParameter(dotColor);
645
- apertureCenterXArray = setParameter(apertureCenterX);
646
- apertureCenterYArray = setParameter(apertureCenterY);
647
- RDKArray = setParameter(RDK);
648
- apertureTypeArray = setParameter(apertureType);
649
- reinsertTypeArray = setParameter(reinsertType);
650
- fixationCrossArray = setParameter(fixationCross);
651
- fixationCrossWidthArray = setParameter(fixationCrossWidth);
652
- fixationCrossHeightArray = setParameter(fixationCrossHeight);
653
- fixationCrossColorArray = setParameter(fixationCrossColor);
654
- fixationCrossThicknessArray = setParameter(fixationCrossThickness);
655
- borderArray = setParameter(border);
656
- borderThicknessArray = setParameter(borderThickness);
657
- borderColorArray = setParameter(borderColor);
658
-
659
- currentSetArray = setParameter(0); //Always starts at zero
660
-
661
-
662
- //Loop through the number of apertures to make the dots
663
- for(currentApertureNumber = 0; currentApertureNumber < nApertures; currentApertureNumber++){
664
-
665
- //Initialize the parameters to make the 2d dot array (one for each aperture);
666
- initializeCurrentApertureParameters();
667
-
668
- //Make each 2d array and push it into the 3d array
669
- dotArray3d.push(makeDotArray2d());
670
- }
671
- }
672
-
673
- //Function to set the parameters of the array
674
- function setParameter(originalVariable){
675
- //Check if it is an array and its length matches the aperture then return the original array
676
- if(originalVariable.constructor === Array && originalVariable.length === nApertures){
677
- return originalVariable;
678
- }
679
- //Else if it is not an array, we make it an array with duplicate values
680
- else if(originalVariable.constructor !== Array){
681
-
682
- var tempArray = [];
683
-
684
- //Make a for loop and duplicate the values
685
- for(var i = 0; i < nApertures; i++){
686
- tempArray.push(originalVariable);
687
- }
688
- return tempArray;
689
- }
690
- //Else if the array is not long enough, then print out that error message
691
- else if(originalVariable.constructor === Array && originalVariable.length !== nApertures){
692
- console.error("If you have more than one aperture, please ensure that arrays that are passed in as parameters are the same length as the number of apertures. Else you can use a single value without the array");
693
- }
694
- //Else print a generic error
695
- else{
696
- console.error("A parameter is incorrectly set. Please ensure that the nApertures parameter is set to the correct value (if using more than one aperture), and all others parameters are set correctly.");
697
- }
698
- }
699
-
700
- //Function to set the global variables to the current aperture so that the correct dots are updated and drawn
701
- function initializeCurrentApertureParameters(){
702
-
703
- //Set the global variables to that relevant to the current aperture
704
- nDots = nDotsArray[currentApertureNumber];
705
- nSets = nSetsArray[currentApertureNumber];
706
- coherentDirection = coherentDirectionArray[currentApertureNumber];
707
- coherence = coherenceArray[currentApertureNumber];
708
- oppositeCoherence = oppositeCoherenceArray[currentApertureNumber];
709
- dotRadius = dotRadiusArray[currentApertureNumber];
710
- dotLife = dotLifeArray[currentApertureNumber];
711
- moveDistance = moveDistanceArray[currentApertureNumber];
712
- apertureWidth = apertureWidthArray[currentApertureNumber];
713
- apertureHeight = apertureHeightArray[currentApertureNumber];
714
- dotColor = dotColorArray[currentApertureNumber];
715
- apertureCenterX = apertureCenterXArray[currentApertureNumber];
716
- apertureCenterY = apertureCenterYArray[currentApertureNumber];
717
- RDK = RDKArray[currentApertureNumber];
718
- apertureType = apertureTypeArray[currentApertureNumber];
719
- reinsertType = reinsertTypeArray[currentApertureNumber];
720
- fixationCross = fixationCrossArray[currentApertureNumber];
721
- fixationCrossWidth = fixationCrossWidthArray[currentApertureNumber];
722
- fixationCrossHeight = fixationCrossHeightArray[currentApertureNumber];
723
- fixationCrossColor = fixationCrossColorArray[currentApertureNumber];
724
- fixationCrossThickness = fixationCrossThicknessArray[currentApertureNumber];
725
- border = borderArray[currentApertureNumber];
726
- borderThickness = borderThicknessArray[currentApertureNumber];
727
- borderColor = borderColorArray[currentApertureNumber];
728
-
729
- //Calculate the x and y jump sizes for coherent dots
730
- coherentJumpSizeX = calculateCoherentJumpSizeX(coherentDirection);
731
- coherentJumpSizeY = calculateCoherentJumpSizeY(coherentDirection);
732
-
733
- //Initialize the aperture parameters
734
- initializeApertureDimensions();
735
-
736
- //Calculate the number of coherent, opposite coherent, and incoherent dots
737
- nCoherentDots = nDots * coherence;
738
- nOppositeCoherentDots = nDots * oppositeCoherence;
739
- nIncoherentDots = nDots - (nCoherentDots + nOppositeCoherentDots);
740
-
741
- //If the 3d array has been made, then choose the 2d array and the current set
742
- dotArray2d = dotArray3d.length !==0 ? dotArray3d[currentApertureNumber] : undefined;
743
-
744
- }// End of initializeCurrentApertureParameters
745
-
746
- //Calculate coherent jump size in the x direction
747
- function calculateCoherentJumpSizeX(coherentDirection) {
748
- var angleInRadians = coherentDirection * Math.PI / 180;
749
- return moveDistance * Math.cos(angleInRadians);
750
- }
751
-
752
- //Calculate coherent jump size in the y direction
753
- function calculateCoherentJumpSizeY(coherentDirection) {
754
- var angleInRadians = -coherentDirection * Math.PI / 180; //Negative sign because the y-axis is flipped on screen
755
- return moveDistance * Math.sin(angleInRadians);
756
- }
757
-
758
- //Initialize the parameters for the aperture for further calculation
759
- function initializeApertureDimensions() {
760
- //For circle and square
761
- if (apertureType == 1 || apertureType == 3) {
762
- horizontalAxis = verticalAxis = apertureWidth/2;
763
- }
764
- //For ellipse and rectangle
765
- else if (apertureType == 2 || apertureType == 4) {
766
- horizontalAxis = apertureWidth / 2;
767
- verticalAxis = apertureHeight / 2;
768
- }
769
- }
770
-
771
- //Make the 2d array, which is an array of array of dots
772
- function makeDotArray2d() {
773
- //Declare an array to hold the sets of dot arrays
774
- var tempArray = []
775
- //Loop for each set of dot array
776
- for (var i = 0; i < nSets; i++) {
777
- tempArray.push(makeDotArray()); //Make a dot array and push it into the 2d array
778
- }
779
-
780
- return tempArray;
781
- }
782
-
783
- //Make the dot array
784
- function makeDotArray() {
785
- var tempArray = []
786
- for (var i = 0; i < nDots; i++) {
787
- //Initialize a dot to be modified and inserted into the array
788
- var dot = {
789
- x: 0, //x coordinate
790
- y: 0, //y coordinate
791
- vx: 0, //coherent x jumpsize (if any)
792
- vy: 0, //coherent y jumpsize (if any)
793
- vx2: 0, //incoherent (random) x jumpsize (if any)
794
- vy2: 0, //incoherent (random) y jumpsize (if any)
795
- latestXMove: 0, //Stores the latest x move direction for the dot (to be used in reinsertOnOppositeEdge function below)
796
- latestYMove: 0, //Stores the latest y move direction for the dot (to be used in reinsertOnOppositeEdge function below)
797
- lifeCount: Math.floor(randomNumberBetween(0, dotLife)), //Counter for the dot's life. Updates every time it is shown in a frame
798
- updateType: "" //String to determine how this dot is updated
799
- };
800
-
801
- //randomly set the x and y coordinates
802
- dot = resetLocation(dot);
803
-
804
- //For the same && random position RDK type
805
- if (RDK == 1) {
806
- //For coherent dots
807
- if (i < nCoherentDots) {
808
- dot = setvxvy(dot); // Set dot.vx and dot.vy
809
- dot.updateType = "constant direction";
810
- }
811
- //For opposite coherent dots
812
- else if(i >= nCoherentDots && i < (nCoherentDots + nOppositeCoherentDots)){
813
- dot = setvxvy(dot); // Set dot.vx and dot.vy
814
- dot.updateType = "opposite direction";
815
- }
816
- //For incoherent dots
817
- else {
818
- dot.updateType = "random position";
819
- }
820
- } //End of RDK==1
821
-
822
- //For the same && random walk RDK type
823
- if (RDK == 2) {
824
- //For coherent dots
825
- if (i < nCoherentDots) {
826
- dot = setvxvy(dot); // Set dot.vx and dot.vy
827
- dot.updateType = "constant direction";
828
- }
829
- //For opposite coherent dots
830
- else if(i >= nCoherentDots && i < (nCoherentDots + nOppositeCoherentDots)){
831
- dot = setvxvy(dot); // Set dot.vx and dot.vy
832
- dot.updateType = "opposite direction";
833
- }
834
- //For incoherent dots
835
- else {
836
- dot.updateType = "random walk";
837
- }
838
- } //End of RDK==2
839
-
840
- //For the same && random direction RDK type
841
- if (RDK == 3) {
842
- //For coherent dots
843
- if (i < nCoherentDots) {
844
- dot = setvxvy(dot); // Set dot.vx and dot.vy
845
- dot.updateType = "constant direction";
846
- }
847
- //For opposite coherent dots
848
- else if(i >= nCoherentDots && i < (nCoherentDots + nOppositeCoherentDots)){
849
- dot = setvxvy(dot); // Set dot.vx and dot.vy
850
- dot.updateType = "opposite direction";
851
- }
852
- //For incoherent dots
853
- else {
854
- setvx2vy2(dot); // Set dot.vx2 and dot.vy2
855
- dot.updateType = "random direction";
856
- }
857
- } //End of RDK==3
858
-
859
- //For the different && random position RDK type
860
- if (RDK == 4) {
861
- //For all dots
862
- dot = setvxvy(dot); // Set dot.vx and dot.vy
863
- dot.updateType = "constant direction or opposite direction or random position";
864
- } //End of RDK==4
865
-
866
- //For the different && random walk RDK type
867
- if (RDK == 5) {
868
- //For all dots
869
- dot = setvxvy(dot); // Set dot.vx and dot.vy
870
- dot.updateType = "constant direction or opposite direction or random walk";
871
- } //End of RDK==5
872
-
873
- //For the different && random direction RDK type
874
- if (RDK == 6) {
875
- //For all dots
876
- dot = setvxvy(dot); // Set dot.vx and dot.vy
877
- //Each dot will have its own alternate direction of motion
878
- setvx2vy2(dot); // Set dot.vx2 and dot.vy2
879
- dot.updateType = "constant direction or opposite direction or random direction";
880
- } //End of RDK==6
881
-
882
- tempArray.push(dot);
883
- } //End of for loop
884
- return tempArray;
885
- }
886
-
887
- //Function to update all the dots all the apertures and then draw them
888
- function updateAndDraw(){
889
-
890
- //Three for loops that do things in sequence: clear, update, and draw dots.
891
-
892
- // Clear all the current dots
893
- for(currentApertureNumber = 0; currentApertureNumber < nApertures; currentApertureNumber++){
894
-
895
- //Initialize the variables for each parameter
896
- initializeCurrentApertureParameters(currentApertureNumber);
897
-
898
- //Clear the canvas by drawing over the current dots
899
- clearDots();
900
- }
901
-
902
- // Update all the relevant dots
903
- for(currentApertureNumber = 0; currentApertureNumber < nApertures; currentApertureNumber++){
904
-
905
- //Initialize the variables for each parameter
906
- initializeCurrentApertureParameters(currentApertureNumber);
907
-
908
- //Update the dots
909
- updateDots();
910
- }
911
-
912
- // Draw all the relevant dots on the canvas
913
- for(currentApertureNumber = 0; currentApertureNumber < nApertures; currentApertureNumber++){
914
-
915
- //Initialize the variables for each parameter
916
- initializeCurrentApertureParameters(currentApertureNumber);
917
-
918
- //Draw on the canvas
919
- draw();
920
- }
921
- }
922
-
923
- //Function that clears the dots on the canvas by drawing over it with the color of the baclground
924
- function clearDots(){
925
-
926
- //Load in the current set of dot array for easy handling
927
- var dotArray = dotArray2d[currentSetArray[currentApertureNumber]];
928
-
929
- //Loop through the dots one by one and draw them
930
- for (var i = 0; i < nDots; i++) {
931
- dot = dotArray[i];
932
- ctx.beginPath();
933
- ctx.arc(dot.x, dot.y, dotRadius+1, 0, Math.PI * 2);
934
- ctx.fillStyle = backgroundColor;
935
- ctx.fill();
936
- }
937
- }
938
-
939
- //Draw the dots on the canvas after they're updated
940
- function draw() {
941
-
942
- //Load in the current set of dot array for easy handling
943
- var dotArray = dotArray2d[currentSetArray[currentApertureNumber]];
944
-
945
- //Loop through the dots one by one and draw them
946
- for (var i = 0; i < nDots; i++) {
947
- dot = dotArray[i];
948
- ctx.beginPath();
949
- ctx.arc(dot.x, dot.y, dotRadius, 0, Math.PI * 2);
950
- ctx.fillStyle = dotColor;
951
- ctx.fill();
952
- }
953
-
954
- //Draw the fixation cross if we want it
955
- if(fixationCross === true){
956
- //Horizontal line
957
- ctx.beginPath();
958
- ctx.lineWidth = fixationCrossThickness;
959
- ctx.moveTo(canvasWidth/2 - fixationCrossWidth, canvasHeight/2);
960
- ctx.lineTo(canvasWidth/2 + fixationCrossWidth, canvasHeight/2);
961
- ctx.strokeStyle = fixationCrossColor;
962
- ctx.stroke();
963
-
964
- //Vertical line
965
- ctx.beginPath();
966
- ctx.lineWidth = fixationCrossThickness;
967
- ctx.moveTo(canvasWidth/2, canvasHeight/2 - fixationCrossHeight);
968
- ctx.lineTo(canvasWidth/2, canvasHeight/2 + fixationCrossHeight);
969
- ctx.strokeStyle = fixationCrossColor;
970
- ctx.stroke();
971
- }
972
-
973
- //Draw the border if we want it
974
- if(border === true){
975
-
976
- //For circle and ellipse
977
- if(apertureType === 1 || apertureType === 2){
978
- ctx.lineWidth = borderThickness;
979
- ctx.strokeStyle = borderColor;
980
- ctx.beginPath();
981
- ctx.ellipse(apertureCenterX, apertureCenterY, horizontalAxis+(borderThickness/2), verticalAxis+(borderThickness/2), 0, 0, Math.PI*2);
982
- ctx.stroke();
983
- }//End of if circle or ellipse
984
-
985
- //For square and rectangle
986
- if(apertureType === 3 || apertureType === 4){
987
- ctx.lineWidth = borderThickness;
988
- ctx.strokeStyle = borderColor;
989
- ctx.strokeRect(apertureCenterX-horizontalAxis-(borderThickness/2), apertureCenterY-verticalAxis-(borderThickness/2), (horizontalAxis*2)+borderThickness, (verticalAxis*2)+borderThickness);
990
- }//End of if square or
991
-
992
- }//End of if border === true
993
-
994
- }//End of draw
995
-
996
- //Update the dots with their new location
997
- function updateDots() {
998
-
999
- //Cycle through to the next set of dots
1000
- if (currentSetArray[currentApertureNumber] == nSets - 1) {
1001
- currentSetArray[currentApertureNumber] = 0;
1002
- } else {
1003
- currentSetArray[currentApertureNumber] = currentSetArray[currentApertureNumber] + 1;
1004
- }
1005
-
1006
- //Load in the current set of dot array for easy handling
1007
- var dotArray = dotArray2d[currentSetArray[currentApertureNumber]];
1008
-
1009
- //Load in the current set of dot array for easy handling
1010
- //dotArray = dotArray2d[currentSetArray[currentApertureNumber]]; //Global variable, so the draw function also uses this array
1011
-
1012
- //Loop through the dots one by one and update them accordingly
1013
- for (var i = 0; i < nDots; i++) {
1014
- var dot = dotArray[i]; //Load the current dot into the variable for easy handling
1015
-
1016
- //Generate a random value
1017
- var randomValue = Math.random();
1018
-
1019
- //Update based on the dot's update type
1020
- if (dot.updateType == "constant direction") {
1021
- dot = constantDirectionUpdate(dot);
1022
- } else if (dot.updateType == "opposite direction") {
1023
- dot = oppositeDirectionUpdate(dot);
1024
- } else if (dot.updateType == "random position") {
1025
- dot = resetLocation(dot);
1026
- } else if (dot.updateType == "random walk") {
1027
- dot = randomWalkUpdate(dot);
1028
- } else if (dot.updateType == "random direction") {
1029
- dot = randomDirectionUpdate(dot);
1030
- } else if (dot.updateType == "constant direction or opposite direction or random position") {
1031
-
1032
- //Randomly select if the dot goes in a constant direction or random position, weighted based on the coherence level
1033
- if (randomValue < coherence) {
1034
- dot = constantDirectionUpdate(dot);
1035
- } else if(randomValue >= coherence && randomValue < (coherence + oppositeCoherence)){
1036
- dot = oppositeDirectionUpdate(dot);
1037
- } else {
1038
- dot = resetLocation(dot);
1039
- }
1040
- } else if (dot.updateType == "constant direction or opposite direction or random walk") {
1041
- //Randomly select if the dot goes in a constant direction or random walk, weighted based on the coherence level
1042
- if (randomValue < coherence) {
1043
- dot = constantDirectionUpdate(dot);
1044
- } else if(randomValue >= coherence && randomValue < (coherence + oppositeCoherence)){
1045
- dot = oppositeDirectionUpdate(dot);
1046
- } else {
1047
- dot = randomWalkUpdate(dot);
1048
- }
1049
- } else if (dot.updateType == "constant direction or opposite direction or random direction") {
1050
- //Randomly select if the dot goes in a constant direction or random direction, weighted based on the coherence level
1051
- if (randomValue < coherence) {
1052
- dot = constantDirectionUpdate(dot);
1053
- } else if(randomValue >= coherence && randomValue < (coherence + oppositeCoherence)){
1054
- dot = oppositeDirectionUpdate(dot);
1055
- } else {
1056
- dot = randomDirectionUpdate(dot);
1057
- }
1058
- }//End of if dot.updateType == ...
1059
-
1060
- //Increment the life count
1061
- dot.lifeCount++;
1062
-
1063
- //Check if out of bounds or if life ended
1064
- if (lifeEnded(dot)) {
1065
- dot = resetLocation(dot);
1066
- }
1067
-
1068
- //If it goes out of bounds, do what is necessary (reinsert randomly or reinsert on the opposite edge) based on the parameter chosen
1069
- if (outOfBounds(dot)) {
1070
- switch (reinsertType) {
1071
- case 1:
1072
- dot = resetLocation(dot);
1073
- break;
1074
- case 2:
1075
- dot = reinsertOnOppositeEdge(dot);
1076
- break;
1077
- } //End of switch statement
1078
- } //End of if
1079
-
1080
- } //End of for loop
1081
- } //End of updateDots function
1082
-
1083
- //Function to check if dot life has ended
1084
- function lifeEnded(dot) {
1085
- //If we want infinite dot life
1086
- if (dotLife < 0) {
1087
- dot.lifeCount = 0; //resetting to zero to save memory. Otherwise it might increment to huge numbers.
1088
- return false;
1089
- }
1090
- //Else if the dot's life has reached its end
1091
- else if (dot.lifeCount >= dotLife) {
1092
- dot.lifeCount = 0;
1093
- return true;
1094
- }
1095
- //Else the dot's life has not reached its end
1096
- else {
1097
- return false;
1098
- }
1099
- }
1100
-
1101
- //Function to check if dot is out of bounds
1102
- function outOfBounds(dot) {
1103
- //For circle and ellipse
1104
- if (apertureType == 1 || apertureType == 2) {
1105
- if (dot.x < xValueNegative(dot.y) || dot.x > xValuePositive(dot.y) || dot.y < yValueNegative(dot.x) || dot.y > yValuePositive(dot.x)) {
1106
- return true;
1107
- } else {
1108
- return false;
1109
- }
1110
- }
1111
- //For square and rectangle
1112
- if (apertureType == 3 || apertureType == 4) {
1113
- if (dot.x < (apertureCenterX) - horizontalAxis || dot.x > (apertureCenterX) + horizontalAxis || dot.y < (apertureCenterY) - verticalAxis || dot.y > (apertureCenterY) + verticalAxis) {
1114
- return true;
1115
- } else {
1116
- return false;
1117
- }
1118
- }
1119
-
1120
- }
1121
-
1122
- //Set the vx and vy for the dot to the coherent jump sizes of the X and Y directions
1123
- function setvxvy(dot) {
1124
- dot.vx = coherentJumpSizeX;
1125
- dot.vy = coherentJumpSizeY;
1126
- return dot;
1127
- }
1128
-
1129
- //Set the vx2 and vy2 based on a random angle
1130
- function setvx2vy2(dot) {
1131
- //Generate a random angle of movement
1132
- var theta = randomNumberBetween(-Math.PI, Math.PI);
1133
- //Update properties vx2 and vy2 with the alternate directions
1134
- dot.vx2 = Math.cos(theta) * moveDistance;
1135
- dot.vy2 = -Math.sin(theta) * moveDistance;
1136
- return dot;
1137
- }
1138
-
1139
- //Updates the x and y coordinates by moving it in the x and y coherent directions
1140
- function constantDirectionUpdate(dot) {
1141
- dot.x += dot.vx;
1142
- dot.y += dot.vy;
1143
- dot.latestXMove = dot.vx;
1144
- dot.latestYMove = dot.vy;
1145
- return dot;
1146
- }
1147
-
1148
- //Updates the x and y coordinates by moving it in the opposite x and y coherent directions
1149
- function oppositeDirectionUpdate(dot) {
1150
- dot.x -= dot.vx;
1151
- dot.y -= dot.vy;
1152
- dot.latestXMove = -dot.vx;
1153
- dot.latestYMove = -dot.vy;
1154
- return dot;
1155
- }
1156
-
1157
- //Creates a new angle to move towards and updates the x and y coordinates
1158
- function randomWalkUpdate(dot) {
1159
- //Generate a random angle of movement
1160
- var theta = randomNumberBetween(-Math.PI, Math.PI);
1161
- //Generate the movement from the angle
1162
- dot.latestXMove = Math.cos(theta) * moveDistance;
1163
- dot.latestYMove = -Math.sin(theta) * moveDistance;
1164
- //Update x and y coordinates with the new location
1165
- dot.x += dot.latestXMove;
1166
- dot.y += dot.latestYMove;
1167
- return dot;
1168
- }
1169
-
1170
- //Updates the x and y coordinates with the alternative move direction
1171
- function randomDirectionUpdate(dot) {
1172
- dot.x += dot.vx2;
1173
- dot.y += dot.vy2;
1174
- dot.latestXMove = dot.vx2;
1175
- dot.latestYMove = dot.vy2;
1176
- return dot;
1177
- }
1178
-
1179
- //Calculates a random position on the opposite edge to reinsert the dot
1180
- function reinsertOnOppositeEdge(dot) {
1181
- //If it is a circle or ellipse
1182
- if (apertureType == 1 || apertureType == 2) {
1183
- //Bring the dot back into the aperture by moving back one step
1184
- dot.x -= dot.latestXMove;
1185
- dot.y -= dot.latestYMove;
1186
-
1187
- //Move the dot to the position relative to the origin to be reflected about the origin
1188
- dot.x -= apertureCenterX;
1189
- dot.y -= apertureCenterY;
1190
-
1191
- //Reflect the dot about the origin
1192
- dot.x = -dot.x;
1193
- dot.y = -dot.y;
1194
-
1195
- //Move the dot back to the center of the screen
1196
- dot.x += apertureCenterX;
1197
- dot.y += apertureCenterY;
1198
-
1199
- } //End of if apertureType == 1 | == 2
1200
-
1201
- //If it is a square or rectangle, re-insert on one of the opposite edges
1202
- if (apertureType == 3 || apertureType == 4) {
1203
-
1204
- /* The formula for calculating whether a dot appears from the vertical edge (left or right edges) is dependent on the direction of the dot and the ratio of the vertical and horizontal edge lengths.
1205
- E.g.
1206
- Aperture is 100 px high and 200px wide
1207
- Dot is moving 3 px in x direction and 4px in y direction
1208
- Weight on vertical edge (sides) = (100/(100+200)) * (|3| / (|3| + |4|)) = 1/7
1209
- Weight on horizontal edge (top or bottom) = (200/(100+200)) * (|4| / (|3| + |4|)) = 8/21
1210
-
1211
- The weights above are the ratios to one another.
1212
- E.g. (cont.)
1213
- Ratio (vertical edge : horizontal edge) == (1/7 : 8/21)
1214
- Total probability space = 1/7 + 8/21 = 11/21
1215
- Probability that dot appears on vertical edge = (1/7)/(11/21) = 3/11
1216
- Probability that dot appears on horizontal edge = (8/21)/(11/21) = 8/11
1217
- */
1218
-
1219
- //Get the absolute values of the latest X and Y moves and store them in variables for easy handling.
1220
- var absX = Math.abs(dot.latestXMove);
1221
- var absY = Math.abs(dot.latestYMove);
1222
- //Calculate the direction weights based on direction the dot was moving
1223
- var weightInXDirection = absX / (absX + absY);
1224
- var weightInYDirection = absY / (absX + absY);
1225
- //Calculate the weight of the edge the dot should appear from, based on direction of dot and ratio of the aperture edges
1226
- var weightOnVerticalEdge = (verticalAxis / (verticalAxis + horizontalAxis)) * weightInXDirection;
1227
- var weightOnHorizontalEdge = (horizontalAxis / (verticalAxis + horizontalAxis)) * weightInYDirection;
1228
-
1229
-
1230
- //Generate a bounded random number to determine if the dot should appear on the vertical edge or the horizontal edge
1231
- if (weightOnVerticalEdge > (weightOnHorizontalEdge + weightOnVerticalEdge) * Math.random()) { //If yes, appear on the left or right edge (vertical edge)
1232
- if (dot.latestXMove < 0) { //If dots move left, appear on right edge
1233
- dot.x = apertureCenterX + horizontalAxis;
1234
- dot.y = randomNumberBetween((apertureCenterY) - verticalAxis, (apertureCenterY) + verticalAxis);
1235
- } else { //Else dots move right, so they should appear on the left edge
1236
- dot.x = apertureCenterX - horizontalAxis;
1237
- dot.y = randomNumberBetween((apertureCenterY) - verticalAxis, (apertureCenterY) + verticalAxis);
1238
- }
1239
- } else { //Else appear on the top or bottom edge (horizontal edge)
1240
- if (dot.latestYMove < 0) { //If dots move upwards, then appear on bottom edge
1241
- dot.y = apertureCenterY + verticalAxis;
1242
- dot.x = randomNumberBetween((apertureCenterX) - horizontalAxis, (apertureCenterX) + horizontalAxis)
1243
- } else { //If dots move downwards, then appear on top edge
1244
- dot.y = apertureCenterY - verticalAxis;
1245
- dot.x = randomNumberBetween((apertureCenterX) - horizontalAxis, (apertureCenterX) + horizontalAxis)
1246
- }
1247
- }
1248
- } //End of apertureType == 3
1249
- return dot;
1250
- } //End of reinsertOnOppositeEdge
1251
-
1252
- //Calculate the POSITIVE y value of a point on the edge of the ellipse given an x-value
1253
- function yValuePositive(x) {
1254
- var x = x - (apertureCenterX); //Bring it back to the (0,0) center to calculate accurately (ignore the y-coordinate because it is not necessary for calculation)
1255
- return verticalAxis * Math.sqrt(1 - (Math.pow(x, 2) / Math.pow(horizontalAxis, 2))) + apertureCenterY; //Calculated the positive y value and added apertureCenterY to recenter it on the screen
1256
- }
1257
-
1258
- //Calculate the NEGATIVE y value of a point on the edge of the ellipse given an x-value
1259
- function yValueNegative(x) {
1260
- var x = x - (apertureCenterX); //Bring it back to the (0,0) center to calculate accurately (ignore the y-coordinate because it is not necessary for calculation)
1261
- return -verticalAxis * Math.sqrt(1 - (Math.pow(x, 2) / Math.pow(horizontalAxis, 2))) + apertureCenterY; //Calculated the negative y value and added apertureCenterY to recenter it on the screen
1262
- }
1263
-
1264
- //Calculate the POSITIVE x value of a point on the edge of the ellipse given a y-value
1265
- function xValuePositive(y) {
1266
- var y = y - (apertureCenterY); //Bring it back to the (0,0) center to calculate accurately (ignore the x-coordinate because it is not necessary for calculation)
1267
- return horizontalAxis * Math.sqrt(1 - (Math.pow(y, 2) / Math.pow(verticalAxis, 2))) + apertureCenterX; //Calculated the positive x value and added apertureCenterX to recenter it on the screen
1268
- }
1269
-
1270
- //Calculate the NEGATIVE x value of a point on the edge of the ellipse given a y-value
1271
- function xValueNegative(y) {
1272
- var y = y - (apertureCenterY); //Bring it back to the (0,0) center to calculate accurately (ignore the x-coordinate because it is not necessary for calculation)
1273
- return -horizontalAxis * Math.sqrt(1 - (Math.pow(y, 2) / Math.pow(verticalAxis, 2))) + apertureCenterX; //Calculated the negative x value and added apertureCenterX to recenter it on the screen
1274
- }
1275
-
1276
- //Calculate a random x and y coordinate in the ellipse
1277
- function resetLocation(dot) {
1278
-
1279
- //For circle and ellipse
1280
- if (apertureType == 1 || apertureType == 2) {
1281
- var phi = randomNumberBetween(-Math.PI, Math.PI);
1282
- var rho = Math.random();
1283
-
1284
- x = Math.sqrt(rho) * Math.cos(phi);
1285
- y = Math.sqrt(rho) * Math.sin(phi);
1286
-
1287
- x = x * horizontalAxis + apertureCenterX;
1288
- y = y * verticalAxis + apertureCenterY;
1289
-
1290
- dot.x = x;
1291
- dot.y = y;
1292
- }
1293
- //For square and rectangle
1294
- else if (apertureType == 3 || apertureType == 4) {
1295
- dot.x = randomNumberBetween((apertureCenterX) - horizontalAxis, (apertureCenterX) + horizontalAxis); //Between the left and right edges of the square / rectangle
1296
- dot.y = randomNumberBetween((apertureCenterY) - verticalAxis, (apertureCenterY) + verticalAxis); //Between the top and bottom edges of the square / rectangle
1297
- }
1298
-
1299
- return dot;
1300
- }
1301
-
1302
- //Generates a random number (with decimals) between 2 values
1303
- function randomNumberBetween(lowerBound, upperBound) {
1304
- return lowerBound + Math.random() * (upperBound - lowerBound);
1305
- }
1306
-
1307
- //Function to make the dots move on the canvas
1308
- function animateDotMotion() {
1309
- //frameRequestID saves a long integer that is the ID of this frame request. The ID is then used to terminate the request below.
1310
- var frameRequestID = window.requestAnimationFrame(animate);
1311
-
1312
- //Start to listen to subject's key responses
1313
- startKeyboardListener();
1314
-
1315
- //Delare a timestamp
1316
- var previousTimestamp;
1317
-
1318
- function animate() {
1319
- //If stopping condition has been reached, then stop the animation
1320
- if (stopDotMotion) {
1321
- window.cancelAnimationFrame(frameRequestID); //Cancels the frame request
1322
- }
1323
- //Else continue with another frame request
1324
- else {
1325
- frameRequestID = window.requestAnimationFrame(animate); //Calls for another frame request
1326
-
1327
- //If the timer has not been started and it is set, then start the timer
1328
- if ( (!timerHasStarted) && (trial.trial_duration > 0) ){
1329
- //If the trial duration is set, then set a timer to count down and call the end_trial function when the time is up
1330
- //(If the subject did not press a valid keyboard response within the trial duration, then this will end the trial)
1331
- timeoutID = window.setTimeout(end_trial,trial.trial_duration); //This timeoutID is then used to cancel the timeout should the subject press a valid key
1332
- //The timer has started, so we set the variable to true so it does not start more timers
1333
- timerHasStarted = true;
1334
- }
1335
-
1336
- updateAndDraw(); //Update and draw each of the dots in their respective apertures
1337
-
1338
- //If this is before the first frame, then start the timestamp
1339
- if(previousTimestamp === undefined){
1340
- previousTimestamp = performance.now();
1341
- }
1342
- //Else calculate the time and push it into the array
1343
- else{
1344
- var currentTimeStamp = performance.now(); //Variable to hold current timestamp
1345
- frameRate.push(currentTimeStamp - previousTimestamp); //Push the interval into the frameRate array
1346
- previousTimestamp = currentTimeStamp; //Reset the timestamp
1347
- }
1348
- }
1349
- }
1350
- }
1351
-
1352
- //----RDK Functions End----
1353
-
1354
- //----General Functions Begin//----
1355
-
1356
- //Function to assign the default values for the staircase parameters
1357
- function assignParameterValue(argument, defaultValue){
1358
- return typeof argument !== 'undefined' ? argument : defaultValue;
1359
- }
1360
-
1361
- //----General Functions End//----
1362
-
1363
-
1364
- //-------------------------------------
1365
- //-----------FUNCTIONS END-------------
1366
- //-------------------------------------
1367
-
1368
-
1369
- }; // END OF TRIAL
1370
-
1371
- //Return the plugin object which contains the trial
1372
- return plugin;
1373
- })();