From 7f0e839a0d5c159314b4fed775edf5ea032adb3e Mon Sep 17 00:00:00 2001
From: Sam Zorowitz
Date: Thu, 2 Jun 2022 20:08:58 -0400
Subject: [PATCH] Nivturk v1.2 mturk (#95)
* nivturk-v1.2-mturk (1)
- initial commit; pulled prolific branch into mturk
- integrated changes from #94
* nivturk-v1.2-mturk (2)
- fix code duplication error
- fix redirect bug
---
.gitignore | 1 +
README.rst | 24 +-
app/__init__.py | 94 +-
app/alert.py | 10 +-
app/app.ini | 11 +-
app/complete.py | 38 +-
app/consent.py | 20 +-
app/experiment.py | 29 +-
app/io.py | 2 +
app/static/js/nivturk-plugins.js | 22 +-
app/static/lib/jspsych-6.3.1/css/jspsych.css | 206 -
.../extensions/jspsych-ext-webgazer.js | 265 -
app/static/lib/jspsych-6.3.1/jspsych.js | 3023 -
.../plugins/jspsych-animation.js | 189 -
.../plugins/jspsych-audio-button-response.js | 269 -
.../jspsych-audio-keyboard-response.js | 209 -
.../plugins/jspsych-audio-slider-response.js | 278 -
.../plugins/jspsych-call-function.js | 58 -
.../plugins/jspsych-canvas-button-response.js | 199 -
.../jspsych-canvas-keyboard-response.js | 155 -
.../plugins/jspsych-canvas-slider-response.js | 207 -
.../plugins/jspsych-categorize-animation.js | 266 -
.../plugins/jspsych-categorize-html.js | 220 -
.../plugins/jspsych-categorize-image.js | 222 -
.../jspsych-6.3.1/plugins/jspsych-cloze.js | 112 -
.../plugins/jspsych-external-html.js | 112 -
.../plugins/jspsych-free-sort.js | 478 -
.../plugins/jspsych-fullscreen.js | 106 -
.../plugins/jspsych-html-button-response.js | 188 -
.../plugins/jspsych-html-keyboard-response.js | 149 -
.../plugins/jspsych-html-slider-response.js | 202 -
.../jspsych-6.3.1/plugins/jspsych-iat-html.js | 284 -
.../plugins/jspsych-iat-image.js | 286 -
.../plugins/jspsych-image-button-response.js | 327 -
.../jspsych-image-keyboard-response.js | 263 -
.../plugins/jspsych-image-slider-response.js | 369 -
.../plugins/jspsych-instructions.js | 237 -
.../jspsych-6.3.1/plugins/jspsych-maxdiff.js | 173 -
.../jspsych-6.3.1/plugins/jspsych-preload.js | 345 -
.../lib/jspsych-6.3.1/plugins/jspsych-rdk.js | 1373 -
.../plugins/jspsych-reconstruction.js | 134 -
.../jspsych-6.3.1/plugins/jspsych-resize.js | 166 -
.../plugins/jspsych-same-different-html.js | 168 -
.../plugins/jspsych-same-different-image.js | 169 -
.../jspsych-serial-reaction-time-mouse.js | 212 -
.../plugins/jspsych-serial-reaction-time.js | 247 -
.../plugins/jspsych-survey-html-form.js | 171 -
.../plugins/jspsych-survey-likert.js | 195 -
.../plugins/jspsych-survey-multi-choice.js | 208 -
.../plugins/jspsych-survey-multi-select.js | 232 -
.../plugins/jspsych-survey-text.js | 185 -
.../plugins/jspsych-video-button-response.js | 335 -
.../jspsych-video-keyboard-response.js | 279 -
.../plugins/jspsych-video-slider-response.js | 351 -
.../plugins/jspsych-virtual-chinrest.js | 471 -
.../plugins/jspsych-visual-search-circle.js | 259 -
.../plugins/jspsych-vsl-animate-occlusion.js | 196 -
.../plugins/jspsych-vsl-grid-scene.js | 103 -
.../plugins/jspsych-webgazer-calibrate.js | 161 -
.../plugins/jspsych-webgazer-init-camera.js | 139 -
.../plugins/jspsych-webgazer-validate.js | 314 -
.../template/jspsych-plugin-template.js | 35 -
app/static/lib/jspsych-7.2.1/VERSION.md | 57 +
app/static/lib/jspsych-7.2.1/css/jspsych.css | 516 +
.../extensions/extension-mouse-tracking.js | 112 +
.../extensions/extension-webgazer.js | 232 +
app/static/lib/jspsych-7.2.1/jspsych.js | 3308 +
.../license.txt | 42 +-
.../jspsych-7.2.1/plugins/plugin-animation.js | 252 +
.../plugins/plugin-audio-button-response.js | 304 +
.../plugins/plugin-audio-keyboard-response.js | 237 +
.../plugins/plugin-audio-slider-response.js | 355 +
.../plugins/plugin-browser-check.js | 648 +
.../plugins/plugin-call-function.js | 59 +
.../plugins/plugin-canvas-button-response.js | 234 +
.../plugin-canvas-keyboard-response.js | 176 +
.../plugins/plugin-canvas-slider-response.js | 263 +
.../plugins/plugin-categorize-animation.js | 317 +
.../plugins/plugin-categorize-html.js | 246 +
.../plugins/plugin-categorize-image.js | 246 +
.../lib/jspsych-7.2.1/plugins/plugin-cloze.js | 153 +
.../plugins/plugin-external-html.js | 181 +
.../jspsych-7.2.1/plugins/plugin-free-sort.js | 510 +
.../plugins/plugin-fullscreen.js | 156 +
.../plugins/plugin-html-audio-response.js | 208 +
.../plugins/plugin-html-button-response.js | 216 +
.../plugins/plugin-html-keyboard-response.js | 173 +
.../plugins/plugin-html-slider-response.js | 258 +
.../jspsych-7.2.1/plugins/plugin-iat-html.js | 340 +
.../jspsych-7.2.1/plugins/plugin-iat-image.js | 340 +
.../plugins/plugin-image-button-response.js | 377 +
.../plugins/plugin-image-keyboard-response.js | 284 +
.../plugins/plugin-image-slider-response.js | 447 +
.../plugins/plugin-initialize-microphone.js | 119 +
.../plugins/plugin-instructions.js | 300 +
.../jspsych-7.2.1/plugins/plugin-maxdiff.js | 253 +
.../jspsych-7.2.1/plugins/plugin-preload.js | 398 +
.../plugins/plugin-reconstruction.js | 166 +
.../jspsych-7.2.1/plugins/plugin-resize.js | 153 +
.../plugins/plugin-same-different-html.js | 192 +
.../plugins/plugin-same-different-image.js | 190 +
.../plugin-serial-reaction-time-mouse.js | 259 +
.../plugins/plugin-serial-reaction-time.js | 291 +
.../jspsych-7.2.1/plugins/plugin-sketchpad.js | 601 +
.../plugins/plugin-survey-html-form.js | 173 +
.../plugins/plugin-survey-likert.js | 250 +
.../plugins/plugin-survey-multi-choice.js | 266 +
.../plugins/plugin-survey-multi-select.js | 305 +
.../plugins/plugin-survey-text.js | 273 +
.../jspsych-7.2.1/plugins/plugin-survey.js | 62756 ++++++++++++++++
.../plugins/plugin-video-button-response.js | 389 +
.../plugins/plugin-video-keyboard-response.js | 330 +
.../plugins/plugin-video-slider-response.js | 436 +
.../plugins/plugin-virtual-chinrest.js | 423 +
.../plugins/plugin-visual-search-circle.js | 317 +
.../plugins/plugin-webgazer-calibrate.js | 170 +
.../plugins/plugin-webgazer-init-camera.js | 142 +
.../plugins/plugin-webgazer-validate.js | 321 +
app/templates/alert.html | 6 +-
app/templates/experiment.html | 18 +-
incomplete/.placeholder | 0
121 files changed, 80850 insertions(+), 15645 deletions(-)
delete mode 100644 app/static/lib/jspsych-6.3.1/css/jspsych.css
delete mode 100644 app/static/lib/jspsych-6.3.1/extensions/jspsych-ext-webgazer.js
delete mode 100644 app/static/lib/jspsych-6.3.1/jspsych.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-animation.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-audio-button-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-audio-keyboard-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-audio-slider-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-call-function.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-canvas-button-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-canvas-keyboard-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-canvas-slider-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-categorize-animation.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-categorize-html.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-categorize-image.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-cloze.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-external-html.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-free-sort.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-fullscreen.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-html-button-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-html-keyboard-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-html-slider-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-iat-html.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-iat-image.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-image-button-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-image-keyboard-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-image-slider-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-instructions.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-maxdiff.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-preload.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-rdk.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-reconstruction.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-resize.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-same-different-html.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-same-different-image.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-serial-reaction-time-mouse.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-serial-reaction-time.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-html-form.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-likert.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-multi-choice.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-multi-select.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-text.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-video-button-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-video-keyboard-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-video-slider-response.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-virtual-chinrest.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-visual-search-circle.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-vsl-animate-occlusion.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-vsl-grid-scene.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-webgazer-calibrate.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-webgazer-init-camera.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/jspsych-webgazer-validate.js
delete mode 100644 app/static/lib/jspsych-6.3.1/plugins/template/jspsych-plugin-template.js
create mode 100644 app/static/lib/jspsych-7.2.1/VERSION.md
create mode 100644 app/static/lib/jspsych-7.2.1/css/jspsych.css
create mode 100644 app/static/lib/jspsych-7.2.1/extensions/extension-mouse-tracking.js
create mode 100644 app/static/lib/jspsych-7.2.1/extensions/extension-webgazer.js
create mode 100644 app/static/lib/jspsych-7.2.1/jspsych.js
rename app/static/lib/{jspsych-6.3.1 => jspsych-7.2.1}/license.txt (94%)
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-animation.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-audio-button-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-audio-keyboard-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-audio-slider-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-browser-check.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-call-function.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-canvas-button-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-canvas-keyboard-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-canvas-slider-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-categorize-animation.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-categorize-html.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-categorize-image.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-cloze.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-external-html.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-free-sort.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-fullscreen.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-html-audio-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-html-button-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-html-keyboard-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-html-slider-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-iat-html.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-iat-image.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-image-button-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-image-keyboard-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-image-slider-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-initialize-microphone.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-instructions.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-maxdiff.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-preload.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-reconstruction.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-resize.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-same-different-html.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-same-different-image.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-serial-reaction-time-mouse.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-serial-reaction-time.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-sketchpad.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-survey-html-form.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-survey-likert.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-survey-multi-choice.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-survey-multi-select.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-survey-text.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-survey.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-video-button-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-video-keyboard-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-video-slider-response.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-virtual-chinrest.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-visual-search-circle.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-webgazer-calibrate.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-webgazer-init-camera.js
create mode 100644 app/static/lib/jspsych-7.2.1/plugins/plugin-webgazer-validate.js
create mode 100644 incomplete/.placeholder
diff --git a/.gitignore b/.gitignore
index fc413cab..398b86fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,4 +19,5 @@ build/
# project specific
data/**
metadata/**
+incomplete/**
reject/**
diff --git a/README.rst b/README.rst
index 1a8a8a83..af7e0336 100644
--- a/README.rst
+++ b/README.rst
@@ -4,31 +4,27 @@
.. image:: https://img.shields.io/github/license/mashape/apistatus.svg
:target: https://github.com/nivlab/NivLink/blob/master/LICENSE
+.. image:: https://zenodo.org/badge/182183266.svg
+ :target: https://zenodo.org/badge/latestdoi/182183266
+
NivTurk
=======
Niv lab tools for securely serving and storing data from online computational psychiatry experiments.
-Quickstart
-^^^^^^^^^^
-
-The following is the minimal set of commands needed to get started with NivTurk (assuming you have already a virtual machine with python 3.6+ installed):
-
-.. code-block:: bash
-
- ssh @.princeton.edu
- git clone https://github.com/nivlab/nivturk.git
- cd nivturk
- pip install -r requirements.txt
- gunicorn -b 0.0.0.0:9000 -w 4 app:app
-
-
Documentation
^^^^^^^^^^^^^
For details on how to serve your experiment, how the code is organized, and how data is stored, please see the
`Documentation `_.
+Citation
+^^^^^^^^
+
+If you use this library in academic work, please cite the following:
+
+ | Samuel Zorowitz & Daniel Bennett. (2022). NivTurk (v1.2-prolific). Zenodo. https://doi.org/10.5281/zenodo.6609218
+
Acknowledgements
^^^^^^^^^^^^^^^^
NivTurk was developed with support from the National Center for Advancing Translational Sciences (NCATS), a component of the National Institute of Health (NIH), under award number UL1TR003017.
diff --git a/app/__init__.py b/app/__init__.py
index 9992ac32..a96141d1 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -1,9 +1,9 @@
-import os, sys, configparser, warnings
+import os, sys, re, configparser, warnings
from flask import (Flask, redirect, render_template, request, session, url_for)
from app import consent, alert, experiment, complete, error
from .io import write_metadata
from .utils import gen_code
-__version__ = '1.1'
+__version__ = '1.2'
## Define root directory.
ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
@@ -17,6 +17,8 @@
if not os.path.isdir(data_dir): os.makedirs(data_dir)
meta_dir = os.path.join(ROOT_DIR, cfg['IO']['METADATA'])
if not os.path.isdir(meta_dir): os.makedirs(meta_dir)
+incomplete_dir = os.path.join(ROOT_DIR, cfg['IO']['INCOMPLETE'])
+if not os.path.isdir(incomplete_dir): os.makedirs(incomplete_dir)
reject_dir = os.path.join(ROOT_DIR, cfg['IO']['REJECT'])
if not os.path.isdir(reject_dir): os.makedirs(reject_dir)
@@ -30,6 +32,9 @@
if secret_key == "PLEASE_CHANGE_THIS":
warnings.warn("WARNING: Flask password is currently default. This should be changed prior to production.")
+## Check restart mode; if true, participants can restart experiment.
+allow_restart = cfg['FLASK'].getboolean('ALLOW_RESTART')
+
## Initialize Flask application.
app = Flask(__name__)
app.secret_key = secret_key
@@ -52,7 +57,9 @@ def index():
## Store directories in session object.
session['data'] = data_dir
session['metadata'] = meta_dir
+ session['incomplete'] = incomplete_dir
session['reject'] = reject_dir
+ session['allow_restart'] = allow_restart
## Record incoming metadata.
info = dict(
@@ -72,47 +79,23 @@ def index():
version = request.user_agent.version, # User metadata
)
- ## Case 1: workerId absent.
+ ## Case 1: workerId absent form URL.
if info['workerId'] is None:
## Redirect participant to error (missing workerId).
return redirect(url_for('error.error', errornum=1000))
- ## Case 2: mobile user.
+ ## Case 2: mobile / tablet user.
elif info['platform'] in ['android','iphone','ipad','wii']:
## Redirect participant to error (platform error).
return redirect(url_for('error.error', errornum=1001))
- ## Case 3: repeat visit, preexisting log but no session data.
- elif not 'workerId' in session and info['workerId'] in os.listdir(meta_dir):
-
- ## Consult log file.
- with open(os.path.join(session['metadata'], info['workerId']),'r') as f:
- logs = f.read()
-
- ## Case 3a: previously started experiment.
- if 'experiment' in logs:
-
- ## Update metadata.
- session['workerId'] = info['workerId']
- session['ERROR'] = '1004: Suspected incognito user.'
- session['complete'] = 'error'
- write_metadata(session, ['ERROR','complete'], 'a')
-
- ## Redirect participant to error (previous participation).
- return redirect(url_for('error.error', errornum=1004))
-
- ## Case 3b: no previous experiment starts.
- else:
-
- ## Update metadata.
- for k, v in info.items(): session[k] = v
- session['WARNING'] = "Assigned new subId."
- write_metadata(session, ['subId','WARNING'], 'a')
+ ## Case 3: previous complete.
+ elif 'complete' in session:
- ## Redirect participant to consent form.
- return redirect(url_for('consent.consent'))
+ ## Redirect participant to complete page.
+ return redirect(url_for('complete.complete'))
## Case 4: repeat visit, manually changed workerId.
elif 'workerId' in session and session['workerId'] != info['workerId']:
@@ -125,25 +108,46 @@ def index():
## Redirect participant to error (unusual activity).
return redirect(url_for('error.error', errornum=1005))
- ## Case 5: repeat visit, previously completed experiment.
- elif 'complete' in session:
+ ## Case 5: repeat visit, preexisting activity.
+ elif 'workerId' in session:
- ## Update metadata.
- session['WARNING'] = "Revisited home."
- write_metadata(session, ['WARNING'], 'a')
+ ## Redirect participant to consent form.
+ return redirect(url_for('consent.consent'))
- ## Redirect participant to complete page.
- return redirect(url_for('complete.complete'))
+ ## Case 6: repeat visit, preexisting log but no session data.
+ elif not 'workerId' in session and info['workerId'] in os.listdir(meta_dir):
- ## Case 6: repeat visit, preexisting activity.
- elif 'workerId' in session:
+ ## Parse log file.
+ with open(os.path.join(session['metadata'], info['workerId']), 'r') as f:
+ logs = f.read()
+
+ ## Extract subject ID.
+ info['subId'] = re.search('subId\t(.*)\n', logs).group(1)
+
+ ## Check for previous consent.
+ consent = re.search('consent\t(.*)\n', logs)
+ if consent and consent.group(1) == 'True': info['consent'] = True # consent = true
+ elif consent and consent.group(1) == 'False': info['consent'] = False # consent = false
+ elif consent: info['consent'] = consent.group(1) # consent = bot
+
+ ## Check for previous experiment.
+ experiment = re.search('experiment\t(.*)\n', logs)
+ if experiment: info['experiment'] = experiment.group(1)
+
+ ## Check for previous complete.
+ complete = re.search('complete\t(.*)\n', logs)
+ if complete: info['complete'] = complete.group(1)
## Update metadata.
- session['WARNING'] = "Revisited home."
- write_metadata(session, ['WARNING'], 'a')
+ for k, v in info.items(): session[k] = v
- ## Redirect participant to consent form.
- return redirect(url_for('consent.consent'))
+ ## Redirect participant as appropriate.
+ if 'complete' in session:
+ return redirect(url_for('complete.complete'))
+ elif 'experiment' in session:
+ return redirect(url_for('experiment.experiment'))
+ else:
+ return redirect(url_for('consent.consent'))
## Case 7: first visit, workerId present.
else:
diff --git a/app/alert.py b/app/alert.py
index 4003a9bc..604e7bce 100644
--- a/app/alert.py
+++ b/app/alert.py
@@ -17,21 +17,13 @@ def alert():
## Case 1: previously completed experiment.
elif 'complete' in session:
- ## Update metadata.
- session['WARNING'] = "Revisited alert page."
- write_metadata(session, ['WARNING'], 'a')
-
## Redirect participant to complete page.
return redirect(url_for('complete.complete'))
## Case 2: repeat visit.
elif 'alert' in session:
- ## Update participant metadata.
- session['WARNING'] = "Revisited alert page."
- write_metadata(session, ['WARNING'], 'a')
-
- ## Redirect participant to error (previous participation).
+ ## Redirect participant to experiment.
return redirect(url_for('experiment.experiment'))
## Case 3: first visit.
diff --git a/app/app.ini b/app/app.ini
index 001ecfdc..66e24281 100644
--- a/app/app.ini
+++ b/app/app.ini
@@ -1,10 +1,14 @@
[FLASK]
# Flask secret key for encrypting session objects
-# Suggested: get key from https://randomkeygen.com
+# Recommended: get key from https://randomkeygen.com
SECRET_KEY = PLEASE_CHANGE_THIS
-# Toggle debug mode (allow repeat visits from same session)
+# Allow participants to restart experiments
+# Accepts true or false
+ALLOW_RESTART = false
+
+# Toggle debug mode (session cookies cleared on start)
# Accepts true or false
DEBUG = true
@@ -16,5 +20,8 @@ METADATA = ../metadata
# Path to data folder [default: ../data]
DATA = ../data
+# Path to incomplete data folder [default: ../incomplete]
+INCOMPLETE = ../incomplete
+
# Path to reject folder [default: ../reject]
REJECT = ../reject
diff --git a/app/complete.py b/app/complete.py
index db436160..60fd0832 100644
--- a/app/complete.py
+++ b/app/complete.py
@@ -11,7 +11,7 @@ def complete():
## Access query string.
query_info = request.args
- ## Confirm all TurkPrime metadata present.
+ ## Confirm all CloudResearch metadata present.
fields = ['workerId','assignmentId','hitId','a','tp_a','b','tp_b','c','tp_c']
all_fields = all([f in query_info for f in fields])
@@ -21,38 +21,40 @@ def complete():
## Redirect participant to error (missing workerId).
return redirect(url_for('error.error', errornum=1000))
- ## Case 1: visit complete page with previous rejection.
- elif session.get('complete') == 'reject':
+ ## Case 1: visit complete page without previous completion.
+ elif 'complete' not in session:
- ## Update metadata.
- session['WARNING'] = "Revisited complete."
- write_metadata(session, ['WARNING'], 'a')
+ ## Flag experiment as complete.
+ session['ERROR'] = "1005: Visited complete page before completion."
+ session['complete'] = 'reject'
+ write_metadata(session, ['ERROR','complete'], 'a')
## Redirect participant to error (unusual activity).
return redirect(url_for('error.error', errornum=1005))
- ## Case 2: visit complete page with previous error.
- elif session.get('complete') == 'error':
-
- ## Update metadata.
- session['WARNING'] = "Revisited complete."
- write_metadata(session, ['WARNING'], 'a')
+ ## Case 2: visit complete page with previous rejection.
+ elif session['complete'] == 'reject':
## Redirect participant to error (unusual activity).
return redirect(url_for('error.error', errornum=1005))
- ## Case 3: visit complete page but missing metadata.
- elif session.get('complete') == 'success' and not all_fields:
+ ## Case 3: visit complete page with previous error.
+ elif session['complete'] == 'error':
+
+ ## Determine error code.
+ errornum = 1002 if not session['consent'] else 1005
+
+ ## Redirect participant to error (unusual activity).
+ return redirect(url_for('error.error', errornum=errornum))
- ## Update metadata.
- session['WARNING'] = "Revisited complete."
- write_metadata(session, ['WARNING'], 'a')
+ ## Case 4: visit complete page with previous success.
+ elif session['complete'] == 'success' and not all_fields:
## Redirect participant with complete metadata.
url = "/complete?workerId=%s&assignmentId=%s&hitId=%s&a=%s&tp_a=%s&b=%s&tp_b=%s&c=%s&tp_c=%s" %(session['workerId'], session['assignmentId'], session['hitId'], session['a'], session['tp_a'], session['b'], session['tp_b'], session['c'], session['tp_c'])
return redirect(url)
- ## Case 4: all else.
+ ## Case 5: all else.
else:
## Redirect participant with completion code.
diff --git a/app/consent.py b/app/consent.py
index fa6bbd1a..93f17a5f 100644
--- a/app/consent.py
+++ b/app/consent.py
@@ -17,10 +17,6 @@ def consent():
## Case 1: previously completed experiment.
elif 'complete' in session:
- ## Update metadata.
- session['WARNING'] = "Revisited consent page."
- write_metadata(session, ['WARNING'], 'a')
-
## Redirect participant to complete page.
return redirect(url_for('complete.complete'))
@@ -33,30 +29,18 @@ def consent():
## Case 3: repeat visit, previous bot-detection.
elif session['consent'] == 'BOT':
- ## Update participant metadata.
- session['WARNING'] = "Revisited consent form."
- write_metadata(session, ['WARNING'], 'a')
-
## Redirect participant to error (unusual activity).
return redirect(url_for('error.error', errornum=1005))
## Case 4: repeat visit, previous non-consent.
elif session['consent'] == False:
- ## Update participant metadata.
- session['WARNING'] = "Revisited consent form."
- write_metadata(session, ['WARNING'], 'a')
-
## Redirect participant to error (decline consent).
return redirect(url_for('error.error', errornum=1002))
## Case 5: repeat visit, previous consent.
else:
- ## Update participant metadata.
- session['WARNING'] = "Revisited consent form."
- write_metadata(session, ['WARNING'], 'a')
-
## Redirect participant to alert page.
return redirect(url_for('alert.alert'))
@@ -73,9 +57,8 @@ def consent_post():
## Update participant metadata.
session['consent'] = 'BOT'
- session['experiment'] = False # Prevents incognito users
session['complete'] = 'error'
- write_metadata(session, ['consent','experiment','complete'], 'a')
+ write_metadata(session, ['consent','complete'], 'a')
## Redirect participant to error (unusual activity).
return redirect(url_for('error.error', errornum=1005))
@@ -94,6 +77,7 @@ def consent_post():
## Update participant metadata.
session['consent'] = False
+ session['complete'] = 'error'
write_metadata(session, ['consent'], 'a')
## Redirect participant to error (decline consent).
diff --git a/app/experiment.py b/app/experiment.py
index 338a0257..7cd59f46 100644
--- a/app/experiment.py
+++ b/app/experiment.py
@@ -17,15 +17,11 @@ def experiment():
## Case 1: previously completed experiment.
elif 'complete' in session:
- ## Update metadata.
- session['WARNING'] = "Revisited experiment page."
- write_metadata(session, ['WARNING'], 'a')
-
## Redirect participant to complete page.
return redirect(url_for('complete.complete'))
## Case 2: repeat visit.
- elif 'experiment' in session:
+ elif not session['allow_restart'] and 'experiment' in session:
## Update participant metadata.
session['ERROR'] = "1004: Revisited experiment."
@@ -65,6 +61,29 @@ def pass_message():
## https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
return ('', 200)
+@bp.route('/incomplete_save', methods=['POST'])
+def incomplete_save():
+ """Save incomplete jsPsych dataset to disk."""
+
+ if request.is_json:
+
+ ## Retrieve jsPsych data.
+ JSON = request.get_json()
+
+ ## Save jsPsch data to disk.
+ write_data(session, JSON, method='incomplete')
+
+ ## Flag partial data saving.
+ session['MESSAGE'] = 'incomplete dataset saved'
+ write_metadata(session, ['MESSAGE'], 'a')
+
+ ## DEV NOTE:
+ ## This function returns the HTTP response status code: 200
+ ## Code 200 signifies the POST request has succeeded.
+ ## For a full list of status codes, see:
+ ## https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
+ return ('', 200)
+
@bp.route('/redirect_success', methods = ['POST'])
def redirect_success():
"""Save complete jsPsych dataset to disk."""
diff --git a/app/io.py b/app/io.py
index 26b452a8..636d99c2 100644
--- a/app/io.py
+++ b/app/io.py
@@ -41,5 +41,7 @@ def write_data(session, json, method='pass'):
fout = os.path.join(session['data'], '%s.json' %session['subId'])
elif method == 'reject':
fout = os.path.join(session['reject'], '%s.json' %session['subId'])
+ elif method == 'incomplete':
+ fout = os.path.join(session['incomplete'], '%s.json' %session['subId'])
with open(fout, 'w') as f: f.write(json)
diff --git a/app/static/js/nivturk-plugins.js b/app/static/js/nivturk-plugins.js
index 1deae1a3..003230b8 100644
--- a/app/static/js/nivturk-plugins.js
+++ b/app/static/js/nivturk-plugins.js
@@ -14,6 +14,22 @@ function pass_message(msg) {
}
+// Save an incomplete dataset.
+function incomplete_save() {
+
+ $.ajax({
+ url: "/incomplete_save",
+ method: 'POST',
+ data: JSON.stringify(jsPsych.data.get().json()),
+ contentType: "application/json; charset=utf-8",
+ }).done(function(data, textStatus, jqXHR) {
+ // do nothing
+ }).fail(function(error) {
+ // do nothing
+ });
+
+}
+
// Successful completion of experiment: redirect with completion code.
function redirect_success(workerId, assignmentId, hitId, a, tp_a, b, tp_b, c, tp_c) {
@@ -28,7 +44,7 @@ function redirect_success(workerId, assignmentId, hitId, a, tp_a, b, tp_b, c, tp
}).done(function(data, textStatus, jqXHR) {
window.location.replace(url);
}).fail(function(error) {
- console.log(error);
+ window.location.replace(url);
});
}
@@ -47,7 +63,7 @@ function redirect_reject(error) {
}).done(function(data, textStatus, jqXHR) {
window.location.replace(url);
}).fail(function(error) {
- console.log(error);
+ window.location.replace(url);
});
}
@@ -65,7 +81,7 @@ function redirect_error(error) {
}).done(function(data, textStatus, jqXHR) {
window.location.replace(url);
}).fail(function(error) {
- console.log(error);
+ window.location.replace(url);
});
}
diff --git a/app/static/lib/jspsych-6.3.1/css/jspsych.css b/app/static/lib/jspsych-6.3.1/css/jspsych.css
deleted file mode 100644
index 3b6d1774..00000000
--- a/app/static/lib/jspsych-6.3.1/css/jspsych.css
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * CSS for jsPsych experiments.
- *
- * This stylesheet provides minimal styling to make jsPsych
- * experiments look polished without any additional styles.
- */
-
- @import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700);
-
-/* Container holding jsPsych content */
-
- .jspsych-display-element {
- display: flex;
- flex-direction: column;
- overflow-y: auto;
- }
-
- .jspsych-display-element:focus {
- outline: none;
- }
-
- .jspsych-content-wrapper {
- display: flex;
- margin: auto;
- flex: 1 1 100%;
- width: 100%;
- }
-
- .jspsych-content {
- max-width: 95%; /* this is mainly an IE 10-11 fix */
- text-align: center;
- margin: auto; /* this is for overflowing content */
- }
-
- .jspsych-top {
- align-items: flex-start;
- }
-
- .jspsych-middle {
- align-items: center;
- }
-
-/* fonts and type */
-
-.jspsych-display-element {
- font-family: 'Open Sans', 'Arial', sans-serif;
- font-size: 18px;
- line-height: 1.6em;
-}
-
-/* Form elements like input fields and buttons */
-
-.jspsych-display-element input[type="text"] {
- font-family: 'Open Sans', 'Arial', sans-serif;
- font-size: 14px;
-}
-
-/* borrowing Bootstrap style for btn elements, but combining styles a bit */
-.jspsych-btn {
- display: inline-block;
- padding: 6px 12px;
- margin: 0px;
- font-size: 14px;
- font-weight: 400;
- font-family: 'Open Sans', 'Arial', sans-serif;
- cursor: pointer;
- line-height: 1.4;
- text-align: center;
- white-space: nowrap;
- vertical-align: middle;
- background-image: none;
- border: 1px solid transparent;
- border-radius: 4px;
- color: #333;
- background-color: #fff;
- border-color: #ccc;
-}
-
-/* only apply the hover style on devices with a mouse/pointer that can hover - issue #977 */
-@media (hover: hover) {
- .jspsych-btn:hover {
- background-color: #ddd;
- border-color: #aaa;
- }
-}
-
-.jspsych-btn:active {
- background-color: #ddd;
- border-color:#000000;
-}
-
-.jspsych-btn:disabled {
- background-color: #eee;
- color: #aaa;
- border-color: #ccc;
- cursor: not-allowed;
-}
-
-/* custom style for input[type="range] (slider) to improve alignment between positions and labels */
-
-.jspsych-slider {
- appearance: none;
- -webkit-appearance: none;
- -moz-appearance: none;
- width: 100%;
- background: transparent;
-}
-.jspsych-slider:focus {
- outline: none;
-}
-/* track */
-.jspsych-slider::-webkit-slider-runnable-track {
- appearance: none;
- -webkit-appearance: none;
- width: 100%;
- height: 8px;
- cursor: pointer;
- background: #eee;
- box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d;
- border-radius: 2px;
- border: 1px solid #aaa;
-}
-.jspsych-slider::-moz-range-track {
- appearance: none;
- width: 100%;
- height: 8px;
- cursor: pointer;
- background: #eee;
- box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d;
- border-radius: 2px;
- border: 1px solid #aaa;
-}
-.jspsych-slider::-ms-track {
- appearance: none;
- width: 99%;
- height: 14px;
- cursor: pointer;
- background: #eee;
- box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d;
- border-radius: 2px;
- border: 1px solid #aaa;
-}
-/* thumb */
-.jspsych-slider::-webkit-slider-thumb {
- border: 1px solid #666;
- height: 24px;
- width: 15px;
- border-radius: 5px;
- background: #ffffff;
- cursor: pointer;
- -webkit-appearance: none;
- margin-top: -9px;
-}
-.jspsych-slider::-moz-range-thumb {
- border: 1px solid #666;
- height: 24px;
- width: 15px;
- border-radius: 5px;
- background: #ffffff;
- cursor: pointer;
-}
-.jspsych-slider::-ms-thumb {
- border: 1px solid #666;
- height: 20px;
- width: 15px;
- border-radius: 5px;
- background: #ffffff;
- cursor: pointer;
- margin-top: -2px;
-}
-
-/* jsPsych progress bar */
-
-#jspsych-progressbar-container {
- color: #555;
- border-bottom: 1px solid #dedede;
- background-color: #f9f9f9;
- margin-bottom: 1em;
- text-align: center;
- padding: 8px 0px;
- width: 100%;
- line-height: 1em;
-}
-#jspsych-progressbar-container span {
- font-size: 14px;
- padding-right: 14px;
-}
-#jspsych-progressbar-outer {
- background-color: #eee;
- width: 50%;
- margin: auto;
- height: 14px;
- display: inline-block;
- vertical-align: middle;
- box-shadow: inset 0 1px 2px rgba(0,0,0,0.1);
-}
-#jspsych-progressbar-inner {
- background-color: #aaa;
- width: 0%;
- height: 100%;
-}
-
-/* Control appearance of jsPsych.data.displayData() */
-#jspsych-data-display {
- text-align: left;
-}
diff --git a/app/static/lib/jspsych-6.3.1/extensions/jspsych-ext-webgazer.js b/app/static/lib/jspsych-6.3.1/extensions/jspsych-ext-webgazer.js
deleted file mode 100644
index f8ffe668..00000000
--- a/app/static/lib/jspsych-6.3.1/extensions/jspsych-ext-webgazer.js
+++ /dev/null
@@ -1,265 +0,0 @@
-jsPsych.extensions['webgazer'] = (function () {
-
- var extension = {};
-
- // private state for the extension
- // extension authors can define public functions to interact
- // with the state. recommend not exposing state directly
- // so that state manipulations are checked.
- var state = {};
-
- // required, will be called at jsPsych.init
- // should return a Promise
- extension.initialize = function (params) {
- // setting default values for params if not defined
- params.round_predictions = typeof params.round_predictions === 'undefined' ? true : params.round_predictions;
- params.auto_initialize = typeof params.auto_initialize === 'undefined' ? false : params.auto_initialize;
- params.sampling_interval = typeof params.sampling_interval === 'undefined' ? 34 : params.sampling_interval;
-
- return new Promise(function (resolve, reject) {
- if (typeof params.webgazer === 'undefined') {
- if (window.webgazer) {
- state.webgazer = window.webgazer;
- } else {
- reject(new Error('Webgazer extension failed to initialize. webgazer.js not loaded. Load webgazer.js before calling jsPsych.init()'));
- }
- } else {
- state.webgazer = params.webgazer;
- }
-
- // sets up event handler for webgazer data
- state.webgazer.setGazeListener(handleGazeDataUpdate);
-
- // default to threadedRidge regression
- // NEVER MIND... kalman filter is too useful.
- //state.webgazer.workerScriptURL = 'js/webgazer/ridgeWorker.mjs';
- //state.webgazer.setRegression('threadedRidge');
- //state.webgazer.applyKalmanFilter(false); // kalman filter doesn't seem to work yet with threadedridge.
-
- // set state parameters
- state.round_predictions = params.round_predictions;
- state.sampling_interval = params.sampling_interval;
-
- // sets state for initialization
- state.initialized = false;
- state.activeTrial = false;
- state.gazeUpdateCallbacks = [];
- state.domObserver = new MutationObserver(mutationObserverCallback);
-
- // hide video by default
- extension.hideVideo();
-
- // hide predictions by default
- extension.hidePredictions();
-
- if (params.auto_initialize) {
- // starts webgazer, and once it initializes we stop mouseCalibration and
- // pause webgazer data.
- state.webgazer.begin().then(function () {
- state.initialized = true;
- extension.stopMouseCalibration();
- extension.pause();
- resolve();
- }).catch(function (error) {
- console.error(error);
- reject(error);
- });
- } else {
- resolve();
- }
- })
- }
-
- // required, will be called when the trial starts (before trial loads)
- extension.on_start = function (params) {
- state.currentTrialData = [];
- state.currentTrialTargets = {};
- state.currentTrialSelectors = params.targets;
-
- state.domObserver.observe(jsPsych.getDisplayElement(), {childList: true})
-
- }
-
- // required will be called when the trial loads
- extension.on_load = function (params) {
-
- // set current trial start time
- state.currentTrialStart = performance.now();
-
- // resume data collection
- // state.webgazer.resume();
-
- extension.startSampleInterval();
-
- // set internal flag
- state.activeTrial = true;
- }
-
- // required, will be called when jsPsych.finishTrial() is called
- // must return data object to be merged into data.
- extension.on_finish = function (params) {
-
- // pause the eye tracker
- extension.stopSampleInterval();
-
- // stop watching the DOM
- state.domObserver.disconnect();
-
- // state.webgazer.pause();
-
- // set internal flag
- state.activeTrial = false;
-
- // send back the gazeData
- return {
- webgazer_data: state.currentTrialData,
- webgazer_targets: state.currentTrialTargets
- }
- }
-
- extension.start = function () {
- if(typeof state.webgazer == 'undefined'){
- console.error('Failed to start webgazer. Things to check: Is webgazer.js loaded? Is the webgazer extension included in jsPsych.init?')
- return;
- }
- return new Promise(function (resolve, reject) {
- state.webgazer.begin().then(function () {
- state.initialized = true;
- extension.stopMouseCalibration();
- extension.pause();
- resolve();
- }).catch(function (error) {
- console.error(error);
- reject(error);
- });
- });
- }
-
- extension.startSampleInterval = function(interval){
- interval = typeof interval == 'undefined' ? state.sampling_interval : interval;
- state.gazeInterval = setInterval(function(){
- state.webgazer.getCurrentPrediction().then(handleGazeDataUpdate);
- }, state.sampling_interval);
- // repeat the call here so that we get one immediate execution. above will not
- // start until state.sampling_interval is reached the first time.
- state.webgazer.getCurrentPrediction().then(handleGazeDataUpdate);
- }
-
- extension.stopSampleInterval = function(){
- clearInterval(state.gazeInterval);
- }
-
- extension.isInitialized = function(){
- return state.initialized;
- }
-
- extension.faceDetected = function () {
- return state.webgazer.getTracker().predictionReady;
- }
-
- extension.showPredictions = function () {
- state.webgazer.showPredictionPoints(true);
- }
-
- extension.hidePredictions = function () {
- state.webgazer.showPredictionPoints(false);
- }
-
- extension.showVideo = function () {
- state.webgazer.showVideo(true);
- state.webgazer.showFaceOverlay(true);
- state.webgazer.showFaceFeedbackBox(true);
- }
-
- extension.hideVideo = function () {
- state.webgazer.showVideo(false);
- state.webgazer.showFaceOverlay(false);
- state.webgazer.showFaceFeedbackBox(false);
- }
-
- extension.resume = function () {
- state.webgazer.resume();
- }
-
- extension.pause = function () {
- state.webgazer.pause();
- // sometimes gaze dot will show and freeze after pause?
- if(document.querySelector('#webgazerGazeDot')){
- document.querySelector('#webgazerGazeDot').style.display = 'none';
- }
- }
-
- extension.resetCalibration = function(){
- state.webgazer.clearData();
- }
-
- extension.stopMouseCalibration = function () {
- state.webgazer.removeMouseEventListeners()
- }
-
- extension.startMouseCalibration = function () {
- state.webgazer.addMouseEventListeners()
- }
-
- extension.calibratePoint = function (x, y) {
- state.webgazer.recordScreenPosition(x, y, 'click');
- }
-
- extension.setRegressionType = function (regression_type) {
- var valid_regression_models = ['ridge', 'weightedRidge', 'threadedRidge'];
- if (valid_regression_models.includes(regression_type)) {
- state.webgazer.setRegression(regression_type)
- } else {
- console.warn('Invalid regression_type parameter for webgazer.setRegressionType. Valid options are ridge, weightedRidge, and threadedRidge.')
- }
- }
-
- extension.getCurrentPrediction = function () {
- return state.webgazer.getCurrentPrediction();
- }
-
- extension.onGazeUpdate = function(callback){
- state.gazeUpdateCallbacks.push(callback);
- return function(){
- state.gazeUpdateCallbacks = state.gazeUpdateCallbacks.filter(function(item){
- return item !== callback;
- });
- }
- }
-
- function handleGazeDataUpdate(gazeData, elapsedTime) {
- if (gazeData !== null){
- var d = {
- x: state.round_predictions ? Math.round(gazeData.x) : gazeData.x,
- y: state.round_predictions ? Math.round(gazeData.y) : gazeData.y,
- t: gazeData.t
- }
- if(state.activeTrial) {
- //console.log(`handleUpdate: t = ${Math.round(gazeData.t)}, now = ${Math.round(performance.now())}`);
- d.t = Math.round(gazeData.t - state.currentTrialStart)
- state.currentTrialData.push(d); // add data to current trial's data
- }
- state.currentGaze = d;
- for(var i=0; i tag and the entire page
- if(typeof opts.display_element == 'undefined'){
- // check if there is a body element on the page
- var body = document.querySelector('body');
- if (body === null) {
- document.documentElement.appendChild(document.createElement('body'));
- }
- // using the full page, so we need the HTML element to
- // have 100% height, and body to be full width and height with
- // no margin
- document.querySelector('html').style.height = '100%';
- document.querySelector('body').style.margin = '0px';
- document.querySelector('body').style.height = '100%';
- document.querySelector('body').style.width = '100%';
- opts.display_element = document.querySelector('body');
- } else {
- // make sure that the display element exists on the page
- var display;
- if (opts.display_element instanceof Element) {
- var display = opts.display_element;
- } else {
- var display = document.querySelector('#' + opts.display_element);
- }
- if(display === null) {
- console.error('The display_element specified in jsPsych.init() does not exist in the DOM.');
- } else {
- opts.display_element = display;
- }
- }
- opts.display_element.innerHTML = '
';
- DOM_container = opts.display_element;
- DOM_target = document.querySelector('#jspsych-content');
-
-
- // add tabIndex attribute to scope event listeners
- opts.display_element.tabIndex = 0;
-
- // add CSS class to DOM_target
- if(opts.display_element.className.indexOf('jspsych-display-element') == -1){
- opts.display_element.className += ' jspsych-display-element';
- }
- DOM_target.className += 'jspsych-content';
-
- // set experiment_width if not null
- if(opts.experiment_width !== null){
- DOM_target.style.width = opts.experiment_width + "px";
- }
-
- // create experiment timeline
- timeline = new TimelineNode({
- timeline: opts.timeline
- });
-
- // initialize audio context based on options and browser capabilities
- jsPsych.pluginAPI.initAudio();
-
- // below code resets event listeners that may have lingered from
- // a previous incomplete experiment loaded in same DOM.
- jsPsych.pluginAPI.reset(opts.display_element);
- // create keyboard event listeners
- jsPsych.pluginAPI.createKeyboardEventListeners(opts.display_element);
- // create listeners for user browser interaction
- jsPsych.data.createInteractionListeners();
-
- // add event for closing window
- window.addEventListener('beforeunload', opts.on_close);
-
- // check exclusions before continuing
- checkExclusions(opts.exclusions,
- function(){
- // success! user can continue...
- // start experiment
- loadExtensions();
- },
- function(){
- // fail. incompatible user.
- }
- );
-
- function loadExtensions() {
- // run the .initialize method of any extensions that are in use
- // these should return a Promise to indicate when loading is complete
- if (opts.extensions.length == 0) {
- startExperiment();
- } else {
- var loaded_extensions = 0;
- for (var i = 0; i < opts.extensions.length; i++) {
- var ext_params = opts.extensions[i].params;
- if (!ext_params) {
- ext_params = {}
- }
- jsPsych.extensions[opts.extensions[i].type].initialize(ext_params)
- .then(() => {
- loaded_extensions++;
- if (loaded_extensions == opts.extensions.length) {
- startExperiment();
- }
- })
- .catch((error_message) => {
- console.error(error_message);
- })
- }
- }
- }
-
- };
-
- // execute init() when the document is ready
- if (document.readyState === "complete") {
- init();
- } else {
- window.addEventListener("load", init);
- }
- }
-
- core.progress = function() {
-
- var percent_complete = typeof timeline == 'undefined' ? 0 : timeline.percentComplete();
-
- var obj = {
- "total_trials": typeof timeline == 'undefined' ? undefined : timeline.length(),
- "current_trial_global": global_trial_index,
- "percent_complete": percent_complete
- };
-
- return obj;
- };
-
- core.startTime = function() {
- return exp_start_time;
- };
-
- core.totalTime = function() {
- if(typeof exp_start_time == 'undefined'){ return 0; }
- return (new Date()).getTime() - exp_start_time.getTime();
- };
-
- core.getDisplayElement = function() {
- return DOM_target;
- };
-
- core.getDisplayContainerElement = function(){
- return DOM_container;
- }
-
- core.finishTrial = function(data) {
-
- if(current_trial_finished){ return; }
- current_trial_finished = true;
-
- // remove any CSS classes that were added to the DOM via css_classes parameter
- if(typeof current_trial.css_classes !== 'undefined' && Array.isArray(current_trial.css_classes)){
- DOM_target.classList.remove(...current_trial.css_classes);
- }
-
- // write the data from the trial
- data = typeof data == 'undefined' ? {} : data;
- jsPsych.data.write(data);
-
- // get back the data with all of the defaults in
- var trial_data = jsPsych.data.get().filter({trial_index: global_trial_index});
-
- // for trial-level callbacks, we just want to pass in a reference to the values
- // of the DataCollection, for easy access and editing.
- var trial_data_values = trial_data.values()[0];
-
- if(typeof current_trial.save_trial_parameters == 'object'){
- var keys = Object.keys(current_trial.save_trial_parameters);
- for(var i=0; i 0) {
- setTimeout(nextTrial, opts.default_iti);
- } else {
- nextTrial();
- }
- } else {
- if (current_trial.post_trial_gap > 0) {
- setTimeout(nextTrial, current_trial.post_trial_gap);
- } else {
- nextTrial();
- }
- }
- }
-
- core.endExperiment = function(end_message) {
- timeline.end_message = end_message;
- timeline.end();
- jsPsych.pluginAPI.cancelAllKeyboardResponses();
- jsPsych.pluginAPI.clearAllTimeouts();
- core.finishTrial();
- }
-
- core.endCurrentTimeline = function() {
- timeline.endActiveNode();
- }
-
- core.currentTrial = function() {
- return current_trial;
- };
-
- core.initSettings = function() {
- return opts;
- };
-
- core.currentTimelineNodeID = function() {
- return timeline.activeID();
- };
-
- core.timelineVariable = function(varname, immediate){
- if(typeof immediate == 'undefined'){ immediate = false; }
- if(jsPsych.internal.call_immediate || immediate === true){
- return timeline.timelineVariable(varname);
- } else {
- return function() { return timeline.timelineVariable(varname); }
- }
- }
-
- core.allTimelineVariables = function(){
- return timeline.allTimelineVariables();
- }
-
- core.addNodeToEndOfTimeline = function(new_timeline, preload_callback){
- timeline.insert(new_timeline);
- }
-
- core.pauseExperiment = function(){
- paused = true;
- }
-
- core.resumeExperiment = function(){
- paused = false;
- if(waiting){
- waiting = false;
- nextTrial();
- }
- }
-
- core.loadFail = function(message){
- message = message || '
The experiment failed to load.
';
- loadfail = true;
- DOM_target.innerHTML = message;
- }
-
- core.getSafeModeStatus = function() {
- return file_protocol;
- }
-
- function TimelineNode(parameters, parent, relativeID) {
-
- // a unique ID for this node, relative to the parent
- var relative_id;
-
- // store the parent for this node
- var parent_node;
-
- // parameters for the trial if the node contains a trial
- var trial_parameters;
-
- // parameters for nodes that contain timelines
- var timeline_parameters;
-
- // stores trial information on a node that contains a timeline
- // used for adding new trials
- var node_trial_data;
-
- // track progress through the node
- var progress = {
- current_location: -1, // where on the timeline (which timelinenode)
- current_variable_set: 0, // which set of variables to use from timeline_variables
- current_repetition: 0, // how many times through the variable set on this run of the node
- current_iteration: 0, // how many times this node has been revisited
- done: false
- }
-
- // reference to self
- var self = this;
-
- // recursively get the next trial to run.
- // if this node is a leaf (trial), then return the trial.
- // otherwise, recursively find the next trial in the child timeline.
- this.trial = function() {
- if (typeof timeline_parameters == 'undefined') {
- // returns a clone of the trial_parameters to
- // protect functions.
- return jsPsych.utils.deepCopy(trial_parameters);
- } else {
- if (progress.current_location >= timeline_parameters.timeline.length) {
- return null;
- } else {
- return timeline_parameters.timeline[progress.current_location].trial();
- }
- }
- }
-
- this.markCurrentTrialComplete = function() {
- if(typeof timeline_parameters == 'undefined'){
- progress.done = true;
- } else {
- timeline_parameters.timeline[progress.current_location].markCurrentTrialComplete();
- }
- }
-
- this.nextRepetiton = function() {
- this.setTimelineVariablesOrder();
- progress.current_location = -1;
- progress.current_variable_set = 0;
- progress.current_repetition++;
- for (var i = 0; i < timeline_parameters.timeline.length; i++) {
- timeline_parameters.timeline[i].reset();
- }
- }
-
- // set the order for going through the timeline variables array
- this.setTimelineVariablesOrder = function() {
-
- // check to make sure this node has variables
- if(typeof timeline_parameters === 'undefined' || typeof timeline_parameters.timeline_variables === 'undefined'){
- return;
- }
-
- var order = [];
- for(var i=0; i 1, and only when on the first variable set
- if (typeof timeline_parameters.conditional_function !== 'undefined' && progress.current_repetition == 0 && progress.current_variable_set == 0) {
- jsPsych.internal.call_immediate = true;
- var conditional_result = timeline_parameters.conditional_function();
- jsPsych.internal.call_immediate = false;
- // if the conditional_function() returns false, then the timeline
- // doesn't run and is marked as complete.
- if (conditional_result == false) {
- progress.done = true;
- return true;
- }
- }
-
- // if we reach this point then the node has its own timeline and will start
- // so we need to check if there is an on_timeline_start function if we are on the first variable set
- if (typeof timeline_parameters.on_timeline_start !== 'undefined' && progress.current_variable_set == 0) {
- timeline_parameters.on_timeline_start();
- }
-
-
- }
- // if we reach this point, then either the node doesn't have a timeline of the
- // conditional function returned true and it can start
- progress.current_location = 0;
- // call advance again on this node now that it is pointing to a new location
- return this.advance();
- }
-
- // if this node has a timeline, propogate down to the current trial.
- if (typeof timeline_parameters !== 'undefined') {
-
- var have_node_to_run = false;
- // keep incrementing the location in the timeline until one of the nodes reached is incomplete
- while (progress.current_location < timeline_parameters.timeline.length && have_node_to_run == false) {
-
- // check to see if the node currently pointed at is done
- var target_complete = timeline_parameters.timeline[progress.current_location].advance();
- if (!target_complete) {
- have_node_to_run = true;
- return false;
- } else {
- progress.current_location++;
- }
-
- }
-
- // if we've reached the end of the timeline (which, if the code is here, we have)
-
- // there are a few steps to see what to do next...
-
- // first, check the timeline_variables to see if we need to loop through again
- // with a new set of variables
- if (progress.current_variable_set < progress.order.length - 1) {
- // reset the progress of the node to be with the new set
- this.nextSet();
- // then try to advance this node again.
- return this.advance();
- }
-
- // if we're all done with the timeline_variables, then check to see if there are more repetitions
- else if (progress.current_repetition < timeline_parameters.repetitions - 1) {
- this.nextRepetiton();
- // check to see if there is an on_timeline_finish function
- if (typeof timeline_parameters.on_timeline_finish !== 'undefined') {
- timeline_parameters.on_timeline_finish();
- }
- return this.advance();
- }
-
-
- // if we're all done with the repetitions...
- else {
- // check to see if there is an on_timeline_finish function
- if (typeof timeline_parameters.on_timeline_finish !== 'undefined') {
- timeline_parameters.on_timeline_finish();
- }
-
- // if we're all done with the repetitions, check if there is a loop function.
- if (typeof timeline_parameters.loop_function !== 'undefined') {
- jsPsych.internal.call_immediate = true;
- if (timeline_parameters.loop_function(this.generatedData())) {
- this.reset();
- jsPsych.internal.call_immediate = false;
- return parent_node.advance();
- } else {
- progress.done = true;
- jsPsych.internal.call_immediate = false;
- return true;
- }
- }
-
-
- }
-
- // no more loops on this timeline, we're done!
- progress.done = true;
- return true;
- }
- }
-
- // check the status of the done flag
- this.isComplete = function() {
- return progress.done;
- }
-
- // getter method for timeline variables
- this.getTimelineVariableValue = function(variable_name){
- if(typeof timeline_parameters == 'undefined'){
- return undefined;
- }
- var v = timeline_parameters.timeline_variables[progress.order[progress.current_variable_set]][variable_name];
- return v;
- }
-
- // recursive upward search for timeline variables
- this.findTimelineVariable = function(variable_name){
- var v = this.getTimelineVariableValue(variable_name);
- if(typeof v == 'undefined'){
- if(typeof parent_node !== 'undefined'){
- return parent_node.findTimelineVariable(variable_name);
- } else {
- return undefined;
- }
- } else {
- return v;
- }
- }
-
- // recursive downward search for active trial to extract timeline variable
- this.timelineVariable = function(variable_name){
- if(typeof timeline_parameters == 'undefined'){
- return this.findTimelineVariable(variable_name);
- } else {
- // if progress.current_location is -1, then the timeline variable is being evaluated
- // in a function that runs prior to the trial starting, so we should treat that trial
- // as being the active trial for purposes of finding the value of the timeline variable
- var loc = Math.max(0, progress.current_location);
- // if loc is greater than the number of elements on this timeline, then the timeline
- // variable is being evaluated in a function that runs after the trial on the timeline
- // are complete but before advancing to the next (like a loop_function).
- // treat the last active trial as the active trial for this purpose.
- if(loc == timeline_parameters.timeline.length){
- loc = loc - 1;
- }
- // now find the variable
- return timeline_parameters.timeline[loc].timelineVariable(variable_name);
- }
- }
-
- // recursively get all the timeline variables for this trial
- this.allTimelineVariables = function(){
- var all_tvs = this.allTimelineVariablesNames();
- var all_tvs_vals = {};
- for(var i=0; i'+
- '
The minimum width is '+mw+'px. Your current width is '+w+'px.
'+
- '
The minimum height is '+mh+'px. Your current height is '+h+'px.
';
- core.getDisplayElement().innerHTML = msg;
- } else {
- clearInterval(interval);
- core.getDisplayElement().innerHTML = '';
- checkExclusions(exclusions, success, fail);
- }
- }, 100);
- return; // prevents checking other exclusions while this is being fixed
- }
- }
-
- // WEB AUDIO API
- if(typeof exclusions.audio !== 'undefined' && exclusions.audio) {
- if(window.hasOwnProperty('AudioContext') || window.hasOwnProperty('webkitAudioContext')){
- // clear
- } else {
- clear = false;
- var msg = '
Your browser does not support the WebAudio API, which means that you will not '+
- 'be able to complete the experiment.
Browsers that support the WebAudio API include '+
- 'Chrome, Firefox, Safari, and Edge.
');
- }
-
- function updateProgressBar() {
- var progress = jsPsych.progress().percent_complete;
- core.setProgressBar(progress / 100);
- }
-
- var progress_bar_amount = 0;
-
- core.setProgressBar = function(proportion_complete){
- proportion_complete = Math.max(Math.min(1,proportion_complete),0);
- document.querySelector('#jspsych-progressbar-inner').style.width = (proportion_complete*100) + "%";
- progress_bar_amount = proportion_complete;
- }
-
- core.getProgressBarCompleted = function(){
- return progress_bar_amount;
- }
-
- //Leave a trace in the DOM that jspsych was loaded
- document.documentElement.setAttribute('jspsych', 'present');
-
- return core;
-})();
-
-jsPsych.internal = (function() {
- var module = {};
-
- // this flag is used to determine whether we are in a scope where
- // jsPsych.timelineVariable() should be executed immediately or
- // whether it should return a function to access the variable later.
- module.call_immediate = false;
-
- return module;
-})();
-
-jsPsych.plugins = (function() {
-
- var module = {};
-
- // enumerate possible parameter types for plugins
- module.parameterType = {
- BOOL: 0,
- STRING: 1,
- INT: 2,
- FLOAT: 3,
- FUNCTION: 4,
- KEY: 5,
- SELECT: 6,
- HTML_STRING: 7,
- IMAGE: 8,
- AUDIO: 9,
- VIDEO: 10,
- OBJECT: 11,
- COMPLEX: 12,
- TIMELINE: 13
- }
-
- module.universalPluginParameters = {
- data: {
- type: module.parameterType.OBJECT,
- pretty_name: 'Data',
- default: {},
- description: 'Data to add to this trial (key-value pairs)'
- },
- on_start: {
- type: module.parameterType.FUNCTION,
- pretty_name: 'On start',
- default: function() { return; },
- description: 'Function to execute when trial begins'
- },
- on_finish: {
- type: module.parameterType.FUNCTION,
- pretty_name: 'On finish',
- default: function() { return; },
- description: 'Function to execute when trial is finished'
- },
- on_load: {
- type: module.parameterType.FUNCTION,
- pretty_name: 'On load',
- default: function() { return; },
- description: 'Function to execute after the trial has loaded'
- },
- post_trial_gap: {
- type: module.parameterType.INT,
- pretty_name: 'Post trial gap',
- default: null,
- description: 'Length of gap between the end of this trial and the start of the next trial'
- },
- css_classes: {
- type: module.parameterType.STRING,
- pretty_name: 'Custom CSS classes',
- default: null,
- description: 'A list of CSS classes to add to the jsPsych display element for the duration of this trial'
- }
- }
-
- return module;
-})();
-
-jsPsych.extensions = (function(){
- return {};
-})();
-
-jsPsych.data = (function() {
-
- var module = {};
-
- // data storage object
- var allData = DataCollection();
-
- // browser interaction event data
- var interactionData = DataCollection();
-
- // data properties for all trials
- var dataProperties = {};
-
- // cache the query_string
- var query_string;
-
- // DataCollection
- function DataCollection(data){
-
- var data_collection = {};
-
- var trials = typeof data === 'undefined' ? [] : data;
-
- data_collection.push = function(new_data){
- trials.push(new_data);
- return data_collection;
- }
-
- data_collection.join = function(other_data_collection){
- trials = trials.concat(other_data_collection.values());
- return data_collection;
- }
-
- data_collection.top = function(){
- if(trials.length <= 1){
- return data_collection;
- } else {
- return DataCollection([trials[trials.length-1]]);
- }
- }
-
- /**
- * Queries the first n elements in a collection of trials.
- *
- * @param {number} n A positive integer of elements to return. A value of
- * n that is less than 1 will throw an error.
- *
- * @return {Array} First n objects of a collection of trials. If fewer than
- * n trials are available, the trials.length elements will
- * be returned.
- *
- */
- data_collection.first = function(n){
- if (typeof n == 'undefined') { n = 1 }
- if (n < 1) {
- throw `You must query with a positive nonzero integer. Please use a
- different value for n.`;
- }
- if (trials.length == 0) return DataCollection([]);
- if (n > trials.length) n = trials.length;
- return DataCollection(trials.slice(0, n));
- }
-
- /**
- * Queries the last n elements in a collection of trials.
- *
- * @param {number} n A positive integer of elements to return. A value of
- * n that is less than 1 will throw an error.
- *
- * @return {Array} Last n objects of a collection of trials. If fewer than
- * n trials are available, the trials.length elements will
- * be returned.
- *
- */
- data_collection.last = function(n) {
- if (typeof n == 'undefined') { n = 1 }
- if (n < 1) {
- throw `You must query with a positive nonzero integer. Please use a
- different value for n.`;
- }
- if (trials.length == 0) return DataCollection([]);
- if (n > trials.length) n = trials.length;
- return DataCollection(trials.slice(trials.length - n, trials.length));
- }
-
- data_collection.values = function(){
- return trials;
- }
-
- data_collection.count = function(){
- return trials.length;
- }
-
- data_collection.readOnly = function(){
- return DataCollection(jsPsych.utils.deepCopy(trials));
- }
-
- data_collection.addToAll = function(properties){
- for (var i = 0; i < trials.length; i++) {
- for (var key in properties) {
- trials[i][key] = properties[key];
- }
- }
- return data_collection;
- }
-
- data_collection.addToLast = function(properties){
- if(trials.length != 0){
- for (var key in properties) {
- trials[trials.length-1][key] = properties[key];
- }
- }
- return data_collection;
- }
-
- data_collection.filter = function(filters){
- // [{p1: v1, p2:v2}, {p1:v2}]
- // {p1: v1}
- if(!Array.isArray(filters)){
- var f = jsPsych.utils.deepCopy([filters]);
- } else {
- var f = jsPsych.utils.deepCopy(filters);
- }
-
- var filtered_data = [];
- for(var x=0; x < trials.length; x++){
- var keep = false;
- for(var i=0; iclick to download');
- document.getElementById('jspsych-download-as-text-link').click();
- }
-
- //
- // A few helper functions to handle data format conversion
- //
-
- // this function based on code suggested by StackOverflow users:
- // http://stackoverflow.com/users/64741/zachary
- // http://stackoverflow.com/users/317/joseph-sturtevant
-
- function JSON2CSV(objArray) {
- var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
- var line = '';
- var result = '';
- var columns = [];
-
- var i = 0;
- for (var j = 0; j < array.length; j++) {
- for (var key in array[j]) {
- var keyString = key + "";
- keyString = '"' + keyString.replace(/"/g, '""') + '",';
- if (!columns.includes(key)) {
- columns[i] = key;
- line += keyString;
- i++;
- }
- }
- }
-
- line = line.slice(0, -1);
- result += line + '\r\n';
-
- for (var i = 0; i < array.length; i++) {
- var line = '';
- for (var j = 0; j < columns.length; j++) {
- var value = (typeof array[i][columns[j]] === 'undefined') ? '' : array[i][columns[j]];
- if(typeof value == 'object') {
- value = JSON.stringify(value);
- }
- var valueString = value + "";
- line += '"' + valueString.replace(/"/g, '""') + '",';
- }
-
- line = line.slice(0, -1);
- result += line + '\r\n';
- }
-
- return result;
- }
-
- // this function is modified from StackOverflow:
- // http://stackoverflow.com/posts/3855394
-
- function getQueryString() {
- var a = window.location.search.substr(1).split('&');
- if (a == "") return {};
- var b = {};
- for (var i = 0; i < a.length; ++i)
- {
- var p=a[i].split('=', 2);
- if (p.length == 1)
- b[p[0]] = "";
- else
- b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
- }
- return b;
- }
-
- return module;
-
-})();
-
-jsPsych.turk = (function() {
-
- var module = {};
-
- // core.turkInfo gets information relevant to mechanical turk experiments. returns an object
- // containing the workerID, assignmentID, and hitID, and whether or not the HIT is in
- // preview mode, meaning that they haven't accepted the HIT yet.
- module.turkInfo = function() {
-
- var turk = {};
-
- var param = function(url, name) {
- name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
- var regexS = "[\\?&]" + name + "=([^]*)";
- var regex = new RegExp(regexS);
- var results = regex.exec(url);
- return (results == null) ? "" : results[1];
- };
-
- var src = param(window.location.href, "assignmentId") ? window.location.href : document.referrer;
-
- var keys = ["assignmentId", "hitId", "workerId", "turkSubmitTo"];
- keys.map(
-
- function(key) {
- turk[key] = unescape(param(src, key));
- });
-
- turk.previewMode = (turk.assignmentId == "ASSIGNMENT_ID_NOT_AVAILABLE");
-
- turk.outsideTurk = (!turk.previewMode && turk.hitId === "" && turk.assignmentId == "" && turk.workerId == "")
-
- turk_info = turk;
-
- return turk;
-
- };
-
- // core.submitToTurk will submit a MechanicalTurk ExternalHIT type
- module.submitToTurk = function(data) {
-
- var turkInfo = jsPsych.turk.turkInfo();
- var assignmentId = turkInfo.assignmentId;
- var turkSubmitTo = turkInfo.turkSubmitTo;
-
- if (!assignmentId || !turkSubmitTo) return;
-
- var dataString = [];
-
- for (var key in data) {
-
- if (data.hasOwnProperty(key)) {
- dataString.push(key + "=" + escape(data[key]));
- }
- }
-
- dataString.push("assignmentId=" + assignmentId);
-
- var url = turkSubmitTo + "/mturk/externalSubmit?" + dataString.join("&");
-
- window.location.href = url;
- };
-
- return module;
-
-})();
-
-jsPsych.randomization = (function() {
-
- var module = {};
-
- module.repeat = function(array, repetitions, unpack) {
-
- var arr_isArray = Array.isArray(array);
- var rep_isArray = Array.isArray(repetitions);
-
- // if array is not an array, then we just repeat the item
- if (!arr_isArray) {
- if (!rep_isArray) {
- array = [array];
- repetitions = [repetitions];
- } else {
- repetitions = [repetitions[0]];
- console.log('Unclear parameters given to randomization.repeat. Multiple set sizes specified, but only one item exists to sample. Proceeding using the first set size.');
- }
- } else {
- if (!rep_isArray) {
- var reps = [];
- for (var i = 0; i < array.length; i++) {
- reps.push(repetitions);
- }
- repetitions = reps;
- } else {
- if (array.length != repetitions.length) {
- console.warning('Unclear parameters given to randomization.repeat. Items and repetitions are unequal lengths. Behavior may not be as expected.');
- // throw warning if repetitions is too short, use first rep ONLY.
- if (repetitions.length < array.length) {
- var reps = [];
- for (var i = 0; i < array.length; i++) {
- reps.push(repetitions);
- }
- repetitions = reps;
- } else {
- // throw warning if too long, and then use the first N
- repetitions = repetitions.slice(0, array.length);
- }
- }
- }
- }
-
- // should be clear at this point to assume that array and repetitions are arrays with == length
- var allsamples = [];
- for (var i = 0; i < array.length; i++) {
- for (var j = 0; j < repetitions[i]; j++) {
- if(array[i] == null || typeof array[i] != 'object'){
- allsamples.push(array[i]);
- } else {
- allsamples.push(Object.assign({}, array[i]));
- }
-
- }
- }
-
- var out = shuffle(allsamples);
-
- if (unpack) {
- out = unpackArray(out);
- }
-
- return out;
- }
-
- module.shuffle = function(arr) {
- if(!Array.isArray(arr)){
- console.error('Argument to jsPsych.randomization.shuffle() must be an array.')
- }
- return shuffle(arr);
- }
-
- module.shuffleNoRepeats = function(arr, equalityTest) {
- if(!Array.isArray(arr)){
- console.error('First argument to jsPsych.randomization.shuffleNoRepeats() must be an array.')
- }
- if(typeof equalityTest !== 'undefined' && typeof equalityTest !== 'function'){
- console.error('Second argument to jsPsych.randomization.shuffleNoRepeats() must be a function.')
- }
- // define a default equalityTest
- if (typeof equalityTest == 'undefined') {
- equalityTest = function(a, b) {
- if (a === b) {
- return true;
- } else {
- return false;
- }
- }
- }
-
- var random_shuffle = shuffle(arr);
- for (var i = 0; i < random_shuffle.length - 1; i++) {
- if (equalityTest(random_shuffle[i], random_shuffle[i + 1])) {
- // neighbors are equal, pick a new random neighbor to swap (not the first or last element, to avoid edge cases)
- var random_pick = Math.floor(Math.random() * (random_shuffle.length - 2)) + 1;
- // test to make sure the new neighbor isn't equal to the old one
- while (
- equalityTest(random_shuffle[i + 1], random_shuffle[random_pick]) ||
- (equalityTest(random_shuffle[i + 1], random_shuffle[random_pick + 1]) || equalityTest(random_shuffle[i + 1], random_shuffle[random_pick - 1]))
- ) {
- random_pick = Math.floor(Math.random() * (random_shuffle.length - 2)) + 1;
- }
- var new_neighbor = random_shuffle[random_pick];
- random_shuffle[random_pick] = random_shuffle[i + 1];
- random_shuffle[i + 1] = new_neighbor;
- }
- }
-
- return random_shuffle;
- }
-
- module.shuffleAlternateGroups = function(arr_groups, random_group_order){
- if(typeof random_group_order == 'undefined'){
- random_group_order = false;
- }
-
- var n_groups = arr_groups.length;
- if(n_groups == 1){
- console.warn('jsPsych.randomization.shuffleAlternateGroups was called with only one group. Defaulting to simple shuffle.');
- return(module.shuffle(arr_groups[0]));
- }
-
- var group_order = [];
- for(var i=0; i arr.length) {
- console.error("Cannot take a sample " +
- "larger than the size of the set of items to sample.");
- }
- return jsPsych.randomization.shuffle(arr).slice(0,size);
- }
-
- module.sampleWithReplacement = function(arr, size, weights) {
- if(!Array.isArray(arr)){
- console.error("First argument to jsPsych.randomization.sampleWithReplacement() must be an array")
- }
-
- var normalized_weights = [];
- if(typeof weights !== 'undefined'){
- if(weights.length !== arr.length){
- console.error('The length of the weights array must equal the length of the array '+
- 'to be sampled from.');
- }
- var weight_sum = 0;
- for(var i=0; i cumulative_weights[index]) { index++; }
- samp.push(arr[index]);
- }
- return samp;
- }
-
- module.factorial = function(factors, repetitions, unpack) {
-
- var factorNames = Object.keys(factors);
-
- var factor_combinations = [];
-
- for (var i = 0; i < factors[factorNames[0]].length; i++) {
- factor_combinations.push({});
- factor_combinations[i][factorNames[0]] = factors[factorNames[0]][i];
- }
-
- for (var i = 1; i < factorNames.length; i++) {
- var toAdd = factors[factorNames[i]];
- var n = factor_combinations.length;
- for (var j = 0; j < n; j++) {
- var base = factor_combinations[j];
- for (var k = 0; k < toAdd.length; k++) {
- var newpiece = {};
- newpiece[factorNames[i]] = toAdd[k];
- factor_combinations.push(Object.assign({}, base, newpiece));
- }
- }
- factor_combinations.splice(0, n);
- }
-
- repetitions = (typeof repetitions === 'undefined') ? 1 : repetitions;
- var with_repetitions = module.repeat(factor_combinations, repetitions, unpack);
-
- return with_repetitions;
- }
-
- module.randomID = function(length){
- var result = '';
- var length = (typeof length == 'undefined') ? 32 : length;
- var chars = '0123456789abcdefghjklmnopqrstuvwxyz';
- for(var i = 0; i= 0) {
- k = n;
- } else {
- k = len + n;
- if (k < 0) {k = 0;}
- }
- var currentElement;
- while (k < len) {
- currentElement = O[k];
- if (searchElement === currentElement ||
- (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN
- return true;
- }
- k++;
- }
- return false;
- };
-}
-
-// polyfill for Array.isArray
-if (!Array.isArray) {
- Array.isArray = function(arg) {
- return Object.prototype.toString.call(arg) === '[object Array]';
- };
-}
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-animation.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-animation.js
deleted file mode 100644
index 1c4ae168..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-animation.js
+++ /dev/null
@@ -1,189 +0,0 @@
-/**
- * jsPsych plugin for showing animations and recording keyboard responses
- * Josh de Leeuw
- *
- * documentation: docs.jspsych.org
- */
-
-jsPsych.plugins.animation = (function() {
-
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('animation', 'stimuli', 'image');
-
- plugin.info = {
- name: 'animation',
- description: '',
- parameters: {
- stimuli: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Stimuli',
- default: undefined,
- array: true,
- description: 'The images to be displayed.'
- },
- frame_time: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Frame time',
- default: 250,
- description: 'Duration to display each image.'
- },
- frame_isi: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Frame gap',
- default: 0,
- description: 'Length of gap to be shown between each image.'
- },
- sequence_reps: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Sequence repetitions',
- default: 1,
- description: 'Number of times to show entire sequence.'
- },
- choices: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Choices',
- default: jsPsych.ALL_KEYS,
- array: true,
- description: 'Keys subject uses to respond to stimuli.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below stimulus.'
- },
- render_on_canvas: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Render on canvas',
- default: true,
- description: 'If true, the images will be drawn onto a canvas element (prevents blank screen between consecutive images in some browsers).'+
- 'If false, the image will be shown via an img element.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- var interval_time = trial.frame_time + trial.frame_isi;
- var animate_frame = -1;
- var reps = 0;
- var startTime = performance.now();
- var animation_sequence = [];
- var responses = [];
- var current_stim = "";
-
- if (trial.render_on_canvas) {
- // first clear the display element (because the render_on_canvas method appends to display_element instead of overwriting it with .innerHTML)
- if (display_element.hasChildNodes()) {
- // can't loop through child list because the list will be modified by .removeChild()
- while (display_element.firstChild) {
- display_element.removeChild(display_element.firstChild);
- }
- }
- var canvas = document.createElement("canvas");
- canvas.id = "jspsych-animation-image";
- canvas.style.margin = 0;
- canvas.style.padding = 0;
- display_element.insertBefore(canvas, null);
- var ctx = canvas.getContext("2d");
- }
-
- var animate_interval = setInterval(function() {
- var showImage = true;
- if (!trial.render_on_canvas) {
- display_element.innerHTML = ''; // clear everything
- }
- animate_frame++;
- if (animate_frame == trial.stimuli.length) {
- animate_frame = 0;
- reps++;
- if (reps >= trial.sequence_reps) {
- endTrial();
- clearInterval(animate_interval);
- showImage = false;
- }
- }
- if (showImage) {
- show_next_frame();
- }
- }, interval_time);
-
- function show_next_frame() {
- if (trial.render_on_canvas) {
- display_element.querySelector('#jspsych-animation-image').style.visibility = 'visible';
- var img = new Image();
- img.src = trial.stimuli[animate_frame];
- canvas.height = img.naturalHeight;
- canvas.width = img.naturalWidth;
- ctx.drawImage(img,0,0);
- if (trial.prompt !== null & animate_frame == 0 & reps == 0) {
- display_element.insertAdjacentHTML('beforeend', trial.prompt);
- }
- } else {
- // show image
- display_element.innerHTML = '';
- if (trial.prompt !== null) {
- display_element.innerHTML += trial.prompt;
- }
- }
- current_stim = trial.stimuli[animate_frame];
-
- // record when image was shown
- animation_sequence.push({
- "stimulus": trial.stimuli[animate_frame],
- "time": performance.now() - startTime
- });
-
- if (trial.frame_isi > 0) {
- jsPsych.pluginAPI.setTimeout(function() {
- display_element.querySelector('#jspsych-animation-image').style.visibility = 'hidden';
- current_stim = 'blank';
- // record when blank image was shown
- animation_sequence.push({
- "stimulus": 'blank',
- "time": performance.now() - startTime
- });
- }, trial.frame_time);
- }
- }
-
- var after_response = function(info) {
-
- responses.push({
- key_press: info.key,
- rt: info.rt,
- stimulus: current_stim
- });
-
- // after a valid response, the stimulus will have the CSS class 'responded'
- // which can be used to provide visual feedback that a response was recorded
- display_element.querySelector('#jspsych-animation-image').className += ' responded';
- }
-
- // hold the jspsych response listener object in memory
- // so that we can turn off the response collection when
- // the trial ends
- var response_listener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: trial.choices,
- rt_method: 'performance',
- persist: true,
- allow_held_key: false
- });
-
- function endTrial() {
-
- jsPsych.pluginAPI.cancelKeyboardResponse(response_listener);
-
- var trial_data = {
- animation_sequence: animation_sequence,
- response: responses
- };
-
- jsPsych.finishTrial(trial_data);
- }
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-audio-button-response.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-audio-button-response.js
deleted file mode 100644
index ee4214ef..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-audio-button-response.js
+++ /dev/null
@@ -1,269 +0,0 @@
-/**
- * jspsych-audio-button-response
- * Kristin Diep
- *
- * plugin for playing an audio file and getting a keyboard response
- *
- * documentation: docs.jspsych.org
- *
- **/
-
-jsPsych.plugins["audio-button-response"] = (function () {
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('audio-button-response', 'stimulus', 'audio');
-
- plugin.info = {
- name: 'audio-button-response',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.AUDIO,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The audio to be played.'
- },
- choices: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Choices',
- default: undefined,
- array: true,
- description: 'The button labels.'
- },
- button_html: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'Button HTML',
- default: '',
- array: true,
- description: 'Custom button. Can make your own style.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the stimulus.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'The maximum duration to wait for a response.'
- },
- margin_vertical: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Margin vertical',
- default: '0px',
- description: 'Vertical margin of button.'
- },
- margin_horizontal: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Margin horizontal',
- default: '8px',
- description: 'Horizontal margin of button.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, the trial will end when user makes a response.'
- },
- trial_ends_after_audio: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Trial ends after audio',
- default: false,
- description: 'If true, then the trial will end as soon as the audio file finishes playing.'
- },
- response_allowed_while_playing: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response allowed while playing',
- default: true,
- description: 'If true, then responses are allowed while the audio is playing. ' +
- 'If false, then the audio must finish playing before a response is accepted.'
- }
- }
- }
-
- plugin.trial = function (display_element, trial) {
-
- // setup stimulus
- var context = jsPsych.pluginAPI.audioContext();
- var audio;
-
- // store response
- var response = {
- rt: null,
- button: null
- };
-
- // record webaudio context start time
- var startTime;
-
- // load audio file
- jsPsych.pluginAPI.getAudioBuffer(trial.stimulus)
- .then(function (buffer) {
- if (context !== null) {
- audio = context.createBufferSource();
- audio.buffer = buffer;
- audio.connect(context.destination);
- } else {
- audio = buffer;
- audio.currentTime = 0;
- }
- setupTrial();
- })
- .catch(function (err) {
- console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`)
- console.error(err)
- });
-
- function setupTrial() {
- // set up end event if trial needs it
- if (trial.trial_ends_after_audio) {
- audio.addEventListener('ended', end_trial);
- }
-
- // enable buttons after audio ends if necessary
- if ((!trial.response_allowed_while_playing) & (!trial.trial_ends_after_audio)) {
- audio.addEventListener('ended', enable_buttons);
- }
-
- //display buttons
- var buttons = [];
- if (Array.isArray(trial.button_html)) {
- if (trial.button_html.length == trial.choices.length) {
- buttons = trial.button_html;
- } else {
- console.error('Error in audio-button-response plugin. The length of the button_html array does not equal the length of the choices array');
- }
- } else {
- for (var i = 0; i < trial.choices.length; i++) {
- buttons.push(trial.button_html);
- }
- }
-
- var html = '
';
- for (var i = 0; i < trial.choices.length; i++) {
- var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
- html += '
' + str + '
';
- }
- html += '
';
-
- //show prompt if there is one
- if (trial.prompt !== null) {
- html += trial.prompt;
- }
-
- display_element.innerHTML = html;
-
- if (trial.response_allowed_while_playing) {
- enable_buttons();
- } else {
- disable_buttons();
- }
-
- // start time
- startTime = performance.now();
-
- // start audio
- if (context !== null) {
- startTime = context.currentTime;
- audio.start(startTime);
- } else {
- audio.play();
- }
-
- // end trial if time limit is set
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function () {
- end_trial();
- }, trial.trial_duration);
- }
- }
-
-
-
- // function to handle responses by the subject
- function after_response(choice) {
-
- // measure rt
- var endTime = performance.now();
- var rt = endTime - startTime;
- if (context !== null) {
- endTime = context.currentTime;
- rt = Math.round((endTime - startTime) * 1000);
- }
- response.button = parseInt(choice);
- response.rt = rt;
-
- // disable all the buttons after a response
- disable_buttons();
-
- if (trial.response_ends_trial) {
- end_trial();
- }
- }
-
- // function to end trial when it is time
- function end_trial() {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // stop the audio file if it is playing
- // remove end event listeners if they exist
- if (context !== null) {
- audio.stop();
- } else {
- audio.pause();
- }
-
- audio.removeEventListener('ended', end_trial);
- audio.removeEventListener('ended', enable_buttons);
-
- // gather the data to store for the trial
- var trial_data = {
- rt: response.rt,
- stimulus: trial.stimulus,
- response: response.button
- };
-
- // clear the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- }
-
- function button_response(e) {
- var choice = e.currentTarget.getAttribute('data-choice'); // don't use dataset for jsdom compatibility
- after_response(choice);
- }
-
- function disable_buttons() {
- var btns = document.querySelectorAll('.jspsych-audio-button-response-button');
- for (var i = 0; i < btns.length; i++) {
- var btn_el = btns[i].querySelector('button');
- if (btn_el) {
- btn_el.disabled = true;
- }
- btns[i].removeEventListener('click', button_response);
- }
- }
-
- function enable_buttons() {
- var btns = document.querySelectorAll('.jspsych-audio-button-response-button');
- for (var i = 0; i < btns.length; i++) {
- var btn_el = btns[i].querySelector('button');
- if (btn_el) {
- btn_el.disabled = false;
- }
- btns[i].addEventListener('click', button_response);
- }
- }
-
-
-
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-audio-keyboard-response.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-audio-keyboard-response.js
deleted file mode 100644
index 79e7da11..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-audio-keyboard-response.js
+++ /dev/null
@@ -1,209 +0,0 @@
-/**
- * jspsych-audio-keyboard-response
- * Josh de Leeuw
- *
- * plugin for playing an audio file and getting a keyboard response
- *
- * documentation: docs.jspsych.org
- *
- **/
-
-jsPsych.plugins["audio-keyboard-response"] = (function () {
-
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('audio-keyboard-response', 'stimulus', 'audio');
-
- plugin.info = {
- name: 'audio-keyboard-response',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.AUDIO,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The audio to be played.'
- },
- choices: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Choices',
- array: true,
- default: jsPsych.ALL_KEYS,
- description: 'The keys the subject is allowed to press to respond to the stimulus.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the stimulus.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'The maximum duration to wait for a response.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, the trial will end when user makes a response.'
- },
- trial_ends_after_audio: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Trial ends after audio',
- default: false,
- description: 'If true, then the trial will end as soon as the audio file finishes playing.'
- },
- response_allowed_while_playing: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response allowed while playing',
- default: true,
- description: 'If true, then responses are allowed while the audio is playing. ' +
- 'If false, then the audio must finish playing before a response is accepted.'
- }
- }
- }
-
- plugin.trial = function (display_element, trial) {
-
- // setup stimulus
- var context = jsPsych.pluginAPI.audioContext();
- var audio;
-
- // store response
- var response = {
- rt: null,
- key: null
- };
-
- // record webaudio context start time
- var startTime;
-
- // load audio file
- jsPsych.pluginAPI.getAudioBuffer(trial.stimulus)
- .then(function (buffer) {
- if (context !== null) {
- audio = context.createBufferSource();
- audio.buffer = buffer;
- audio.connect(context.destination);
- } else {
- audio = buffer;
- audio.currentTime = 0;
- }
- setupTrial();
- })
- .catch(function (err) {
- console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`)
- console.error(err)
- });
-
- function setupTrial() {
- // set up end event if trial needs it
- if (trial.trial_ends_after_audio) {
- audio.addEventListener('ended', end_trial);
- }
-
- // show prompt if there is one
- if (trial.prompt !== null) {
- display_element.innerHTML = trial.prompt;
- }
-
- // start audio
- if (context !== null) {
- startTime = context.currentTime;
- audio.start(startTime);
- } else {
- audio.play();
- }
-
- // start keyboard listener when trial starts or sound ends
- if (trial.response_allowed_while_playing) {
- setup_keyboard_listener();
- } else if (!trial.trial_ends_after_audio) {
- audio.addEventListener('ended', setup_keyboard_listener);
- }
-
- // end trial if time limit is set
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function () {
- end_trial();
- }, trial.trial_duration);
- }
- }
-
-
- // function to end trial when it is time
- function end_trial() {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // stop the audio file if it is playing
- // remove end event listeners if they exist
- if (context !== null) {
- audio.stop();
- } else {
- audio.pause();
- }
-
- audio.removeEventListener('ended', end_trial);
- audio.removeEventListener('ended', setup_keyboard_listener);
-
-
- // kill keyboard listeners
- jsPsych.pluginAPI.cancelAllKeyboardResponses();
-
- // gather the data to store for the trial
- var trial_data = {
- rt: response.rt,
- stimulus: trial.stimulus,
- response: response.key
- };
-
- // clear the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- }
-
- // function to handle responses by the subject
- function after_response(info) {
-
- // only record the first response
- if (response.key == null) {
- response = info;
- }
-
- if (trial.response_ends_trial) {
- end_trial();
- }
- };
-
- function setup_keyboard_listener() {
- // start the response listener
- if (context !== null) {
- jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: trial.choices,
- rt_method: 'audio',
- persist: false,
- allow_held_key: false,
- audio_context: context,
- audio_context_start_time: startTime
- });
- } else {
- jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: trial.choices,
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
- }
- }
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-audio-slider-response.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-audio-slider-response.js
deleted file mode 100644
index 28ffdd56..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-audio-slider-response.js
+++ /dev/null
@@ -1,278 +0,0 @@
-jsPsych.plugins['audio-slider-response'] = (function () {
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('audio-slider-response', 'stimulus', 'audio');
-
- plugin.info = {
- name: 'audio-slider-response',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.AUDIO,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The image to be displayed'
- },
- min: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Min slider',
- default: 0,
- description: 'Sets the minimum value of the slider.'
- },
- max: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Max slider',
- default: 100,
- description: 'Sets the maximum value of the slider',
- },
- slider_start: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Slider starting value',
- default: 50,
- description: 'Sets the starting value of the slider',
- },
- step: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Step',
- default: 1,
- description: 'Sets the step of the slider'
- },
- labels: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'Labels',
- default: [],
- array: true,
- description: 'Labels of the slider.',
- },
- slider_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Slider width',
- default: null,
- description: 'Width of the slider in pixels.'
- },
- button_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button label',
- default: 'Continue',
- array: false,
- description: 'Label of the button to advance.'
- },
- require_movement: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Require movement',
- default: false,
- description: 'If true, the participant will have to move the slider before continuing.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the slider.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show the trial.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, trial will end when user makes a response.'
- },
- response_allowed_while_playing: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response allowed while playing',
- default: true,
- description: 'If true, then responses are allowed while the audio is playing. ' +
- 'If false, then the audio must finish playing before a response is accepted.'
- }
- }
- }
-
- plugin.trial = function (display_element, trial) {
-
- // half of the thumb width value from jspsych.css, used to adjust the label positions
- var half_thumb_width = 7.5;
-
- // setup stimulus
- var context = jsPsych.pluginAPI.audioContext();
- var audio;
-
- // record webaudio context start time
- var startTime;
-
- // for storing data related to response
- var response;
-
-
- // load audio file
- jsPsych.pluginAPI.getAudioBuffer(trial.stimulus)
- .then(function (buffer) {
- if (context !== null) {
- audio = context.createBufferSource();
- audio.buffer = buffer;
- audio.connect(context.destination);
- } else {
- audio = buffer;
- audio.currentTime = 0;
- }
- setupTrial();
- })
- .catch(function (err) {
- console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`)
- console.error(err)
- });
-
- function setupTrial() {
-
-
- // set up end event if trial needs it
- if (trial.trial_ends_after_audio) {
-
- audio.addEventListener('ended', end_trial);
-
- }
-
- // enable slider after audio ends if necessary
- if ((!trial.response_allowed_while_playing) & (!trial.trial_ends_after_audio)) {
-
- audio.addEventListener('ended', enable_slider);
-
- }
-
- var html = '
';
- html += '
';
- html += '
'
- for (var j = 0; j < trial.labels.length; j++) {
- var label_width_perc = 100 / (trial.labels.length - 1);
- var percent_of_range = j * (100 / (trial.labels.length - 1));
- var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;
- var offset = (percent_dist_from_center * half_thumb_width) / 100;
- html += '
';
- html += '' + trial.labels[j] + '';
- html += '
'
- }
- html += '
';
- html += '
';
- html += '
';
-
- if (trial.prompt !== null) {
- html += trial.prompt;
- }
-
- // add submit button
- var next_disabled_attribute = "";
- if (trial.require_movement | !trial.response_allowed_while_playing) {
- next_disabled_attribute = "disabled";
- }
- html += '';
-
- display_element.innerHTML = html;
-
- response = {
- rt: null,
- response: null
- };
-
- if (!trial.response_allowed_while_playing) {
- display_element.querySelector('#jspsych-audio-slider-response-response').disabled = true;
- display_element.querySelector('#jspsych-audio-slider-response-next').disabled = true;
- }
-
- if (trial.require_movement) {
- display_element.querySelector('#jspsych-audio-slider-response-response').addEventListener('click', function () {
- display_element.querySelector('#jspsych-audio-slider-response-next').disabled = false;
- });
- }
-
- display_element.querySelector('#jspsych-audio-slider-response-next').addEventListener('click', function () {
- // measure response time
- var endTime = performance.now();
- var rt = endTime - startTime;
- if (context !== null) {
- endTime = context.currentTime;
- rt = Math.round((endTime - startTime) * 1000);
- }
- response.rt = rt;
- response.response = display_element.querySelector('#jspsych-audio-slider-response-response').valueAsNumber;
-
- if (trial.response_ends_trial) {
- end_trial();
- } else {
- display_element.querySelector('#jspsych-audio-slider-response-next').disabled = true;
- }
-
- });
-
- startTime = performance.now();
- // start audio
- if (context !== null) {
- startTime = context.currentTime;
- audio.start(startTime);
- } else {
- audio.play();
- }
-
- // end trial if trial_duration is set
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function () {
- end_trial();
- }, trial.trial_duration);
- }
- }
-
- // function to enable slider after audio ends
- function enable_slider() {
- document.querySelector('#jspsych-audio-slider-response-response').disabled = false;
- if (!trial.require_movement) {
- document.querySelector('#jspsych-audio-slider-response-next').disabled = false;
- }
- }
-
- function end_trial() {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // stop the audio file if it is playing
- // remove end event listeners if they exist
- if (context !== null) {
- audio.stop();
- } else {
- audio.pause();
- }
-
- audio.removeEventListener('ended', end_trial);
- audio.removeEventListener('ended', enable_slider);
-
-
- // save data
- var trialdata = {
- rt: response.rt,
- stimulus: trial.stimulus,
- slider_start: trial.slider_start,
- response: response.response
- };
-
- display_element.innerHTML = '';
-
- // next trial
- jsPsych.finishTrial(trialdata);
- }
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-call-function.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-call-function.js
deleted file mode 100644
index 9d6a85a7..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-call-function.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * jspsych-call-function
- * plugin for calling an arbitrary function during a jspsych experiment
- * Josh de Leeuw
- *
- * documentation: docs.jspsych.org
- *
- **/
-
-jsPsych.plugins['call-function'] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'call-function',
- description: '',
- parameters: {
- func: {
- type: jsPsych.plugins.parameterType.FUNCTION,
- pretty_name: 'Function',
- default: undefined,
- description: 'Function to call'
- },
- async: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Asynchronous',
- default: false,
- description: 'Is the function call asynchronous?'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
- trial.post_trial_gap = 0;
- var return_val;
-
- if(trial.async){
- var done = function(data){
- return_val = data;
- end_trial();
- }
- trial.func(done);
- } else {
- return_val = trial.func();
- end_trial();
- }
-
- function end_trial(){
- var trial_data = {
- value: return_val
- };
-
- jsPsych.finishTrial(trial_data);
- }
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-canvas-button-response.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-canvas-button-response.js
deleted file mode 100644
index 8bd9aabd..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-canvas-button-response.js
+++ /dev/null
@@ -1,199 +0,0 @@
-/**
- * jspsych-canvas-button-response
- * Chris Jungerius (modified from Josh de Leeuw)
- *
- * a jsPsych plugin for displaying a canvas stimulus and getting a button response
- *
- * documentation: docs.jspsych.org
- *
- **/
-
-jsPsych.plugins["canvas-button-response"] = (function () {
-
- var plugin = {};
-
- plugin.info = {
- name: 'canvas-button-response',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.FUNCTION,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The drawing function to apply to the canvas. Should take the canvas object as argument.'
- },
- choices: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Choices',
- default: undefined,
- array: true,
- description: 'The labels for the buttons.'
- },
- button_html: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button HTML',
- default: '',
- array: true,
- description: 'The html of the button. Can create own style.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed under the button.'
- },
- stimulus_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Stimulus duration',
- default: null,
- description: 'How long to hide the stimulus.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show the trial.'
- },
- margin_vertical: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Margin vertical',
- default: '0px',
- description: 'The vertical margin of the button.'
- },
- margin_horizontal: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Margin horizontal',
- default: '8px',
- description: 'The horizontal margin of the button.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, then trial will end when user responds.'
- },
- canvas_size: {
- type: jsPsych.plugins.parameterType.INT,
- array: true,
- pretty_name: 'Canvas size',
- default: [500, 500],
- description: 'Array containing the height (first value) and width (second value) of the canvas element.'
- }
-
- }
- }
-
- plugin.trial = function (display_element, trial) {
-
- // create canvas
- var html = '
' + '' + '
';
-
- //display buttons
- var buttons = [];
- if (Array.isArray(trial.button_html)) {
- if (trial.button_html.length == trial.choices.length) {
- buttons = trial.button_html;
- } else {
- console.error('Error in canvas-button-response plugin. The length of the button_html array does not equal the length of the choices array');
- }
- } else {
- for (var i = 0; i < trial.choices.length; i++) {
- buttons.push(trial.button_html);
- }
- }
- html += '
';
- for (var i = 0; i < trial.choices.length; i++) {
- var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
- html += '
' + str + '
';
- }
- html += '
';
-
- //show prompt if there is one
- if (trial.prompt !== null) {
- html += trial.prompt;
- }
- display_element.innerHTML = html;
-
- //draw
- let c = document.getElementById("jspsych-canvas-stimulus")
- trial.stimulus(c)
-
- // start time
- var start_time = performance.now();
-
- // add event listeners to buttons
- for (var i = 0; i < trial.choices.length; i++) {
- display_element.querySelector('#jspsych-canvas-button-response-button-' + i).addEventListener('click', function (e) {
- var choice = e.currentTarget.getAttribute('data-choice'); // don't use dataset for jsdom compatibility
- after_response(choice);
- });
- }
-
- // store response
- var response = {
- rt: null,
- button: null
- };
-
- // function to handle responses by the subject
- function after_response(choice) {
-
- // measure rt
- var end_time = performance.now();
- var rt = end_time - start_time;
- response.button = parseInt(choice);
- response.rt = rt;
-
- // after a valid response, the stimulus will have the CSS class 'responded'
- // which can be used to provide visual feedback that a response was recorded
- display_element.querySelector('#jspsych-canvas-button-response-stimulus').className += ' responded';
-
- // disable all the buttons after a response
- var btns = document.querySelectorAll('.jspsych-canvas-button-response-button button');
- for (var i = 0; i < btns.length; i++) {
- //btns[i].removeEventListener('click');
- btns[i].setAttribute('disabled', 'disabled');
- }
-
- if (trial.response_ends_trial) {
- end_trial();
- }
- };
-
- // function to end trial when it is time
- function end_trial() {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // gather the data to store for the trial
- var trial_data = {
- rt: response.rt,
- response: response.button
- };
-
- // clear the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- };
-
- // hide image if timing is set
- if (trial.stimulus_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function () {
- display_element.querySelector('#jspsych-canvas-button-response-stimulus').style.visibility = 'hidden';
- }, trial.stimulus_duration);
- }
-
- // end trial if time limit is set
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function () {
- end_trial();
- }, trial.trial_duration);
- }
-
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-canvas-keyboard-response.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-canvas-keyboard-response.js
deleted file mode 100644
index 46843505..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-canvas-keyboard-response.js
+++ /dev/null
@@ -1,155 +0,0 @@
-/**
- * jspsych-canvas-keyboard-response
- * Chris Jungerius (modified from Josh de Leeuw)
- *
- * a jsPsych plugin for displaying a canvas stimulus and getting a keyboard response
- *
- * documentation: docs.jspsych.org
- *
- **/
-
-
-jsPsych.plugins["canvas-keyboard-response"] = (function () {
-
- var plugin = {};
-
- plugin.info = {
- name: 'canvas-keyboard-response',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.FUNCTION,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The drawing function to apply to the canvas. Should take the canvas object as argument.'
- },
- choices: {
- type: jsPsych.plugins.parameterType.KEY,
- array: true,
- pretty_name: 'Choices',
- default: jsPsych.ALL_KEYS,
- description: 'The keys the subject is allowed to press to respond to the stimulus.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the stimulus.'
- },
- stimulus_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Stimulus duration',
- default: null,
- description: 'How long to hide the stimulus.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show trial before it ends.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, trial will end when subject makes a response.'
- },
- canvas_size: {
- type: jsPsych.plugins.parameterType.INT,
- array: true,
- pretty_name: 'Canvas size',
- default: [500, 500],
- description: 'Array containing the height (first value) and width (second value) of the canvas element.'
- }
-
- }
- }
-
- plugin.trial = function (display_element, trial) {
-
- var new_html = '
' + '' + '
';
- // add prompt
- if (trial.prompt !== null) {
- new_html += trial.prompt;
- }
-
- // draw
- display_element.innerHTML = new_html;
- let c = document.getElementById("jspsych-canvas-stimulus")
- trial.stimulus(c)
- // store response
- var response = {
- rt: null,
- key: null
- };
-
- // function to end trial when it is time
- var end_trial = function () {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // kill keyboard listeners
- if (typeof keyboardListener !== 'undefined') {
- jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
- }
-
- // gather the data to store for the trial
- var trial_data = {
- rt: response.rt,
- response: response.key
- };
-
- // clear the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- };
-
- // function to handle responses by the subject
- var after_response = function (info) {
-
- // after a valid response, the stimulus will have the CSS class 'responded'
- // which can be used to provide visual feedback that a response was recorded
- display_element.querySelector('#jspsych-canvas-keyboard-response-stimulus').className += ' responded';
-
- // only record the first response
- if (response.key == null) {
- response = info;
- }
-
- if (trial.response_ends_trial) {
- end_trial();
- }
- };
-
- // start the response listener
- if (trial.choices != jsPsych.NO_KEYS) {
- var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: trial.choices,
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
- }
-
- // hide stimulus if stimulus_duration is set
- if (trial.stimulus_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function () {
- display_element.querySelector('#jspsych-canvas-keyboard-response-stimulus').style.visibility = 'hidden';
- }, trial.stimulus_duration);
- }
-
- // end trial if trial_duration is set
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function () {
- end_trial();
- }, trial.trial_duration);
- }
-
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-canvas-slider-response.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-canvas-slider-response.js
deleted file mode 100644
index ca2f7087..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-canvas-slider-response.js
+++ /dev/null
@@ -1,207 +0,0 @@
-/**
- * jspsych-canvas-slider-response
- * Chris Jungerius (modified from Josh de Leeuw)
- *
- * a jsPsych plugin for displaying a canvas stimulus and getting a slider response
- *
- * documentation: docs.jspsych.org
- *
- */
-
-
-jsPsych.plugins['canvas-slider-response'] = (function () {
-
- var plugin = {};
-
- plugin.info = {
- name: 'canvas-slider-response',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.FUNCTION,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The drawing function to apply to the canvas. Should take the canvas object as argument.'
- },
- min: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Min slider',
- default: 0,
- description: 'Sets the minimum value of the slider.'
- },
- max: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Max slider',
- default: 100,
- description: 'Sets the maximum value of the slider',
- },
- slider_start: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Slider starting value',
- default: 50,
- description: 'Sets the starting value of the slider',
- },
- step: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Step',
- default: 1,
- description: 'Sets the step of the slider'
- },
- labels: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'Labels',
- default: [],
- array: true,
- description: 'Labels of the slider.',
- },
- slider_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Slider width',
- default: null,
- description: 'Width of the slider in pixels.'
- },
- button_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button label',
- default: 'Continue',
- array: false,
- description: 'Label of the button to advance.'
- },
- require_movement: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Require movement',
- default: false,
- description: 'If true, the participant will have to move the slider before continuing.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the slider.'
- },
- stimulus_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Stimulus duration',
- default: null,
- description: 'How long to hide the stimulus.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show the trial.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, trial will end when user makes a response.'
- },
- canvas_size: {
- type: jsPsych.plugins.parameterType.INT,
- array: true,
- pretty_name: 'Canvas size',
- default: [500, 500],
- description: 'Array containing the height (first value) and width (second value) of the canvas element.'
- }
-
- }
- }
-
- plugin.trial = function (display_element, trial) {
-
- var html = '
';
- html += '
' + '' + '
';
- html += '
';
- html += '';
- html += '
'
- for (var j = 0; j < trial.labels.length; j++) {
- var width = 100 / (trial.labels.length - 1);
- var left_offset = (j * (100 / (trial.labels.length - 1))) - (width / 2);
- html += '
';
- html += '' + trial.labels[j] + '';
- html += '
'
- }
- html += '
';
- html += '
';
- html += '
';
-
- if (trial.prompt !== null) {
- html += trial.prompt;
- }
-
- // add submit button
- html += '';
-
- display_element.innerHTML = html;
-
- // draw
- let c = document.getElementById("jspsych-canvas-stimulus")
- trial.stimulus(c)
-
- var response = {
- rt: null,
- response: null
- };
-
- if (trial.require_movement) {
- display_element.querySelector('#jspsych-canvas-slider-response-response').addEventListener('click', function () {
- display_element.querySelector('#jspsych-canvas-slider-response-next').disabled = false;
- })
- }
-
- display_element.querySelector('#jspsych-canvas-slider-response-next').addEventListener('click', function () {
- // measure response time
- var endTime = performance.now();
- response.rt = endTime - startTime;
- response.response = display_element.querySelector('#jspsych-canvas-slider-response-response').valueAsNumber;
-
- if (trial.response_ends_trial) {
- end_trial();
- } else {
- display_element.querySelector('#jspsych-canvas-slider-response-next').disabled = true;
- }
-
- });
-
- function end_trial() {
-
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // save data
- var trialdata = {
- rt: response.rt,
- response: response.response,
- slider_start: trial.slider_start
- };
-
- display_element.innerHTML = '';
-
- // next trial
- jsPsych.finishTrial(trialdata);
- }
-
- if (trial.stimulus_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function () {
- display_element.querySelector('#jspsych-canvas-slider-response-stimulus').style.visibility = 'hidden';
- }, trial.stimulus_duration);
- }
-
- // end trial if trial_duration is set
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function () {
- end_trial();
- }, trial.trial_duration);
- }
-
- var startTime = performance.now();
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-categorize-animation.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-categorize-animation.js
deleted file mode 100644
index e1af93d5..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-categorize-animation.js
+++ /dev/null
@@ -1,266 +0,0 @@
-/**
- * jspsych plugin for categorization trials with feedback and animated stimuli
- * Josh de Leeuw
- *
- * documentation: docs.jspsych.org
- **/
-
-
-jsPsych.plugins["categorize-animation"] = (function() {
-
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('categorize-animation', 'stimuli', 'image');
-
- plugin.info = {
- name: 'categorize-animation',
- description: '',
- parameters: {
- stimuli: {
- type: jsPsych.plugins.parameterType.IMAGE,
- pretty_name: 'Stimuli',
- default: undefined,
- description: 'Array of paths to image files.'
- },
- key_answer: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Key answer',
- default: undefined,
- description: 'The key to indicate correct response'
- },
- choices: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Choices',
- default: jsPsych.ALL_KEYS,
- array: true,
- description: 'The keys subject is allowed to press to respond to stimuli.'
- },
- text_answer: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Text answer',
- default: null,
- description: 'Text to describe correct answer.'
- },
- correct_text: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Correct text',
- default: 'Correct.',
- description: 'String to show when subject gives correct answer'
- },
- incorrect_text: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Incorrect text',
- default: 'Wrong.',
- description: 'String to show when subject gives incorrect answer.'
- },
- frame_time: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Frame time',
- default: 500,
- description: 'Duration to display each image.'
- },
- sequence_reps: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Sequence repetitions',
- default: 1,
- description: 'How many times to display entire sequence.'
- },
- allow_response_before_complete: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Allow response before complete',
- default: false,
- description: 'If true, subject can response before the animation sequence finishes'
- },
- feedback_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Feedback duration',
- default: 2000,
- description: 'How long to show feedback'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the stimulus.'
- },
- render_on_canvas: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Render on canvas',
- default: true,
- description: 'If true, the images will be drawn onto a canvas element (prevents blank screen between consecutive images in some browsers).'+
- 'If false, the image will be shown via an img element.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- var animate_frame = -1;
- var reps = 0;
-
- var showAnimation = true;
-
- var responded = false;
- var timeoutSet = false;
- var correct;
-
- if (trial.render_on_canvas) {
- // first clear the display element (because the render_on_canvas method appends to display_element instead of overwriting it with .innerHTML)
- if (display_element.hasChildNodes()) {
- // can't loop through child list because the list will be modified by .removeChild()
- while (display_element.firstChild) {
- display_element.removeChild(display_element.firstChild);
- }
- }
- var canvas = document.createElement("canvas");
- canvas.id = "jspsych-categorize-animation-stimulus";
- canvas.style.margin = 0;
- canvas.style.padding = 0;
- display_element.insertBefore(canvas, null);
- var ctx = canvas.getContext("2d");
- if (trial.prompt !== null) {
- var prompt_div = document.createElement("div");
- prompt_div.id = "jspsych-categorize-animation-prompt";
- prompt_div.style.visibility = "hidden";
- prompt_div.innerHTML = trial.prompt;
- display_element.insertBefore(prompt_div, canvas.nextElementSibling);
- }
- var feedback_div = document.createElement("div");
- display_element.insertBefore(feedback_div, display_element.nextElementSibling);
- }
-
- // show animation
- var animate_interval = setInterval(function() {
- if (!trial.render_on_canvas) {
- display_element.innerHTML = ''; // clear everything
- }
- animate_frame++;
- if (animate_frame == trial.stimuli.length) {
- animate_frame = 0;
- reps++;
- // check if reps complete //
- if (trial.sequence_reps != -1 && reps >= trial.sequence_reps) {
- // done with animation
- showAnimation = false;
- }
- }
-
- if (showAnimation) {
- if (trial.render_on_canvas) {
- display_element.querySelector('#jspsych-categorize-animation-stimulus').style.visibility = 'visible';
- var img = new Image();
- img.src = trial.stimuli[animate_frame];
- canvas.height = img.naturalHeight;
- canvas.width = img.naturalWidth;
- ctx.drawImage(img,0,0);
- } else {
- display_element.innerHTML += '';
- }
- }
-
- if (!responded && trial.allow_response_before_complete) {
- // in here if the user can respond before the animation is done
- if (trial.prompt !== null) {
- if (trial.render_on_canvas) {
- prompt_div.style.visibility = "visible";
- } else {
- display_element.innerHTML += trial.prompt;
- }
- }
- if (trial.render_on_canvas) {
- if (!showAnimation) {
- canvas.remove();
- }
- }
- } else if (!responded) {
- // in here if the user has to wait to respond until animation is done.
- // if this is the case, don't show the prompt until the animation is over.
- if (!showAnimation) {
- if (trial.prompt !== null) {
- if (trial.render_on_canvas) {
- prompt_div.style.visibility = "visible";
- } else {
- display_element.innerHTML += trial.prompt;
- }
- }
- if (trial.render_on_canvas) {
- canvas.remove();
- }
- }
- } else {
- // user has responded if we get here.
-
- // show feedback
- var feedback_text = "";
- if (correct) {
- feedback_text = trial.correct_text.replace("%ANS%", trial.text_answer);
- } else {
- feedback_text = trial.incorrect_text.replace("%ANS%", trial.text_answer);
- }
- if (trial.render_on_canvas) {
- if (trial.prompt !== null) {
- prompt_div.remove();
- }
- feedback_div.innerHTML = feedback_text;
- } else {
- display_element.innerHTML += feedback_text;
- }
-
- // set timeout to clear feedback
- if (!timeoutSet) {
- timeoutSet = true;
- jsPsych.pluginAPI.setTimeout(function() {
- endTrial();
- }, trial.feedback_duration);
- }
- }
-
-
- }, trial.frame_time);
-
-
- var keyboard_listener;
- var trial_data = {};
-
- var after_response = function(info) {
- // ignore the response if animation is playing and subject
- // not allowed to respond before it is complete
- if (!trial.allow_response_before_complete && showAnimation) {
- return false;
- }
-
- correct = false;
- if (jsPsych.pluginAPI.compareKeys(trial.key_answer, info.key)) {
- correct = true;
- }
-
- responded = true;
-
- trial_data = {
- stimulus: trial.stimuli,
- rt: info.rt,
- correct: correct,
- response: info.key
- };
-
- jsPsych.pluginAPI.cancelKeyboardResponse(keyboard_listener);
-
- }
-
- keyboard_listener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: trial.choices,
- rt_method: 'performance',
- persist: true,
- allow_held_key: false
- });
-
- function endTrial() {
- clearInterval(animate_interval); // stop animation!
- display_element.innerHTML = ''; // clear everything
- jsPsych.finishTrial(trial_data);
- }
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-categorize-html.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-categorize-html.js
deleted file mode 100644
index 3b3a02be..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-categorize-html.js
+++ /dev/null
@@ -1,220 +0,0 @@
-/**
- * jspsych plugin for categorization trials with feedback
- * Josh de Leeuw
- *
- * documentation: docs.jspsych.org
- **/
-
-
-jsPsych.plugins['categorize-html'] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'categorize-html',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The HTML content to be displayed.'
- },
- key_answer: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Key answer',
- default: undefined,
- description: 'The key to indicate the correct response.'
- },
- choices: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Choices',
- default: jsPsych.ALL_KEYS,
- array: true,
- description: 'The keys the subject is allowed to press to respond to the stimulus.'
- },
- text_answer: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Text answer',
- default: null,
- description: 'Label that is associated with the correct answer.'
- },
- correct_text: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Correct text',
- default: "
Correct
",
- description: 'String to show when correct answer is given.'
- },
- incorrect_text: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Incorrect text',
- default: "
Incorrect
",
- description: 'String to show when incorrect answer is given.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the stimulus.'
- },
- force_correct_button_press: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Force correct button press',
- default: false,
- description: 'If set to true, then the subject must press the correct response key after feedback in order to advance to next trial.'
- },
- show_stim_with_feedback: {
- type: jsPsych.plugins.parameterType.BOOL,
- default: true,
- no_function: false,
- description: ''
- },
- show_feedback_on_timeout: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Show feedback on timeout',
- default: false,
- description: 'If true, stimulus will be shown during feedback. If false, only the text feedback will be displayed during feedback.'
- },
- timeout_message: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Timeout message',
- default: "
Please respond faster.
",
- description: 'The message displayed on a timeout non-response.'
- },
- stimulus_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Stimulus duration',
- default: null,
- description: 'How long to hide stimulus.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show trial'
- },
- feedback_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Feedback duration',
- default: 2000,
- description: 'How long to show feedback.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- display_element.innerHTML = '
'+trial.stimulus+'
';
-
- // hide image after time if the timing parameter is set
- if (trial.stimulus_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- display_element.querySelector('#jspsych-categorize-html-stimulus').style.visibility = 'hidden';
- }, trial.stimulus_duration);
- }
-
- // if prompt is set, show prompt
- if (trial.prompt !== null) {
- display_element.innerHTML += trial.prompt;
- }
-
- var trial_data = {};
-
- // create response function
- var after_response = function(info) {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // clear keyboard listener
- jsPsych.pluginAPI.cancelAllKeyboardResponses();
-
- var correct = false;
- if (jsPsych.pluginAPI.compareKeys(trial.key_answer,info.key)) {
- correct = true;
- }
-
- // save data
- trial_data = {
- rt: info.rt,
- correct: correct,
- stimulus: trial.stimulus,
- response: info.key
- };
-
- display_element.innerHTML = '';
-
- var timeout = info.rt == null;
- doFeedback(correct, timeout);
- }
-
- jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: trial.choices,
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
-
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- after_response({
- key: null,
- rt: null
- });
- }, trial.trial_duration);
- }
-
- function doFeedback(correct, timeout) {
-
- if (timeout && !trial.show_feedback_on_timeout) {
- display_element.innerHTML += trial.timeout_message;
- } else {
- // show image during feedback if flag is set
- if (trial.show_stim_with_feedback) {
- display_element.innerHTML = '
'+trial.stimulus+'
';
- }
-
- // substitute answer in feedback string.
- var atext = "";
- if (correct) {
- atext = trial.correct_text.replace("%ANS%", trial.text_answer);
- } else {
- atext = trial.incorrect_text.replace("%ANS%", trial.text_answer);
- }
-
- // show the feedback
- display_element.innerHTML += atext;
- }
- // check if force correct button press is set
- if (trial.force_correct_button_press && correct === false && ((timeout && trial.show_feedback_on_timeout) || !timeout)) {
-
- var after_forced_response = function(info) {
- endTrial();
- }
-
- jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_forced_response,
- valid_responses: [trial.key_answer],
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
-
- } else {
- jsPsych.pluginAPI.setTimeout(function() {
- endTrial();
- }, trial.feedback_duration);
- }
-
- }
-
- function endTrial() {
- display_element.innerHTML = '';
- jsPsych.finishTrial(trial_data);
- }
-
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-categorize-image.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-categorize-image.js
deleted file mode 100644
index 16c71d8a..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-categorize-image.js
+++ /dev/null
@@ -1,222 +0,0 @@
-/**
- * jspsych plugin for categorization trials with feedback
- * Josh de Leeuw
- *
- * documentation: docs.jspsych.org
- **/
-
-
-jsPsych.plugins['categorize-image'] = (function() {
-
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('categorize-image', 'stimulus', 'image');
-
- plugin.info = {
- name: 'categorize-image',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.IMAGE,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The image content to be displayed.'
- },
- key_answer: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Key answer',
- default: undefined,
- description: 'The key to indicate the correct response.'
- },
- choices: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Choices',
- default: jsPsych.ALL_KEYS,
- array: true,
- description: 'The keys the subject is allowed to press to respond to the stimulus.'
- },
- text_answer: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Text answer',
- default: null,
- description: 'Label that is associated with the correct answer.'
- },
- correct_text: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Correct text',
- default: "
Correct
",
- description: 'String to show when correct answer is given.'
- },
- incorrect_text: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Incorrect text',
- default: "
Incorrect
",
- description: 'String to show when incorrect answer is given.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the stimulus.'
- },
- force_correct_button_press: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Force correct button press',
- default: false,
- description: 'If set to true, then the subject must press the correct response key after feedback in order to advance to next trial.'
- },
- show_stim_with_feedback: {
- type: jsPsych.plugins.parameterType.BOOL,
- default: true,
- no_function: false,
- description: ''
- },
- show_feedback_on_timeout: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Show feedback on timeout',
- default: false,
- description: 'If true, stimulus will be shown during feedback. If false, only the text feedback will be displayed during feedback.'
- },
- timeout_message: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Timeout message',
- default: "
Please respond faster.
",
- description: 'The message displayed on a timeout non-response.'
- },
- stimulus_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Stimulus duration',
- default: null,
- description: 'How long to hide stimulus.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show trial'
- },
- feedback_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Feedback duration',
- default: 2000,
- description: 'How long to show feedback.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- display_element.innerHTML = '';
-
- // hide image after time if the timing parameter is set
- if (trial.stimulus_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- display_element.querySelector('#jspsych-categorize-image-stimulus').style.visibility = 'hidden';
- }, trial.stimulus_duration);
- }
-
- // if prompt is set, show prompt
- if (trial.prompt !== null) {
- display_element.innerHTML += trial.prompt;
- }
-
- var trial_data = {};
-
- // create response function
- var after_response = function(info) {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // clear keyboard listener
- jsPsych.pluginAPI.cancelAllKeyboardResponses();
-
- var correct = false;
- if (jsPsych.pluginAPI.compareKeys(trial.key_answer, info.key)) {
- correct = true;
- }
-
- // save data
- trial_data = {
- rt: info.rt,
- correct: correct,
- stimulus: trial.stimulus,
- response: info.key
- };
-
- display_element.innerHTML = '';
-
- var timeout = info.rt == null;
- doFeedback(correct, timeout);
- }
-
- jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: trial.choices,
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
-
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- after_response({
- key: null,
- rt: null
- });
- }, trial.trial_duration);
- }
-
- function doFeedback(correct, timeout) {
-
- if (timeout && !trial.show_feedback_on_timeout) {
- display_element.innerHTML += trial.timeout_message;
- } else {
- // show image during feedback if flag is set
- if (trial.show_stim_with_feedback) {
- display_element.innerHTML = '';
- }
-
- // substitute answer in feedback string.
- var atext = "";
- if (correct) {
- atext = trial.correct_text.replace("%ANS%", trial.text_answer);
- } else {
- atext = trial.incorrect_text.replace("%ANS%", trial.text_answer);
- }
-
- // show the feedback
- display_element.innerHTML += atext;
- }
- // check if force correct button press is set
- if (trial.force_correct_button_press && correct === false && ((timeout && trial.show_feedback_on_timeout) || !timeout)) {
-
- var after_forced_response = function(info) {
- endTrial();
- }
-
- jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_forced_response,
- valid_responses: [trial.key_answer],
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
-
- } else {
- jsPsych.pluginAPI.setTimeout(function() {
- endTrial();
- }, trial.feedback_duration);
- }
-
- }
-
- function endTrial() {
- display_element.innerHTML = '';
- jsPsych.finishTrial(trial_data);
- }
-
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-cloze.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-cloze.js
deleted file mode 100644
index 890c5394..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-cloze.js
+++ /dev/null
@@ -1,112 +0,0 @@
-/**
- * jspsych-cloze
- * Philipp Sprengholz
- *
- * Plugin for displaying a cloze test and checking participants answers against a correct solution.
- *
- * documentation: docs.jspsych.org
- **/
-
-jsPsych.plugins['cloze'] = (function () {
-
- var plugin = {};
-
- plugin.info = {
- name: 'cloze',
- description: '',
- parameters: {
- text: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Cloze text',
- default: undefined,
- description: 'The cloze text to be displayed. Blanks are indicated by %% signs and automatically replaced by input fields. If there is a correct answer you want the system to check against, it must be typed between the two percentage signs (i.e. %solution%).'
- },
- button_text: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button text',
- default: 'OK',
- description: 'Text of the button participants have to press for finishing the cloze test.'
- },
- check_answers: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Check answers',
- default: false,
- description: 'Boolean value indicating if the answers given by participants should be compared against a correct solution given in the text (between % signs) after the button was clicked.'
- },
- mistake_fn: {
- type: jsPsych.plugins.parameterType.FUNCTION,
- pretty_name: 'Mistake function',
- default: function () {},
- description: 'Function called if check_answers is set to TRUE and there is a difference between the participants answers and the correct solution provided in the text.'
- }
- }
- };
-
- plugin.trial = function (display_element, trial) {
-
- var html = '
';
- var elements = trial.text.split('%');
- var solutions = [];
-
- for (var i=0; i';
- }
- }
- html += '
';
-
- display_element.innerHTML = html;
-
- var check = function() {
-
- var answers = [];
- var answers_correct = true;
-
- for (var i=0; i';
- display_element.querySelector('#finish_cloze_button').addEventListener('click', check);
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-external-html.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-external-html.js
deleted file mode 100644
index 83a9a051..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-external-html.js
+++ /dev/null
@@ -1,112 +0,0 @@
-/** (July 2012, Erik Weitnauer)
-The html-plugin will load and display an external html pages. To proceed to the next, the
-user might either press a button on the page or a specific key. Afterwards, the page get hidden and
-the plugin will wait of a specified time before it proceeds.
-
-documentation: docs.jspsych.org
-*/
-
-jsPsych.plugins['external-html'] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'external-html',
- description: '',
- parameters: {
- url: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'URL',
- default: undefined,
- description: 'The url of the external html page'
- },
- cont_key: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Continue key',
- default: null,
- description: 'The key to continue to the next page.'
- },
- cont_btn: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Continue button',
- default: null,
- description: 'The button to continue to the next page.'
- },
- check_fn: {
- type: jsPsych.plugins.parameterType.FUNCTION,
- pretty_name: 'Check function',
- default: function() { return true; },
- description: ''
- },
- force_refresh: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Force refresh',
- default: false,
- description: 'Refresh page.'
- },
- // if execute_Script == true, then all javascript code on the external page
- // will be executed in the plugin site within your jsPsych test
- execute_script: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Execute scripts',
- default: false,
- description: 'If true, JS scripts on the external html file will be executed.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- var url = trial.url;
- if (trial.force_refresh) {
- url = trial.url + "?t=" + performance.now();
- }
-
- load(display_element, url, function() {
- var t0 = performance.now();
- var finish = function() {
- if (trial.check_fn && !trial.check_fn(display_element)) { return };
- if (trial.cont_key) { display_element.removeEventListener('keydown', key_listener); }
- var trial_data = {
- rt: performance.now() - t0,
- url: trial.url
- };
- display_element.innerHTML = '';
- jsPsych.finishTrial(trial_data);
- };
-
- // by default, scripts on the external page are not executed with XMLHttpRequest().
- // To activate their content through DOM manipulation, we need to relocate all script tags
- if (trial.execute_script) {
- for (const scriptElement of display_element.getElementsByTagName("script")) {
- const relocatedScript = document.createElement("script");
- relocatedScript.text = scriptElement.text;
- scriptElement.parentNode.replaceChild(relocatedScript, scriptElement);
- };
- }
-
- if (trial.cont_btn) { display_element.querySelector('#'+trial.cont_btn).addEventListener('click', finish); }
- if (trial.cont_key) {
- var key_listener = function(e) {
- if (jsPsych.pluginAPI.compareKeys(e.key,trial.cont_key)) finish();
- };
- display_element.addEventListener('keydown', key_listener);
- }
- });
- };
-
- // helper to load via XMLHttpRequest
- function load(element, file, callback){
- var xmlhttp = new XMLHttpRequest();
- xmlhttp.open("GET", file, true);
- xmlhttp.onload = function(){
- if(xmlhttp.status == 200 || xmlhttp.status == 0){ //Check if loaded
- element.innerHTML = xmlhttp.responseText;
- callback();
- }
- }
- xmlhttp.send();
- }
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-free-sort.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-free-sort.js
deleted file mode 100644
index accc1896..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-free-sort.js
+++ /dev/null
@@ -1,478 +0,0 @@
-/**
- * jspsych-free-sort
- * plugin for drag-and-drop sorting of a collection of images
- * Josh de Leeuw
- *
- * documentation: docs.jspsych.org
- */
-
-
-jsPsych.plugins['free-sort'] = (function() {
-
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('free-sort', 'stimuli', 'image');
-
- plugin.info = {
- name: 'free-sort',
- description: '',
- parameters: {
- stimuli: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Stimuli',
- default: undefined,
- array: true,
- description: 'items to be displayed.'
- },
- stim_height: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Stimulus height',
- default: 100,
- description: 'Height of items in pixels.'
- },
- stim_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Stimulus width',
- default: 100,
- description: 'Width of items in pixels'
- },
- scale_factor: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: 'Stimulus scaling factor',
- default: 1.5,
- description: 'How much larger to make the stimulus while moving (1 = no scaling)'
- },
- sort_area_height: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Sort area height',
- default: 700,
- description: 'The height in pixels of the container that subjects can move the stimuli in.'
- },
- sort_area_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Sort area width',
- default: 700,
- description: 'The width in pixels of the container that subjects can move the stimuli in.'
- },
- sort_area_shape: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Sort area shape',
- options: ['square','ellipse'],
- default: 'ellipse',
- description: 'The shape of the sorting area'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: '',
- description: 'It can be used to provide a reminder about the action the subject is supposed to take.'
- },
- prompt_location: {
- type: jsPsych.plugins.parameterType.SELECT,
- pretty_name: 'Prompt location',
- options: ['above','below'],
- default: 'above',
- description: 'Indicates whether to show prompt "above" or "below" the sorting area.'
- },
- button_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button label',
- default: 'Continue',
- description: 'The text that appears on the button to continue to the next trial.'
- },
- change_border_background_color: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Change border background color',
- default: true,
- description: 'If true, the sort area border color will change while items are being moved in and out of '+
- 'the sort area, and the background color will change once all items have been moved into the '+
- 'sort area. If false, the border will remain black and the background will remain white throughout the trial.'
- },
- border_color_in: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Border color - in',
- default: '#a1d99b',
- description: 'If change_border_background_color is true, the sort area border will change to this color '+
- 'when an item is being moved into the sort area, and the background will change to this color '+
- 'when all of the items have been moved into the sort area.'
- },
- border_color_out: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Border color - out',
- default: '#fc9272',
- description: 'If change_border_background_color is true, this will be the color of the sort area border '+
- 'when there are one or more items that still need to be moved into the sort area.'
- },
- border_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Border width',
- default: null,
- description: 'The width in pixels of the border around the sort area. If null, the border width '+
- 'defaults to 3% of the sort area height.'
- },
- counter_text_unfinished: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Counter text unfinished',
- default: 'You still need to place %n% item%s% inside the sort area.',
- description: 'Text to display when there are one or more items that still need to be placed in the sort area. '+
- 'If "%n%" is included in the string, it will be replaced with the number of items that still need to be moved inside. '+
- 'If "%s%" is included in the string, a "s" will be included when the number of items remaining is greater than one.'
- },
- counter_text_finished: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Counter text finished',
- default: 'All items placed. Feel free to reposition items if necessary.',
- description: 'Text that will take the place of the counter_text_unfinished text when all items have been moved inside the sort area.'
- },
- stim_starts_inside: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Stim starts inside',
- default: false,
- description: 'If false, the images will be positioned to the left and right of the sort area when the trial loads. '+
- 'If true, the images will be positioned at random locations inside the sort area when the trial loads.'
- },
- column_spread_factor: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: 'column spread factor',
- default: 1,
- description: 'When the images appear outside the sort area, this determines the x-axis spread of the image columns. '+
- 'Default value is 1. Values less than 1 will compress the image columns along the x-axis, and values greater than 1 will spread them farther apart.'
- },
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- var start_time = performance.now();
-
- if (trial.change_border_background_color == false) {
- trial.border_color_out = "#000000";
- }
-
- if (trial.border_width == null) {
- trial.border_width = trial.sort_area_height*.03;
- }
-
- let html =
- '
';
-
- // another div for border
- html += ''
- } else {
- html += 'webkit-border-radius: 0%; moz-border-radius: 0%; border-radius: 0%">
'
- }
-
- // variable that has the prompt text and counter
- const html_text = '
' + trial.prompt +
- '
'+get_counter_text(trial.stimuli.length)+'
';
-
- // position prompt above or below
- if (trial.prompt_location == "below") {
- html += html_text
- } else {
- html = html_text + html
- }
- // add button
- html += '';
-
- display_element.innerHTML = html;
-
- // store initial location data
- let init_locations = [];
-
- if (!trial.stim_starts_inside) {
- // determine number of rows and colums, must be a even number
- let num_rows = Math.ceil(Math.sqrt(trial.stimuli.length))
- if ( num_rows % 2 != 0) {
- num_rows = num_rows + 1
- }
-
- // compute coords for left and right side of arena
- var r_coords = [];
- var l_coords = [];
- for (const x of make_arr(0, trial.sort_area_width - trial.stim_width, num_rows) ) {
- for (const y of make_arr(0, trial.sort_area_height - trial.stim_height, num_rows) ) {
- if ( x > ( (trial.sort_area_width - trial.stim_width) * .5 ) ) {
- //r_coords.push({ x:x, y:y } )
- r_coords.push({ x:x + (trial.sort_area_width) * (.5*trial.column_spread_factor) , y:y });
- } else {
- l_coords.push({ x:x - (trial.sort_area_width) * (.5*trial.column_spread_factor) , y:y });
- //l_coords.push({ x:x, y:y } )
- }
- }
- }
-
- // repeat coordinates until you have enough coords (may be obsolete)
- while ( ( r_coords.length + l_coords.length ) < trial.stimuli.length ) {
- r_coords = r_coords.concat(r_coords)
- l_coords = l_coords.concat(l_coords)
- }
- // reverse left coords, so that coords closest to arena is used first
- l_coords = l_coords.reverse()
-
- // shuffle stimuli, so that starting positions are random
- trial.stimuli = shuffle(trial.stimuli);
- }
-
- let inside = []
- for (let i = 0; i < trial.stimuli.length; i++) {
- var coords;
- if (trial.stim_starts_inside) {
- coords = random_coordinate(trial.sort_area_width - trial.stim_width, trial.sort_area_height - trial.stim_height);
- } else {
- if ( (i % 2) == 0 ) {
- coords = r_coords[Math.floor(i * .5)];
- } else {
- coords = l_coords[Math.floor(i * .5)];
- }
- }
-
- display_element.querySelector("#jspsych-free-sort-arena").innerHTML += ''+
- '';
-
- init_locations.push({
- src: trial.stimuli[i],
- x: coords.x,
- y: coords.y
- });
- if (trial.stim_starts_inside) {
- inside.push(true);
- } else {
- inside.push(false);
- }
- }
-
- // moves within a trial
- let moves = [];
-
- // are objects currently inside
- let cur_in = false
-
- // draggable items
- const draggables = display_element.querySelectorAll('.jspsych-free-sort-draggable');
-
- // button (will show when all items are inside) and border (will change color)
- const border = display_element.querySelector("#jspsych-free-sort-border")
- const button = display_element.querySelector('#jspsych-free-sort-done-btn')
-
- // when trial starts, modify text and border/background if all items are inside (stim_starts_inside: true)
- if (inside.some(Boolean) && trial.change_border_background_color) {
- border.style.borderColor = trial.border_color_in;
- }
- if (inside.every(Boolean)) {
- if (trial.change_border_background_color) {
- border.style.background = trial.border_color_in;
- }
- button.style.visibility = "visible";
- display_element.querySelector("#jspsych-free-sort-counter").innerHTML = trial.counter_text_finished;
- }
-
- let start_event_name = 'mousedown';
- let move_event_name = 'mousemove';
- let end_event_name = 'mouseup';
- if (typeof document.ontouchend !== 'undefined'){ // for touch devices
- start_event_name = 'touchstart'
- move_event_name = 'touchmove'
- end_event_name = 'touchend'
- }
-
- for(let i=0; i 1) {
- text_out += "s";
- }
- }
- }
- return text_out;
- }
- };
-
- // helper functions
-
- function shuffle(array) {
- // define three variables
- let cur_idx = array.length, tmp_val, rand_idx;
-
- // While there remain elements to shuffle...
- while (0 !== cur_idx) {
- // Pick a remaining element...
- rand_idx = Math.floor(Math.random() * cur_idx);
- cur_idx -= 1;
-
- // And swap it with the current element.
- tmp_val = array[cur_idx];
- array[cur_idx] = array[rand_idx];
- array[rand_idx] = tmp_val;
- }
- return array;
- }
-
- function make_arr(startValue, stopValue, cardinality) {
- const step = (stopValue - startValue) / (cardinality - 1);
- let arr = [];
- for (let i = 0; i < cardinality; i++) {
- arr.push(startValue + (step * i));
- }
- return arr;
- }
-
- function inside_ellipse(x, y, x0, y0, rx, ry, square=false) {
- const results = [];
- if (square) {
- result = ( Math.abs(x - x0) <= rx ) && ( Math.abs(y - y0) <= ry )
- } else {
- result = (( x - x0 ) * ( x - x0 )) * (ry * ry) + ((y - y0) * ( y - y0 )) * ( rx * rx ) <= ( (rx * rx) * (ry * ry) )
- }
- return result
- }
-
- function random_coordinate(max_width, max_height) {
- const rnd_x = Math.floor(Math.random() * (max_width - 1));
- const rnd_y = Math.floor(Math.random() * (max_height - 1));
- return {
- x: rnd_x,
- y: rnd_y
- };
- }
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-fullscreen.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-fullscreen.js
deleted file mode 100644
index c3d0bc1f..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-fullscreen.js
+++ /dev/null
@@ -1,106 +0,0 @@
-/* jspsych-fullscreen.js
- * Josh de Leeuw
- *
- * toggle fullscreen mode in the browser
- *
- */
-
-jsPsych.plugins.fullscreen = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'fullscreen',
- description: '',
- parameters: {
- fullscreen_mode: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Fullscreen mode',
- default: true,
- array: false,
- description: 'If true, experiment will enter fullscreen mode. If false, the browser will exit fullscreen mode.'
- },
- message: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Message',
- default: '
The experiment will switch to full screen mode when you press the button below
',
- array: false,
- description: 'HTML content to display above the button to enter fullscreen mode.'
- },
- button_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button label',
- default: 'Continue',
- array: false,
- description: 'The text that appears on the button to enter fullscreen.'
- },
- delay_after: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Delay after',
- default: 1000,
- array: false,
- description: 'The length of time to delay after entering fullscreen mode before ending the trial.'
- },
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- // check if keys are allowed in fullscreen mode
- var keyboardNotAllowed = typeof Element !== 'undefined' && 'ALLOW_KEYBOARD_INPUT' in Element;
- if (keyboardNotAllowed) {
- // This is Safari, and keyboard events will be disabled. Don't allow fullscreen here.
- // do something else?
- endTrial();
- } else {
- if(trial.fullscreen_mode){
- display_element.innerHTML = trial.message + '';
- var listener = display_element.querySelector('#jspsych-fullscreen-btn').addEventListener('click', function() {
- var element = document.documentElement;
- if (element.requestFullscreen) {
- element.requestFullscreen();
- } else if (element.mozRequestFullScreen) {
- element.mozRequestFullScreen();
- } else if (element.webkitRequestFullscreen) {
- element.webkitRequestFullscreen();
- } else if (element.msRequestFullscreen) {
- element.msRequestFullscreen();
- }
- endTrial();
- });
- } else {
- if ( document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement ) {
- if (document.exitFullscreen) {
- document.exitFullscreen();
- } else if (document.msExitFullscreen) {
- document.msExitFullscreen();
- } else if (document.mozCancelFullScreen) {
- document.mozCancelFullScreen();
- } else if (document.webkitExitFullscreen) {
- document.webkitExitFullscreen();
- }
- }
- endTrial();
- }
- }
-
- function endTrial() {
-
- display_element.innerHTML = '';
-
- jsPsych.pluginAPI.setTimeout(function(){
-
- var trial_data = {
- success: !keyboardNotAllowed
- };
-
- jsPsych.finishTrial(trial_data);
-
- }, trial.delay_after);
-
- }
-
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-html-button-response.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-html-button-response.js
deleted file mode 100644
index 24332891..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-html-button-response.js
+++ /dev/null
@@ -1,188 +0,0 @@
-/**
- * jspsych-html-button-response
- * Josh de Leeuw
- *
- * plugin for displaying a stimulus and getting a button response
- *
- * documentation: docs.jspsych.org
- *
- **/
-
-jsPsych.plugins["html-button-response"] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'html-button-response',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The HTML string to be displayed'
- },
- choices: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Choices',
- default: undefined,
- array: true,
- description: 'The labels for the buttons.'
- },
- button_html: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button HTML',
- default: '',
- array: true,
- description: 'The html of the button. Can create own style.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed under the button.'
- },
- stimulus_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Stimulus duration',
- default: null,
- description: 'How long to hide the stimulus.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show the trial.'
- },
- margin_vertical: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Margin vertical',
- default: '0px',
- description: 'The vertical margin of the button.'
- },
- margin_horizontal: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Margin horizontal',
- default: '8px',
- description: 'The horizontal margin of the button.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, then trial will end when user responds.'
- },
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- // display stimulus
- var html = '
'+trial.stimulus+'
';
-
- //display buttons
- var buttons = [];
- if (Array.isArray(trial.button_html)) {
- if (trial.button_html.length == trial.choices.length) {
- buttons = trial.button_html;
- } else {
- console.error('Error in html-button-response plugin. The length of the button_html array does not equal the length of the choices array');
- }
- } else {
- for (var i = 0; i < trial.choices.length; i++) {
- buttons.push(trial.button_html);
- }
- }
- html += '
';
- for (var i = 0; i < trial.choices.length; i++) {
- var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
- html += '
'+str+'
';
- }
- html += '
';
-
- //show prompt if there is one
- if (trial.prompt !== null) {
- html += trial.prompt;
- }
- display_element.innerHTML = html;
-
- // start time
- var start_time = performance.now();
-
- // add event listeners to buttons
- for (var i = 0; i < trial.choices.length; i++) {
- display_element.querySelector('#jspsych-html-button-response-button-' + i).addEventListener('click', function(e){
- var choice = e.currentTarget.getAttribute('data-choice'); // don't use dataset for jsdom compatibility
- after_response(choice);
- });
- }
-
- // store response
- var response = {
- rt: null,
- button: null
- };
-
- // function to handle responses by the subject
- function after_response(choice) {
-
- // measure rt
- var end_time = performance.now();
- var rt = end_time - start_time;
- response.button = parseInt(choice);
- response.rt = rt;
-
- // after a valid response, the stimulus will have the CSS class 'responded'
- // which can be used to provide visual feedback that a response was recorded
- display_element.querySelector('#jspsych-html-button-response-stimulus').className += ' responded';
-
- // disable all the buttons after a response
- var btns = document.querySelectorAll('.jspsych-html-button-response-button button');
- for(var i=0; i';
-
- // add prompt
- if(trial.prompt !== null){
- new_html += trial.prompt;
- }
-
- // draw
- display_element.innerHTML = new_html;
-
- // store response
- var response = {
- rt: null,
- key: null
- };
-
- // function to end trial when it is time
- var end_trial = function() {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // kill keyboard listeners
- if (typeof keyboardListener !== 'undefined') {
- jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
- }
-
- // gather the data to store for the trial
- var trial_data = {
- rt: response.rt,
- stimulus: trial.stimulus,
- response: response.key
- };
-
- // clear the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- };
-
- // function to handle responses by the subject
- var after_response = function(info) {
-
- // after a valid response, the stimulus will have the CSS class 'responded'
- // which can be used to provide visual feedback that a response was recorded
- display_element.querySelector('#jspsych-html-keyboard-response-stimulus').className += ' responded';
-
- // only record the first response
- if (response.key == null) {
- response = info;
- }
-
- if (trial.response_ends_trial) {
- end_trial();
- }
- };
-
- // start the response listener
- if (trial.choices != jsPsych.NO_KEYS) {
- var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: trial.choices,
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
- }
-
- // hide stimulus if stimulus_duration is set
- if (trial.stimulus_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- display_element.querySelector('#jspsych-html-keyboard-response-stimulus').style.visibility = 'hidden';
- }, trial.stimulus_duration);
- }
-
- // end trial if trial_duration is set
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- end_trial();
- }, trial.trial_duration);
- }
-
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-html-slider-response.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-html-slider-response.js
deleted file mode 100644
index bee229da..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-html-slider-response.js
+++ /dev/null
@@ -1,202 +0,0 @@
-/**
- * jspsych-html-slider-response
- * a jspsych plugin for free response survey questions
- *
- * Josh de Leeuw
- *
- * documentation: docs.jspsych.org
- *
- */
-
-
-jsPsych.plugins['html-slider-response'] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'html-slider-response',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The HTML string to be displayed'
- },
- min: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Min slider',
- default: 0,
- description: 'Sets the minimum value of the slider.'
- },
- max: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Max slider',
- default: 100,
- description: 'Sets the maximum value of the slider',
- },
- slider_start: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Slider starting value',
- default: 50,
- description: 'Sets the starting value of the slider',
- },
- step: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Step',
- default: 1,
- description: 'Sets the step of the slider'
- },
- labels: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name:'Labels',
- default: [],
- array: true,
- description: 'Labels of the slider.',
- },
- slider_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name:'Slider width',
- default: null,
- description: 'Width of the slider in pixels.'
- },
- button_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button label',
- default: 'Continue',
- array: false,
- description: 'Label of the button to advance.'
- },
- require_movement: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Require movement',
- default: false,
- description: 'If true, the participant will have to move the slider before continuing.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the slider.'
- },
- stimulus_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Stimulus duration',
- default: null,
- description: 'How long to hide the stimulus.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show the trial.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, trial will end when user makes a response.'
- },
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- // half of the thumb width value from jspsych.css, used to adjust the label positions
- var half_thumb_width = 7.5;
-
- var html = '
';
- html += '
' + trial.stimulus + '
';
- html += '
';
- html += '';
- html += '
'
- for(var j=0; j < trial.labels.length; j++){
- var label_width_perc = 100/(trial.labels.length-1);
- var percent_of_range = j * (100/(trial.labels.length - 1));
- var percent_dist_from_center = ((percent_of_range-50)/50)*100;
- var offset = (percent_dist_from_center * half_thumb_width)/100;
- html += '
';
- html += ''+trial.labels[j]+'';
- html += '
'
- }
- html += '
';
- html += '
';
- html += '
';
-
- if (trial.prompt !== null){
- html += trial.prompt;
- }
-
- // add submit button
- html += '';
-
- display_element.innerHTML = html;
-
- var response = {
- rt: null,
- response: null
- };
-
- if(trial.require_movement){
- display_element.querySelector('#jspsych-html-slider-response-response').addEventListener('click', function(){
- display_element.querySelector('#jspsych-html-slider-response-next').disabled = false;
- });
- }
-
- display_element.querySelector('#jspsych-html-slider-response-next').addEventListener('click', function() {
- // measure response time
- var endTime = performance.now();
- response.rt = endTime - startTime;
- response.response = display_element.querySelector('#jspsych-html-slider-response-response').valueAsNumber;
-
- if(trial.response_ends_trial){
- end_trial();
- } else {
- display_element.querySelector('#jspsych-html-slider-response-next').disabled = true;
- }
-
- });
-
- function end_trial(){
-
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // save data
- var trialdata = {
- rt: response.rt,
- stimulus: trial.stimulus,
- slider_start: trial.slider_start,
- response: response.response
- };
-
- display_element.innerHTML = '';
-
- // next trial
- jsPsych.finishTrial(trialdata);
- }
-
- if (trial.stimulus_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- display_element.querySelector('#jspsych-html-slider-response-stimulus').style.visibility = 'hidden';
- }, trial.stimulus_duration);
- }
-
- // end trial if trial_duration is set
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- end_trial();
- }, trial.trial_duration);
- }
-
- var startTime = performance.now();
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-iat-html.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-iat-html.js
deleted file mode 100644
index 513bd312..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-iat-html.js
+++ /dev/null
@@ -1,284 +0,0 @@
-/**
- * jspsych-iat
- * Kristin Diep
- *
- * plugin for running an IAT (Implicit Association Test) with an HTML-formatted stimulus
- *
- * documentation: docs.jspsych.org
- *
- **/
-
-
- jsPsych.plugins['iat-html'] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'iat-html',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The HTML string to be displayed.'
- },
- left_category_key: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Left category key',
- default: 'e',
- description: 'Key press that is associated with the left category label.'
- },
- right_category_key: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Right category key',
- default: 'i',
- description: 'Key press that is associated with the right category label.'
- },
- left_category_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Left category label',
- array: true,
- default: ['left'],
- description: 'The label that is associated with the stimulus. Aligned to the left side of page.'
- },
- right_category_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Right category label',
- array: true,
- default: ['right'],
- description: 'The label that is associated with the stimulus. Aligned to the right side of the page.'
- },
- key_to_move_forward: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Key to move forward',
- array: true,
- default: jsPsych.ALL_KEYS,
- description: 'The keys that allow the user to advance to the next trial if their key press was incorrect.'
- },
- display_feedback: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Display feedback',
- default: false,
- description: 'If true, then html when wrong will be displayed when user makes an incorrect key press.'
- },
- html_when_wrong: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'HTML when wrong',
- default: 'X',
- description: 'The HTML to display when a user presses the wrong key.'
- },
- bottom_instructions: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'Bottom instructions',
- default: '
If you press the wrong key, a red X will appear. Press any key to continue.
',
- description: 'Instructions shown at the bottom of the page.'
- },
- force_correct_key_press: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Force correct key press',
- default: false,
- description: 'If true, in order to advance to the next trial after a wrong key press the user will be forced to press the correct key.'
- },
- stim_key_association: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'Stimulus key association',
- options: ['left', 'right'],
- default: undefined,
- description: 'Stimulus will be associated with either "left" or "right".'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, trial will end when user makes a response.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show the trial.'
- },
- }
- }
-
-
- plugin.trial = function(display_element, trial) {
-
- var html_str = "";
-
- html_str += "
";
-
- display_element.innerHTML = html_str;
-
-
- // store response
- var response = {
- rt: null,
- key: null,
- correct: false
- };
-
- // function to end trial when it is time
- var end_trial = function() {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // kill keyboard listeners
- if (typeof keyboardListener !== 'undefined') {
- jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
- }
-
- // gather the data to store for the trial
- var trial_data = {
- rt: response.rt,
- stimulus: trial.stimulus,
- response: response.key,
- correct: response.correct
- };
-
- // clears the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- };
-
- var leftKeyCode = trial.left_category_key;
- var rightKeyCode = trial.right_category_key;
-
- // function to handle responses by the subject
- var after_response = function(info) {
- var wImg = document.getElementById("wrongImgContainer");
- // after a valid response, the stimulus will have the CSS class 'responded'
- // which can be used to provide visual feedback that a response was recorded
- display_element.querySelector('#jspsych-iat-stim').className += ' responded';
-
- // only record the first response
- if (response.key == null ) {
- response = info;
- }
-
- if(trial.stim_key_association == "right") {
- if(response.rt !== null && jsPsych.pluginAPI.compareKeys(response.key, rightKeyCode)) {
- response.correct = true;
- if (trial.response_ends_trial) {
- end_trial();
- }
- } else {
- response.correct = false;
- if(!trial.response_ends_trial && trial.display_feedback == true) {
- wImg.style.visibility = "visible";
- }
- if (trial.response_ends_trial && trial.display_feedback == true) {
- wImg.style.visibility = "visible";
- if(trial.force_correct_key_press) {
- var keyListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: end_trial,
- valid_responses: [trial.right_category_key]
- });
- } else {
- var keyListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: end_trial,
- valid_responses: trial.key_to_move_forward
- });}
- } else if(trial.response_ends_trial && trial.display_feedback != true) {
- var keyListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: end_trial,
- valid_responses: [jsPsych.ALL_KEYS]
- });
- } else if(!trial.response_ends_trial && trial.display_feedback != true) {
-
- }
- }
- } else if(trial.stim_key_association == "left") {
- if(response.rt !== null && jsPsych.pluginAPI.compareKeys(response.key, leftKeyCode)) {
- response.correct = true;
- if (trial.response_ends_trial) {
- end_trial();
- }
- } else {
- response.correct = false;
- if(!trial.response_ends_trial && trial.display_feedback == true) {
- wImg.style.visibility = "visible";
- }
- if (trial.response_ends_trial && trial.display_feedback == true) {
- wImg.style.visibility = "visible";
- if(trial.force_correct_key_press) {
- var keyListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: end_trial,
- valid_responses: [trial.left_category_key]
- });
- } else {
- var keyListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: end_trial,
- valid_responses: trial.key_to_move_forward
- });}
- } else if(trial.response_ends_trial && trial.display_feedback != true) {
- var keyListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: end_trial,
- valid_responses: [jsPsych.ALL_KEYS]
- });
- } else if(!trial.response_ends_trial && trial.display_feedback != true) {
-
- }
- }
- }
- };
-
- // start the response listener
- if (trial.left_category_key != jsPsych.NO_KEYS && trial.right_category_key != jsPsych.NO_KEYS) {
- var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: [trial.left_category_key, trial.right_category_key],
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
- }
-
- // end trial if time limit is set
- if (trial.trial_duration !== null && trial.response_ends_trial != true) {
- jsPsych.pluginAPI.setTimeout(function() {
- end_trial();
- }, trial.trial_duration);
- }
-
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-iat-image.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-iat-image.js
deleted file mode 100644
index 0f37bac6..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-iat-image.js
+++ /dev/null
@@ -1,286 +0,0 @@
-/**
- * jspsych-iat
- * Kristin Diep
- *
- * plugin for running an IAT (Implicit Association Test) with an image stimulus
- *
- * documentation: docs.jspsych.org
- *
- **/
-
-
- jsPsych.plugins['iat-image'] = (function() {
-
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('iat-image', 'stimulus', 'image');
-
- plugin.info = {
- name: 'iat-image',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.IMAGE,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The image to be displayed'
- },
- left_category_key: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Left category key',
- default: 'e',
- description: 'Key press that is associated with the left category label.'
- },
- right_category_key: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Right category key',
- default: 'i',
- description: 'Key press that is associated with the right category label.'
- },
- left_category_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Left category label',
- array: true,
- default: ['left'],
- description: 'The label that is associated with the stimulus. Aligned to the left side of page.'
- },
- right_category_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Right category label',
- array: true,
- default: ['right'],
- description: 'The label that is associated with the stimulus. Aligned to the right side of the page.'
- },
- key_to_move_forward: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Key to move forward',
- array: true,
- default: jsPsych.ALL_KEYS,
- description: 'The keys that allow the user to advance to the next trial if their key press was incorrect.'
- },
- display_feedback: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Display feedback',
- default: false,
- description: 'If true, then html when wrong will be displayed when user makes an incorrect key press.'
- },
- html_when_wrong: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'HTML when wrong',
- default: 'X',
- description: 'The HTML to display when a user presses the wrong key.'
- },
- bottom_instructions: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'Bottom instructions',
- default: '
If you press the wrong key, a red X will appear. Press any key to continue.
',
- description: 'Instructions shown at the bottom of the page.'
- },
- force_correct_key_press: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Force correct key press',
- default: false,
- description: 'If true, in order to advance to the next trial after a wrong key press the user will be forced to press the correct key.'
- },
- stim_key_association: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'Stimulus key association',
- options: ['left', 'right'],
- default: undefined,
- description: 'Stimulus will be associated with either "left" or "right".'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, trial will end when user makes a response.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show the trial.'
- },
- }
- }
-
-
- plugin.trial = function(display_element, trial) {
-
- var html_str = "";
-
- html_str += "";
-
- html_str += "
";
-
- display_element.innerHTML = html_str;
-
-
- // store response
- var response = {
- rt: null,
- key: null,
- correct: false
- };
-
- // function to end trial when it is time
- var end_trial = function() {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // kill keyboard listeners
- if (typeof keyboardListener !== 'undefined') {
- jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
- }
-
- // gather the data to store for the trial
- var trial_data = {
- rt: response.rt,
- stimulus: trial.stimulus,
- response: response.key,
- correct: response.correct
- };
-
- // clears the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- };
-
- var leftKeyCode = trial.left_category_key;
- var rightKeyCode = trial.right_category_key;
-
- // function to handle responses by the subject
- var after_response = function(info) {
- var wImg = document.getElementById("wrongImgContainer");
- // after a valid response, the stimulus will have the CSS class 'responded'
- // which can be used to provide visual feedback that a response was recorded
- display_element.querySelector('#jspsych-iat-stim').className += ' responded';
-
- // only record the first response
- if (response.key == null ) {
- response = info;
- }
-
- if(trial.stim_key_association == "right") {
- if(response.rt !== null && jsPsych.pluginAPI.compareKeys(response.key, rightKeyCode)) {
- response.correct = true;
- if (trial.response_ends_trial) {
- end_trial();
- }
- } else {
- response.correct = false;
- if(!trial.response_ends_trial && trial.display_feedback == true) {
- wImg.style.visibility = "visible";
- }
- if (trial.response_ends_trial && trial.display_feedback == true) {
- wImg.style.visibility = "visible";
- if(trial.force_correct_key_press) {
- var keyListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: end_trial,
- valid_responses: [trial.right_category_key]
- });
- } else {
- var keyListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: end_trial,
- valid_responses: trial.key_to_move_forward
- });}
- } else if(trial.response_ends_trial && trial.display_feedback != true) {
- var keyListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: end_trial,
- valid_responses: [jsPsych.ALL_KEYS]
- });
- } else if(!trial.response_ends_trial && trial.display_feedback != true) {
-
- }
- }
- } else if(trial.stim_key_association == "left") {
- if(response.rt !== null && jsPsych.pluginAPI.compareKeys(response.key, leftKeyCode)) {
- response.correct = true;
- if (trial.response_ends_trial) {
- end_trial();
- }
- } else {
- response.correct = false;
- if(!trial.response_ends_trial && trial.display_feedback == true) {
- wImg.style.visibility = "visible";
- }
- if (trial.response_ends_trial && trial.display_feedback == true) {
- wImg.style.visibility = "visible";
- if(trial.force_correct_key_press) {
- var keyListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: end_trial,
- valid_responses: [trial.left_category_key]
- });
- } else {
- var keyListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: end_trial,
- valid_responses: trial.key_to_move_forward
- });}
- } else if(trial.response_ends_trial && trial.display_feedback != true) {
- var keyListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: end_trial,
- valid_responses: [jsPsych.ALL_KEYS]
- });
- } else if(!trial.response_ends_trial && trial.display_feedback != true) {
-
- }
- }
- }
- };
-
- // start the response listener
- if (trial.left_category_key != jsPsych.NO_KEYS && trial.right_category_key != jsPsych.NO_KEYS) {
- var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: [trial.left_category_key, trial.right_category_key],
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
- }
-
- // end trial if time limit is set
- if (trial.trial_duration !== null && trial.response_ends_trial != true) {
- jsPsych.pluginAPI.setTimeout(function() {
- end_trial();
- }, trial.trial_duration);
- }
-
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-image-button-response.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-image-button-response.js
deleted file mode 100644
index ee426b80..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-image-button-response.js
+++ /dev/null
@@ -1,327 +0,0 @@
-/**
- * jspsych-image-button-response
- * Josh de Leeuw
- *
- * plugin for displaying a stimulus and getting a button response
- *
- * documentation: docs.jspsych.org
- *
- **/
-
-jsPsych.plugins["image-button-response"] = (function() {
-
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('image-button-response', 'stimulus', 'image');
-
- plugin.info = {
- name: 'image-button-response',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.IMAGE,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The image to be displayed'
- },
- stimulus_height: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Image height',
- default: null,
- description: 'Set the image height in pixels'
- },
- stimulus_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Image width',
- default: null,
- description: 'Set the image width in pixels'
- },
- maintain_aspect_ratio: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Maintain aspect ratio',
- default: true,
- description: 'Maintain the aspect ratio after setting width or height'
- },
- choices: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Choices',
- default: undefined,
- array: true,
- description: 'The labels for the buttons.'
- },
- button_html: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button HTML',
- default: '',
- array: true,
- description: 'The html of the button. Can create own style.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed under the button.'
- },
- stimulus_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Stimulus duration',
- default: null,
- description: 'How long to hide the stimulus.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show the trial.'
- },
- margin_vertical: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Margin vertical',
- default: '0px',
- description: 'The vertical margin of the button.'
- },
- margin_horizontal: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Margin horizontal',
- default: '8px',
- description: 'The horizontal margin of the button.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, then trial will end when user responds.'
- },
- render_on_canvas: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Render on canvas',
- default: true,
- description: 'If true, the image will be drawn onto a canvas element (prevents blank screen between consecutive images in some browsers).'+
- 'If false, the image will be shown via an img element.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- var height, width;
- var html;
- if (trial.render_on_canvas) {
- var image_drawn = false;
- // first clear the display element (because the render_on_canvas method appends to display_element instead of overwriting it with .innerHTML)
- if (display_element.hasChildNodes()) {
- // can't loop through child list because the list will be modified by .removeChild()
- while (display_element.firstChild) {
- display_element.removeChild(display_element.firstChild);
- }
- }
- // create canvas element and image
- var canvas = document.createElement("canvas");
- canvas.id = "jspsych-image-button-response-stimulus";
- canvas.style.margin = 0;
- canvas.style.padding = 0;
- var ctx = canvas.getContext("2d");
- var img = new Image();
- img.onload = function() {
- // if image wasn't preloaded, then it will need to be drawn whenever it finishes loading
- if (!image_drawn) {
- getHeightWidth(); // only possible to get width/height after image loads
- ctx.drawImage(img,0,0,width,height);
- }
- };
- img.src = trial.stimulus;
- // get/set image height and width - this can only be done after image loads because uses image's naturalWidth/naturalHeight properties
- function getHeightWidth() {
- if (trial.stimulus_height !== null) {
- height = trial.stimulus_height;
- if (trial.stimulus_width == null && trial.maintain_aspect_ratio) {
- width = img.naturalWidth * (trial.stimulus_height/img.naturalHeight);
- }
- } else {
- height = img.naturalHeight;
- }
- if (trial.stimulus_width !== null) {
- width = trial.stimulus_width;
- if (trial.stimulus_height == null && trial.maintain_aspect_ratio) {
- height = img.naturalHeight * (trial.stimulus_width/img.naturalWidth);
- }
- } else if (!(trial.stimulus_height !== null & trial.maintain_aspect_ratio)) {
- // if stimulus width is null, only use the image's natural width if the width value wasn't set
- // in the if statement above, based on a specified height and maintain_aspect_ratio = true
- width = img.naturalWidth;
- }
- canvas.height = height;
- canvas.width = width;
- }
- getHeightWidth(); // call now, in case image loads immediately (is cached)
- // create buttons
- var buttons = [];
- if (Array.isArray(trial.button_html)) {
- if (trial.button_html.length == trial.choices.length) {
- buttons = trial.button_html;
- } else {
- console.error('Error in image-button-response plugin. The length of the button_html array does not equal the length of the choices array');
- }
- } else {
- for (var i = 0; i < trial.choices.length; i++) {
- buttons.push(trial.button_html);
- }
- }
- var btngroup_div = document.createElement('div');
- btngroup_div.id = "jspsych-image-button-response-btngroup";
- html = '';
- for (var i = 0; i < trial.choices.length; i++) {
- var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
- html += '
'+str+'
';
- }
- btngroup_div.innerHTML = html;
- // add canvas to screen and draw image
- display_element.insertBefore(canvas, null);
- if (img.complete && Number.isFinite(width) && Number.isFinite(height)) {
- // if image has loaded and width/height have been set, then draw it now
- // (don't rely on img onload function to draw image when image is in the cache, because that causes a delay in the image presentation)
- ctx.drawImage(img,0,0,width,height);
- image_drawn = true;
- }
- // add buttons to screen
- display_element.insertBefore(btngroup_div, canvas.nextElementSibling);
- // add prompt if there is one
- if (trial.prompt !== null) {
- display_element.insertAdjacentHTML('beforeend', trial.prompt);
- }
-
- } else {
-
- // display stimulus as an image element
- html = '';
- //display buttons
- var buttons = [];
- if (Array.isArray(trial.button_html)) {
- if (trial.button_html.length == trial.choices.length) {
- buttons = trial.button_html;
- } else {
- console.error('Error in image-button-response plugin. The length of the button_html array does not equal the length of the choices array');
- }
- } else {
- for (var i = 0; i < trial.choices.length; i++) {
- buttons.push(trial.button_html);
- }
- }
- html += '
';
-
- for (var i = 0; i < trial.choices.length; i++) {
- var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
- html += '
'+str+'
';
- }
- html += '
';
- // add prompt
- if (trial.prompt !== null){
- html += trial.prompt;
- }
- // update the page content
- display_element.innerHTML = html;
-
- // set image dimensions after image has loaded (so that we have access to naturalHeight/naturalWidth)
- var img = display_element.querySelector('#jspsych-image-button-response-stimulus');
- if (trial.stimulus_height !== null) {
- height = trial.stimulus_height;
- if (trial.stimulus_width == null && trial.maintain_aspect_ratio) {
- width = img.naturalWidth * (trial.stimulus_height/img.naturalHeight);
- }
- } else {
- height = img.naturalHeight;
- }
- if (trial.stimulus_width !== null) {
- width = trial.stimulus_width;
- if (trial.stimulus_height == null && trial.maintain_aspect_ratio) {
- height = img.naturalHeight * (trial.stimulus_width/img.naturalWidth);
- }
- } else if (!(trial.stimulus_height !== null & trial.maintain_aspect_ratio)) {
- // if stimulus width is null, only use the image's natural width if the width value wasn't set
- // in the if statement above, based on a specified height and maintain_aspect_ratio = true
- width = img.naturalWidth;
- }
- img.style.height = height.toString() + "px";
- img.style.width = width.toString() + "px";
- }
-
- // start timing
- var start_time = performance.now();
-
- for (var i = 0; i < trial.choices.length; i++) {
- display_element.querySelector('#jspsych-image-button-response-button-' + i).addEventListener('click', function(e){
- var choice = e.currentTarget.getAttribute('data-choice'); // don't use dataset for jsdom compatibility
- after_response(choice);
- });
- }
-
- // store response
- var response = {
- rt: null,
- button: null
- };
-
- // function to handle responses by the subject
- function after_response(choice) {
-
- // measure rt
- var end_time = performance.now();
- var rt = end_time - start_time;
- response.button = parseInt(choice);
- response.rt = rt;
-
- // after a valid response, the stimulus will have the CSS class 'responded'
- // which can be used to provide visual feedback that a response was recorded
- display_element.querySelector('#jspsych-image-button-response-stimulus').className += ' responded';
-
- // disable all the buttons after a response
- var btns = document.querySelectorAll('.jspsych-image-button-response-button button');
- for(var i=0; i';
- // add prompt
- if (trial.prompt !== null){
- html += trial.prompt;
- }
- // update the page content
- display_element.innerHTML = html;
-
- // set image dimensions after image has loaded (so that we have access to naturalHeight/naturalWidth)
- var img = display_element.querySelector('#jspsych-image-keyboard-response-stimulus');
- if (trial.stimulus_height !== null) {
- height = trial.stimulus_height;
- if (trial.stimulus_width == null && trial.maintain_aspect_ratio) {
- width = img.naturalWidth * (trial.stimulus_height/img.naturalHeight);
- }
- } else {
- height = img.naturalHeight;
- }
- if (trial.stimulus_width !== null) {
- width = trial.stimulus_width;
- if (trial.stimulus_height == null && trial.maintain_aspect_ratio) {
- height = img.naturalHeight * (trial.stimulus_width/img.naturalWidth);
- }
- } else if (!(trial.stimulus_height !== null & trial.maintain_aspect_ratio)) {
- // if stimulus width is null, only use the image's natural width if the width value wasn't set
- // in the if statement above, based on a specified height and maintain_aspect_ratio = true
- width = img.naturalWidth;
- }
- img.style.height = height.toString() + "px";
- img.style.width = width.toString() + "px";
- }
-
- // store response
- var response = {
- rt: null,
- key: null
- };
-
- // function to end trial when it is time
- var end_trial = function() {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // kill keyboard listeners
- if (typeof keyboardListener !== 'undefined') {
- jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
- }
-
- // gather the data to store for the trial
- var trial_data = {
- rt: response.rt,
- stimulus: trial.stimulus,
- response: response.key
- };
-
- // clear the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- };
-
- // function to handle responses by the subject
- var after_response = function(info) {
-
- // after a valid response, the stimulus will have the CSS class 'responded'
- // which can be used to provide visual feedback that a response was recorded
- display_element.querySelector('#jspsych-image-keyboard-response-stimulus').className += ' responded';
-
- // only record the first response
- if (response.key == null) {
- response = info;
- }
-
- if (trial.response_ends_trial) {
- end_trial();
- }
- };
-
- // start the response listener
- if (trial.choices != jsPsych.NO_KEYS) {
- var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: trial.choices,
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
- }
-
- // hide stimulus if stimulus_duration is set
- if (trial.stimulus_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- display_element.querySelector('#jspsych-image-keyboard-response-stimulus').style.visibility = 'hidden';
- }, trial.stimulus_duration);
- }
-
- // end trial if trial_duration is set
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- end_trial();
- }, trial.trial_duration);
- } else if (trial.response_ends_trial === false) {
- console.warn("The experiment may be deadlocked. Try setting a trial duration or set response_ends_trial to true.");
- }
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-image-slider-response.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-image-slider-response.js
deleted file mode 100644
index 66bfc877..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-image-slider-response.js
+++ /dev/null
@@ -1,369 +0,0 @@
-/**
- * jspsych-image-slider-response
- * a jspsych plugin for free response survey questions
- *
- * Josh de Leeuw
- *
- * documentation: docs.jspsych.org
- *
- */
-
-
-jsPsych.plugins['image-slider-response'] = (function() {
-
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('image-slider-response', 'stimulus', 'image');
-
- plugin.info = {
- name: 'image-slider-response',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.IMAGE,
- pretty_name: 'Stimulus',
- default: undefined,
- description: 'The image to be displayed'
- },
- stimulus_height: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Image height',
- default: null,
- description: 'Set the image height in pixels'
- },
- stimulus_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Image width',
- default: null,
- description: 'Set the image width in pixels'
- },
- maintain_aspect_ratio: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Maintain aspect ratio',
- default: true,
- description: 'Maintain the aspect ratio after setting width or height'
- },
- min: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Min slider',
- default: 0,
- description: 'Sets the minimum value of the slider.'
- },
- max: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Max slider',
- default: 100,
- description: 'Sets the maximum value of the slider',
- },
- slider_start: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Slider starting value',
- default: 50,
- description: 'Sets the starting value of the slider',
- },
- step: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Step',
- default: 1,
- description: 'Sets the step of the slider'
- },
- labels: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name:'Labels',
- default: [],
- array: true,
- description: 'Labels of the slider.',
- },
- slider_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name:'Slider width',
- default: null,
- description: 'Width of the slider in pixels.'
- },
- button_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button label',
- default: 'Continue',
- array: false,
- description: 'Label of the button to advance.'
- },
- require_movement: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Require movement',
- default: false,
- description: 'If true, the participant will have to move the slider before continuing.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the slider.'
- },
- stimulus_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Stimulus duration',
- default: null,
- description: 'How long to hide the stimulus.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show the trial.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, trial will end when user makes a response.'
- },
- render_on_canvas: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Render on canvas',
- default: true,
- description: 'If true, the image will be drawn onto a canvas element (prevents blank screen between consecutive images in some browsers).'+
- 'If false, the image will be shown via an img element.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- var height, width;
- var html;
- // half of the thumb width value from jspsych.css, used to adjust the label positions
- var half_thumb_width = 7.5;
-
- if (trial.render_on_canvas) {
- var image_drawn = false;
- // first clear the display element (because the render_on_canvas method appends to display_element instead of overwriting it with .innerHTML)
- if (display_element.hasChildNodes()) {
- // can't loop through child list because the list will be modified by .removeChild()
- while (display_element.firstChild) {
- display_element.removeChild(display_element.firstChild);
- }
- }
- // create wrapper div, canvas element and image
- var content_wrapper = document.createElement('div');
- content_wrapper.id = "jspsych-image-slider-response-wrapper";
- content_wrapper.style.margin = "100px 0px";
- var canvas = document.createElement("canvas");
- canvas.id = "jspsych-image-slider-response-stimulus";
- canvas.style.margin = 0;
- canvas.style.padding = 0;
- var ctx = canvas.getContext("2d");
- var img = new Image();
- img.onload = function() {
- // if image wasn't preloaded, then it will need to be drawn whenever it finishes loading
- if (!image_drawn) {
- getHeightWidth(); // only possible to get width/height after image loads
- ctx.drawImage(img,0,0,width,height);
- }
- };
- img.src = trial.stimulus;
- // get/set image height and width - this can only be done after image loads because uses image's naturalWidth/naturalHeight properties
- function getHeightWidth() {
- if (trial.stimulus_height !== null) {
- height = trial.stimulus_height;
- if (trial.stimulus_width == null && trial.maintain_aspect_ratio) {
- width = img.naturalWidth * (trial.stimulus_height/img.naturalHeight);
- }
- } else {
- height = img.naturalHeight;
- }
- if (trial.stimulus_width !== null) {
- width = trial.stimulus_width;
- if (trial.stimulus_height == null && trial.maintain_aspect_ratio) {
- height = img.naturalHeight * (trial.stimulus_width/img.naturalWidth);
- }
- } else if (!(trial.stimulus_height !== null & trial.maintain_aspect_ratio)) {
- // if stimulus width is null, only use the image's natural width if the width value wasn't set
- // in the if statement above, based on a specified height and maintain_aspect_ratio = true
- width = img.naturalWidth;
- }
- canvas.height = height;
- canvas.width = width;
- }
- getHeightWidth(); // call now, in case image loads immediately (is cached)
- // create container with slider and labels
- var slider_container = document.createElement('div');
- slider_container.classList.add("jspsych-image-slider-response-container");
- slider_container.style.position = "relative";
- slider_container.style.margin = "0 auto 3em auto";
- if(trial.slider_width !== null){
- slider_container.style.width = trial.slider_width.toString()+'px';
- }
- // create html string with slider and labels, and add to slider container
- html ='';
- html += '
'
- for(var j=0; j < trial.labels.length; j++){
- var label_width_perc = 100/(trial.labels.length-1);
- var percent_of_range = j * (100/(trial.labels.length - 1));
- var percent_dist_from_center = ((percent_of_range-50)/50)*100;
- var offset = (percent_dist_from_center * half_thumb_width)/100;
- html += '
';
- html += ''+trial.labels[j]+'';
- html += '
'
- }
- html += '
';
- slider_container.innerHTML = html;
- // add canvas and slider to content wrapper div
- content_wrapper.insertBefore(canvas, content_wrapper.firstElementChild);
- content_wrapper.insertBefore(slider_container, canvas.nextElementSibling);
- // add content wrapper div to screen and draw image on canvas
- display_element.insertBefore(content_wrapper, null);
- if (img.complete && Number.isFinite(width) && Number.isFinite(height)) {
- // if image has loaded and width/height have been set, then draw it now
- // (don't rely on img onload function to draw image when image is in the cache, because that causes a delay in the image presentation)
- ctx.drawImage(img,0,0,width,height);
- image_drawn = true;
- }
- // add prompt if there is one
- if (trial.prompt !== null) {
- display_element.insertAdjacentHTML('beforeend', trial.prompt);
- }
- // add submit button
- var submit_btn = document.createElement('button');
- submit_btn.id = "jspsych-image-slider-response-next";
- submit_btn.classList.add("jspsych-btn");
- submit_btn.disabled = (trial.require_movement) ? true : false;
- submit_btn.innerHTML = trial.button_label;
- display_element.insertBefore(submit_btn, display_element.nextElementSibling);
-
- } else {
-
- html = '
';
- html += '
';
- html += '';
- html += '
';
- html += '
';
- html += '';
- html += '
'
- for(var j=0; j < trial.labels.length; j++){
- var label_width_perc = 100/(trial.labels.length-1);
- var percent_of_range = j * (100/(trial.labels.length - 1));
- var percent_dist_from_center = ((percent_of_range-50)/50)*100;
- var offset = (percent_dist_from_center * half_thumb_width)/100;
- html += '
';
- html += ''+trial.labels[j]+'';
- html += '
'
- }
- html += '
';
- html += '
';
- html += '
';
-
- if (trial.prompt !== null){
- html += trial.prompt;
- }
-
- // add submit button
- html += '';
-
- display_element.innerHTML = html;
-
- // set image dimensions after image has loaded (so that we have access to naturalHeight/naturalWidth)
- var img = display_element.querySelector('img');
- if (trial.stimulus_height !== null) {
- height = trial.stimulus_height;
- if (trial.stimulus_width == null && trial.maintain_aspect_ratio) {
- width = img.naturalWidth * (trial.stimulus_height/img.naturalHeight);
- }
- } else {
- height = img.naturalHeight;
- }
- if (trial.stimulus_width !== null) {
- width = trial.stimulus_width;
- if (trial.stimulus_height == null && trial.maintain_aspect_ratio) {
- height = img.naturalHeight * (trial.stimulus_width/img.naturalWidth);
- }
- } else if (!(trial.stimulus_height !== null & trial.maintain_aspect_ratio)) {
- // if stimulus width is null, only use the image's natural width if the width value wasn't set
- // in the if statement above, based on a specified height and maintain_aspect_ratio = true
- width = img.naturalWidth;
- }
- img.style.height = height.toString() + "px";
- img.style.width = width.toString() + "px";
- }
-
- var response = {
- rt: null,
- response: null
- };
-
- if(trial.require_movement){
- display_element.querySelector('#jspsych-image-slider-response-response').addEventListener('click', function(){
- display_element.querySelector('#jspsych-image-slider-response-next').disabled = false;
- });
- }
-
- display_element.querySelector('#jspsych-image-slider-response-next').addEventListener('click', function() {
- // measure response time
- var endTime = performance.now();
- response.rt = endTime - startTime;
- response.response = display_element.querySelector('#jspsych-image-slider-response-response').valueAsNumber;
-
- if(trial.response_ends_trial){
- end_trial();
- } else {
- display_element.querySelector('#jspsych-image-slider-response-next').disabled = true;
- }
-
- });
-
- function end_trial(){
-
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // save data
- var trialdata = {
- rt: response.rt,
- stimulus: trial.stimulus,
- slider_start: trial.slider_start,
- response: response.response
- };
-
- display_element.innerHTML = '';
-
- // next trial
- jsPsych.finishTrial(trialdata);
- }
-
- if (trial.stimulus_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- display_element.querySelector('#jspsych-image-slider-response-stimulus').style.visibility = 'hidden';
- }, trial.stimulus_duration);
- }
-
- // end trial if trial_duration is set
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- end_trial();
- }, trial.trial_duration);
- }
-
- var startTime = performance.now();
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-instructions.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-instructions.js
deleted file mode 100644
index 7624b3ad..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-instructions.js
+++ /dev/null
@@ -1,237 +0,0 @@
-/* jspsych-instructions.js
- * Josh de Leeuw
- *
- * This plugin displays text (including HTML formatted strings) during the experiment.
- * Use it to show instructions, provide performance feedback, etc...
- *
- * Page numbers can be displayed to help with navigation by setting show_page_number
- * to true.
- *
- * documentation: docs.jspsych.org
- *
- *
- */
-
-jsPsych.plugins.instructions = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'instructions',
- description: '',
- parameters: {
- pages: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'Pages',
- default: undefined,
- array: true,
- description: 'Each element of the array is the content for a single page.'
- },
- key_forward: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Key forward',
- default: 'ArrowRight',
- description: 'The key the subject can press in order to advance to the next page.'
- },
- key_backward: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Key backward',
- default: 'ArrowLeft',
- description: 'The key that the subject can press to return to the previous page.'
- },
- allow_backward: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Allow backward',
- default: true,
- description: 'If true, the subject can return to the previous page of the instructions.'
- },
- allow_keys: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Allow keys',
- default: true,
- description: 'If true, the subject can use keyboard keys to navigate the pages.'
- },
- show_clickable_nav: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Show clickable nav',
- default: false,
- description: 'If true, then a "Previous" and "Next" button will be displayed beneath the instructions.'
- },
- show_page_number: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Show page number',
- default: false,
- description: 'If true, and clickable navigation is enabled, then Page x/y will be shown between the nav buttons.'
- },
- page_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Page label',
- default: 'Page',
- description: 'The text that appears before x/y (current/total) pages displayed with show_page_number'
- },
- button_label_previous: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button label previous',
- default: 'Previous',
- description: 'The text that appears on the button to go backwards.'
- },
- button_label_next: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button label next',
- default: 'Next',
- description: 'The text that appears on the button to go forwards.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- var current_page = 0;
-
- var view_history = [];
-
- var start_time = performance.now();
-
- var last_page_update_time = start_time;
-
- function btnListener(evt){
- evt.target.removeEventListener('click', btnListener);
- if(this.id === "jspsych-instructions-back"){
- back();
- }
- else if(this.id === 'jspsych-instructions-next'){
- next();
- }
- }
-
- function show_current_page() {
- var html = trial.pages[current_page];
-
- var pagenum_display = "";
- if(trial.show_page_number) {
- pagenum_display = ""+ trial.page_label + ' ' +(current_page+1)+"/"+trial.pages.length+"";
- }
-
- if (trial.show_clickable_nav) {
-
- var nav_html = "
";
-
- html += nav_html;
- display_element.innerHTML = html;
- if (current_page != 0 && trial.allow_backward) {
- display_element.querySelector('#jspsych-instructions-back').addEventListener('click', btnListener);
- }
-
- display_element.querySelector('#jspsych-instructions-next').addEventListener('click', btnListener);
- } else {
- if (trial.show_page_number && trial.pages.length > 1) {
- // page numbers for non-mouse navigation
- html += "
"+pagenum_display+"
"
- }
- display_element.innerHTML = html;
- }
-
- }
-
- function next() {
-
- add_current_page_to_view_history()
-
- current_page++;
-
- // if done, finish up...
- if (current_page >= trial.pages.length) {
- endTrial();
- } else {
- show_current_page();
- }
-
- }
-
- function back() {
-
- add_current_page_to_view_history()
-
- current_page--;
-
- show_current_page();
- }
-
- function add_current_page_to_view_history() {
-
- var current_time = performance.now();
-
- var page_view_time = current_time - last_page_update_time;
-
- view_history.push({
- page_index: current_page,
- viewing_time: page_view_time
- });
-
- last_page_update_time = current_time;
- }
-
- function endTrial() {
-
- if (trial.allow_keys) {
- jsPsych.pluginAPI.cancelKeyboardResponse(keyboard_listener);
- }
-
- display_element.innerHTML = '';
-
- var trial_data = {
- view_history: view_history,
- rt: performance.now() - start_time
- };
-
- jsPsych.finishTrial(trial_data);
- }
-
- var after_response = function(info) {
-
- // have to reinitialize this instead of letting it persist to prevent accidental skips of pages by holding down keys too long
- keyboard_listener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: [trial.key_forward, trial.key_backward],
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
- // check if key is forwards or backwards and update page
- if (jsPsych.pluginAPI.compareKeys(info.key, trial.key_backward)) {
- if (current_page !== 0 && trial.allow_backward) {
- back();
- }
- }
-
- if (jsPsych.pluginAPI.compareKeys(info.key, trial.key_forward)) {
- next();
- }
-
- };
-
- show_current_page();
-
- if (trial.allow_keys) {
- var keyboard_listener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: [trial.key_forward, trial.key_backward],
- rt_method: 'performance',
- persist: false
- });
- }
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-maxdiff.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-maxdiff.js
deleted file mode 100644
index b976f884..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-maxdiff.js
+++ /dev/null
@@ -1,173 +0,0 @@
-/**
- * jspsych-maxdiff
- * Angus Hughes
- *
- * a jspsych plugin for maxdiff/conjoint analysis designs
- *
- */
-
-jsPsych.plugins['maxdiff'] = (function () {
-
- var plugin = {};
-
- plugin.info = {
- name: 'maxdiff',
- description: '',
- parameters: {
- alternatives: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Alternatives',
- array: true,
- default: undefined,
- description: 'Alternatives presented in the maxdiff table.'
- },
- labels: {
- type: jsPsych.plugins.parameterType.STRING,
- array: true,
- pretty_name: 'Labels',
- default: undefined,
- description: 'Labels to display for left and right response columns.'
- },
- randomize_alternative_order: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Randomize Alternative Order',
- default: false,
- description: 'If true, the order of the alternatives will be randomized'
- },
- preamble: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Preamble',
- default: '',
- description: 'String to display at top of the page.'
- },
- button_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button Label',
- default: 'Continue',
- description: 'Label of the button.'
- },
- required: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Required',
- default: false,
- description: 'Makes answering the alternative required.'
- }
- }
- }
-
- plugin.trial = function (display_element, trial) {
-
- var html = "";
- // inject CSS for trial
- html += '';
-
- // show preamble text
- if (trial.preamble !== null) {
- html += '
' + trial.preamble + '
';
- }
- html += '';
-
- display_element.innerHTML = html;
-
- // function to control responses
- // first checks that the same alternative cannot be endorsed in the left and right columns simultaneously.
- // then enables the submit button if the trial is required.
- const left_right = ["left", "right"]
- left_right.forEach(function(p) {
- // Get all elements either 'left' or 'right'
- document.getElementsByName(p).forEach(function(alt) {
- alt.addEventListener('click', function() {
- // Find the opposite (if left, then right & vice versa) identified by the class (jspsych-maxdiff-alt-1, 2, etc)
- var op = alt.name == 'left' ? 'right' : 'left';
- var n = document.getElementsByClassName(alt.className).namedItem(op);
- // If it's checked, uncheck it.
- if (n.checked) {
- n.checked = false;
- }
-
- // check response
- if (trial.required){
- // Now check if one of both left and right have been enabled to allow submission
- var left_checked = [...document.getElementsByName('left')].some(c => c.checked);
- var right_checked = [...document.getElementsByName('right')].some(c => c.checked);
- if (left_checked && right_checked) {
- document.getElementById("jspsych-maxdiff-next").disabled = false;
- } else {
- document.getElementById("jspsych-maxdiff-next").disabled = true;
- }
- }
- });
- });
- });
-
- // Get the data once the submit button is clicked
- // Get the data once the submit button is clicked
- display_element.querySelector('#jspsych-maxdiff-form').addEventListener('submit', function(e){
- e.preventDefault();
-
- // measure response time
- var endTime = performance.now();
- var response_time = endTime - startTime;
-
- // get the alternative by the data-name attribute, allowing a null response if unchecked
- get_response = function(side){
- var col = display_element.querySelectorAll('[name=\"' + side + '\"]:checked')[0];
- if (col === undefined){
- return null;
- } else {
- var i = parseInt(col.getAttribute('data-name'));
- return trial.alternatives[i];
- }
- }
-
- // data saving
- var trial_data = {
- rt: response_time,
- labels: {left: trial.labels[0], right: trial.labels[1]},
- response: {left: get_response('left'), right: get_response('right')}
- };
-
- // next trial
- jsPsych.finishTrial(trial_data);
- });
-
- var startTime = performance.now();
- };
-
- return plugin;
-})();
\ No newline at end of file
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-preload.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-preload.js
deleted file mode 100644
index 01623c27..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-preload.js
+++ /dev/null
@@ -1,345 +0,0 @@
-/**
- * jspsych-preload
- * documentation: docs.jspsych.org
- **/
-
-jsPsych.plugins['preload'] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'preload',
- description: '',
- parameters: {
- auto_preload: {
- type: jsPsych.plugins.parameterType.BOOL,
- default: false,
- description: 'Whether or not to automatically preload any media files based on the timeline passed to jsPsych.init.'
- },
- trials: {
- type: jsPsych.plugins.parameterType.TIMELINE,
- default: [],
- description: 'Array with a timeline of trials to automatically preload. If one or more trial objects is provided, '+
- 'then the plugin will attempt to preload the media files used in the trial(s).'
- },
- images: {
- type: jsPsych.plugins.parameterType.STRING,
- default: [],
- description: 'Array with one or more image files to load. This parameter is often used in cases where media files cannot '+
- 'be automatically preloaded based on the timeline, e.g. because the media files are passed into an image plugin/parameter with '+
- 'timeline variables or dynamic parameters, or because the image is embedded in an HTML string.'
- },
- audio: {
- type: jsPsych.plugins.parameterType.STRING,
- default: [],
- description: 'Array with one or more audio files to load. This parameter is often used in cases where media files cannot '+
- 'be automatically preloaded based on the timeline, e.g. because the media files are passed into an audio plugin/parameter with '+
- 'timeline variables or dynamic parameters, or because the audio is embedded in an HTML string.'
- },
- video: {
- type: jsPsych.plugins.parameterType.STRING,
- default: [],
- description: 'Array with one or more video files to load. This parameter is often used in cases where media files cannot '+
- 'be automatically preloaded based on the timeline, e.g. because the media files are passed into a video plugin/parameter with '+
- 'timeline variables or dynamic parameters, or because the video is embedded in an HTML string.'
- },
- message: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- default: null,
- description: 'HTML-formatted message to be shown above the progress bar while the files are loading.'
- },
- show_progress_bar: {
- type: jsPsych.plugins.parameterType.BOOL,
- default: true,
- description: 'Whether or not to show the loading progress bar.'
- },
- continue_after_error: {
- type: jsPsych.plugins.parameterType.BOOL,
- default: false,
- description: 'Whether or not to continue with the experiment if a loading error occurs. If false, then if a loading error occurs, '+
- 'the error_message will be shown on the page and the trial will not end. If true, then if if a loading error occurs, the trial will end '+
- 'and preloading failure will be logged in the trial data.'
- },
- error_message: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- default: 'The experiment failed to load.',
- description: 'Error message to show on the page in case of any loading errors. This parameter is only relevant when continue_after_error is false.'
- },
- show_detailed_errors: {
- type: jsPsych.plugins.parameterType.BOOL,
- default: false,
- description: 'Whether or not to show a detailed error message on the page. If true, then detailed error messages will be shown on the '+
- 'page for all files that failed to load, along with the general error_message. This parameter is only relevant when continue_after_error is false.'
- },
- max_load_time: {
- type: jsPsych.plugins.parameterType.INT,
- default: null,
- description: 'The maximum amount of time that the plugin should wait before stopping the preload and either ending the trial '+
- '(if continue_after_error is true) or stopping the experiment with an error message (if continue_after_error is false). '+
- 'If null, the plugin will wait indefintely for the files to load.'
- },
- on_error: {
- type: jsPsych.plugins.parameterType.FUNCTION,
- default: null,
- description: 'Function to be called after a file fails to load. The function takes the file name as its only argument.'
- },
- on_success: {
- type: jsPsych.plugins.parameterType.FUNCTION,
- default: null,
- description: 'Function to be called after a file loads successfully. The function takes the file name as its only argument.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- var success = null;
- var timeout = false;
- var failed_images = [];
- var failed_audio = [];
- var failed_video = [];
- var detailed_errors = [];
- var in_safe_mode = jsPsych.getSafeModeStatus();
-
- // create list of media to preload //
-
- var images = [];
- var audio = [];
- var video = [];
-
- if(trial.auto_preload){
- var auto_preload = jsPsych.pluginAPI.getAutoPreloadList();
- images = images.concat(auto_preload.images);
- audio = audio.concat(auto_preload.audio);
- video = video.concat(auto_preload.video);
- }
-
- if(trial.trials.length > 0){
- var trial_preloads = jsPsych.pluginAPI.getAutoPreloadList(trial.trials);
- images = images.concat(trial_preloads.images);
- audio = audio.concat(trial_preloads.audio);
- video = video.concat(trial_preloads.video);
- }
-
- images = images.concat(trial.images);
- audio = audio.concat(trial.audio);
- video = video.concat(trial.video);
-
- images = jsPsych.utils.unique(jsPsych.utils.flatten(images));
- audio = jsPsych.utils.unique(jsPsych.utils.flatten(audio));
- video = jsPsych.utils.unique(jsPsych.utils.flatten(video));
-
- if (in_safe_mode) {
- // don't preload video if in safe mode (experiment is running via file protocol)
- video = [];
- }
-
- // render display of message and progress bar
-
- var html = '';
-
- if(trial.message !== null){
- html += trial.message;
- }
-
- if(trial.show_progress_bar){
- html += `
-
-
-
`;
- }
-
- display_element.innerHTML = html;
-
- // do preloading
-
- if(trial.max_load_time !== null){
- jsPsych.pluginAPI.setTimeout(on_timeout, trial.max_load_time);
- }
-
- var total_n = images.length + audio.length + video.length;
- var loaded = 0; // success or error count
- var loaded_success = 0; // success count
-
- if (total_n == 0) {
- on_success();
- } else {
- function load_video(cb){
- jsPsych.pluginAPI.preloadVideo(video, cb, file_loading_success, file_loading_error);
- }
- function load_audio(cb){
- jsPsych.pluginAPI.preloadAudio(audio, cb, file_loading_success, file_loading_error);
- }
- function load_images(cb){
- jsPsych.pluginAPI.preloadImages(images, cb, file_loading_success, file_loading_error);
- }
- if (video.length > 0) { load_video(function () { }) }
- if (audio.length > 0) { load_audio(function () { }) }
- if (images.length > 0) { load_images(function () { }) }
- }
-
- // helper functions and callbacks
-
- function update_loading_progress_bar(){
- loaded++;
- if(trial.show_progress_bar){
- var percent_loaded = (loaded/total_n)*100;
- var preload_progress_bar = jsPsych.getDisplayElement().querySelector('#jspsych-loading-progress-bar');
- if (preload_progress_bar !== null) {
- preload_progress_bar.style.width = percent_loaded+"%";
- }
- }
- }
-
- // called when a single file loading fails
- function file_loading_error(e) {
- // update progress bar even if there's an error
- update_loading_progress_bar();
- // change success flag after first file loading error
- if (success == null) {
- success = false;
- }
- // add file to failed media list
- var source = "unknown file";
- if (e.source) {
- source = e.source;
- }
- if (e.error && e.error.path && e.error.path.length > 0) {
- if (e.error.path[0].localName == "img") {
- failed_images.push(source);
- } else if (e.error.path[0].localName == "audio") {
- failed_audio.push(source);
- } else if (e.error.path[0].localName == "video") {
- failed_video.push(source);
- }
- }
- // construct detailed error message
- var err_msg = '
Error loading file: '+source+' ';
- if (e.error.statusText) {
- err_msg += 'File request response status: '+e.error.statusText+' ';
- }
- if (e.error == "404") {
- err_msg += '404 - file not found. ';
- }
- if (typeof e.error.loaded !== 'undefined' && e.error.loaded !== null && e.error.loaded !== 0) {
- err_msg += e.error.loaded+' bytes transferred.';
- } else {
- err_msg += 'File did not begin loading. Check that file path is correct and reachable by the browser, '+
- 'and that loading is not blocked by cross-origin resource sharing (CORS) errors.';
- }
- err_msg += '
';
- detailed_errors.push(err_msg);
- // call trial's on_error function
- after_error(source);
- // if this is the last file
- if (loaded == total_n) {
- if (trial.continue_after_error) {
- // if continue_after_error is false, then stop with an error
- end_trial();
- } else {
- // otherwise end the trial and continue
- stop_with_error_message();
- }
- }
- }
-
- // called when a single file loads successfully
- function file_loading_success(source) {
- update_loading_progress_bar();
- // call trial's on_success function
- after_success(source);
- loaded_success++;
- if (loaded_success == total_n) {
- // if this is the last file and all loaded successfully, call success function
- on_success();
- } else if (loaded == total_n) {
- // if this is the last file and there was at least one error
- if (trial.continue_after_error) {
- // end the trial and continue with experiment
- end_trial();
- } else {
- // if continue_after_error is false, then stop with an error
- stop_with_error_message();
- }
- }
- }
-
- // called if all files load successfully
- function on_success() {
- if (typeof timeout !== 'undefined' && timeout === false) {
- // clear timeout immediately after finishing, to handle race condition with max_load_time
- jsPsych.pluginAPI.clearAllTimeouts();
- // need to call cancel preload function to clear global jsPsych preload_request list, even when they've all succeeded
- jsPsych.pluginAPI.cancelPreloads();
- success = true;
- end_trial();
- }
- }
-
- // called if all_files haven't finished loading when max_load_time is reached
- function on_timeout() {
- //console.log('timeout fired');
- jsPsych.pluginAPI.cancelPreloads();
- if (typeof success !== 'undefined' && (success === false || success === null)) {
- timeout = true;
- if (loaded_success < total_n) {
- success = false;
- }
- after_error('timeout'); // call trial's on_error event handler here, in case loading timed out with no file errors
- detailed_errors.push('
Loading timed out. '+
- 'Consider compressing your stimuli files, loading your files in smaller batches, '+
- 'and/or increasing the max_load_time parameter.
');
- if (trial.continue_after_error) {
- end_trial();
- } else {
- stop_with_error_message();
- }
- }
- }
-
- function stop_with_error_message() {
- jsPsych.pluginAPI.clearAllTimeouts();
- jsPsych.pluginAPI.cancelPreloads();
- // show error message
- display_element.innerHTML = trial.error_message;
- // show detailed errors, if necessary
- if (trial.show_detailed_errors) {
- display_element.innerHTML += '
Error details:
';
- detailed_errors.forEach(function(e) {
- display_element.innerHTML += e;
- });
- }
- }
-
- function after_error(source) {
- // call on_error function and pass file name
- if (trial.on_error !== null) {
- trial.on_error(source);
- }
- }
- function after_success(source) {
- // call on_success function and pass file name
- if (trial.on_success !== null) {
- trial.on_success(source);
- }
- }
-
- function end_trial(){
- // clear timeout again when end_trial is called, to handle race condition with max_load_time
- jsPsych.pluginAPI.clearAllTimeouts();
- var trial_data = {
- success: success,
- timeout: timeout,
- failed_images: failed_images,
- failed_audio: failed_audio,
- failed_video: failed_video
- };
- // clear the display
- display_element.innerHTML = '';
- jsPsych.finishTrial(trial_data);
- }
- };
-
- return plugin;
- })();
-
\ No newline at end of file
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-rdk.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-rdk.js
deleted file mode 100644
index 5b07d9d8..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-rdk.js
+++ /dev/null
@@ -1,1373 +0,0 @@
-/*
-
- RDK plugin for JsPsych
- ----------------------
-
- This code was created in the Consciousness and Metacognition Lab at UCLA,
- under the supervision of Brian Odegaard and Hakwan Lau
-
- We would appreciate it if you cited this paper when you use the RDK:
- 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]
-
- ----------------------
-
- Copyright (C) 2017 Sivananda Rajananda
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-*/
-
-
-jsPsych.plugins["rdk"] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: "rdk",
- parameters: {
- choices: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: "Choices",
- default: jsPsych.ALL_KEYS,
- array: true,
- description: "The valid keys that the subject can press to indicate a response"
- },
- correct_choice: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: "Correct choice",
- default: undefined,
- array: true,
- description: "The correct keys for that trial"
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Trial duration",
- default: 500,
- description: "The length of stimulus presentation"
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: "Response ends trial",
- default: true,
- description: "If true, then any valid key will end the trial"
- },
- number_of_apertures: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Number of apertures",
- default: 1,
- 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)"
- },
- number_of_dots: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Number of dots",
- default: 300,
- description: "The number of dots per set in the stimulus"
- },
- number_of_sets: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Number of sets",
- default: 1,
- description: "The number of sets of dots to cycle through"
- },
- coherent_direction: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Coherent direction",
- default: 0,
- description: "The direction of coherent motion in degrees"
- },
- coherence: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: "Coherence",
- default: 0.5,
- description: "The percentage of dots moving in the coherent direction"
- },
- opposite_coherence: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: "Opposite coherence",
- default: 0,
- description: "The percentage of dots moving in the direction opposite of the coherent direction"
- },
- dot_radius: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Dot radius",
- default: 2,
- description: "The radius of the dots in pixels"
- },
- dot_life: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Dot life",
- default: -1,
- description: "The number of frames that pass before each dot disappears and reappears somewhere else"
- },
- move_distance: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Move distance",
- default: 1,
- description: "The distance in pixels each dot moves per frame"
- },
- aperture_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Aperture width",
- default: 600,
- description: "The width of the aperture in pixels"
- },
- aperture_height: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Aperture height",
- default: 400,
- description: "The height of the aperture in pixels"
- },
- dot_color: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: "Dot color",
- default: "white",
- description: "The color of the dots"
- },
- background_color: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: "Background color",
- default: "gray",
- description: "The background of the stimulus"
- },
- RDK_type: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "RDK type",
- default: 3,
- description: "The Type of RDK (refer to documentation for details)"
- },
- aperture_type: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Aperture Type",
- default: 2,
- description: "The shape of the aperture"
- },
- reinsert_type: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Reinsert type",
- default: 2,
- description: "The reinsertion rule for dots that move out of the aperture"
- },
- aperture_center_x: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Aperture center X",
- default: window.innerWidth/2,
- description: "The x-coordinate of the center of the aperture"
- },
- aperture_center_y: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Aperture center Y",
- default: window.innerHeight/2,
- description: "The y-coordinate of the center of the aperture"
- },
- fixation_cross: {
- type: jsPsych.plugins.parameterType.INT, //boolean
- pretty_name: "Fixation cross",
- default: false,
- description: "If true, then a fixation cross will be present in the middle of the screen"
- },
- fixation_cross_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Fixation cross width",
- default: 20,
- description: "The width of the fixation cross in pixels"
- },
- fixation_cross_height: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Fixation cross height",
- default: 20,
- description: "The height of the fixation cross in pixels"
- },
- fixation_cross_color: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: "Fixation cross color",
- default: "black",
- description: "The color of the fixation cross"
- },
- fixation_cross_thickness: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Fixation cross thickness",
- default: 1,
- description: "The thickness of the fixation cross"
- },
- border: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: "Border",
- default: false,
- description: "The presence of a border around the aperture"
- },
- border_thickness: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Border width",
- default: 1,
- description: "The thickness of the border in pixels"
- },
- border_color: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: "Border Color",
- default: 1,
- description: "The color of the border"
- }
- }
- }
-
-
- //BEGINNING OF TRIAL
- plugin.trial = function(display_element, trial) {
-
- //--------------------------------------
- //---------SET PARAMETERS BEGIN---------
- //--------------------------------------
-
-
- //Note on '||' logical operator: If the first option is 'undefined', it evalutes to 'false' and the second option is returned as the assignment
- trial.choices = assignParameterValue(trial.choices, []);
- trial.correct_choice = assignParameterValue(trial.correct_choice, undefined);
- trial.trial_duration = assignParameterValue(trial.trial_duration, 500);
- trial.response_ends_trial = assignParameterValue(trial.response_ends_trial, true);
- trial.number_of_apertures = assignParameterValue(trial.number_of_apertures, 1);
- trial.number_of_dots = assignParameterValue(trial.number_of_dots, 300);
- trial.number_of_sets = assignParameterValue(trial.number_of_sets, 1);
- trial.coherent_direction = assignParameterValue(trial.coherent_direction, 0);
- trial.coherence = assignParameterValue(trial.coherence, 0.5);
- trial.opposite_coherence = assignParameterValue(trial.opposite_coherence, 0);
- trial.dot_radius = assignParameterValue(trial.dot_radius, 2);
- trial.dot_life = assignParameterValue(trial.dot_life, -1);
- trial.move_distance = assignParameterValue(trial.move_distance, 1);
- trial.aperture_width = assignParameterValue(trial.aperture_width, 600);
- trial.aperture_height = assignParameterValue(trial.aperture_height, 400);
- trial.dot_color = assignParameterValue(trial.dot_color, "white");
- trial.background_color = assignParameterValue(trial.background_color, "gray");
- trial.RDK_type = assignParameterValue(trial.RDK_type, 3);
- trial.aperture_type = assignParameterValue(trial.aperture_type, 2);
- trial.reinsert_type = assignParameterValue(trial.reinsert_type, 2);
- trial.aperture_center_x = assignParameterValue(trial.aperture_center_x, window.innerWidth/2);
- trial.aperture_center_y = assignParameterValue(trial.aperture_center_y, window.innerHeight/2);
- trial.fixation_cross = assignParameterValue(trial.fixation_cross, false);
- trial.fixation_cross_width = assignParameterValue(trial.fixation_cross_width, 20);
- trial.fixation_cross_height = assignParameterValue(trial.fixation_cross_height, 20);
- trial.fixation_cross_color = assignParameterValue(trial.fixation_cross_color, "black");
- trial.fixation_cross_thickness = assignParameterValue(trial.fixation_cross_thickness, 1);
- trial.border = assignParameterValue(trial.border, false);
- trial.border_thickness = assignParameterValue(trial.border_thickness, 1);
- trial.border_color = assignParameterValue(trial.border_color, "black");
-
-
- //For square and circle, set the aperture height == aperture width
- if (apertureType == 1 || apertureType == 3) {
- trial.aperture_height = trial.aperture_width;
- }
-
- //Convert the parameter variables to those that the code below can use
-
- var nApertures = trial.number_of_apertures; //The number of apertures
- var nDots = trial.number_of_dots; //Number of dots per set (equivalent to number of dots per frame)
- var nSets = trial.number_of_sets; //Number of sets to cycle through per frame
- 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
- var coherence = trial.coherence; //Proportion of dots to move together, range from 0 to 1
- var oppositeCoherence = trial.opposite_coherence; // The coherence for the dots going the opposite direction as the coherent dots
- var dotRadius = trial.dot_radius; //Radius of each dot in pixels
- 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).
- var moveDistance = trial.move_distance; //How many pixels the dots move per frame
- 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.
- 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.
- var dotColor = trial.dot_color; //Color of the dots
- var backgroundColor = trial.background_color; //Color of the background
- var apertureCenterX = trial.aperture_center_x; // The x-coordinate of center of the aperture on the screen, in pixels
- var apertureCenterY = trial.aperture_center_y; // The y-coordinate of center of the aperture on the screen, in pixels
-
-
- /* RDK type parameter
- ** See Fig. 1 in Scase, Braddick, and Raymond (1996) for a visual depiction of these different signal selection rules and noise types
-
- -------------------
- SUMMARY:
-
- Signal Selection rule:
- -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.
- -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.
-
- Noise Type:
- -Random position: The incoherent dots appear in a random location in the aperture in each frame
- -Random walk: The incoherent dots will move in a random direction (designated randomly in each frame) in each frame.
- -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.
-
- -------------------
-
- 1 - same && random position
- 2 - same && random walk
- 3 - same && random direction
- 4 - different && random position
- 5 - different && random walk
- 6 - different && random direction */
-
- var RDK = trial.RDK_type;
-
-
- /*
- Shape of aperture
- 1 - Circle
- 2 - Ellipse
- 3 - Square
- 4 - Rectangle
- */
- var apertureType = trial.aperture_type;
-
- /*
- Out of Bounds Decision
- How we reinsert a dot that has moved outside the edges of the aperture:
- 1 - Randomly appear anywhere in the aperture
- 2 - Appear on the opposite edge of the aperture (Random if square or rectangle, reflected about origin in circle and ellipse)
- */
- var reinsertType = trial.reinsert_type;
-
- //Fixation Cross Parameters
- var fixationCross = trial.fixation_cross; //To display or not to display the cross
- var fixationCrossWidth = trial.fixation_cross_width; //The width of the fixation cross in pixels
- var fixationCrossHeight = trial.fixation_cross_height; //The height of the fixation cross in pixels
- var fixationCrossColor = trial.fixation_cross_color; //The color of the fixation cross
- var fixationCrossThickness = trial.fixation_cross_thickness; //The thickness of the fixation cross, must be positive number above 1
-
- //Border Parameters
- var border = trial.border; //To display or not to display the border
- var borderThickness = trial.border_thickness; //The width of the border in pixels
- var borderColor = trial.border_color; //The color of the border
-
-
-
- //--------------------------------------
- //----------SET PARAMETERS END----------
- //--------------------------------------
-
- //--------Set up Canvas begin-------
-
- //Create a canvas element and append it to the DOM
- var canvas = document.createElement("canvas");
- display_element.appendChild(canvas);
-
-
- //The document body IS 'display_element' (i.e. .... )
- var body = document.getElementsByClassName("jspsych-display-element")[0];
-
- //Save the current settings to be restored later
- var originalMargin = body.style.margin;
- var originalPadding = body.style.padding;
- var originalBackgroundColor = body.style.backgroundColor;
-
- //Remove the margins and paddings of the display_element
- body.style.margin = 0;
- body.style.padding = 0;
- 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
-
- //Remove the margins and padding of the canvas
- canvas.style.margin = 0;
- canvas.style.padding = 0;
- // use absolute positioning in top left corner to get rid of scroll bars
- canvas.style.position = 'absolute';
- canvas.style.top = 0;
- canvas.style.left = 0;
-
- //Get the context of the canvas so that it can be painted on.
- var ctx = canvas.getContext("2d");
-
- //Declare variables for width and height, and also set the canvas width and height to the window width and height
- var canvasWidth = canvas.width = window.innerWidth;
- var canvasHeight = canvas.height = window.innerHeight;
-
- //Set the canvas background color
- canvas.style.backgroundColor = backgroundColor;
-
- //--------Set up Canvas end-------
-
-
-
- //--------RDK variables and function calls begin--------
-
- //This is the main part of the trial that makes everything run
-
- //Global variable for the current aperture number
- var currentApertureNumber;
-
- //3D Array to hold the dots (1st D is Apertures, 2nd D is Sets, 3rd D is Dots)
- var dotArray3d = [];
-
- //Variables for different apertures (initialized in setUpMultipleApertures function below)
- var nDotsArray;
- var nSetsArray;
- var coherentDirectionArray;
- var coherenceArray;
- var oppositeCoherenceArray;
- var dotRadiusArray;
- var dotLifeArray;
- var moveDistanceArray;
- var apertureWidthArray;
- var apertureHeightArray;
- var dotColorArray;
- var apertureCenterXArray;
- var apertureCenterYArray;
-
- // Set up multiple apertures
- setUpMultipleApertures();
-
- //Declare aperture parameters for initialization based on shape (used in initializeApertureDimensions function below)
- var horizontalAxis;
- var verticalAxis;
-
- //Calculate the x and y jump sizes for coherent dots
- var coherentJumpSizeX;
- var coherentJumpSizeY;
-
- //Calculate the number of coherent, opposite coherent, and incoherent dots
- var nCoherentDots;
- var nOppositeCoherentDots;
- var nIncoherentDots;
-
- //Make the array of arrays containing dot objects
- var dotArray2d;
-
- var dotArray; //Declare a global variable to hold the current array
- var currentSetArray; //Declare and initialize a global variable to cycle through the dot arrays
-
-
- //Initialize stopping condition for animateDotMotion function that runs in a loop
- var stopDotMotion = false;
-
- //Variable to control the frame rate, to ensure that the first frame is skipped because it follows a different timing
- var firstFrame = true; //Used to skip the first frame in animate function below (in animateDotMotion function)
-
- //Variable to start the timer when the time comes
- var timerHasStarted = false;
-
- //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
- var response = {
- rt: -1,
- key: -1
- }
-
- //Declare a global timeout ID to be initialized below in animateDotMotion function and to be used in after_response function
- var timeoutID;
-
- //Declare global variable to be defined in startKeyboardListener function and to be used in end_trial function
- var keyboardListener;
-
- //Declare global variable to store the frame rate of the trial
- 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.
-
- //variable to store how many frames were presented.
- var numberOfFrames = 0;
-
- //This runs the dot motion simulation, updating it according to the frame refresh rate of the screen.
- animateDotMotion();
-
-
- //--------RDK variables and function calls end--------
-
-
-
- //-------------------------------------
- //-----------FUNCTIONS BEGIN-----------
- //-------------------------------------
-
- //----JsPsych Functions Begin----
-
-
- //Function to start the keyboard listener
- function startKeyboardListener(){
- //Start the response listener if there are choices for keys
- if (trial.choices != jsPsych.NO_KEYS) {
- //Create the keyboard listener to listen for subjects' key response
- keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response, //Function to call once the subject presses a valid key
- valid_responses: trial.choices, //The keys that will be considered a valid response and cause the callback function to be called
- rt_method: 'performance', //The type of method to record timing information.
- 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.
- 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').
- });
- }
- }
-
- //Function to end the trial proper
- function end_trial() {
-
- //Stop the dot motion animation
- stopDotMotion = true;
-
- //Store the number of frames
- numberOfFrames = frameRate.length;
-
- //Variable to store the frame rate array
- var frameRateArray = frameRate;
-
- //Calculate the average frame rate
- if(frameRate.length > 0){//Check to make sure that the array is not empty
- frameRate = frameRate.reduce((total,current) => total + current)/frameRate.length; //Sum up all the elements in the array
- }else{
- frameRate = 0; //Set to zero if the subject presses an answer before a frame is shown (i.e. if frameRate is an empty array)
- }
-
- //Kill the keyboard listener if keyboardListener has been defined
- if (typeof keyboardListener !== 'undefined') {
- jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
- }
-
- //Place all the data to be saved from this trial in one data object
- var trial_data = {
- rt: response.rt, //The response time
- response: response.key, //The key that the subject pressed
- correct: correctOrNot(), //If the subject response was correct
- choices: trial.choices, //The set of valid keys
- correct_choice: trial.correct_choice, //The correct choice
- trial_duration: trial.trial_duration, //The trial duration
- response_ends_trial: trial.response_ends_trial, //If the response ends the trial
- number_of_apertures: trial.number_of_apertures,
- number_of_dots: trial.number_of_dots,
- number_of_sets: trial.number_of_sets,
- coherent_direction: trial.coherent_direction,
- coherence: trial.coherence,
- opposite_coherence: trial.opposite_coherence,
- dot_radius: trial.dot_radius,
- dot_life: trial.dot_life,
- move_distance: trial.move_distance,
- aperture_width: trial.aperture_width,
- aperture_height: trial.aperture_height,
- dot_color: trial.dot_color,
- background_color: trial.background_color,
- RDK_type: trial.RDK_type,
- aperture_type: trial.aperture_type,
- reinsert_type: trial.reinsert_type,
- frame_rate: frameRate, //The average frame rate for the trial
- frame_rate_array: frameRateArray, //The array of ms per frame in this trial
- number_of_frames: numberOfFrames, //The number of frames in this trial
- aperture_center_x: trial.aperture_center_x,
- aperture_center_y: trial.aperture_center_y,
- fixation_cross: trial.fixation_cross,
- fixation_cross_width: trial.fixation_cross_width,
- fixation_cross_height: trial.fixation_cross_height,
- fixation_cross_color: trial.fixation_cross_color,
- fixation_cross_thickness: trial.fixation_cross_thickness,
- border: trial.border,
- border_thickness: trial.border_thickness,
- border_color: trial.border_color,
- canvas_width: canvasWidth,
- canvas_height: canvasHeight
- }
-
- //Remove the canvas as the child of the display_element element
- display_element.innerHTML='';
-
- //Restore the settings to JsPsych defaults
- body.style.margin = originalMargin;
- body.style.padding = originalPadding;
- body.style.backgroundColor = originalBackgroundColor
-
- //End this trial and move on to the next trial
- jsPsych.finishTrial(trial_data);
-
- } //End of end_trial
-
- //Function to record the first response by the subject
- function after_response(info) {
-
- //If the response has not been recorded, record it
- if (response.key == -1) {
- response = info; //Replace the response object created above
- }
-
- //If the parameter is set such that the response ends the trial, then kill the timeout and end the trial
- if (trial.response_ends_trial) {
- window.clearTimeout(timeoutID);
- end_trial();
- }
-
- } //End of after_response
-
- //Function that determines if the response is correct
- function correctOrNot(){
-
- //Check that the correct_choice has been defined
- if(typeof trial.correct_choice !== 'undefined'){
- //If the correct_choice variable holds an array
- if(trial.correct_choice.constructor === Array){ //If it is an array
- //If the elements are characters
- if(typeof trial.correct_choice[0] === 'string' || trial.correct_choice[0] instanceof String){
- var key_in_choices = trial.correct_choice.every(function(x) {
- return jsPsych.pluginAPI.compareKeys(x,response.key);
- });
- return key_in_choices; //If the response is included in the correct_choice array, return true. Else, return false.
- }
- //Else if the elements are numbers (javascript character codes)
- else if (typeof trial.correct_choice[0] === 'number'){
- console.error('Error in RDK plugin: correct_choice value must be a string.');
- }
- }
- //Else compare the char with the response key
- else{
- //If the element is a character
- if(typeof trial.correct_choice === 'string' || trial.correct_choice instanceof String){
- //Return true if the user's response matches the correct answer. Return false otherwise.
- return jsPsych.pluginAPI.compareKeys(response.key, trial.correct_choice);
- }
- //Else if the element is a number (javascript character codes)
- else if (typeof trial.correct_choice === 'number'){
- console.error('Error in RDK plugin: correct_choice value must be a string.');
- }
- }
- }
- }
-
- //----JsPsych Functions End----
-
- //----RDK Functions Begin----
-
- //Set up the variables for the apertures
- function setUpMultipleApertures(){
- nDotsArray = setParameter(nDots);
- nSetsArray = setParameter(nSets);
- coherentDirectionArray = setParameter(coherentDirection);
- coherenceArray = setParameter(coherence);
- oppositeCoherenceArray = setParameter(oppositeCoherence);
- dotRadiusArray = setParameter(dotRadius);
- dotLifeArray = setParameter(dotLife);
- moveDistanceArray = setParameter(moveDistance);
- apertureWidthArray = setParameter(apertureWidth);
- apertureHeightArray = setParameter(apertureHeight);
- dotColorArray = setParameter(dotColor);
- apertureCenterXArray = setParameter(apertureCenterX);
- apertureCenterYArray = setParameter(apertureCenterY);
- RDKArray = setParameter(RDK);
- apertureTypeArray = setParameter(apertureType);
- reinsertTypeArray = setParameter(reinsertType);
- fixationCrossArray = setParameter(fixationCross);
- fixationCrossWidthArray = setParameter(fixationCrossWidth);
- fixationCrossHeightArray = setParameter(fixationCrossHeight);
- fixationCrossColorArray = setParameter(fixationCrossColor);
- fixationCrossThicknessArray = setParameter(fixationCrossThickness);
- borderArray = setParameter(border);
- borderThicknessArray = setParameter(borderThickness);
- borderColorArray = setParameter(borderColor);
-
- currentSetArray = setParameter(0); //Always starts at zero
-
-
- //Loop through the number of apertures to make the dots
- for(currentApertureNumber = 0; currentApertureNumber < nApertures; currentApertureNumber++){
-
- //Initialize the parameters to make the 2d dot array (one for each aperture);
- initializeCurrentApertureParameters();
-
- //Make each 2d array and push it into the 3d array
- dotArray3d.push(makeDotArray2d());
- }
- }
-
- //Function to set the parameters of the array
- function setParameter(originalVariable){
- //Check if it is an array and its length matches the aperture then return the original array
- if(originalVariable.constructor === Array && originalVariable.length === nApertures){
- return originalVariable;
- }
- //Else if it is not an array, we make it an array with duplicate values
- else if(originalVariable.constructor !== Array){
-
- var tempArray = [];
-
- //Make a for loop and duplicate the values
- for(var i = 0; i < nApertures; i++){
- tempArray.push(originalVariable);
- }
- return tempArray;
- }
- //Else if the array is not long enough, then print out that error message
- else if(originalVariable.constructor === Array && originalVariable.length !== nApertures){
- 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");
- }
- //Else print a generic error
- else{
- 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.");
- }
- }
-
- //Function to set the global variables to the current aperture so that the correct dots are updated and drawn
- function initializeCurrentApertureParameters(){
-
- //Set the global variables to that relevant to the current aperture
- nDots = nDotsArray[currentApertureNumber];
- nSets = nSetsArray[currentApertureNumber];
- coherentDirection = coherentDirectionArray[currentApertureNumber];
- coherence = coherenceArray[currentApertureNumber];
- oppositeCoherence = oppositeCoherenceArray[currentApertureNumber];
- dotRadius = dotRadiusArray[currentApertureNumber];
- dotLife = dotLifeArray[currentApertureNumber];
- moveDistance = moveDistanceArray[currentApertureNumber];
- apertureWidth = apertureWidthArray[currentApertureNumber];
- apertureHeight = apertureHeightArray[currentApertureNumber];
- dotColor = dotColorArray[currentApertureNumber];
- apertureCenterX = apertureCenterXArray[currentApertureNumber];
- apertureCenterY = apertureCenterYArray[currentApertureNumber];
- RDK = RDKArray[currentApertureNumber];
- apertureType = apertureTypeArray[currentApertureNumber];
- reinsertType = reinsertTypeArray[currentApertureNumber];
- fixationCross = fixationCrossArray[currentApertureNumber];
- fixationCrossWidth = fixationCrossWidthArray[currentApertureNumber];
- fixationCrossHeight = fixationCrossHeightArray[currentApertureNumber];
- fixationCrossColor = fixationCrossColorArray[currentApertureNumber];
- fixationCrossThickness = fixationCrossThicknessArray[currentApertureNumber];
- border = borderArray[currentApertureNumber];
- borderThickness = borderThicknessArray[currentApertureNumber];
- borderColor = borderColorArray[currentApertureNumber];
-
- //Calculate the x and y jump sizes for coherent dots
- coherentJumpSizeX = calculateCoherentJumpSizeX(coherentDirection);
- coherentJumpSizeY = calculateCoherentJumpSizeY(coherentDirection);
-
- //Initialize the aperture parameters
- initializeApertureDimensions();
-
- //Calculate the number of coherent, opposite coherent, and incoherent dots
- nCoherentDots = nDots * coherence;
- nOppositeCoherentDots = nDots * oppositeCoherence;
- nIncoherentDots = nDots - (nCoherentDots + nOppositeCoherentDots);
-
- //If the 3d array has been made, then choose the 2d array and the current set
- dotArray2d = dotArray3d.length !==0 ? dotArray3d[currentApertureNumber] : undefined;
-
- }// End of initializeCurrentApertureParameters
-
- //Calculate coherent jump size in the x direction
- function calculateCoherentJumpSizeX(coherentDirection) {
- var angleInRadians = coherentDirection * Math.PI / 180;
- return moveDistance * Math.cos(angleInRadians);
- }
-
- //Calculate coherent jump size in the y direction
- function calculateCoherentJumpSizeY(coherentDirection) {
- var angleInRadians = -coherentDirection * Math.PI / 180; //Negative sign because the y-axis is flipped on screen
- return moveDistance * Math.sin(angleInRadians);
- }
-
- //Initialize the parameters for the aperture for further calculation
- function initializeApertureDimensions() {
- //For circle and square
- if (apertureType == 1 || apertureType == 3) {
- horizontalAxis = verticalAxis = apertureWidth/2;
- }
- //For ellipse and rectangle
- else if (apertureType == 2 || apertureType == 4) {
- horizontalAxis = apertureWidth / 2;
- verticalAxis = apertureHeight / 2;
- }
- }
-
- //Make the 2d array, which is an array of array of dots
- function makeDotArray2d() {
- //Declare an array to hold the sets of dot arrays
- var tempArray = []
- //Loop for each set of dot array
- for (var i = 0; i < nSets; i++) {
- tempArray.push(makeDotArray()); //Make a dot array and push it into the 2d array
- }
-
- return tempArray;
- }
-
- //Make the dot array
- function makeDotArray() {
- var tempArray = []
- for (var i = 0; i < nDots; i++) {
- //Initialize a dot to be modified and inserted into the array
- var dot = {
- x: 0, //x coordinate
- y: 0, //y coordinate
- vx: 0, //coherent x jumpsize (if any)
- vy: 0, //coherent y jumpsize (if any)
- vx2: 0, //incoherent (random) x jumpsize (if any)
- vy2: 0, //incoherent (random) y jumpsize (if any)
- latestXMove: 0, //Stores the latest x move direction for the dot (to be used in reinsertOnOppositeEdge function below)
- latestYMove: 0, //Stores the latest y move direction for the dot (to be used in reinsertOnOppositeEdge function below)
- lifeCount: Math.floor(randomNumberBetween(0, dotLife)), //Counter for the dot's life. Updates every time it is shown in a frame
- updateType: "" //String to determine how this dot is updated
- };
-
- //randomly set the x and y coordinates
- dot = resetLocation(dot);
-
- //For the same && random position RDK type
- if (RDK == 1) {
- //For coherent dots
- if (i < nCoherentDots) {
- dot = setvxvy(dot); // Set dot.vx and dot.vy
- dot.updateType = "constant direction";
- }
- //For opposite coherent dots
- else if(i >= nCoherentDots && i < (nCoherentDots + nOppositeCoherentDots)){
- dot = setvxvy(dot); // Set dot.vx and dot.vy
- dot.updateType = "opposite direction";
- }
- //For incoherent dots
- else {
- dot.updateType = "random position";
- }
- } //End of RDK==1
-
- //For the same && random walk RDK type
- if (RDK == 2) {
- //For coherent dots
- if (i < nCoherentDots) {
- dot = setvxvy(dot); // Set dot.vx and dot.vy
- dot.updateType = "constant direction";
- }
- //For opposite coherent dots
- else if(i >= nCoherentDots && i < (nCoherentDots + nOppositeCoherentDots)){
- dot = setvxvy(dot); // Set dot.vx and dot.vy
- dot.updateType = "opposite direction";
- }
- //For incoherent dots
- else {
- dot.updateType = "random walk";
- }
- } //End of RDK==2
-
- //For the same && random direction RDK type
- if (RDK == 3) {
- //For coherent dots
- if (i < nCoherentDots) {
- dot = setvxvy(dot); // Set dot.vx and dot.vy
- dot.updateType = "constant direction";
- }
- //For opposite coherent dots
- else if(i >= nCoherentDots && i < (nCoherentDots + nOppositeCoherentDots)){
- dot = setvxvy(dot); // Set dot.vx and dot.vy
- dot.updateType = "opposite direction";
- }
- //For incoherent dots
- else {
- setvx2vy2(dot); // Set dot.vx2 and dot.vy2
- dot.updateType = "random direction";
- }
- } //End of RDK==3
-
- //For the different && random position RDK type
- if (RDK == 4) {
- //For all dots
- dot = setvxvy(dot); // Set dot.vx and dot.vy
- dot.updateType = "constant direction or opposite direction or random position";
- } //End of RDK==4
-
- //For the different && random walk RDK type
- if (RDK == 5) {
- //For all dots
- dot = setvxvy(dot); // Set dot.vx and dot.vy
- dot.updateType = "constant direction or opposite direction or random walk";
- } //End of RDK==5
-
- //For the different && random direction RDK type
- if (RDK == 6) {
- //For all dots
- dot = setvxvy(dot); // Set dot.vx and dot.vy
- //Each dot will have its own alternate direction of motion
- setvx2vy2(dot); // Set dot.vx2 and dot.vy2
- dot.updateType = "constant direction or opposite direction or random direction";
- } //End of RDK==6
-
- tempArray.push(dot);
- } //End of for loop
- return tempArray;
- }
-
- //Function to update all the dots all the apertures and then draw them
- function updateAndDraw(){
-
- //Three for loops that do things in sequence: clear, update, and draw dots.
-
- // Clear all the current dots
- for(currentApertureNumber = 0; currentApertureNumber < nApertures; currentApertureNumber++){
-
- //Initialize the variables for each parameter
- initializeCurrentApertureParameters(currentApertureNumber);
-
- //Clear the canvas by drawing over the current dots
- clearDots();
- }
-
- // Update all the relevant dots
- for(currentApertureNumber = 0; currentApertureNumber < nApertures; currentApertureNumber++){
-
- //Initialize the variables for each parameter
- initializeCurrentApertureParameters(currentApertureNumber);
-
- //Update the dots
- updateDots();
- }
-
- // Draw all the relevant dots on the canvas
- for(currentApertureNumber = 0; currentApertureNumber < nApertures; currentApertureNumber++){
-
- //Initialize the variables for each parameter
- initializeCurrentApertureParameters(currentApertureNumber);
-
- //Draw on the canvas
- draw();
- }
- }
-
- //Function that clears the dots on the canvas by drawing over it with the color of the baclground
- function clearDots(){
-
- //Load in the current set of dot array for easy handling
- var dotArray = dotArray2d[currentSetArray[currentApertureNumber]];
-
- //Loop through the dots one by one and draw them
- for (var i = 0; i < nDots; i++) {
- dot = dotArray[i];
- ctx.beginPath();
- ctx.arc(dot.x, dot.y, dotRadius+1, 0, Math.PI * 2);
- ctx.fillStyle = backgroundColor;
- ctx.fill();
- }
- }
-
- //Draw the dots on the canvas after they're updated
- function draw() {
-
- //Load in the current set of dot array for easy handling
- var dotArray = dotArray2d[currentSetArray[currentApertureNumber]];
-
- //Loop through the dots one by one and draw them
- for (var i = 0; i < nDots; i++) {
- dot = dotArray[i];
- ctx.beginPath();
- ctx.arc(dot.x, dot.y, dotRadius, 0, Math.PI * 2);
- ctx.fillStyle = dotColor;
- ctx.fill();
- }
-
- //Draw the fixation cross if we want it
- if(fixationCross === true){
- //Horizontal line
- ctx.beginPath();
- ctx.lineWidth = fixationCrossThickness;
- ctx.moveTo(canvasWidth/2 - fixationCrossWidth, canvasHeight/2);
- ctx.lineTo(canvasWidth/2 + fixationCrossWidth, canvasHeight/2);
- ctx.strokeStyle = fixationCrossColor;
- ctx.stroke();
-
- //Vertical line
- ctx.beginPath();
- ctx.lineWidth = fixationCrossThickness;
- ctx.moveTo(canvasWidth/2, canvasHeight/2 - fixationCrossHeight);
- ctx.lineTo(canvasWidth/2, canvasHeight/2 + fixationCrossHeight);
- ctx.strokeStyle = fixationCrossColor;
- ctx.stroke();
- }
-
- //Draw the border if we want it
- if(border === true){
-
- //For circle and ellipse
- if(apertureType === 1 || apertureType === 2){
- ctx.lineWidth = borderThickness;
- ctx.strokeStyle = borderColor;
- ctx.beginPath();
- ctx.ellipse(apertureCenterX, apertureCenterY, horizontalAxis+(borderThickness/2), verticalAxis+(borderThickness/2), 0, 0, Math.PI*2);
- ctx.stroke();
- }//End of if circle or ellipse
-
- //For square and rectangle
- if(apertureType === 3 || apertureType === 4){
- ctx.lineWidth = borderThickness;
- ctx.strokeStyle = borderColor;
- ctx.strokeRect(apertureCenterX-horizontalAxis-(borderThickness/2), apertureCenterY-verticalAxis-(borderThickness/2), (horizontalAxis*2)+borderThickness, (verticalAxis*2)+borderThickness);
- }//End of if square or
-
- }//End of if border === true
-
- }//End of draw
-
- //Update the dots with their new location
- function updateDots() {
-
- //Cycle through to the next set of dots
- if (currentSetArray[currentApertureNumber] == nSets - 1) {
- currentSetArray[currentApertureNumber] = 0;
- } else {
- currentSetArray[currentApertureNumber] = currentSetArray[currentApertureNumber] + 1;
- }
-
- //Load in the current set of dot array for easy handling
- var dotArray = dotArray2d[currentSetArray[currentApertureNumber]];
-
- //Load in the current set of dot array for easy handling
- //dotArray = dotArray2d[currentSetArray[currentApertureNumber]]; //Global variable, so the draw function also uses this array
-
- //Loop through the dots one by one and update them accordingly
- for (var i = 0; i < nDots; i++) {
- var dot = dotArray[i]; //Load the current dot into the variable for easy handling
-
- //Generate a random value
- var randomValue = Math.random();
-
- //Update based on the dot's update type
- if (dot.updateType == "constant direction") {
- dot = constantDirectionUpdate(dot);
- } else if (dot.updateType == "opposite direction") {
- dot = oppositeDirectionUpdate(dot);
- } else if (dot.updateType == "random position") {
- dot = resetLocation(dot);
- } else if (dot.updateType == "random walk") {
- dot = randomWalkUpdate(dot);
- } else if (dot.updateType == "random direction") {
- dot = randomDirectionUpdate(dot);
- } else if (dot.updateType == "constant direction or opposite direction or random position") {
-
- //Randomly select if the dot goes in a constant direction or random position, weighted based on the coherence level
- if (randomValue < coherence) {
- dot = constantDirectionUpdate(dot);
- } else if(randomValue >= coherence && randomValue < (coherence + oppositeCoherence)){
- dot = oppositeDirectionUpdate(dot);
- } else {
- dot = resetLocation(dot);
- }
- } else if (dot.updateType == "constant direction or opposite direction or random walk") {
- //Randomly select if the dot goes in a constant direction or random walk, weighted based on the coherence level
- if (randomValue < coherence) {
- dot = constantDirectionUpdate(dot);
- } else if(randomValue >= coherence && randomValue < (coherence + oppositeCoherence)){
- dot = oppositeDirectionUpdate(dot);
- } else {
- dot = randomWalkUpdate(dot);
- }
- } else if (dot.updateType == "constant direction or opposite direction or random direction") {
- //Randomly select if the dot goes in a constant direction or random direction, weighted based on the coherence level
- if (randomValue < coherence) {
- dot = constantDirectionUpdate(dot);
- } else if(randomValue >= coherence && randomValue < (coherence + oppositeCoherence)){
- dot = oppositeDirectionUpdate(dot);
- } else {
- dot = randomDirectionUpdate(dot);
- }
- }//End of if dot.updateType == ...
-
- //Increment the life count
- dot.lifeCount++;
-
- //Check if out of bounds or if life ended
- if (lifeEnded(dot)) {
- dot = resetLocation(dot);
- }
-
- //If it goes out of bounds, do what is necessary (reinsert randomly or reinsert on the opposite edge) based on the parameter chosen
- if (outOfBounds(dot)) {
- switch (reinsertType) {
- case 1:
- dot = resetLocation(dot);
- break;
- case 2:
- dot = reinsertOnOppositeEdge(dot);
- break;
- } //End of switch statement
- } //End of if
-
- } //End of for loop
- } //End of updateDots function
-
- //Function to check if dot life has ended
- function lifeEnded(dot) {
- //If we want infinite dot life
- if (dotLife < 0) {
- dot.lifeCount = 0; //resetting to zero to save memory. Otherwise it might increment to huge numbers.
- return false;
- }
- //Else if the dot's life has reached its end
- else if (dot.lifeCount >= dotLife) {
- dot.lifeCount = 0;
- return true;
- }
- //Else the dot's life has not reached its end
- else {
- return false;
- }
- }
-
- //Function to check if dot is out of bounds
- function outOfBounds(dot) {
- //For circle and ellipse
- if (apertureType == 1 || apertureType == 2) {
- if (dot.x < xValueNegative(dot.y) || dot.x > xValuePositive(dot.y) || dot.y < yValueNegative(dot.x) || dot.y > yValuePositive(dot.x)) {
- return true;
- } else {
- return false;
- }
- }
- //For square and rectangle
- if (apertureType == 3 || apertureType == 4) {
- if (dot.x < (apertureCenterX) - horizontalAxis || dot.x > (apertureCenterX) + horizontalAxis || dot.y < (apertureCenterY) - verticalAxis || dot.y > (apertureCenterY) + verticalAxis) {
- return true;
- } else {
- return false;
- }
- }
-
- }
-
- //Set the vx and vy for the dot to the coherent jump sizes of the X and Y directions
- function setvxvy(dot) {
- dot.vx = coherentJumpSizeX;
- dot.vy = coherentJumpSizeY;
- return dot;
- }
-
- //Set the vx2 and vy2 based on a random angle
- function setvx2vy2(dot) {
- //Generate a random angle of movement
- var theta = randomNumberBetween(-Math.PI, Math.PI);
- //Update properties vx2 and vy2 with the alternate directions
- dot.vx2 = Math.cos(theta) * moveDistance;
- dot.vy2 = -Math.sin(theta) * moveDistance;
- return dot;
- }
-
- //Updates the x and y coordinates by moving it in the x and y coherent directions
- function constantDirectionUpdate(dot) {
- dot.x += dot.vx;
- dot.y += dot.vy;
- dot.latestXMove = dot.vx;
- dot.latestYMove = dot.vy;
- return dot;
- }
-
- //Updates the x and y coordinates by moving it in the opposite x and y coherent directions
- function oppositeDirectionUpdate(dot) {
- dot.x -= dot.vx;
- dot.y -= dot.vy;
- dot.latestXMove = -dot.vx;
- dot.latestYMove = -dot.vy;
- return dot;
- }
-
- //Creates a new angle to move towards and updates the x and y coordinates
- function randomWalkUpdate(dot) {
- //Generate a random angle of movement
- var theta = randomNumberBetween(-Math.PI, Math.PI);
- //Generate the movement from the angle
- dot.latestXMove = Math.cos(theta) * moveDistance;
- dot.latestYMove = -Math.sin(theta) * moveDistance;
- //Update x and y coordinates with the new location
- dot.x += dot.latestXMove;
- dot.y += dot.latestYMove;
- return dot;
- }
-
- //Updates the x and y coordinates with the alternative move direction
- function randomDirectionUpdate(dot) {
- dot.x += dot.vx2;
- dot.y += dot.vy2;
- dot.latestXMove = dot.vx2;
- dot.latestYMove = dot.vy2;
- return dot;
- }
-
- //Calculates a random position on the opposite edge to reinsert the dot
- function reinsertOnOppositeEdge(dot) {
- //If it is a circle or ellipse
- if (apertureType == 1 || apertureType == 2) {
- //Bring the dot back into the aperture by moving back one step
- dot.x -= dot.latestXMove;
- dot.y -= dot.latestYMove;
-
- //Move the dot to the position relative to the origin to be reflected about the origin
- dot.x -= apertureCenterX;
- dot.y -= apertureCenterY;
-
- //Reflect the dot about the origin
- dot.x = -dot.x;
- dot.y = -dot.y;
-
- //Move the dot back to the center of the screen
- dot.x += apertureCenterX;
- dot.y += apertureCenterY;
-
- } //End of if apertureType == 1 | == 2
-
- //If it is a square or rectangle, re-insert on one of the opposite edges
- if (apertureType == 3 || apertureType == 4) {
-
- /* 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.
- E.g.
- Aperture is 100 px high and 200px wide
- Dot is moving 3 px in x direction and 4px in y direction
- Weight on vertical edge (sides) = (100/(100+200)) * (|3| / (|3| + |4|)) = 1/7
- Weight on horizontal edge (top or bottom) = (200/(100+200)) * (|4| / (|3| + |4|)) = 8/21
-
- The weights above are the ratios to one another.
- E.g. (cont.)
- Ratio (vertical edge : horizontal edge) == (1/7 : 8/21)
- Total probability space = 1/7 + 8/21 = 11/21
- Probability that dot appears on vertical edge = (1/7)/(11/21) = 3/11
- Probability that dot appears on horizontal edge = (8/21)/(11/21) = 8/11
- */
-
- //Get the absolute values of the latest X and Y moves and store them in variables for easy handling.
- var absX = Math.abs(dot.latestXMove);
- var absY = Math.abs(dot.latestYMove);
- //Calculate the direction weights based on direction the dot was moving
- var weightInXDirection = absX / (absX + absY);
- var weightInYDirection = absY / (absX + absY);
- //Calculate the weight of the edge the dot should appear from, based on direction of dot and ratio of the aperture edges
- var weightOnVerticalEdge = (verticalAxis / (verticalAxis + horizontalAxis)) * weightInXDirection;
- var weightOnHorizontalEdge = (horizontalAxis / (verticalAxis + horizontalAxis)) * weightInYDirection;
-
-
- //Generate a bounded random number to determine if the dot should appear on the vertical edge or the horizontal edge
- if (weightOnVerticalEdge > (weightOnHorizontalEdge + weightOnVerticalEdge) * Math.random()) { //If yes, appear on the left or right edge (vertical edge)
- if (dot.latestXMove < 0) { //If dots move left, appear on right edge
- dot.x = apertureCenterX + horizontalAxis;
- dot.y = randomNumberBetween((apertureCenterY) - verticalAxis, (apertureCenterY) + verticalAxis);
- } else { //Else dots move right, so they should appear on the left edge
- dot.x = apertureCenterX - horizontalAxis;
- dot.y = randomNumberBetween((apertureCenterY) - verticalAxis, (apertureCenterY) + verticalAxis);
- }
- } else { //Else appear on the top or bottom edge (horizontal edge)
- if (dot.latestYMove < 0) { //If dots move upwards, then appear on bottom edge
- dot.y = apertureCenterY + verticalAxis;
- dot.x = randomNumberBetween((apertureCenterX) - horizontalAxis, (apertureCenterX) + horizontalAxis)
- } else { //If dots move downwards, then appear on top edge
- dot.y = apertureCenterY - verticalAxis;
- dot.x = randomNumberBetween((apertureCenterX) - horizontalAxis, (apertureCenterX) + horizontalAxis)
- }
- }
- } //End of apertureType == 3
- return dot;
- } //End of reinsertOnOppositeEdge
-
- //Calculate the POSITIVE y value of a point on the edge of the ellipse given an x-value
- function yValuePositive(x) {
- 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)
- 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
- }
-
- //Calculate the NEGATIVE y value of a point on the edge of the ellipse given an x-value
- function yValueNegative(x) {
- 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)
- 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
- }
-
- //Calculate the POSITIVE x value of a point on the edge of the ellipse given a y-value
- function xValuePositive(y) {
- 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)
- 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
- }
-
- //Calculate the NEGATIVE x value of a point on the edge of the ellipse given a y-value
- function xValueNegative(y) {
- 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)
- 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
- }
-
- //Calculate a random x and y coordinate in the ellipse
- function resetLocation(dot) {
-
- //For circle and ellipse
- if (apertureType == 1 || apertureType == 2) {
- var phi = randomNumberBetween(-Math.PI, Math.PI);
- var rho = Math.random();
-
- x = Math.sqrt(rho) * Math.cos(phi);
- y = Math.sqrt(rho) * Math.sin(phi);
-
- x = x * horizontalAxis + apertureCenterX;
- y = y * verticalAxis + apertureCenterY;
-
- dot.x = x;
- dot.y = y;
- }
- //For square and rectangle
- else if (apertureType == 3 || apertureType == 4) {
- dot.x = randomNumberBetween((apertureCenterX) - horizontalAxis, (apertureCenterX) + horizontalAxis); //Between the left and right edges of the square / rectangle
- dot.y = randomNumberBetween((apertureCenterY) - verticalAxis, (apertureCenterY) + verticalAxis); //Between the top and bottom edges of the square / rectangle
- }
-
- return dot;
- }
-
- //Generates a random number (with decimals) between 2 values
- function randomNumberBetween(lowerBound, upperBound) {
- return lowerBound + Math.random() * (upperBound - lowerBound);
- }
-
- //Function to make the dots move on the canvas
- function animateDotMotion() {
- //frameRequestID saves a long integer that is the ID of this frame request. The ID is then used to terminate the request below.
- var frameRequestID = window.requestAnimationFrame(animate);
-
- //Start to listen to subject's key responses
- startKeyboardListener();
-
- //Delare a timestamp
- var previousTimestamp;
-
- function animate() {
- //If stopping condition has been reached, then stop the animation
- if (stopDotMotion) {
- window.cancelAnimationFrame(frameRequestID); //Cancels the frame request
- }
- //Else continue with another frame request
- else {
- frameRequestID = window.requestAnimationFrame(animate); //Calls for another frame request
-
- //If the timer has not been started and it is set, then start the timer
- if ( (!timerHasStarted) && (trial.trial_duration > 0) ){
- //If the trial duration is set, then set a timer to count down and call the end_trial function when the time is up
- //(If the subject did not press a valid keyboard response within the trial duration, then this will end the trial)
- timeoutID = window.setTimeout(end_trial,trial.trial_duration); //This timeoutID is then used to cancel the timeout should the subject press a valid key
- //The timer has started, so we set the variable to true so it does not start more timers
- timerHasStarted = true;
- }
-
- updateAndDraw(); //Update and draw each of the dots in their respective apertures
-
- //If this is before the first frame, then start the timestamp
- if(previousTimestamp === undefined){
- previousTimestamp = performance.now();
- }
- //Else calculate the time and push it into the array
- else{
- var currentTimeStamp = performance.now(); //Variable to hold current timestamp
- frameRate.push(currentTimeStamp - previousTimestamp); //Push the interval into the frameRate array
- previousTimestamp = currentTimeStamp; //Reset the timestamp
- }
- }
- }
- }
-
- //----RDK Functions End----
-
- //----General Functions Begin//----
-
- //Function to assign the default values for the staircase parameters
- function assignParameterValue(argument, defaultValue){
- return typeof argument !== 'undefined' ? argument : defaultValue;
- }
-
- //----General Functions End//----
-
-
- //-------------------------------------
- //-----------FUNCTIONS END-------------
- //-------------------------------------
-
-
- }; // END OF TRIAL
-
- //Return the plugin object which contains the trial
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-reconstruction.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-reconstruction.js
deleted file mode 100644
index e39bb180..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-reconstruction.js
+++ /dev/null
@@ -1,134 +0,0 @@
-/**
- * jspsych-reconstruction
- * a jspsych plugin for a reconstruction task where the subject recreates
- * a stimulus from memory
- *
- * Josh de Leeuw
- *
- * documentation: docs.jspsych.org
- *
- */
-
-
-jsPsych.plugins['reconstruction'] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'reconstruction',
- description: '',
- parameters: {
- stim_function: {
- type: jsPsych.plugins.parameterType.FUNCTION,
- pretty_name: 'Stimulus function',
- default: undefined,
- description: 'A function with a single parameter that returns an HTML-formatted string representing the stimulus.'
- },
- starting_value: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: 'Starting value',
- default: 0.5,
- description: 'The starting value of the stimulus parameter.'
- },
- step_size: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: 'Step size',
- default: 0.05,
- description: 'The change in the stimulus parameter caused by pressing one of the modification keys.'
- },
- key_increase: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Key increase',
- default: 'h',
- description: 'The key to press for increasing the parameter value.'
- },
- key_decrease: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Key decrease',
- default: 'g',
- description: 'The key to press for decreasing the parameter value.'
- },
- button_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button label',
- default: 'Continue',
- description: 'The text that appears on the button to finish the trial.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- // current param level
- var param = trial.starting_value;
-
- // set-up key listeners
- var after_response = function(info) {
-
- //console.log('fire');
-
- var key_i = trial.key_increase;
- var key_d = trial.key_decrease;
-
- // get new param value
- if (jsPsych.pluginAPI.compareKeys(info.key, key_i)) {
- param = param + trial.step_size;
- } else if (jsPsych.pluginAPI.compareKeys(info.key, key_d)) {
- param = param - trial.step_size;
- }
- param = Math.max(Math.min(1, param), 0);
-
- // refresh the display
- draw(param);
- }
-
- // listen for responses
- var key_listener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: [trial.key_increase, trial.key_decrease],
- rt_method: 'performance',
- persist: true,
- allow_held_key: true
- });
- // draw first iteration
- draw(param);
-
- function draw(param) {
-
- //console.log(param);
-
- display_element.innerHTML = '
'+trial.stim_function(param)+'
';
-
- // add submit button
- display_element.innerHTML += '';
-
- display_element.querySelector('#jspsych-reconstruction-next').addEventListener('click', endTrial);
- }
-
- function endTrial() {
- // measure response time
- var endTime =performance.now();
- var response_time = endTime - startTime;
-
- // clear keyboard response
- jsPsych.pluginAPI.cancelKeyboardResponse(key_listener);
-
- // save data
- var trial_data = {
- rt: response_time,
- final_value: param,
- start_value: trial.starting_value
- };
-
- display_element.innerHTML = '';
-
- // next trial
- jsPsych.finishTrial(trial_data);
- }
-
- var startTime = performance.now();
-
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-resize.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-resize.js
deleted file mode 100644
index 833e7ae1..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-resize.js
+++ /dev/null
@@ -1,166 +0,0 @@
-/**
-* jspsych-resize
-* Steve Chao
-*
-* plugin for controlling the real world size of the display
-*
-* documentation: docs.jspsych.org
-*
-**/
-
-jsPsych.plugins["resize"] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'resize',
- description: '',
- parameters: {
- item_height: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Item height',
- default: 1,
- description: 'The height of the item to be measured.'
- },
- item_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Item width',
- default: 1,
- description: 'The width of the item to be measured.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'The content displayed below the resizable box and above the button.'
- },
- pixels_per_unit: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Pixels per unit',
- default: 100,
- description: 'After the scaling factor is applied, this many pixels will equal one unit of measurement.'
- },
- starting_size: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Starting size',
- default: 100,
- description: 'The initial size of the box, in pixels, along the larget dimension.'
- },
- button_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button label',
- default: 'Continue',
- description: 'Label to display on the button to complete calibration.'
- },
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- var aspect_ratio = trial.item_width / trial.item_height;
-
- // variables to determine div size
- if(trial.item_width >= trial.item_height){
- var start_div_width = trial.starting_size;
- var start_div_height = Math.round(trial.starting_size / aspect_ratio);
- } else {
- var start_div_height = trial.starting_size;
- var start_div_width = Math.round(trial.starting_size * aspect_ratio);
- }
-
- // create html for display
- var html ='
';
- html += '';
- html += '
';
- if (trial.prompt !== null){
- html += trial.prompt;
- }
- html += ''+trial.button_label+'';
-
- // render
- display_element.innerHTML = html;
-
- // listens for the click
- document.getElementById("jspsych-resize-btn").addEventListener('click', function() {
- scale();
- end_trial();
- });
-
- var dragging = false;
- var origin_x, origin_y;
- var cx, cy;
-
- var mousedownevent = function(e){
- e.preventDefault();
- dragging = true;
- origin_x = e.pageX;
- origin_y = e.pageY;
- cx = parseInt(scale_div.style.width);
- cy = parseInt(scale_div.style.height);
- }
-
- display_element.querySelector('#jspsych-resize-handle').addEventListener('mousedown', mousedownevent);
-
- var mouseupevent = function(e){
- dragging = false;
- }
-
- document.addEventListener('mouseup', mouseupevent);
-
- var scale_div = display_element.querySelector('#jspsych-resize-div');
-
- var resizeevent = function(e){
- if(dragging){
- var dx = (e.pageX - origin_x);
- var dy = (e.pageY - origin_y);
-
- if(Math.abs(dx) >= Math.abs(dy)){
- scale_div.style.width = Math.round(Math.max(20, cx+dx*2)) + "px";
- scale_div.style.height = Math.round(Math.max(20, cx+dx*2) / aspect_ratio ) + "px";
- } else {
- scale_div.style.height = Math.round(Math.max(20, cy+dy*2)) + "px";
- scale_div.style.width = Math.round(aspect_ratio * Math.max(20, cy+dy*2)) + "px";
- }
- }
- }
-
- document.addEventListener('mousemove', resizeevent);
-
- // scales the stimulus
- var scale_factor;
- var final_height_px, final_width_px;
- function scale() {
- final_width_px = scale_div.offsetWidth;
- //final_height_px = scale_div.offsetHeight;
-
- var pixels_unit_screen = final_width_px / trial.item_width;
-
- scale_factor = pixels_unit_screen / trial.pixels_per_unit;
- document.getElementById("jspsych-content").style.transform = "scale(" + scale_factor + ")";
- };
-
-
- // function to end trial
- function end_trial() {
-
- // clear document event listeners
- document.removeEventListener('mousemove', resizeevent);
- document.removeEventListener('mouseup', mouseupevent);
-
- // clear the screen
- display_element.innerHTML = '';
-
- // finishes trial
-
- var trial_data = {
- final_height_px: final_height_px,
- final_width_px: final_width_px,
- scale_factor: scale_factor
- }
-
- jsPsych.finishTrial(trial_data);
- }
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-same-different-html.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-same-different-html.js
deleted file mode 100644
index 76e1d2a3..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-same-different-html.js
+++ /dev/null
@@ -1,168 +0,0 @@
-/**
- * jspsych-same-different
- * Josh de Leeuw
- *
- * plugin for showing two stimuli sequentially and getting a same / different judgment
- *
- * documentation: docs.jspsych.org
- *
- */
-
-jsPsych.plugins['same-different-html'] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'same-different-html',
- description: '',
- parameters: {
- stimuli: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'Stimuli',
- default: undefined,
- array: true,
- description: 'The HTML content to be displayed.'
- },
- answer: {
- type: jsPsych.plugins.parameterType.SELECT,
- pretty_name: 'Answer',
- options: ['same', 'different'],
- default: undefined,
- description: 'Either "same" or "different".'
- },
- same_key: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Same key',
- default: 'q',
- description: ''
- },
- different_key: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Different key',
- default: 'p',
- description: 'The key that subjects should press to indicate that the two stimuli are the same.'
- },
- first_stim_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'First stimulus duration',
- default: null,
- description: 'How long to show the first stimulus for in milliseconds. If null, then the stimulus will remain on the screen until any keypress is made.'
- },
- gap_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Gap duration',
- default: 500,
- description: 'How long to show a blank screen in between the two stimuli.'
- },
- second_stim_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Second stimulus duration',
- default: null,
- description: 'How long to show the second stimulus for in milliseconds. If null, then the stimulus will remain on the screen until a valid response is made.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the stimulus.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- display_element.innerHTML = '
';
- //show prompt here
- if (trial.prompt !== null) {
- html += trial.prompt;
- }
- display_element.innerHTML = html;
-
- if (trial.second_stim_duration > 0) {
- jsPsych.pluginAPI.setTimeout(function() {
- display_element.querySelector('.jspsych-same-different-stimulus').style.visibility = 'hidden';
- }, trial.second_stim_duration);
- }
-
-
-
- var after_response = function(info) {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- var correct = false;
-
- var skey = trial.same_key;
- var dkey = trial.different_key;
-
- if (jsPsych.pluginAPI.compareKeys(info.key, skey) && trial.answer == 'same') {
- correct = true;
- }
-
- if (jsPsych.pluginAPI.compareKeys(info.key, dkey) && trial.answer == 'different') {
- correct = true;
- }
-
- var trial_data = {
- rt: info.rt,
- answer: trial.answer,
- correct: correct,
- stimulus: [trial.stimuli[0], trial.stimuli[1]],
- response: info.key
- };
- if (first_stim_info) {
- trial_data["rt_stim1"] = first_stim_info.rt;
- trial_data["response_stim1"] = first_stim_info.key;
- }
-
- display_element.innerHTML = '';
-
- jsPsych.finishTrial(trial_data);
- }
-
- jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: [trial.same_key, trial.different_key],
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
-
- }
-
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-same-different-image.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-same-different-image.js
deleted file mode 100644
index 1c469d6a..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-same-different-image.js
+++ /dev/null
@@ -1,169 +0,0 @@
-/**
- * jspsych-same-different
- * Josh de Leeuw
- *
- * plugin for showing two stimuli sequentially and getting a same / different judgment
- *
- * documentation: docs.jspsych.org
- *
- */
-
-jsPsych.plugins['same-different-image'] = (function() {
-
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('same-different-image', 'stimuli', 'image')
-
- plugin.info = {
- name: 'same-different-image',
- description: '',
- parameters: {
- stimuli: {
- type: jsPsych.plugins.parameterType.IMAGE,
- pretty_name: 'Stimuli',
- default: undefined,
- array: true,
- description: 'The images to be displayed.'
- },
- answer: {
- type: jsPsych.plugins.parameterType.SELECT,
- pretty_name: 'Answer',
- options: ['same', 'different'],
- default: undefined,
- description: 'Either "same" or "different".'
- },
- same_key: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Same key',
- default: 'q',
- description: ''
- },
- different_key: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Different key',
- default: 'p',
- description: 'The key that subjects should press to indicate that the two stimuli are the same.'
- },
- first_stim_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'First stimulus duration',
- default: null,
- description: 'How long to show the first stimulus for in milliseconds. If null, then the stimulus will remain on the screen until any keypress is made.'
- },
- gap_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Gap duration',
- default: 500,
- description: 'How long to show a blank screen in between the two stimuli.'
- },
- second_stim_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Second stimulus duration',
- default: null,
- description: 'How long to show the second stimulus for in milliseconds. If null, then the stimulus will remain on the screen until a valid response is made.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the stimulus.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- display_element.innerHTML = '';
-
- var first_stim_info;
- if (trial.first_stim_duration > 0) {
- jsPsych.pluginAPI.setTimeout(function() {
- showBlankScreen();
- }, trial.first_stim_duration);
- } else {
- function afterKeyboardResponse(info) {
- first_stim_info = info;
- showBlankScreen();
- }
- jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: afterKeyboardResponse,
- valid_responses: trial.advance_key,
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
- }
-
- function showBlankScreen() {
- display_element.innerHTML = '';
-
- jsPsych.pluginAPI.setTimeout(function() {
- showSecondStim();
- }, trial.gap_duration);
- }
-
- function showSecondStim() {
-
- var html = '';
- //show prompt
- if (trial.prompt !== null) {
- html += trial.prompt;
- }
-
- display_element.innerHTML = html;
-
- if (trial.second_stim_duration > 0) {
- jsPsych.pluginAPI.setTimeout(function() {
- display_element.querySelector('.jspsych-same-different-stimulus').style.visibility = 'hidden';
- }, trial.second_stim_duration);
- }
-
- var after_response = function(info) {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- var correct = false;
-
- var skey = trial.same_key;
- var dkey = trial.different_key;
-
- if (jsPsych.pluginAPI.compareKeys(info.key,skey) && trial.answer == 'same') {
- correct = true;
- }
-
- if (jsPsych.pluginAPI.compareKeys(info.key, dkey) && trial.answer == 'different') {
- correct = true;
- }
-
- var trial_data = {
- rt: info.rt,
- answer: trial.answer,
- correct: correct,
- stimulus: [trial.stimuli[0], trial.stimuli[1]],
- response: info.key
- };
- if (first_stim_info) {
- trial_data["rt_stim1"] = first_stim_info.rt;
- trial_data["response_stim1"] = first_stim_info.key;
- }
-
- display_element.innerHTML = '';
-
- jsPsych.finishTrial(trial_data);
- }
-
- jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: [trial.same_key, trial.different_key],
- rt_method: 'performance',
- persist: false,
- allow_held_key: false
- });
-
- }
-
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-serial-reaction-time-mouse.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-serial-reaction-time-mouse.js
deleted file mode 100644
index 18121f65..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-serial-reaction-time-mouse.js
+++ /dev/null
@@ -1,212 +0,0 @@
-/**
- * jspsych-serial-reaction-time
- * Josh de Leeuw
- *
- * plugin for running a serial reaction time task
- *
- * documentation: docs.jspsych.org
- *
- **/
-
-jsPsych.plugins["serial-reaction-time-mouse"] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'serial-reaction-time-mouse',
- description: '',
- parameters: {
- target: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Target',
- array: true,
- default: undefined,
- description: 'The location of the target. The array should be the [row, column] of the target.'
- },
- grid: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Grid',
- array: true,
- default: [[1,1,1,1]],
- description: 'This array represents the grid of boxes shown on the screen.'
- },
- grid_square_size: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Grid square size',
- default: 100,
- description: 'The width and height in pixels of each square in the grid.'
- },
- target_color: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Target color',
- default: "#999",
- description: 'The color of the target square.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, the trial ends after a mouse click.'
- },
- pre_target_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Pre-target duration',
- default: 0,
- description: 'The number of milliseconds to display the grid before the target changes color.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show the trial'
- },
- fade_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Fade duration',
- default: null,
- description: 'If a positive number, the target will progressively change color at the start of the trial, with the transition lasting this many milliseconds.'
- },
- allow_nontarget_responses: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Allow nontarget response',
- default: false,
- description: 'If true, then user can make nontarget response.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the stimulus'
- },
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- var startTime = -1;
- var response = {
- rt: null,
- row: null,
- column: null
- }
-
- // display stimulus
- var stimulus = this.stimulus(trial.grid, trial.grid_square_size);
- display_element.innerHTML = stimulus;
-
-
- if(trial.pre_target_duration <= 0){
- showTarget();
- } else {
- jsPsych.pluginAPI.setTimeout(function(){
- showTarget();
- }, trial.pre_target_duration);
- }
-
- //show prompt if there is one
- if (trial.prompt !== null) {
- display_element.insertAdjacentHTML('beforeend', trial.prompt);
- }
-
- function showTarget(){
- var resp_targets;
- if(!trial.allow_nontarget_responses){
- resp_targets = [display_element.querySelector('#jspsych-serial-reaction-time-stimulus-cell-'+trial.target[0]+'-'+trial.target[1])]
- } else {
- resp_targets = display_element.querySelectorAll('.jspsych-serial-reaction-time-stimulus-cell');
- }
- for(var i=0; i";
- for(var i=0; i";
- for(var j=0; j";
- if(typeof labels !=='undefined' && labels[i][j] !== false){
- stimulus += labels[i][j]
- }
- stimulus += "";
- }
- stimulus += "";
- }
- stimulus += "";
-
- return stimulus
- }
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-serial-reaction-time.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-serial-reaction-time.js
deleted file mode 100644
index baa736e9..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-serial-reaction-time.js
+++ /dev/null
@@ -1,247 +0,0 @@
-/**
- * jspsych-serial-reaction-time
- * Josh de Leeuw
- *
- * plugin for running a serial reaction time task
- *
- * documentation: docs.jspsych.org
- *
- **/
-
-jsPsych.plugins["serial-reaction-time"] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'serial-reaction-time',
- description: '',
- parameters: {
- grid: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Grid',
- array: true,
- default: [[1,1,1,1]],
- description: 'This array represents the grid of boxes shown on the screen.'
- },
- target: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Target',
- array: true,
- default: undefined,
- description: 'The location of the target. The array should be the [row, column] of the target.'
- },
- choices: {
- type: jsPsych.plugins.parameterType.KEY,
- pretty_name: 'Choices',
- array: true,
- default: [['3','5','7','9']],
- description: ' Each entry in this array is the key that should be pressed for that corresponding location in the grid.'
- },
- grid_square_size: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Grid square size',
- default: 100,
- description: 'The width and height in pixels of each square in the grid.'
- },
- target_color: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Target color',
- default: "#999",
- description: 'The color of the target square.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, trial ends when user makes a response.'
- },
- pre_target_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Pre-target duration',
- default: 0,
- description: 'The number of milliseconds to display the grid before the target changes color.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show the trial.'
- },
- show_response_feedback: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Show response feedback',
- default: false,
- description: 'If true, show feedback indicating where the user responded and whether it was correct.'
- },
- feedback_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Feedback duration',
- default: 200,
- description: 'The length of time in milliseconds to show the feedback.'
- },
- fade_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Fade duration',
- default: null,
- description: 'If a positive number, the target will progressively change color at the start of the trial, with the transition lasting this many milliseconds.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- no_function: false,
- description: ' Any content here will be displayed below the stimulus.'
- },
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- // create a flattened version of the choices array
- var flat_choices = jsPsych.utils.flatten(trial.choices);
- while(flat_choices.indexOf('') > -1){
- flat_choices.splice(flat_choices.indexOf(''),1);
- }
-
- // display stimulus
- var stimulus = this.stimulus(trial.grid, trial.grid_square_size);
- display_element.innerHTML = stimulus;
-
- if(trial.pre_target_duration <= 0){
- showTarget();
- } else {
- jsPsych.pluginAPI.setTimeout(function(){
- showTarget();
- }, trial.pre_target_duration);
- }
-
- //show prompt if there is one
- if (trial.prompt !== null) {
- display_element.innerHTML += trial.prompt;
- }
-
- var keyboardListener = {};
-
- var response = {
- rt: null,
- key: false,
- correct: false
- }
-
- function showTarget(){
- if(trial.fade_duration == null){
- display_element.querySelector('#jspsych-serial-reaction-time-stimulus-cell-'+trial.target[0]+'-'+trial.target[1]).style.backgroundColor = trial.target_color;
- } else {
- display_element.querySelector('#jspsych-serial-reaction-time-stimulus-cell-'+trial.target[0]+'-'+trial.target[1]).style.transition = "background-color "+trial.fade_duration;
- display_element.querySelector('#jspsych-serial-reaction-time-stimulus-cell-'+trial.target[0]+'-'+trial.target[1]).style.backgroundColor = trial.target_color;
- }
-
- keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: flat_choices,
- allow_held_key: false
- });
-
- if(trial.trial_duration > null){
- jsPsych.pluginAPI.setTimeout(showFeedback, trial.trial_duration);
- }
-
- }
-
- function showFeedback() {
- if(response.rt == null || trial.show_response_feedback == false){
- endTrial();
- } else {
- var color = response.correct ? '#0f0' : '#f00';
- display_element.querySelector('#jspsych-serial-reaction-time-stimulus-cell-'+response.responseLoc[0]+'-'+response.responseLoc[1]).style.transition = "";
- display_element.querySelector('#jspsych-serial-reaction-time-stimulus-cell-'+response.responseLoc[0]+'-'+response.responseLoc[1]).style.backgroundColor = color;
- jsPsych.pluginAPI.setTimeout(endTrial, trial.feedback_duration);
- }
- }
-
- function endTrial() {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // kill keyboard listeners
- if (typeof keyboardListener !== 'undefined') {
- jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
- }
-
- // gather the data to store for the trial
- var trial_data = {
- rt: response.rt,
- response: response.key,
- correct: response.correct,
- grid: trial.grid,
- target: trial.target
- };
-
- // clear the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
-
- };
-
- // function to handle responses by the subject
- function after_response(info) {
-
- // only record first response
- response = response.rt == null ? info : response;
-
- // check if the response is correct
- var responseLoc = [];
- for(var i=0; i";
- for(var i=0; i";
- for(var j=0; j";
- if(typeof labels !=='undefined' && labels[i][j] !== false){
- stimulus += labels[i][j]
- }
- stimulus += "";
- }
- stimulus += "";
- }
- stimulus += "";
-
- return stimulus
- }
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-html-form.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-html-form.js
deleted file mode 100644
index 9eff7885..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-html-form.js
+++ /dev/null
@@ -1,171 +0,0 @@
-/**
- * jspsych-survey-html-form
- * a jspsych plugin for free html forms
- *
- * Jan Simson
- *
- * documentation: docs.jspsych.org
- *
- */
-
-jsPsych.plugins['survey-html-form'] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'survey-html-form',
- description: '',
- parameters: {
- html: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: 'HTML',
- default: null,
- description: 'HTML formatted string containing all the input elements to display. Every element has to have its own distinctive name attribute. The ';
- display_element.innerHTML = html;
-
- if ( trial.autofocus !== '' ) {
- var focus_elements = display_element.querySelectorAll('#'+trial.autofocus);
- if ( focus_elements.length === 0 ) {
- console.warn('No element found with id: '+trial.autofocus);
- } else if ( focus_elements.length > 1 ) {
- console.warn('The id "'+trial.autofocus+'" is not unique so autofocus will not work.');
- } else {
- focus_elements[0].focus();
- }
- }
-
- display_element.querySelector('#jspsych-survey-html-form').addEventListener('submit', function(event) {
- // don't submit form
- event.preventDefault();
-
- // measure response time
- var endTime = performance.now();
- var response_time = endTime - startTime;
-
- var question_data = serializeArray(this);
-
- if (!trial.dataAsArray) {
- question_data = objectifyForm(question_data);
- }
-
- // save data
- var trialdata = {
- rt: response_time,
- response: question_data
- };
-
- display_element.innerHTML = '';
-
- // next trial
- jsPsych.finishTrial(trialdata);
- });
-
- var startTime = performance.now();
- };
-
- /*!
- * Serialize all form data into an array
- * (c) 2018 Chris Ferdinandi, MIT License, https://gomakethings.com
- * @param {Node} form The form to serialize
- * @return {String} The serialized form data
- */
- var serializeArray = function (form) {
- // Setup our serialized data
- var serialized = [];
-
- // Loop through each field in the form
- for (var i = 0; i < form.elements.length; i++) {
- var field = form.elements[i];
-
- // Don't serialize fields without a name, submits, buttons, file and reset inputs, and disabled fields
- if (!field.name || field.disabled || field.type === 'file' || field.type === 'reset' || field.type === 'submit' || field.type === 'button') continue;
-
- // If a multi-select, get all selections
- if (field.type === 'select-multiple') {
- for (var n = 0; n < field.options.length; n++) {
- if (!field.options[n].selected) continue;
- serialized.push({
- name: field.name,
- value: field.options[n].value
- });
- }
- }
-
- // Convert field data to a query string
- else if ((field.type !== 'checkbox' && field.type !== 'radio') || field.checked) {
- serialized.push({
- name: field.name,
- value: field.value
- });
- }
- }
-
- return serialized;
- };
-
- // from https://stackoverflow.com/questions/1184624/convert-form-data-to-javascript-object-with-jquery
- function objectifyForm(formArray) {//serialize data function
- var returnArray = {};
- for (var i = 0; i < formArray.length; i++){
- returnArray[formArray[i]['name']] = formArray[i]['value'];
- }
- return returnArray;
- }
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-likert.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-likert.js
deleted file mode 100644
index 9684ff8d..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-likert.js
+++ /dev/null
@@ -1,195 +0,0 @@
-/**
- * jspsych-survey-likert
- * a jspsych plugin for measuring items on a likert scale
- *
- * Josh de Leeuw
- *
- * documentation: docs.jspsych.org
- *
- */
-
-jsPsych.plugins['survey-likert'] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: 'survey-likert',
- description: '',
- parameters: {
- questions: {
- type: jsPsych.plugins.parameterType.COMPLEX,
- array: true,
- pretty_name: 'Questions',
- nested: {
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: undefined,
- description: 'Questions that are associated with the slider.'
- },
- labels: {
- type: jsPsych.plugins.parameterType.STRING,
- array: true,
- pretty_name: 'Labels',
- default: undefined,
- description: 'Labels to display for individual question.'
- },
- required: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Required',
- default: false,
- description: 'Makes answering the question required.'
- },
- name: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Question Name',
- default: '',
- description: 'Controls the name of data values associated with this question'
- }
- }
- },
- randomize_question_order: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Randomize Question Order',
- default: false,
- description: 'If true, the order of the questions will be randomized'
- },
- preamble: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Preamble',
- default: null,
- description: 'String to display at top of the page.'
- },
- scale_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Scale width',
- default: null,
- description: 'Width of the likert scales in pixels.'
- },
- button_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button label',
- default: 'Continue',
- description: 'Label of the button.'
- },
- autocomplete: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Allow autocomplete',
- default: false,
- description: "Setting this to true will enable browser auto-complete or auto-fill for the form."
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- if(trial.scale_width !== null){
- var w = trial.scale_width + 'px';
- } else {
- var w = '100%';
- }
-
- var html = "";
- // inject CSS for trial
- html += '';
-
- // show preamble text
- if(trial.preamble !== null){
- html += '
'+trial.preamble+'
';
- }
-
- if ( trial.autocomplete ) {
- html += ''
-
- display_element.innerHTML = html;
-
- display_element.querySelector('#jspsych-survey-likert-form').addEventListener('submit', function(e){
- e.preventDefault();
- // measure response time
- var endTime = performance.now();
- var response_time = endTime - startTime;
-
- // create object to hold responses
- var question_data = {};
- var matches = display_element.querySelectorAll('#jspsych-survey-likert-form .jspsych-survey-likert-opts');
- for(var index = 0; index < matches.length; index++){
- var id = matches[index].dataset['radioGroup'];
- var el = display_element.querySelector('input[name="' + id + '"]:checked');
- if (el === null) {
- var response = "";
- } else {
- var response = parseInt(el.value);
- }
- var obje = {};
- if(matches[index].attributes['data-name'].value !== ''){
- var name = matches[index].attributes['data-name'].value;
- } else {
- var name = id;
- }
- obje[name] = response;
- Object.assign(question_data, obje);
- }
-
- // save data
- var trial_data = {
- rt: response_time,
- response: question_data,
- question_order: question_order
- };
-
- display_element.innerHTML = '';
-
- // next trial
- jsPsych.finishTrial(trial_data);
- });
-
- var startTime = performance.now();
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-multi-choice.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-multi-choice.js
deleted file mode 100644
index 540828f4..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-survey-multi-choice.js
+++ /dev/null
@@ -1,208 +0,0 @@
-/**
- * jspsych-survey-multi-choice
- * a jspsych plugin for multiple choice survey questions
- *
- * Shane Martin
- *
- * documentation: docs.jspsych.org
- *
- */
-
-
-jsPsych.plugins['survey-multi-choice'] = (function() {
- var plugin = {};
-
- plugin.info = {
- name: 'survey-multi-choice',
- description: '',
- parameters: {
- questions: {
- type: jsPsych.plugins.parameterType.COMPLEX,
- array: true,
- pretty_name: 'Questions',
- nested: {
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: undefined,
- description: 'The strings that will be associated with a group of options.'
- },
- options: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Options',
- array: true,
- default: undefined,
- description: 'Displays options for an individual question.'
- },
- required: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Required',
- default: false,
- description: 'Subject will be required to pick an option for each question.'
- },
- horizontal: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Horizontal',
- default: false,
- description: 'If true, then questions are centered and options are displayed horizontally.'
- },
- name: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Question Name',
- default: '',
- description: 'Controls the name of data values associated with this question'
- }
- }
- },
- randomize_question_order: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Randomize Question Order',
- default: false,
- description: 'If true, the order of the questions will be randomized'
- },
- preamble: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Preamble',
- default: null,
- description: 'HTML formatted string to display at the top of the page above all the questions.'
- },
- button_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button label',
- default: 'Continue',
- description: 'Label of the button.'
- },
- autocomplete: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Allow autocomplete',
- default: false,
- description: "Setting this to true will enable browser auto-complete or auto-fill for the form."
- }
- }
- }
- plugin.trial = function(display_element, trial) {
- var plugin_id_name = "jspsych-survey-multi-choice";
-
- var html = "";
-
- // inject CSS for trial
- html += '';
-
- // show preamble text
- if(trial.preamble !== null){
- html += '
'+trial.preamble+'
';
- }
-
- // form element
- if ( trial.autocomplete ) {
- html += '';
-
- // render
- display_element.innerHTML = html;
-
- document.querySelector('form').addEventListener('submit', function(event) {
- event.preventDefault();
- // measure response time
- var endTime = performance.now();
- var response_time = endTime - startTime;
-
- // create object to hold responses
- var question_data = {};
- for(var i=0; i';
-
- // form element
- var trial_form_id = _join(plugin_id_name, "form");
- display_element.innerHTML += '';
- var trial_form = display_element.querySelector("#" + trial_form_id);
- if ( !trial.autocomplete ) {
- trial_form.setAttribute('autocomplete',"off");
- }
- // show preamble text
- var preamble_id_name = _join(plugin_id_name, 'preamble');
- if(trial.preamble !== null){
- trial_form.innerHTML += '
'+trial.preamble+'
';
- }
- // generate question order. this is randomized here as opposed to randomizing the order of trial.questions
- // so that the data are always associated with the same question regardless of order
- var question_order = [];
- for(var i=0; i';
-
- var question_selector = _join(plugin_id_selector, question_id);
-
- // add question text
- display_element.querySelector(question_selector).innerHTML += '
' + question.prompt + '
';
-
- // create option check boxes
- for (var j = 0; j < question.options.length; j++) {
- var option_id_name = _join(plugin_id_name, "option", question_id, j);
-
- // add check box container
- display_element.querySelector(question_selector).innerHTML += '';
-
- // add label and question text
- var form = document.getElementById(option_id_name)
- var input_name = _join(plugin_id_name, 'response', question_id);
- var input_id = _join(plugin_id_name, 'response', question_id, j);
- var label = document.createElement('label');
- label.setAttribute('class', plugin_id_name+'-text');
- label.innerHTML = question.options[j];
- label.setAttribute('for', input_id)
-
- // create checkboxes
- var input = document.createElement('input');
- input.setAttribute('type', "checkbox");
- input.setAttribute('name', input_name);
- input.setAttribute('id', input_id);
- input.setAttribute('value', question.options[j])
- form.appendChild(label)
- label.insertBefore(input, label.firstChild)
- }
- }
- // add submit button
- trial_form.innerHTML += ''
- trial_form.innerHTML += '';
-
- // validation check on the data first for custom validation handling
- // then submit the form
- display_element.querySelector('#jspsych-survey-multi-select-next').addEventListener('click', function(){
- for(var i=0; i'+trial.preamble+'';
- }
- // start form
- if (trial.autocomplete) {
- html += ''
- display_element.innerHTML = html;
-
- // backup in case autofocus doesn't work
- display_element.querySelector('#input-'+question_order[0]).focus();
-
- display_element.querySelector('#jspsych-survey-text-form').addEventListener('submit', function(e) {
- e.preventDefault();
- // measure response time
- var endTime = performance.now();
- var response_time = endTime - startTime;
-
- // create object to hold responses
- var question_data = {};
-
- for(var index=0; index < trial.questions.length; index++){
- var id = "Q" + index;
- var q_element = document.querySelector('#jspsych-survey-text-'+index).querySelector('textarea, input');
- var val = q_element.value;
- var name = q_element.attributes['data-name'].value;
- if(name == ''){
- name = id;
- }
- var obje = {};
- obje[name] = val;
- Object.assign(question_data, obje);
- }
- // save data
- var trialdata = {
- rt: response_time,
- response: question_data
- };
-
- display_element.innerHTML = '';
-
- // next trial
- jsPsych.finishTrial(trialdata);
- });
-
- var startTime = performance.now();
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-video-button-response.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-video-button-response.js
deleted file mode 100644
index 54f50efd..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-video-button-response.js
+++ /dev/null
@@ -1,335 +0,0 @@
-/**
- * jspsych-video-button-response
- * Josh de Leeuw
- *
- * plugin for playing a video file and getting a button response
- *
- * documentation: docs.jspsych.org
- *
- **/
-
-jsPsych.plugins["video-button-response"] = (function() {
-
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('video-button-response', 'stimulus', 'video');
-
- plugin.info = {
- name: 'video-button-response',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.VIDEO,
- pretty_name: 'Video',
- default: undefined,
- description: 'The video file to play.'
- },
- choices: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Choices',
- default: undefined,
- array: true,
- description: 'The labels for the buttons.'
- },
- button_html: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button HTML',
- default: '',
- array: true,
- description: 'The html of the button. Can create own style.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the buttons.'
- },
- width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Width',
- default: '',
- description: 'The width of the video in pixels.'
- },
- height: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Height',
- default: '',
- description: 'The height of the video display in pixels.'
- },
- autoplay: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Autoplay',
- default: true,
- description: 'If true, the video will begin playing as soon as it has loaded.'
- },
- controls: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Controls',
- default: false,
- description: 'If true, the subject will be able to pause the video or move the playback to any point in the video.'
- },
- start: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: 'Start',
- default: null,
- description: 'Time to start the clip.'
- },
- stop: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: 'Stop',
- default: null,
- description: 'Time to stop the clip.'
- },
- rate: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: 'Rate',
- default: 1,
- description: 'The playback rate of the video. 1 is normal, <1 is slower, >1 is faster.'
- },
- trial_ends_after_video: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'End trial after video finishes',
- default: false,
- description: 'If true, the trial will end immediately after the video finishes playing.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show trial before it ends.'
- },
- margin_vertical: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Margin vertical',
- default: '0px',
- description: 'The vertical margin of the button.'
- },
- margin_horizontal: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Margin horizontal',
- default: '8px',
- description: 'The horizontal margin of the button.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, the trial will end when subject makes a response.'
- },
- response_allowed_while_playing: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response allowed while playing',
- default: true,
- description: 'If true, then responses are allowed while the video is playing. '+
- 'If false, then the video must finish playing before a response is accepted.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- // setup stimulus
- var video_html = '
'
- video_html += '";
- video_html += "
";
-
- //display buttons
- var buttons = [];
- if (Array.isArray(trial.button_html)) {
- if (trial.button_html.length == trial.choices.length) {
- buttons = trial.button_html;
- } else {
- console.error('Error in video-button-response plugin. The length of the button_html array does not equal the length of the choices array');
- }
- } else {
- for (var i = 0; i < trial.choices.length; i++) {
- buttons.push(trial.button_html);
- }
- }
- video_html += '
';
- for (var i = 0; i < trial.choices.length; i++) {
- var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
- video_html += '
'+str+'
';
- }
- video_html += '
';
-
- // add prompt if there is one
- if (trial.prompt !== null) {
- video_html += trial.prompt;
- }
-
- display_element.innerHTML = video_html;
-
- var start_time = performance.now();
-
- var video_element = display_element.querySelector('#jspsych-video-button-response-stimulus');
-
- if(video_preload_blob){
- video_element.src = video_preload_blob;
- }
-
- video_element.onended = function(){
- if(trial.trial_ends_after_video){
- end_trial();
- } else if (!trial.response_allowed_while_playing) {
- enable_buttons();
- }
- }
-
- video_element.playbackRate = trial.rate;
-
- // if video start time is specified, hide the video and set the starting time
- // before showing and playing, so that the video doesn't automatically show the first frame
- if(trial.start !== null){
- video_element.pause();
- video_element.currentTime = trial.start;
- video_element.onseeked = function() {
- video_element.style.visibility = "visible";
- if (trial.autoplay) {
- video_element.play();
- }
- }
- }
-
- if(trial.stop !== null){
- video_element.addEventListener('timeupdate', function(e){
- var currenttime = video_element.currentTime;
- if(currenttime >= trial.stop){
- video_element.pause();
- }
- })
- }
-
- if(trial.response_allowed_while_playing){
- enable_buttons();
- } else {
- disable_buttons();
- }
-
- // store response
- var response = {
- rt: null,
- button: null
- };
-
- // function to end trial when it is time
- function end_trial() {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // stop the video file if it is playing
- // remove any remaining end event handlers
- display_element.querySelector('#jspsych-video-button-response-stimulus').pause();
- display_element.querySelector('#jspsych-video-button-response-stimulus').onended = function() {};
-
- // gather the data to store for the trial
- var trial_data = {
- rt: response.rt,
- stimulus: trial.stimulus,
- response: response.button
- };
-
- // clear the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- }
-
- // function to handle responses by the subject
- function after_response(choice) {
-
- // measure rt
- var end_time = performance.now();
- var rt = end_time - start_time;
- response.button = parseInt(choice);
- response.rt = rt;
-
- // after a valid response, the stimulus will have the CSS class 'responded'
- // which can be used to provide visual feedback that a response was recorded
- video_element.className += ' responded';
-
- // disable all the buttons after a response
- disable_buttons();
-
- if (trial.response_ends_trial) {
- end_trial();
- }
- }
-
- function button_response(e){
- var choice = e.currentTarget.getAttribute('data-choice'); // don't use dataset for jsdom compatibility
- after_response(choice);
- }
-
- function disable_buttons() {
- var btns = document.querySelectorAll('.jspsych-video-button-response-button');
- for (var i=0; i1 is faster.'
- },
- trial_ends_after_video: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'End trial after video finishes',
- default: false,
- description: 'If true, the trial will end immediately after the video finishes playing.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show trial before it ends.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, the trial will end when subject makes a response.'
- },
- response_allowed_while_playing: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response allowed while playing',
- default: true,
- description: 'If true, then responses are allowed while the video is playing. '+
- 'If false, then the video must finish playing before a response is accepted.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- // setup stimulus
- var video_html = '
'
- video_html += '";
- video_html += "
";
-
- // add prompt if there is one
- if (trial.prompt !== null) {
- video_html += trial.prompt;
- }
-
- display_element.innerHTML = video_html;
-
- var video_element = display_element.querySelector('#jspsych-video-keyboard-response-stimulus');
-
- if(video_preload_blob){
- video_element.src = video_preload_blob;
- }
-
- video_element.onended = function(){
- if(trial.trial_ends_after_video){
- end_trial();
- }
- if ((trial.response_allowed_while_playing == false) & (!trial.trial_ends_after_video)) {
- // start keyboard listener
- var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: trial.choices,
- rt_method: 'performance',
- persist: false,
- allow_held_key: false,
- });
- }
- }
-
- video_element.playbackRate = trial.rate;
-
- // if video start time is specified, hide the video and set the starting time
- // before showing and playing, so that the video doesn't automatically show the first frame
- if(trial.start !== null){
- video_element.pause();
- video_element.currentTime = trial.start;
- video_element.onseeked = function() {
- video_element.style.visibility = "visible";
- if (trial.autoplay) {
- video_element.play();
- }
- }
- }
-
- if(trial.stop !== null){
- video_element.addEventListener('timeupdate', function(e){
- var currenttime = video_element.currentTime;
- if(currenttime >= trial.stop){
- video_element.pause();
- }
- })
- }
-
- // store response
- var response = {
- rt: null,
- key: null
- };
-
- // function to end trial when it is time
- function end_trial() {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // kill keyboard listeners
- jsPsych.pluginAPI.cancelAllKeyboardResponses();
-
- // stop the video file if it is playing
- // remove end event listeners if they exist
- display_element.querySelector('#jspsych-video-keyboard-response-stimulus').pause();
- display_element.querySelector('#jspsych-video-keyboard-response-stimulus').onended = function(){ };
-
- // gather the data to store for the trial
- var trial_data = {
- rt: response.rt,
- stimulus: trial.stimulus,
- response: response.key
- };
-
- // clear the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- }
-
- // function to handle responses by the subject
- var after_response = function(info) {
-
- // after a valid response, the stimulus will have the CSS class 'responded'
- // which can be used to provide visual feedback that a response was recorded
- display_element.querySelector('#jspsych-video-keyboard-response-stimulus').className += ' responded';
-
- // only record the first response
- if (response.key == null) {
- response = info;
- }
-
- if (trial.response_ends_trial) {
- end_trial();
- }
- };
-
- // start the response listener
- if ((trial.choices != jsPsych.NO_KEYS) & (trial.response_allowed_while_playing)) {
- var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
- callback_function: after_response,
- valid_responses: trial.choices,
- rt_method: 'performance',
- persist: false,
- allow_held_key: false,
- });
- }
-
- // end trial if time limit is set
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- end_trial();
- }, trial.trial_duration);
- }
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-video-slider-response.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-video-slider-response.js
deleted file mode 100644
index 7954ad94..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-video-slider-response.js
+++ /dev/null
@@ -1,351 +0,0 @@
-/**
- * jspsych-video-slider-response
- * Josh de Leeuw
- *
- * plugin for playing a video file and getting a slider response
- *
- * documentation: docs.jspsych.org
- *
- **/
-
-jsPsych.plugins["video-slider-response"] = (function() {
-
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('video-slider-response', 'stimulus', 'video');
-
- plugin.info = {
- name: 'video-slider-response',
- description: '',
- parameters: {
- stimulus: {
- type: jsPsych.plugins.parameterType.VIDEO,
- pretty_name: 'Video',
- default: undefined,
- description: 'The video file to play.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the stimulus.'
- },
- width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Width',
- default: '',
- description: 'The width of the video in pixels.'
- },
- height: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Height',
- default: '',
- description: 'The height of the video display in pixels.'
- },
- autoplay: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Autoplay',
- default: true,
- description: 'If true, the video will begin playing as soon as it has loaded.'
- },
- controls: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Controls',
- default: false,
- description: 'If true, the subject will be able to pause the video or move the playback to any point in the video.'
- },
- start: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: 'Start',
- default: null,
- description: 'Time to start the clip.'
- },
- stop: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: 'Stop',
- default: null,
- description: 'Time to stop the clip.'
- },
- rate: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: 'Rate',
- default: 1,
- description: 'The playback rate of the video. 1 is normal, <1 is slower, >1 is faster.'
- },
- min: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Min slider',
- default: 0,
- description: 'Sets the minimum value of the slider.'
- },
- max: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Max slider',
- default: 100,
- description: 'Sets the maximum value of the slider',
- },
- slider_start: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Slider starting value',
- default: 50,
- description: 'Sets the starting value of the slider',
- },
- step: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Step',
- default: 1,
- description: 'Sets the step of the slider'
- },
- labels: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name:'Labels',
- default: [],
- array: true,
- description: 'Labels of the slider.',
- },
- slider_width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name:'Slider width',
- default: null,
- description: 'Width of the slider in pixels.'
- },
- button_label: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Button label',
- default: 'Continue',
- array: false,
- description: 'Label of the button to advance.'
- },
- require_movement: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Require movement',
- default: false,
- description: 'If true, the participant will have to move the slider before continuing.'
- },
- trial_ends_after_video: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'End trial after video finishes',
- default: false,
- description: 'If true, the trial will end immediately after the video finishes playing.'
- },
- trial_duration: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Trial duration',
- default: null,
- description: 'How long to show trial before it ends.'
- },
- response_ends_trial: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response ends trial',
- default: true,
- description: 'If true, the trial will end when subject makes a response.'
- },
- response_allowed_while_playing: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Response allowed while playing',
- default: true,
- description: 'If true, then responses are allowed while the video is playing. '+
- 'If false, then the video must finish playing before a response is accepted.'
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- // half of the thumb width value from jspsych.css, used to adjust the label positions
- var half_thumb_width = 7.5;
-
- // setup stimulus
- var video_html = '";
-
- var html = '
';
- html += '
' + video_html + '
';
- html += '
';
- html += '
'
- for(var j=0; j < trial.labels.length; j++){
- var label_width_perc = 100/(trial.labels.length-1);
- var percent_of_range = j * (100/(trial.labels.length - 1));
- var percent_dist_from_center = ((percent_of_range-50)/50)*100;
- var offset = (percent_dist_from_center * half_thumb_width)/100;
- html += '
';
- html += ''+trial.labels[j]+'';
- html += '
'
- }
- html += '
';
- html += '
';
- html += '
';
-
- // add prompt if there is one
- if (trial.prompt !== null) {
- html += '
'+trial.prompt+'
';
- }
-
- // add submit button
- var next_disabled_attribute = "";
- if (trial.require_movement | !trial.response_allowed_while_playing) {
- next_disabled_attribute = "disabled";
- }
- html += '';
-
- display_element.innerHTML = html;
-
- var video_element = display_element.querySelector('#jspsych-video-slider-response-stimulus-video');
-
- if(video_preload_blob){
- video_element.src = video_preload_blob;
- }
-
- video_element.onended = function(){
- if(trial.trial_ends_after_video){
- end_trial();
- } else if (!trial.response_allowed_while_playing) {
- enable_slider();
- }
- }
-
- video_element.playbackRate = trial.rate;
-
- // if video start time is specified, hide the video and set the starting time
- // before showing and playing, so that the video doesn't automatically show the first frame
- if(trial.start !== null){
- video_element.pause();
- video_element.currentTime = trial.start;
- video_element.onseeked = function() {
- video_element.style.visibility = "visible";
- if (trial.autoplay) {
- video_element.play();
- }
- }
- }
-
- if(trial.stop !== null){
- video_element.addEventListener('timeupdate', function(e){
- var currenttime = video_element.currentTime;
- if(currenttime >= trial.stop){
- video_element.pause();
- }
- })
- }
-
- if(trial.require_movement){
- display_element.querySelector('#jspsych-video-slider-response-response').addEventListener('click', function(){
- display_element.querySelector('#jspsych-video-slider-response-next').disabled = false;
- });
- }
-
- var startTime = performance.now();
-
- // store response
- var response = {
- rt: null,
- response: null
- };
-
- display_element.querySelector('#jspsych-video-slider-response-next').addEventListener('click', function() {
- // measure response time
- var endTime = performance.now();
- response.rt = endTime - startTime;
- response.response = display_element.querySelector('#jspsych-video-slider-response-response').valueAsNumber;
-
- if(trial.response_ends_trial){
- end_trial();
- } else {
- display_element.querySelector('#jspsych-video-slider-response-next').disabled = true;
- }
-
- });
-
- // function to end trial when it is time
- function end_trial() {
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // stop the video file if it is playing
- // remove any remaining end event handlers
- display_element.querySelector('#jspsych-video-slider-response-stimulus-video').pause();
- display_element.querySelector('#jspsych-video-slider-response-stimulus-video').onended = function() {};
-
- // gather the data to store for the trial
- var trial_data = {
- rt: response.rt,
- stimulus: trial.stimulus,
- start: trial.start,
- slider_start: trial.slider_start,
- response: response.response
- };
-
- // clear the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- };
-
- // function to enable slider after video ends
- function enable_slider() {
- document.querySelector('#jspsych-video-slider-response-response').disabled = false;
- if (!trial.require_movement) {
- document.querySelector('#jspsych-video-slider-response-next').disabled = false;
- }
- }
-
- // end trial if time limit is set
- if (trial.trial_duration !== null) {
- jsPsych.pluginAPI.setTimeout(function() {
- end_trial();
- }, trial.trial_duration);
- }
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-virtual-chinrest.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-virtual-chinrest.js
deleted file mode 100644
index 7899b1c4..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-virtual-chinrest.js
+++ /dev/null
@@ -1,471 +0,0 @@
-/*
- * virtual chinrest plugin for jsPsych, based on Qisheng Li 11/2019. /// https://github.com/QishengLi/virtual_chinrest
-
- Modified by Gustavo Juantorena 08/2020 // https://github.com/GEJ1
-
- Contributions from Peter J. Kohler: https://github.com/pjkohler
- */
-
-jsPsych.plugins["virtual-chinrest"] = (function () {
- var plugin = {};
-
- plugin.info = {
- name: "virtual-chinrest",
- parameters: {
- resize_units: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: "Resize units",
- default: "none",
- description:
- 'What units to resize to? ["none"/"cm"/"inch"/"deg"]. If "none", no resizing will be done to the jsPsych content after this trial.',
- },
- pixels_per_unit: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Pixels per unit",
- default: 100,
- description:
- "After the scaling factor is applied, this many pixels will equal one unit of measurement.",
- },
- // mouse_adjustment: {
- // type: jsPsych.plugins.parameterType.BOOL,
- // pretty_name: "Adjust Using Mouse?",
- // default: true,
- // },
- adjustment_prompt: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: "Adjustment prompt",
- default: `
-
-
Click and drag the lower right corner of the image until it is the same size as a credit card held up to the screen.
-
You can use any card that is the same size as a credit card, like a membership card or driver's license.
-
If you do not have access to a real card you can use a ruler to measure the image width to 3.37 inches or 85.6 mm.
-
`,
- description:
- "Any content here will be displayed above the card stimulus.",
- },
- adjustment_button_prompt: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: "Adjustment button prompt",
- default: "Click here when the image is the correct size",
- description:
- " Content of the button displayed below the card stimulus.",
- },
- item_path: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: "Item path",
- default: "img/card.png",
- description: "Path to an image to be shown in the resizable item div."
- },
- item_height_mm: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: "Item height (mm)",
- default: 53.98,
- description: "The height of the item to be measured, in mm.",
- },
- item_width_mm: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: "Item width (mm)",
- default: 85.6,
- description: "The width of the item to be measured, in mm.",
- },
- item_init_size: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Initial Size",
- default: 250,
- description:
- "The initial size of the card, in pixels, along the largest dimension.",
- },
- blindspot_reps: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: "Blindspot measurement repetitions",
- default: 5,
- description:
- "How many times to measure the blindspot location? If 0, blindspot will not be detected, and viewing distance and degree data not computed.",
- },
- blindspot_prompt: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: "Blindspot prompt",
- default: `
-
Now we will quickly measure how far away you are sitting.
-
-
-
Put your left hand on the space bar.
-
Cover your right eye with your right hand.
-
Using your left eye, focus on the black square. Keep your focus on the black square.
-
The red ball will disappear as it moves from right to left. Press the space bar as soon as the ball disappears.
-
-
-
Press the space bar when you are ready to begin.
- `,
- description: "HTML-formatted prompt to be shown on the screen during blindspot estimates."
- },
- // blindspot_start_prompt: {
- // type: jsPsych.plugins.parameterType.HTML_STRING,
- // pretty_name: "Blindspot start prompt",
- // default: "Start",
- // description: "Content of the start button for the blindspot tasks.",
- // },
- blindspot_measurements_prompt: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: "Blindspot measurements prompt",
- default: "Remaining measurements: ",
- description: "Text accompanying the remaining measures counter",
- },
- viewing_distance_report: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: "Viewing distance report",
- default: "
Based on your responses, you are sitting about from the screen.
Does that seem about right?
",
- description:
- 'If "none" is given, viewing distance will not be reported to the participant',
- },
- redo_measurement_button_label: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: "Re-do measurement button label",
- default: 'No, that is not close. Try again.',
- description: "Label for the button that can be clicked on the viewing distance report screen to re-do the blindspot estimate(s)."
- },
- blindspot_done_prompt: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- pretty_name: "Blindspot done prompt",
- default: "Yes",
- description: "Label for the button that can be clicked on the viewing distance report screen to accept the viewing distance estimate.",
- },
- },
- };
-
- plugin.trial = function (display_element, trial) {
- /* check parameter compatibility */
- if (!(trial.blindspot_reps > 0) && (trial.resize_units == "deg" || trial.resize_units == "degrees")) {
- console.error("Blindspot repetitions set to 0, so resizing to degrees of visual angle is not possible!");
- return;
- }
-
- /* some additional parameter configuration */
- let trial_data = {
- item_width_mm: trial.item_width_mm,
- item_height_mm: trial.item_height_mm, //card dimension: 85.60 × 53.98 mm (3.370 × 2.125 in)
- };
-
- let blindspot_config_data = {
- ball_pos: [],
- slider_clck: false,
- };
-
- let aspect_ratio = trial.item_width_mm / trial.item_height_mm;
-
- const start_div_height =
- aspect_ratio < 1
- ? trial.item_init_size
- : Math.round(trial.item_init_size / aspect_ratio);
- const start_div_width =
- aspect_ratio < 1
- ? Math.round(trial.item_init_size * aspect_ratio)
- : trial.item_init_size;
- const adjust_size = Math.round(start_div_width * 0.1);
-
- /* create content for first screen, resizing card */
- let pagesize_content = `
-
-
-
-
-
- ${trial.adjustment_prompt}
-
-
- `
-
- /* create content for second screen, blind spot */
- let blindspot_content = `
-
`
-
- display_element.innerHTML = html;
-
- var wg_container = display_element.querySelector('#webgazer-calibrate-container');
-
- var reps_completed = 0;
- var points_completed = -1;
- var cal_points = null;
-
- calibrate();
-
- function calibrate(){
- jsPsych.extensions['webgazer'].resume();
- if(trial.calibration_mode == 'click'){
- jsPsych.extensions['webgazer'].startMouseCalibration();
- }
- next_calibration_round();
- }
-
- function next_calibration_round(){
- if(trial.randomize_calibration_order){
- cal_points = jsPsych.randomization.shuffle(trial.calibration_points);
- } else {
- cal_points = trial.calibration_points;
- }
- points_completed = -1;
- next_calibration_point();
- }
-
- function next_calibration_point(){
- points_completed++;
- if(points_completed == cal_points.length){
- reps_completed++;
- if(reps_completed == trial.repetitions_per_point){
- calibration_done();
- } else {
- next_calibration_round();
- }
- } else {
- var pt = cal_points[points_completed];
- calibration_display_gaze_only(pt);
- }
- }
-
- function calibration_display_gaze_only(pt){
- var pt_html = ``
- wg_container.innerHTML = pt_html;
-
- var pt_dom = wg_container.querySelector('#calibration-point');
-
- if(trial.calibration_mode == 'click'){
- pt_dom.style.cursor = 'pointer';
- pt_dom.addEventListener('click', function(){
- next_calibration_point();
- })
- }
-
- if(trial.calibration_mode == 'view'){
- var br = pt_dom.getBoundingClientRect();
- var x = br.left + br.width / 2;
- var y = br.top + br.height / 2;
-
- var pt_start_cal = performance.now() + trial.time_to_saccade;
- var pt_finish = performance.now() + trial.time_to_saccade + trial.time_per_point;
-
- requestAnimationFrame(function watch_dot(){
-
- if(performance.now() > pt_start_cal){
- jsPsych.extensions['webgazer'].calibratePoint(x,y,'click');
- }
- if(performance.now() < pt_finish){
- requestAnimationFrame(watch_dot);
- } else {
- next_calibration_point();
- }
- })
- }
- }
-
- function calibration_done(){
- if(trial.calibration_mode == 'click'){
- jsPsych.extensions['webgazer'].stopMouseCalibration();
- }
- wg_container.innerHTML = "";
- end_trial();
- }
-
- // function to end trial when it is time
- function end_trial() {
- jsPsych.extensions['webgazer'].pause();
- jsPsych.extensions['webgazer'].hidePredictions();
- jsPsych.extensions['webgazer'].hideVideo();
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // gather the data to store for the trial
- var trial_data = {
-
- };
-
- // clear the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- };
-
- };
-
- return plugin;
- })();
\ No newline at end of file
diff --git a/app/static/lib/jspsych-6.3.1/plugins/jspsych-webgazer-init-camera.js b/app/static/lib/jspsych-6.3.1/plugins/jspsych-webgazer-init-camera.js
deleted file mode 100644
index 1dc26401..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/jspsych-webgazer-init-camera.js
+++ /dev/null
@@ -1,139 +0,0 @@
-/**
- * jspsych-webgazer-init-camera
- * Josh de Leeuw
- **/
-
- jsPsych.plugins["webgazer-init-camera"] = (function () {
-
- var plugin = {};
-
- plugin.info = {
- name: 'webgazer-init-camera',
- description: '',
- parameters: {
- instructions: {
- type: jsPsych.plugins.parameterType.HTML_STRING,
- default: `
-
Position your head so that the webcam has a good view of your eyes.
-
Center your face in the box and look directly towards the camera.
-
It is important that you try and keep your head reasonably still throughout the experiment, so please take a moment to adjust your setup to be comfortable.
-
When your face is centered in the box and the box is green, you can click to continue.
`
-
- display_element.innerHTML = html;
-
- var wg_container = display_element.querySelector('#webgazer-validate-container');
-
- var points_completed = -1;
- var val_points = null;
- var start = performance.now();
-
- validate();
-
- function validate(){
-
- if(trial.randomize_validation_order){
- val_points = jsPsych.randomization.shuffle(trial.validation_points);
- } else {
- val_points = trial.validation_points;
- }
- trial_data.validation_points = val_points;
- points_completed = -1;
- //jsPsych.extensions['webgazer'].resume();
- jsPsych.extensions.webgazer.startSampleInterval();
- //jsPsych.extensions.webgazer.showPredictions();
- next_validation_point();
- }
-
- function next_validation_point(){
- points_completed++;
- if(points_completed == val_points.length){
- validation_done();
- } else {
- var pt = val_points[points_completed];
- validation_display(pt);
- }
- }
-
- function validation_display(pt){
- var pt_html = drawValidationPoint(pt[0], pt[1]);
- wg_container.innerHTML = pt_html;
-
- var pt_dom = wg_container.querySelector('.validation-point');
-
- var br = pt_dom.getBoundingClientRect();
- var x = br.left + br.width / 2;
- var y = br.top + br.height / 2;
-
- var pt_start_val = performance.now() + trial.time_to_saccade;
- var pt_finish = pt_start_val + trial.validation_duration;
-
- var pt_data = [];
-
- var cancelGazeUpdate = jsPsych.extensions['webgazer'].onGazeUpdate(function(prediction){
- if(performance.now() > pt_start_val){
- pt_data.push({x: prediction.x, y: prediction.y, dx: prediction.x - x, dy: prediction.y - y, t: Math.round(prediction.t-start)});
- }
- });
-
- requestAnimationFrame(function watch_dot(){
- if(performance.now() < pt_finish){
- requestAnimationFrame(watch_dot);
- } else {
- trial_data.raw_gaze.push(pt_data);
- cancelGazeUpdate();
-
- next_validation_point();
- }
- });
-
- }
-
- function drawValidationPoint(x,y){
- if(trial.validation_point_coordinates == 'percent'){
- return drawValidationPoint_PercentMode(x,y);
- }
- if(trial.validation_point_coordinates == 'center-offset-pixels'){
- return drawValidationPoint_CenterOffsetMode(x,y);
- }
- }
-
- function drawValidationPoint_PercentMode(x,y){
- return ``
- }
-
- function drawValidationPoint_CenterOffsetMode(x,y){
- return ``
- }
-
- function drawCircle(target_x, target_y, dx, dy, r){
- if(trial.validation_point_coordinates == 'percent'){
- return drawCircle_PercentMode(target_x, target_y, dx, dy, r);
- }
- if(trial.validation_point_coordinates == 'center-offset-pixels'){
- return drawCircle_CenterOffsetMode(target_x, target_y, dx, dy, r);
- }
- }
-
- function drawCircle_PercentMode(target_x, target_y, dx, dy, r){
- var html = `
-
- `
- return html;
- }
-
- function drawCircle_CenterOffsetMode(target_x, target_y, dx, dy, r){
- var html = `
-
- `
- return html;
- }
-
- function drawRawDataPoint(target_x, target_y, dx, dy, ){
- if(trial.validation_point_coordinates == 'percent'){
- return drawRawDataPoint_PercentMode(target_x, target_y, dx, dy);
- }
- if(trial.validation_point_coordinates == 'center-offset-pixels'){
- return drawRawDataPoint_CenterOffsetMode(target_x, target_y, dx, dy);
- }
- }
-
- function drawRawDataPoint_PercentMode(target_x, target_y, dx, dy){
- var color = Math.sqrt(dx*dx + dy*dy) <= trial.roi_radius ? '#afa' : '#faa';
- return ``
- }
-
- function drawRawDataPoint_CenterOffsetMode(target_x, target_y, dx, dy){
- var color = Math.sqrt(dx*dx + dy*dy) <= trial.roi_radius ? '#afa' : '#faa';
- return ``
- }
-
- function median(arr){
- var mid = Math.floor(arr.length/2);
- var sorted_arr = arr.sort((a,b) => a-b);
- if(arr.length % 2 == 0){
- return sorted_arr[mid-1] + sorted_arr[mid] / 2;
- } else {
- return sorted_arr[mid];
- }
- }
-
- function calculateGazeCentroid(gazeData){
-
- var x_diff_m = gazeData.reduce(function(accumulator, currentValue, index){
- accumulator += currentValue.dx;
- if(index == gazeData.length-1){
- return accumulator / gazeData.length;
- } else {
- return accumulator;
- }
- }, 0);
-
- var y_diff_m = gazeData.reduce(function(accumulator, currentValue, index){
- accumulator += currentValue.dy;
- if(index == gazeData.length-1){
- return accumulator / gazeData.length;
- } else {
- return accumulator;
- }
- }, 0);
-
- var median_distance = median(gazeData.map(function(x){ return(Math.sqrt(Math.pow(x.dx-x_diff_m,2) + Math.pow(x.dy-y_diff_m,2)))}));
-
- return {
- x: x_diff_m,
- y: y_diff_m,
- r: median_distance
- }
- }
-
- function calculatePercentInROI(gazeData){
- var distances = gazeData.map(function(p){
- return(Math.sqrt(Math.pow(p.dx,2) + Math.pow(p.dy,2)))
- });
- var sum_in_roi = distances.reduce(function(accumulator, currentValue){
- if(currentValue <= trial.roi_radius){
- accumulator++;
- }
- return accumulator;
- }, 0);
- var percent = sum_in_roi / gazeData.length * 100;
- return percent;
- }
-
- function calculateSampleRate(gazeData){
- var mean_diff = [];
- for(var i=0; i 1){
- var t_diff = [];
- for(var j=1; j 0){
- return 1000 / (mean_diff.reduce(function(a,b) { return(a+b) }, 0) / mean_diff.length);
- } else {
- return null;
- }
-
- }
-
- function validation_done(){
- trial_data.samples_per_sec = calculateSampleRate(trial_data.raw_gaze).toFixed(2);
- for(var i=0; iContinue';
- wg_container.innerHTML = html;
- wg_container.querySelector('#cont').addEventListener('click', function(){
- jsPsych.extensions.webgazer.pause();
- end_trial();
- });
- // turn on webgazer's loop
- jsPsych.extensions.webgazer.showPredictions();
- jsPsych.extensions.webgazer.stopSampleInterval();
- jsPsych.extensions.webgazer.resume();
- }
-
- // function to end trial when it is time
- function end_trial() {
- jsPsych.extensions.webgazer.stopSampleInterval();
-
- // kill any remaining setTimeout handlers
- jsPsych.pluginAPI.clearAllTimeouts();
-
- // clear the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- };
-
- };
-
- return plugin;
-})();
\ No newline at end of file
diff --git a/app/static/lib/jspsych-6.3.1/plugins/template/jspsych-plugin-template.js b/app/static/lib/jspsych-6.3.1/plugins/template/jspsych-plugin-template.js
deleted file mode 100644
index 98196ea6..00000000
--- a/app/static/lib/jspsych-6.3.1/plugins/template/jspsych-plugin-template.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Example plugin template
- */
-
-jsPsych.plugins["PLUGIN-NAME"] = (function() {
-
- var plugin = {};
-
- plugin.info = {
- name: "PLUGIN-NAME",
- parameters: {
- parameter_name: {
- type: jsPsych.plugins.parameterType.INT, // BOOL, STRING, INT, FLOAT, FUNCTION, KEY, SELECT, HTML_STRING, IMAGE, AUDIO, VIDEO, OBJECT, COMPLEX
- default: undefined
- },
- parameter_name: {
- type: jsPsych.plugins.parameterType.IMAGE,
- default: undefined
- }
- }
- }
-
- plugin.trial = function(display_element, trial) {
-
- // data saving
- var trial_data = {
- parameter_name: 'parameter value'
- };
-
- // end trial
- jsPsych.finishTrial(trial_data);
- };
-
- return plugin;
-})();
diff --git a/app/static/lib/jspsych-7.2.1/VERSION.md b/app/static/lib/jspsych-7.2.1/VERSION.md
new file mode 100644
index 00000000..0f299da9
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/VERSION.md
@@ -0,0 +1,57 @@
+Included in this release:
+
+Package|Version|Documentation
+--- | --- | ---
+jspsych|7.2.1|https://www.jspsych.org/7.2/
+extension-mouse-tracking|1.0.0|https://www.jspsych.org/7.2/extensions/mouse-tracking
+extension-webgazer|1.0.0|https://www.jspsych.org/7.2/extensions/webgazer
+plugin-animation|1.1.0|https://www.jspsych.org/7.2/plugins/animation
+plugin-audio-button-response|1.1.0|https://www.jspsych.org/7.2/plugins/audio-button-response
+plugin-audio-keyboard-response|1.1.0|https://www.jspsych.org/7.2/plugins/audio-keyboard-response
+plugin-audio-slider-response|1.1.0|https://www.jspsych.org/7.2/plugins/audio-slider-response
+plugin-browser-check|1.0.0|https://www.jspsych.org/7.2/plugins/browser-check
+plugin-call-function|1.1.0|https://www.jspsych.org/7.2/plugins/call-function
+plugin-canvas-button-response|1.1.0|https://www.jspsych.org/7.2/plugins/canvas-button-response
+plugin-canvas-keyboard-response|1.1.0|https://www.jspsych.org/7.2/plugins/canvas-keyboard-response
+plugin-canvas-slider-response|1.1.0|https://www.jspsych.org/7.2/plugins/canvas-slider-response
+plugin-categorize-animation|1.1.0|https://www.jspsych.org/7.2/plugins/categorize-animation
+plugin-categorize-html|1.1.0|https://www.jspsych.org/7.2/plugins/categorize-html
+plugin-categorize-image|1.1.0|https://www.jspsych.org/7.2/plugins/categorize-image
+plugin-cloze|1.1.0|https://www.jspsych.org/7.2/plugins/cloze
+plugin-external-html|1.1.0|https://www.jspsych.org/7.2/plugins/external-html
+plugin-free-sort|1.0.0|https://www.jspsych.org/7.2/plugins/free-sort
+plugin-fullscreen|1.1.0|https://www.jspsych.org/7.2/plugins/fullscreen
+plugin-html-audio-response|1.0.0|https://www.jspsych.org/7.2/plugins/html-audio-response
+plugin-html-button-response|1.1.0|https://www.jspsych.org/7.2/plugins/html-button-response
+plugin-html-keyboard-response|1.1.0|https://www.jspsych.org/7.2/plugins/html-keyboard-response
+plugin-html-slider-response|1.1.0|https://www.jspsych.org/7.2/plugins/html-slider-response
+plugin-iat-html|1.1.0|https://www.jspsych.org/7.2/plugins/iat-html
+plugin-iat-image|1.1.0|https://www.jspsych.org/7.2/plugins/iat-image
+plugin-image-button-response|1.1.0|https://www.jspsych.org/7.2/plugins/image-button-response
+plugin-image-keyboard-response|1.1.0|https://www.jspsych.org/7.2/plugins/image-keyboard-response
+plugin-image-slider-response|1.1.0|https://www.jspsych.org/7.2/plugins/image-slider-response
+plugin-initialize-microphone|1.0.0|https://www.jspsych.org/7.2/plugins/initialize-microphone
+plugin-instructions|1.1.0|https://www.jspsych.org/7.2/plugins/instructions
+plugin-maxdiff|1.1.0|https://www.jspsych.org/7.2/plugins/maxdiff
+plugin-preload|1.1.0|https://www.jspsych.org/7.2/plugins/preload
+plugin-reconstruction|1.1.0|https://www.jspsych.org/7.2/plugins/reconstruction
+plugin-resize|1.0.0|https://www.jspsych.org/7.2/plugins/resize
+plugin-same-different-html|1.1.0|https://www.jspsych.org/7.2/plugins/same-different-html
+plugin-same-different-image|1.1.0|https://www.jspsych.org/7.2/plugins/same-different-image
+plugin-serial-reaction-time-mouse|1.1.0|https://www.jspsych.org/7.2/plugins/serial-reaction-time-mouse
+plugin-serial-reaction-time|1.1.0|https://www.jspsych.org/7.2/plugins/serial-reaction-time
+plugin-sketchpad|1.0.1|https://www.jspsych.org/7.2/plugins/sketchpad
+plugin-survey-html-form|1.0.0|https://www.jspsych.org/7.2/plugins/survey-html-form
+plugin-survey-likert|1.1.0|https://www.jspsych.org/7.2/plugins/survey-likert
+plugin-survey-multi-choice|1.1.0|https://www.jspsych.org/7.2/plugins/survey-multi-choice
+plugin-survey-multi-select|1.1.0|https://www.jspsych.org/7.2/plugins/survey-multi-select
+plugin-survey-text|1.1.0|https://www.jspsych.org/7.2/plugins/survey-text
+plugin-survey|0.1.1|https://www.jspsych.org/7.2/plugins/survey
+plugin-video-button-response|1.1.0|https://www.jspsych.org/7.2/plugins/video-button-response
+plugin-video-keyboard-response|1.1.0|https://www.jspsych.org/7.2/plugins/video-keyboard-response
+plugin-video-slider-response|1.1.0|https://www.jspsych.org/7.2/plugins/video-slider-response
+plugin-virtual-chinrest|1.0.0|https://www.jspsych.org/7.2/plugins/virtual-chinrest
+plugin-visual-search-circle|1.1.0|https://www.jspsych.org/7.2/plugins/visual-search-circle
+plugin-webgazer-calibrate|1.0.0|https://www.jspsych.org/7.2/plugins/webgazer-calibrate
+plugin-webgazer-init-camera|1.0.0|https://www.jspsych.org/7.2/plugins/webgazer-init-camera
+plugin-webgazer-validate|1.0.0|https://www.jspsych.org/7.2/plugins/webgazer-validate
diff --git a/app/static/lib/jspsych-7.2.1/css/jspsych.css b/app/static/lib/jspsych-7.2.1/css/jspsych.css
new file mode 100644
index 00000000..79092c41
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/css/jspsych.css
@@ -0,0 +1,516 @@
+/*!*************************************************************************************************************!*\
+ !*** css ../../node_modules/css-loader/dist/cjs.js!../../node_modules/@fontsource/open-sans/400-italic.css ***!
+ \*************************************************************************************************************/
+/* open-sans-cyrillic-ext-400-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* open-sans-cyrillic-400-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* open-sans-greek-ext-400-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+1F00-1FFF;
+}
+/* open-sans-greek-400-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0370-03FF;
+}
+/* open-sans-hebrew-400-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0590-05FF, U+20AA, U+25CC, U+FB1D-FB4F;
+}
+/* open-sans-vietnamese-400-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
+}
+/* open-sans-latin-ext-400-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* open-sans-latin-400-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+/*!*************************************************************************************************************!*\
+ !*** css ../../node_modules/css-loader/dist/cjs.js!../../node_modules/@fontsource/open-sans/700-italic.css ***!
+ \*************************************************************************************************************/
+/* open-sans-cyrillic-ext-700-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,d09GMgABAAAAAEa8ABIAAAAAiTwAAEZUAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGlobinYcPAZgP1NUQVRWAFwIgjgJnxQREAqByVCBrXELgzAAATYCJAOGVAQgBYUGByAMhhYbdHgV45ilwMYBgL21CRhFfdazElEEGwcACv1Osv+/HtAhQ8LWgkz1D5kV1ZVcFaakEBUjU9TIiU0rPyfEliGKopKB6YlWOLlXDg76ekJsZ2/U2rAc34tSVO6VwQ89h1U4osuLZpuGdz1UjDPb6af9T4e8+S8a3ujkCI19kgv//u/3/84kK9nnvo9FJAdMEm1VFaoqAlmWyLqjwvzPJ/Pqqle2Zg0ySGyCwR3PrufkIx/OB4QOCMufOq9P0X2CMuXthlqioknqDMA2xYmJCgoGoiIoIBJlNC2iIhjQBqiIgYkRMXNO56KcTp2fK5ff3Fz9d35f1Ht363+Rj13/U80klSJ1lWZwtj/mO2P3aqVEbnLYdDqAcgCWu6uTiyrXd/PdNC7KB0fKXSx6+8T+6u2e6LTz05mZRKIUpeRDVY20tytilN6GJdMKEScdLKSLNtMKSSRfVul/c1Fq68jOs8I+ba/7ng7InnMI9UeRJiWSDAjnCxBcgFSldFGlNHRVpk+a/iZ1rT225FUx1wj49CMm/U4r+IaPYpoGMxOX/SvLtizL3vXeXm2vtJ9SCkoHKIAPQEdhYGoBsBQCj+YWct2HPhfM5ATJAAJuC5mQ9jtiVhL4n8tmk/+oCS2oXYmSWLfb7/LoH3Olq5OU5s5InATHnAPjQDj+L6fVtWTZbXVnKJMeZPcSnZMs0eB9gQ5H1a8ql1SSYkl2Xiwp6IbtOAN20+s4A5E7Pc9xeiCDcJu9ITlu4nRngWEQbst4uMxp71fg87612M/PDLBiaejd4StIXUi+0q8xrVswpZvicHlNQUMQcEVUTvnahjWtlgRjSp1WMyoiAioqIM3yvHuMYRISd09+fWYzIxIzroX/GW6iPLxFTXif7vC51bt/4A7mGDIAduRqdr6BawA1+XqJsyRhcfa58SkA71uqSwt0ckdpdn5yZ7auNLlXayq8Aaj5utcpAixF5acAh/vuMbcAmIoiKVEOLI0IvjUcDM1gJ/prBHBLzADAHR4QQvR9NTvnvbEs2uItyRSWZsWWZXlRY/93hdVZi3VZv22KmmiyCdtlU3bADtsJO2sX7Vu7bnfsJ3vo9+JB/GZP7S97b/84wCwyM5nT3bzU3dzLfd3fL/uKUz3II5wTNR7jCZ7iKs9wvRfEYy/1Km/wNu/xG/LzG77ZB8/lNt/j+zO9r/xoY/+ULynIxd/c8198zZ/7a//o/8UGKnoqVirucA/vwAYx6BESUcGL2JCELDShC0MUhgn9j6Mj+mI4xmNHTMZsLMRinIkLcRX5IlbjFvudx/Ey3sbn+F9AakZqdrnIQ2j5iawAhYmlaMUrSQqlKUt5KsYsZpXWBb1cPGKmkVNZpIzgmYDcQ4hsX/kqcqBPlmfhXq8RljNLqFOx0nJG5lQ9prg3NqrlES2thJsoOjgptYi4pwoaIz3nYoJiqgNfwaiR0lsI8xUiWkhDigUUSbijCYBAAll5OIxWxOvWPOYCj4iTRnqBlxKfKVK4Kwch0dCYSkHL6oTXNyIyU+MYRxywSf29AOudPBRKyxvsUogyhCenXlkoiW0qXBBp1A9NT6OAeTjgE06QoSuc9aErRcEcS1KnlnnEDJmzClD/otKSipWOOijkAUrIgSK0PgTYAdg+pqZzKHkxJ/U+G/1UOO9wkaA+RWKaL+z4Yg7Z1NBSLU1O9N4XjU0hWqa4hAoqDWcpZ2FRB7zsKn/hGIGz3Dp1qJgJeiqPkD71P5ByHwoi4r2L5zxLPLSEYAYKXz0fX7xXsAKlld2yuz3srnf3xmw+W2mfOAbn212WQBCksevh9l4shcAz4Ym8Nrm2YAH3dBj84Q//bf+VeL1k42ewAORRuta/MsEab5rxvQbdh4Gnc3b4vMvEArPdykjotHHzfgDT7L0A7PUSvfNzUUCAN4yQrUMYWyfawSLhiVtNBjOz/ifuMJjMBPlNxbQZMJQHDC9LbDdYWcF/ANhufmaqmxsoYxXhCg2DNQK50VuOE683UbI6NKCDCbV0irpRrerSsA73+r9fA9j42cavN552WTLe0K/c4Sa3CgYoh3BZPJqSgAHo5+Il65td+v+Y7vzKvgDQLWCBiuEpBeKwz0ib95jUiNHfbdgRtziV3Qb16OI2Y74lT6y/gWZbTalm2hYbtXPWN6LX1Nylcyywo1OAtDdmexNV0VhL+/oGTdhh0rwaOgxkt3evWd+STSHVNNJjnJ4KmjITbbTJNrtMmeu8bkNSbZOm9O+EPsIyKswWb1VPmx7mRFvsNO0Kb+yu77Xq4k80MRYkI1hU0el4o7blv5MO1BaiLkRNh2zYEdOnx24ZH9vc2dHe1trS3NTYUF9XW1NdVVlRbiorLSkuKjQW5NsTXz28vJ/PptUk+C+/+Pyzu9LZF7fPx6NB/9NPPv6o1+20W8X7b2Ub6400psk1X882mo0kJulqbzYDRyBoR/jeYNgQ7MDq3tDmN0prp1jDwtP5cV6YSnlXc0w9aXARPL+y3oh7g9JSLiuAvYrxBS9KHfDZOQtu1x66jB4+k7t5VK0nb1rvhEzAiLhphNCx2VZFel2/3/7jLIaNY6wa1mxnvEExwlZj7K+nadlEKbXA7VTnmJynVC+4JhhUWsiVuWszZ4M1DouMapzmr9MpeciSCIHmSd9KY5NWWF02Di3XL5Q0lCbn6vq8i6rrR6TZiIyrqOj1aLTp6/HS1jkgf07Yn5kWrv2Vi0ficBYxEla4iAoq8iTpT3tDiaZO1bYMFb2rMlpUNWGHyDzuEDb4LNciJo9a3vAurKD1NqKNavOojSR/8AimBIsqvonolHUXG8sXaxfrF1sL2wsq0nX3Z/Xhn4QkpuSXjLbTkKkwnNZUbfFU1UrOh2tbFbeoyq1jrJbyR2hup7WueI5+6GRpf8mwTSrBWXEVv5FmI488ZLhPq1sWrAOIct4pziKhk8zBjqxYn1faHba7aQPPmiRZnil2cccOeZTHPL/uSfug9DOyWzQWK+96I/0NelmWsyM5CEJ3Ki58uo/h6UPv3LPVLSltlYmfecoK6fIIEYiNXdu7LN6J5L2TniVnkXhlC4maXSFVV9yZ58l5JEBhJ2iXwz/DOee0/yAlT4hg+/ZezV2ALGE5wFNXKnq40trtp80G5RFD5qbZ6A2i/dT076yH6FASd0P5pXdDFaaL3WjFWu5fV3XcOGEtl/bmBUf3WSV3pCydA+vt5EBT2AE8TcA4P92yFH9fxqWV7BpPeWSjvZCCqqCockpmK2f33jQb6YGSiuXAxCKxMNw9Xc0/Fq0lJp4nVzAwy+6q3hN9zggRl7u6CJOpX/o4n2bhqAU6nWgfWk1T11/SGAkZI3ugOc0mCF1yt+QXL2KEFR6uQDbvo/hLURwP8/D0MxK5hko9i8YdhXvO5NcLqCYkMucjPpEnpRZq6f7lV1UYmUTgd/gmdddIdqK0g2cKL96mKuxyI5fFA1MeGU8XpAmrwbouBaj65N2h+wgatbWyku/TPPQsGHPNvi1+v7QWe0HG2wTePP0K4YQtys5YO3BrOJdCp4De9XWBcQvsF8duM5319KJwEME3CEznCxYljwyHGeSKKU0C5HCJKY4BRfNVztoFcQduhEbL1YzJafsrUQVh0Rv4ek+2OXleZh6xmCUFK6tuvWFNGRXUznZQDjSA6IDtjkEfUmJNZMnhTAb3TVxcfeeGQeaheXBGZJYkYMxiJN8ICfK1x57Huw8SUPMcjZW24UUQECDP7vgWZF2tJGcTeIJY3kZ0nrPUDfKiARlzhY6TWOWUaTSCg5yhGEX2AwO1huWJsC5JdZBChak4EubjygMFPRZxI77lM76nZxv6vhAqD53u28DHAlXXS/XycD2DKkG3EWkBYVKuIVOoLazGjpoBuPwQSUWn+uF7RkJEiAr4rD6j/EbnjgvqeslpZMoWwO0S+tWAZEMOXUe3zKmEtRK8PaJqCKgafKIx6M5qzXpVBSTWQtOccSOlXeYrh5rdwIAfZ6K04KSAH5VgVwKodnrqTSSIbLCMpyX8MyON+GCO4gUj0ypo10hUkG1vSclHxk/fSxf3LXBOnYShIURMoVvFi3FpoQm7rJZcf1E982V7GiaBXB6Jl8OxLXSiiPgpmUAjkieNQAEkQMZek7gE3JomFBnpQcRAAtHFMGtrc7F1zG5bZboLSvXuCQsYzq64cc2PizuD08vMXqz4CRJ9iPAEhMQt+XcGGNFrRDdk7o5tLay0zRy9GdQznvFXv8bvUogCQwnAIqjrdcxEkDYAENjBgeUxSfBMILojURx1v4kC2CCyNgfo6Czg87JRDIcyOSkuCd7Lm6yMXdiR5MbGdkktOySGMgh95ErNjHro2nIhuWv4bsRH3C1uM/RDNPAI2OQjyyvErFy9NfDeNBvi1pq4Vdc4xvaEdXBqm8Ed9lbIJ+1I2z91gQ9LIm79xZ7B1z/8F27Ge9pucq0OrYjufUQhTyQBOSkXM+hG7FZ52ji2P4CTvP+wViK/Rh+FajYwRPy7/aH5KFf80LH65+8FyaNKegjw2lN2V3zqyYKYeCAjnCcuBER4Rz/LGEbbJ7c3DIxXNSotZ5SeJCdqLMOmGAWW4GkbKD10oJW6fuYyVcTyidZj0VikzPbCQ0RuAxX41rTsYPDvU5gQGAT8wGvGORTgvGRrQfpH8MxkTNsgfYP46Ooir5kkQbilIFXjFn+txVBUY4m/0aEihpSf2RIbWIsGnzpxZISaSKVv7TNJQISOgUdyvmqCig6O5h8ouwOymRZ1PZcrTTP+Cb0k721yULsnfhP6m0PzA31IK402+xygmMB3RrdYFmSONSfEoQvjBb6jZsgJzERGyEJ93TVsYBurdcO4un/t0bqCf/aunzZF/a/wH4X+K+zkOVigIsc2XvF3Grf5OLwey68rGCO0ApD8n4flIJ+wYoSFXzGgq4kzJ8qxOYVlrREn08lzecCoMRS6WjYof2XoHBCE5QZhtBCflNwIaJfnnqN8Oaud/TCX7po3Ba/8i+Bl6XfD5DPhh657PGyZFkUWlvB4R99INnlQm5Xy9nR76NdqMzSDJ3iAD/AO/vugDOrML4e8qstSyhuPU65pahFuctWsZP8IrlSrlLfnDIcdD/unpbodt1BKmPKgG0jYv6139BKuvxJ8J22rWbPINH7f/xR/5D/Mp6ssI6SelWmDj/6pRXYqS8YCu4Rv/NyZ/uDU2LaLeVmdPHFKsrDWqRQlAv7AJbVIcm4dpyC4CvEbu/Qq/vvlnMMeYvObQBf2rBDd7WM68BZgqJuasCFJDAYuipEiePUQ8P+fENKtvR9qDPKyqJCpfcVDOTvjVkWUfA2WgBYLsPBU67RmV3qEQo5GeZHfAyRAi55lCnJiED1+yoXB3TCQu40vJJz2ARjVwaDWaQE1cpkPA8vBMXDhiyNEBf6O/JEoF3l0IKk4AwAEgQCrPlSgNMizO+gL7+RNyiD6qbVkcqHxqXXpNVZUPHQImcDblKQcCyTjOsqopfp09tvUA8KUVnAnafddiAehjjMvKtlM1sB4/kIjoCpBIq8cFEGhbBMT4ieyL46jXGpceu1LkChQhNoE0vfbBXoFq2Da/Sy3TdjK0PiV9y2yddGUMoyztEcRLD9hqWV+4KJK63sT+Z/X89M3EggjPJza0G7eMUwjjvyOVV0igzLBOjsDln+JzofS9glQxN5I6yruqPBn1SN/gkqmF40upQ7eI7DqRx5X8dBYMGO/ws6WsPX+pDVdPHJ6dn2bW81PQIb3gPOBlAN7Z0gwtkhURGs2mRDhMjzprxoNl+RC7ak2ZEzvtwqOz2C1uGRCZcFuvW6IOd3RyrnHsOgkU0WWSEj/p4haZCrjErkonvgjgcVaPcJ+8PCnY331WJYF2WF9uF3uYEJfzx53byilcbHfKvmob3ZXtrMyfgf3IcvxcglMmdsOK2sf4qIn9e/P69uj4PXCMF6RdpwOgn9XSYXz5xtWH73/iH7vHSRDw2rgBhfZfzc6UypIdrpKEAxgUTPmwsl0kLWxXhW6DtUvwsA83RsSYt5zgqM2r/ZnqJwXn5iaXKikDdwYaqMVBEnYkM2OZ0OV52oJBrlmGxx5yAKm+2m3fS+GDFtktNq2T3I8Pmf6tR58Ps6R+ygn16CmBDkivIyw4vRRhyTjghdnQctOMWt/7b24SS1uegdy93ZPT/HpAzsEiuqRdwiyjflQDNWTvb1oyS2VOoIT2uGih7zBUeRCB8nwQpodxrNyZbmQBEvleFOoJzURo/6ocCgqpdeJxPgMcwSYX4dE453SLrD+l2ZraOi7Ep4bQdCN9q37k3JNSI/xqBHah/k4l6lQOg2QQYDt30jlyHmE1i+gtdGhcF5n8q9lVkfiLPKPPIj63T+3lf8VaLa62nLH8MllCdqdqvN+dHrTOT4q0E5gZTZ1g/Pp97Zs7rhd/G3jXHpiZwY0CERzj0LbB7H2KpUXT3P3xfetfV5DxYUr7J27uw9f6NIjv1JuzP6Dt9YLR3TaHzzxzUwYu2vJtaLDuUHKsDn/pr1zXIinltMD8dUVGWNBBlYkbsox53mUz1KWS2+0DEgYHT5wkHbUcr9GTK3VQpdCf8M9M8x9DqWi2CFEVXXQLj39wqBJXNKKKaGRx9NccqTeypAqtyrV1XbV4+ENWWVWmwFtBhwf7198a7muZdVSW5Yrqji9IZ3xYHCrGt1lj6W9V9eY+gy6XpOtyOJIoj6K0+xMNnrz9UBvrF12SeeZu1p+2QjZ9trLiSkL+0rjGe1Yb0Y1/USxnYTdXFR2wIFWklO9cs4qE5/pV99jRUBgtCKIkzTjJwENxH3rYjQi7eAOhWYCJ+wEapj0JgBwAXuZNbWNB4XNL0fscOH3GHzS+xh0Y8EHtHIJDUkvlGx5P9lzqI6tyYMwom2tSlcOWkZ+DoIdhdTGGdIsQp4qmSh7tEwCRRDAYCAphD4VFAzNbqIrg6cKEQisGPAsRh4RPTQdeTMUrI7a1ne5vdacsya6NQGLDSEMM/EufOh2Gt5VKUnl7UKR9Pb9MqQJdjfZ5M+BUskTpTx7Lvi7qokVFSwhuBD+1BJPFCjE0kAziwJBK9GCYIqscOYyYYo7QfTEJZJt97owmKm92frcDByrwtwuYriXWa8nQia+F40lUNtpUVO69Qlx250n44ZKCmOQm6Zmp7P3KWOInwcoquFjbMOik62j1gdaRc0YrQ71KAXSupCYxHtPtcNPRyyYxpP/Br2aDVUpJO36i76h/g26Ya6pbqZuzoWUCu2MdrcnckwSBrXWUt7mphKU2DapdJgCYHfO/YXiq+Y8wHoldQNyEFQ95WhB644wxCZdT4LUEcI7CV3d34VkAcUvLlGUO5w1GMn+TZwdrQUbsN3dIvMUi+bBDvpVsvMW922O/AEvSEi70nZlPOdQ35eHZ6FuHESnXvk9rkQDEa/J/WQeBGQsMXdjHPav9jnlz6hU9FuYs7Hk8Uhi7sRGi8OzNoY0Nz+V1SkwpMnBITR4lR/kLBky+NmWOp5BYQUboKJxmt6ww6uoF5xtVQjjqayyg5GKoD+JVXh2RYEZR9atMVaFmosybFRSw0FLD5sWoBQCdrYuUl37Yts8SiBZ53HJk+aEdOYU8aJO+1XQB6Bmlq7RLTUUN/z+VlUlgskJ87w/iBP9zhisq/EyXmReJgsvnPk5F6DEY5YcyyxDehvoZ2Q6OvreVNntkIcOaFRYEuFpoYhG6Ra1WH7B7l0kLi7lZjK0I53Pks2DRyENHUoLDyFwbNEfHlAADIAcLGYtyNDAEDU60PFEK1lfC7tJbCNpLIBFnovVqGCDa0PhT56Q6eudk6ZasN3TW9uDbFYuwtCMEi6P2cjSYPDC4esMkyw50ZxfZgmiZTXA8fY4CxRrlJiUyI+yR01SzRDMoThQVhiWB/CWdhljfTXtAW9G4JXatrlcQx+xkt6q9BnmwRvR4Y6n+DoEkVN1dqRwlGtQngs7XB3LUeLFphYoKMFvhrnu//4+Lln6OxY2nwn/AWnCytjd84o0C1ay5WEksWbVHBtt3XuJxpNUgyfx5S4qWLOGmY6rvSWv0NxqGeVYHqMIFMpmXi5R0xzTmqHdg0L81t0hvaV1Gyqntm4MpPB4+6vHpMTGfAhZQlT7FFXNcDf1u3CUfjInHJdU8R3ejRdCTxfET2fie7sPFIeP3KT5AKCDN29fdbI+1BJZEzBPpLl5nlX+Ud8TsyD/G6A8Wiy3CVzEu6rvz6n90kXwqnCraqQFlmb8Ez4MPfRnXxXgYHOYVeXrk6e/GPf2nmdkBJT/He23KViTuUVc2CWYRR+XTXXvGU2e8U9BAa+sBEACodJFeuAXQKH9OejLz+IqQGDbGdDyRTq0fdFuC/Tw6+7af/aX+v2RdLPh2Fzn/bh2Wzv/z5XYZ5LbdUdnOu7FFplHGAnpSQlVweNex3M763bXMktpGWpRffAWj/k0bXhbCQdCqSA7LHeGNInLxTLs3s4KsusoNHFArDo0Uki+q1hq2LRLmibI9YsTFluH9D91WEv73X7QujhVWIInv43KzYjbCpZVWkooglgNm5gQwtZRG+AbBXKuAv831HQmqfDoSDXtZ+3pgZ6TotKsHRgeSr0my0FxBIWJ8d24WQXTTxpPCIpvJvPQH/m+u/GQkvc6vMn4u5aSxtUQuMLt1pYWcSBdd1w2CTYFe+Q4aF2sEZbgA99S4ulhKf65iEK7HJwkNp0+iv4vAQptvZxWdG6oTwzcce1p5ZYzp2Y7FAkDO/VvgvYyQGYgVU5uumIApyX9Ifc/5v/rBMk0SOMkUqmF9BAjP7kWjoFNwUih9hu9TKjie9YtafFFeE5GUwJzIBMDUu9pqyN19ZXUwpnqIJ7RJFmGV2hXPS2gDUs5BUvtleR7aYv9HV/HVrZ8bci51FdKvKE+2ttxNjbjPT2RzlT5V7h0x4S6Kx4GBssimNn4elhbpFqYCumBZtSLIgd1GFDZ6QY1qatP21t4Ir/AlHjRrfybkORwK+0xbBXkz9br60M9LV4E8UAVrzfl+KTXFpKkg7a8NFywhQhoMMv2s/7BIxmrmzaHC/zb/g8v3jQyH+0euvY5WECNFCVIYhhCcqRQIFv5vh+61Qu/hF1t6L5FicTN+CXhE58gVM8+UDekRzbIJYR+qKZHIzrSVk75WfV1/cA2iYqvxcTE3AKF9J9xVH7YGiLd/VUTo9cPhgJADiBJakV1/Cydg/4jwfc4VWfsC3wBTj1jP2hbrBAa8ZS3xHhKVDK18NUB23SqhJ3OJyWEMHWkBteNgnShRD3dIYU8h4D7xBv5xzba4VLuw52TDElFr2iOKllSrionMZ/lPU+yIbFo/SOBDE07BJ7+Pj/9UK63tc6enXocl/WLptaDTRVnomMdbbnWHtN/l44pLRR1g1t59kN8oYWbKfvM7ra16DFn9SHUQ2jbFU5RS0ZGYt1IjBZszYVrCyQ1rUO3mDXNl4hxfuqfBT4bSUpZaSg3IIOS1Um8sUlk8mayKMGqSGnlVBAH/VHoex6PXHqhXf3FeeWH7b7WwpymdEJTji7fPV6tkAIdbzO9HS+FC0GVIlV3xKoX4gTpgBcnxXX3ffOusxDdKrIee8TOZYZiMzc0hxmac5uL4d/APoxwXB1b9d284r/ifR06egUbi7NKpa9SdPH17qpgqpKZNS0e20h/06j9yrtQmihIS0pLO8SWhxRcN8V+lB3rWNLoJzRVuTtPp1RbWetBXQp+e6JRfGBb1Yso5KCzHvjwkb2e9Z61520pF2Glt1qwwYPfwSs86sHcEvk1i3N/A6WVgX8FPc4BU8h8Tw6BxXa85cTYUsHcs9/f3UdyVV6VgJm0lA6PfnNGj7CsoMagFhka1WRkbl+lu7KoyJeWbx4kh7LEHKF4CM9H9vTTodmLrsPdR6KCijlwV591vKY2+fjOmoeRA9QvfEmBDzvSU0ySpqdoRec6SvQw7rcVYhpadd43iQTmYerxpeAA01xy5T5sNSLgC9gDbp8T0l0aLVP2+al9xR8y/H6lVh580Vb535G2INuyN0fP/GOEl0IJXKsy8l90nkuoKpCXSq526YlOF2jZ6Mbn75/LwBHTPzy47YDwmMq8jjqmtB2ek7HQ03okxkjkWLUx/kKnuIZgTNQtSI/RstDFz94/+1a7aCY1/et+B3fgV+kXUPjLPQ7PTjvQVX+Iz+iDllwirH7mok4EGgUmOD8qJFQerlgg3qiXlrh00Chcuow7RZCgBQ+0mPuUurJjwYaqrERevokjhFonueTlpxeVT7/TINuZ5H4X5bc2+cooPSpC3ZzAHdRhwGXf0V+TItHaXjfmr6F8nVp4DNHh3kByWEtDvIKOzMoEw2p3m+yVjiKS9lKV3i5C3RcjaVMbMk/fMY384Dt9yojxO1qNRz6NOVaK43BDUr5uAsx/8zy9oLFtHjb/hVRhRdwiQrLBRWXy/zbLDWYrICXF+5BCNcge8U9C3ylifvFJRdGlPeXkW+kLQy2XBHDS0fvTh1XcfpE9Dm4LNgcJlWyJpijRLQfinl+264bCicPo5Ms9mh0HfZbCXPbDQsxcnJ6CFTagwPLQytQTJeYxoFJYJTiB5jGMwlshYRZuhfbqjTtjO3sCX9Ai3E+uGxez1wgecuIVz++s1GYCLyeMgWnli7OkIYd9vI9a+Lg/t8+wzIouxY+8jQXvcswiuG8Dw8D/OVVbszJq3pfgqLN42vJ41o+G8e8sXVn4EeSYLefzQa9G+/eQX6mPfsaeownNeK3S6NnDPXBXn328pjZFHpvEX7IlD5SmJr8ahdDhg1LRSCrGTrMo7XtJWXvlf5VEvIokr6Gov83m2XLLt4nFXTJd9smnFVt/oPDhL2pQIWxOsney9Ylj+z3prHh8PAT21cveyi9zzv4Wl9sraIqwkaqIM+jKujtH971OG2ta8Ri+Nvh+W4NlKR1hWVpBZbxTwBcIA+c+0T9RFW6EVKJj3IAZuAu+OPZ1mOlMcuGxEUdf8HfaVYrD+zSqZv4crnnsuwemvqztbliKvjGFpgcINX05lN/0dpYgJl4qIgTFNZP56I/RvrvwuFr66Jly0KUz7twKWkMFvKOCElPhsZx100aJt2RbCC3ioZG0XKLmJ1Q9HpaWD7qY5hKyDyPeB87Yh3+2T2c61euQaSsP+fFPNA+fiNLsV4KvjEiL4hQnue2MsIKjrLBsqwgwz7ppq0yy2woU7P5+0TAOt9Ic1fOaL/N5oFtprlErcOmKQ86Kx5eVAIcVa90KzWlhxOsRTfavBzEjp65bkAr55R6ia5d7//Hfkt2ydYztVkXepwkul30cJQ0Y67qaoJYaa9/fg+MOXljxzSwx54wS9MdLSRpVoTFM6LBc4JzzEyxHW/xYIPdttJH5Rv3vQqe5n+WqL4D0l3//hgbcmXxx+uRjH0xgp3wP/7t+BNG1C95/4rcEZLYseJN1fUkocOo2vucChecuXY+6dVUbolm1ba+IhJaYcUYolVpw8SDfOedHKL29o+WYR6QgvudqLth6DnviWB/jDboVRun+U499omhDP6wL3UKCOszbzBmLc1MVdXnDz5JPIwwOLcToUDgfnzUtHPbfqLDLsYDAFEXEN1Zp8EK8ETwaUx0QB596HS0ix6eMlud6++FG38Nc4NviOEqVc0MGpzm2Knf3aVm1lXU2qDuZ3yKtS9s3pf8xpM4+w9YED/HBbIpkpQMVwAAe3/lG8J3rr+NdA2STVqnGYb6wRRVPLzfI9gQVggpAHciXwQHyUPP2MluTCmEdjDPeacDwbDSR2l4uIsk76hq8GWjhghZTflEgSioNibw8E1sItR34fvT3bRmz4k85Zdhk4ugEgXckf8CGxWPD/gjyhP8kZFfQ9TpxH1zZbu/trI1oK+VUNVRbIhw7G3bXskppOk1CI9x3+3/tX/ho1Ndk+Yd2gNWkFmuxS43W9lZqXokkWBnJMcCDrT4s4swZEh9AAeWE27zE5eDSXz36LaKh7MR4Zik5HvLdZ7mtO+41MxEj9F4Dwl9bT6bhiEI/QN15G6fAmpAuN3qubmNzTDr232t/hQhdPV8irri3QhB2dofqIliRwf3wi3uy/9szIkLt+vhpZsbtFjeEFc4KA250P78j88OOOsqbzHsT87fKuqCX2eGaltZMkcQY1uc1lSHBNMekCprfEgmTvx/LSpKUBPd6bpeq/DrjVNH9Dwl+LfigwsDLyD2oS3+dyvE7JdlV0zCZlAO+fYlOnB7Zj7zw6VwmZjFhe2X93mQD5NytHn3hAB5P+4l9iijy0jgYQqKx+S8VV4n4ztvScC4hyUNjn06JJesxYdMxkOTY/GK6IDIfZSAVo1hw9yYncQVKS0pFBZM5QQ8CIh3iGkn9YEfxI0WNNwZKDuWywxkhKcUsDDZg591xo/muRt/nwlXjzFD1MU5B7rawrYW/PdJSeFQFOV4eWUkYcp1UbKnYnUtLwBKTewjJPVolipbcKtZ1s454f6NXUUwJoRSyDrMs+J1Hm8Xzm842GZ5uq8e/Tfyx+vjOzouxpuhmjIGD5/P2ibjEj66QpLjcYkZ0lBFlIBWhWHCPBkcJr/8AWcH3w5iQuPrIAYhjySNVpZdvMDGcyw+lB6eUsFHtZkUzmrRDgEaMBLzWLXbVzURplTV+itSH+zlQz0h1BDMLV+1YzciTGITeErIyGIQNsz+TTKNQEiiRcv9K535AwrQwLRQ7eT/knq7UX5Y/I9Md7ttIfZG62NYwxc4sPaLFcMSpu6LZtrZ/v8i+v8P6br/x42RrGKjyzckzX4x7m1amlHtMBMt+6UsKuwUH0Wc1E70TA6RE6yTwg93rPORF3o9q0GiafK5pZPbh4vlLE7BBoTpX4O5PULFFk/BImPtefo3X3ktKxN7oOo+9x0IaYo6Fu7awJqZd8ffYF13zC2QnXT4MVGTl5ugKCiykEkWcFyOSjVDH3/wxtt84q4RMQ05mfv6+4pw4ZBHLI49xxLZMMkTy94z3ol9Y9mPcC+UmE8yUvgMqNEs0GOKumx9wE0gHRzBUbLud4t24Vot8YAwspXN53OgEegJDQBNM4l8nC0l78k///bkuoYGRJBO2jBykTYGew8bY9nq+i4am4M7ZxPTOb0akIGVoqYt0/jd+HjfrrlZrRsOJcSk+yU8E3H/M6LgEUMDh/8bL422/y0lvnevr4JUulr0PlCkIOVKK1rIuqOZvYMvIpkwtNAV71lZoHa6dtZh5E2pXr/TH3/rgWwdowUeccXe9qx82uIOD9wYXoUesJ7JVNTRhRqOINajDQEaWb33QH43/QYrAU3nkJokJdPBFbQ76MozcTPfe/Dn4ywfvrdz5YB0Co+iOUVnAL0CZASl+XSD5Ym/nDr9Lu8i/kKi/UK5+WtadU7mIbY8/mxizyPIK8U6yvlq2kwHTwsfmfhWOH76Tl3GucVf//crwYNN8Irv7eLo1fVuBlS3Xst5hYSnANJMYXkfBKEI1GXLtIJN+fQ8LtMfABLFpR5z1sY8hMzYUtbw8KCZxQKw8PGok35lleC92pqTzUv1ZwnlrCwm0CxIvSOZ/SxSHsHWUZYQbg4Il51ATaOHJBAOiyC4HL47NoAU2JI8FfRltyiQHfgFUXg8M/hJYl+yJUe7upT7v1V5fC/h0nHzPkvrcUr7b3W2djLpnA5+2wuO4xSS5NLLcezBpcmiu0yfG0teEJkGliwQ+S3jtkxpEVC9EDLRJ70yU0X7JujAxejMF1fYhtAW2D1xY4BLK0RH4CXg91BSZn9Kohj15JG5YDABKH1LAR4toEKF+pkx6rVOFOic+1Nn/TZJPUsmseiZmnDiGbBL5fbH88of+nVi1dkjd0QCfWD0OcgUdGLOck4IR0utF0Yw2/Vx/vEMiLZ8eDW7qGgwMYs9qZo6AV8dWvdwP49Ccx5yCf+PyS5+Bncg3g+mDveu1yZvk8D6NXGh13ErxC3WM/qGrB/lp6b6oYRAMNCQu5fkIYed0+TdEDzXuq2OrPiowMI1UaLVkJfmD6kaMtUKb0HKlHWNb+7kun0AHHq054EszooPu0dF+Be1MLD0f+zCJhQlxGAjwwB7/y3vQu0iHKgNSg7RUH0ZM4qMm/i/w2lFegFnl4ByiJsJbOWR18yriNuiPaiF1rZJFjrgWBMtsVj2DbK+4G8Rrd4ep0CC+Sx79O5GB7KHUwQ3pKyrtil9eI1U5TwKNZD3s7knwrEP0AWbB4LWjdAIdnhikIa3UNRnoZjUpd6mnZA9A+tuFBZeuQ+O5RWRF7QrkQGIZf1O9IIXwmEf9FAjw6OkTmPi9gOjZfEUDIisPRgy2ye5MZPqcFczmDwwYjoT6MM9/DG2BHQM39DMklaELgvZWR4OWq7n19+T+JWtnSdvISa+mTmXH/01iYopTEntpIneBvdASN/2upfWfI91h4Ko3x09+ytsRW+68OfmVh8SRoR5OMHYLD/peyEjxH1CoeBvA7949R46qRqN/rx3bE6fJGRInbC2RY2djd+b1coM/XvRE8+0J7zxz7f7bOhptZDQi2pneP+13Rp8k5roNyYNhqf4R12NiaqluaUUugo6TBenf9ucRVzRH+juXRfX0GlhJ9IOrXFsfbnaoIINU5dwUkYDMLmUvtSx7eZWz5IQxeWk7W920s3tAFrorrWl0zzAUfbrqh6Dn+htSUbAWWcRAd8PhWdvt4/E+DgrflRn1DBK8djQjyOxtkJnWbu9Zj/6drvLEofTB1uTsbA9jFpO8LFgfRAuQswXlQVsdlenXlwrJ70MZcq6gIuOkmM1Dax99a3+OIbspLKGsrgqaZ43n5GW3hon6euY3YUDKPWEeZLwd2gsBP87v6qH0vTC8sAEGjF9ZOIv6UGsYrTrYL5GL9U3LeeEayJ5Vz4zNefV6yZj//nmSNW6RZ6HwvnXouXX/Tu+lwdTBXXVSK2naLxG/ebNLRe9EuhvUNrptV4+34E2V+UNlat4MMOPZZR5egQCPUr0tHfhh5IhniONQ+iD7A4K32Yqsqyme2aMOgJQviqv+oa55Hmv8LJ/WWnNdw2JpMUWuJL37X6b39XA7LvUxEUPKsNzzQD96IAFLnv07UfShofRBIJNcCU+fswlj98eq5jmo7jxwT8sKaokK/YLPHWKRs+Yj82isOozFOcYTDHPDTyqQe+ecvkeM8Shyr6v6tHXg4i+9gX93k2/t2DO/6TifGkugrsWG3rsbnR283cXgbnAtykJVIEQdq4ti6FVU6bdIeoF5tADKTmDHigfx0cjefmTYb7DLr7fkAyac8ZYG4Fi4JF8UgTN6/EdExWLRrTRbfF/Xw1O7X8j6e0+kavaXlqqOfd2A5azkgcwdt7fOTegO+SchN6T4XsFBk6s0iu3O1QF8JRf3dJXEqmQY0sWtmKeL3L9egjzMlMAGFjeHSfdQXb39yZ1DxBPQPt3bf37VvGvu+27j0zaTerDGd2wH7349zxLhBE7ufv40QMhdyfHa4aO2kG4BPXLg+7uDfzioHk6C1+SxJ4tBUJDatp7J0u1OGu0j9s7io3pxSVbiJqtNbvwviLO1Fa1Xj5rFfU80wpN8kiLF8d6+ZAOrvz02jySxdy+wsj2T95/XQQ5XF/88nUe6q13eMn5HgfrzQ3AVpNoYTi4kUOUVE8lQI6riCky9FWie/YQod/XVRzJanbRS3HBOc2wvFByuMRDPqyb7as/yUIyhWYsZOwjwKMTDFPF7xLWQeTiL81XXmWlBGT7G/pv/q557EsW+4iJCsZy6MXA+7Hv2W5+eVAjQq3vwAr86eG/wPWi5/OGaY6Q0IMoQuhWdjdLzjii6muMzyFxb4ueYClfcGDmDUO8WTevLuN7aeZkzbjjuMwriqv4HFyG91slGFxbcsUHiogGFKZrjlLvgjXl/26KfsdNovd+F7f++qkb89CGuHj5gGVeAZMZoWVF1Tir5PscE84B13Gtlp+sapqI8A/zTqmDLVgJeO0ALOoL8lu3ye/ddI0evjf6ZkZ0u3DD9vyEhlOtJWwpej5dqxps4ONbz7nKfa/aO8cnrAwZzyYPecbx9PD8MHtnj1YZqdddLbXw2GeHmt4zW4eB851bXtiP901CodHETEMa3QafwpwPpbUZxE4fuDf5Z4wcR+Q5ds3RvDUZtuBkMtNE3rg57Vr6Hs8PFjJ0bjjhYwn0cDthBm8GQIh9bWzs7W9t1PnpJk7G/VJ+wcKwjIO5xPsLJadPGfQPGI4Qk5IZk36t+dVfeby4239sUalnx+viZ/4w7zeIbGPo0cQtCPJG7GZURQUqmEw0v4DPQ06dlHPVLrxGiUZ1jyGG15DUV9Stbryi5v1FsDvH9rDN33As18lNqIBKfD4xwu/jG1AQtvYgR+Vn900xscw8U1XopIiiqyGSjftMW4kqN+vug9Vb/5Vv7Zp2+Pj1Zozyxr3Yt0sfrVelp/1hf29QavMmZrG6jSlPwhVGsoMzSUFUn8U5/0yPqU5nf8m9LJpu9etleQ4fi3r4tj5PqzyB5PuXdjHoG8QntHxjVVxc8+nc6Xh1MHwQ63Ep7SP90vCX/964eRzu7B68oGQ7/vt5yyhPzE0Voz4GICZ86b817K07UAm/R752IQzHevBaOvLxisHjre7qFth/Cy+fcl3nvva+o6ZE8C8qrT7z362jCLlfhU34j91HAl3++FZXknRV9dPyHuXEaVuGdVXPhHHXtyrFjUtnCY1dE7trlC9HKNuyN2fJAXXa8Z9+Ou9Dcw3oTdUDcWPlfYpO74hPq/GO0STJ9sgdXAtPuZ/fzQgd4RfVzGvl00Vc73rs0HsuooFLsb8ECIx1CnwdHJ+rdipL7ArU3PPvqFs4nLa/j2F5U3omAL4HyzlCRSUlHsb73WYWeOvXjlXTcinipemK3Ws/VEYTC7dZOFjEg5fdI/2GDgXkhSmjwF4ZEaIDXBHymcaKINx+eOdk8mYGJsfMojq1Cgd4SBP6hsUStR5FDSZCEky6kSEJYOlKD20aBPE5B4BTDGg7FpY0a4ZxoT6q/MA0vfkx+sCN7C1WmT2JKRGLFDl5ZyQ1l5OvE8aYTCt3WTL1863a9AW5jRJQ1FiLcoM7BB5iC+BznwuS+IN0Nj77aheUkDNMbVn2In9GdDYKClHmhhUPEG63SElgHg/JQzT3wnY/4zX3ZBSXcydpowdK8RJCzTRA7gBYbp3bWR8BpnKjy0NIkqL/PqmewCQW54/BkY4rxMePVD7wkI0p9jaOqPcuDRie/qlKtm4W8CpGbJWsAlokY2On9y2D64LugvzUNsJjUsLQfySQyc03sBJjtnZelS4QEplf83tXjnQgN6jvtzo86Ff2X2Ic4n2pf9k9jjgv9+zB+st6lfWWPnW+vOxmbM1qMBSV8s/kKxCWVukidpUwQ2xjvKX588BRSYb1HUuSjOg+5RvvtwzXHJF1q3AlS1/uCC+zq9wLoVd8PS0k+SQuwuuOi7PESAjjdjgfThfEB+7J/iiqRNqbonXcN0i2EFjbBeo9W5frTRLpmhL+fQzNpQbAgTnCQQKR3K9L05w59Egpi4J2H+ZDu5ysL9rrpputBCYTSf94MBBZmTcY0NEqnCUtZo+3zo4ZeWfKwo8oYbLaAPuY5kfN+cuaNAh4E7bmbVnNxvCbkXuHCnqbb/FbjBRoHU/CosMkvJcZElqeEmzx7EzSZxem1VXVQOZD8RzByu9/yzKO3WvgHaONuYPyKLsjs/yCzoh2fGo2QuvGhRuF7h/gW2ntELhvX+dvLLyc3RZPXvqauxaevxjTggz06N8rZIBmr1G4nIdjQ0ccpzfvr2gonL6oaHa1N6LpNZaXp23ZknCWkF+zDdDHv9v32/2FmNiPZ5YwDPqlevbeX/qZWMu5YS4lRxWq6RLWFKxFstF1wO3bSBRn1+2tqeBOUjx+2ju5jvQ1fCZvx4DPHu+Ym+fkvk4z3j9VjVdn03qD5iNv8/7GjqeC2TkL4k7Kv2D2IPWIKwqcUZ2cfWsx2mYGLbwmpPyfeLwKEDjNHv1F7MoYCAmkZJ45KYhoSkmMNDTQ78CtdHYkNoXhn7Nze/4Gdc5KTHOWlc9Gu+la+PvmeekJh98dT8xKLGpwR86hsgqEZ7nb5nT4ER0Gylmush03pouw8WUQ21hPX3lndp90R0yAHqaPPaz6zd7JCbAHB1v7ZEt9cPFsZzxNFF29iOxCeq6USiKGCJNaJB8M0+vvuRJaSd+Jz20rG1vf9LriEZjjKkMPYLESotr2gBuYfvejuYKn8Idr1z4nBQlyfu9xpgyUEAEhqj30JGWQDv9eUKeZY6L4wACALRFOYx1G7tLpkZN+m39lhCwMGkG+4kabf2KKFJEYJmBugwQ1R67TNojGsRPaoEuSKLJhangHL66KyvTBq1kCtZIWqYiX32JjPXbyblEFkZRozl/FR28ia2Ij8WXRTxb+I9sYWQy7McruZRHBgSUAg8okMb2J2VrZvYtyPwjPGyqcQOUC2aML817RbowoKpy2Qz1hBAo0/T1I0WPzveMuTGIBroi/SLaKWOxoZKDmgqriWHhtz79Pgt47NDTCDX2khxOVfRr0jk+VhmZxhoiizGokRwW+5a4nhJkjS+Eay/OTDAZYYL7kHzP+e9Pq2CeAA+ACgfs5FKmdYeZYTJwDyi1GNHoDeLBS1M2zJkNdQzbAFU2XMAYwPBpNOgPoqn4CfhRbyO+CziP9UJrpLBk+Pt+hUX56Wi//RswJ0X8FgaK+4SrcWjJfHWL80Lx/LxeShdiVvVVAp+gTkAH06NHvuKKA9HuAAFNUVlRHIjW2JfrXhCSetITEQgpmIaTwg9sWhC0MG3I7/XJCdpbYeQLzUdTgPQFSDc9p7tj3HTbel0F3yGgYtistQ5mhKIKQSrcBWm+0D+F82wzSUByCqYbRp46gOHhXK9dp9cJiE+QY2Z+zmanlwKhYnFzPRuO1F+qdg4OzHhKKy07dOfe1EyDEmpACd7ujYj8P2xHcUeaQg32paqkWHRVUKMVfXhKF/cyeR3lUolddCriq54FkVFrNiJxpDk1QcgVjAfX+dogQhZZKAkmvTwbDE6EZlQErRArlQNEOA6Y/LFxonSVEM2vlHxsCjmyZJnNON7rCmtMv9BIMggZ0JDtRXlIwML0vBqzelHEGSpoTQgOI3EW5QGSe09gU2tViwqpV+ZdHo0/vDkH+wnEw5CaP2RpDgMrXGWTp0pJLoC8NvGFOYb9F7s9ktRSE7D+zuu6XdQNN/vOqFnND69GlKQvT5tctU+rXRyBH1j5W7vjDNxN74sdH31NbGOqIP5Z3pkS5rjSTi3Lg3mQ4KtZ+hmwiyQM3vUNZ9UddgSWwsm9BlYwsuTQnnxskDXfZIHDFwbtiRcx1mJBuCIJS73cI1FjqJdP8Dxc8rSgkB6xrCpkyYMF6oEzYhc20AmWDJcTVrf6pwpFXA//h935tNWoPVhHf36oIV9LQxGEnIAb9f6PddR4tijTr8OPnsRoTKQlMU1998MzhcLklk48ylS3YjkPqRJ0AkXQBMJTwtCcFNqexliKpGENaJm+lIrS2FdlTI2uPtTZVBR8+OQALSjxO6Wmmet7BBMbqdW9jWbnpltxlSS5l5L3RCkbGozRwWjcPeztnLQw848wIELbBVBScvpOTM4qKBzL5a04/1ZvlqPpdh6bpSVCdMKlzwR328IT2YwKnIlENUQXdxNBeGq1ixoBIlFekKZMJsFEqcsiBJWe8KgxJi5VyrToemjGoM93EeK77RXWKBYqCGAFhz8E7HqO9HAf3IDK21tamDZC86JkKYJhnHpJlnGQhHUE+a7JgicZwodFIwxnR04IpcbNJMLVORcg9mJIpkHKMD6+Q9Qm3zmlQ3U58eNZEwP8C7Gdh0fFpZDINRRZio19pMqRCsSDFss1UjaN3tzM3eX1rjihcqz6eoad/4YAkUTpgwLzNLRMJugPnepQ/Y/qth6A5WN1wfx+HfUQS/2zYriEcgvS/d0MzuOErvKeRI6z7P5JNR6/Vmee01SYJ/Uwp+09dL+59777WgGCiNH4YL1bOES5aBZ681+Sr8U5qCR7PLlVajzB4nkX0J2Dj2irMGCyyJWnN6pPUkRhB8vS471jk+fh20yg2Z2k5cGEfg/TF3sen7fcZw9R2UVGRzAPb24IEzQxNmTVlmwPNDTvQFMnwMpnc85+KhJyHcUiaTmxBTJS+cgrj1ppteuv7mm6+/fl09ohZEkgv+rK/oPj3JF8L/7Whve8rD/Y/ZQPk3cf6kjWdvvvbGG2+49tq1BwEscxN8tdmrJH7MXO1J6c4dPY8MOMr6URH9GUnxUvqV54yo70dLtd/znkUFova+uDcoAqo+if3q40VsT9DMY0iTyZS0OejdwPhnnR4ZhNrD/rYtiQW4ioEKvzINsExqT2AbMSlgjuBCwPDD6qnX9zqhoBIWcgPSwjKQHl4/imOWKMU5Y8TV4Tc9eGYDASfDzI9FbyDhkzed26Z1DbxfeHDS876fsCeY9FhEvPAEO5r5DobZ+8pMGoumCUv71bkcQmdEdDd1+c8ahBOSFgVYqxokha2u4XwmS0QSWW3IZwifNigtP+KaRSehMfiEg2TDY2JWbtuLm4yCExZ24+jU+nHf02ZlA8Q0YiJdSB0XCJJNDWkhLOqjiYJ6pWJ7UnI8KSqt2RwWWPrvsZYMOTL8YJHBg2HJRa54X6oRZCyv2CFb7StMs9vVTyClRF1kkEASABNAqaJh8dQJsmKWE0nJlnS2lNTSYdHovVWFz54jiML3I9ZStoBzCFITI2AmaQS6HpYUlVbNDX34RhxICnFedUiQpDXbAhWUFnWdxpkJlUrHKB1JfJa8qpIorUiicIa8uOngIDaLdebw0u7GEZCt7MQ0JyXZ/IBa7rrNJvLelrx0+jSgxlKsSpqe9zSEGNIbLATYEI3ZUH8Y9h/bWC7yvLMx80sGrgg7Ax9QuNbQIOp2uuS5NQQsfn4JhVVyiKWhlylxPE2W8oeiUEOosVJk3jigly3ziPOZsGDFQwmsoOML53m0ZpIoBUV4GzJy/JAg64Zpg4xPzAJojBdXHGLn3qUiTaM4jiLbhZBEUqIjNggSQz25kTfIXj+axEOZ5XmW+aWE+h133EZBZojYGSIT8wYWrh03v6uyMxc2brk7orV9PwffZBCd26RlggAfMJ7LvrcvLfg8sbECJZ3KyxYJiaJTp5yHhfF9DjFsLES1gYzLCa0C5zo1vkoxG2z3jXi8Poq7v2Km42m4w48086PkuaBUZVGFGvzirI/EWTa0yxSUT7wdQTiYEEFG3Uls4FqjyF1jIwKDh7mV4S286mAa9oOiJogDhyDX5IQx6NqcVGBNCqv5OSAj8CTSBvAmiqBIz3RwdqEorvPry0dDTVaKk2xSZKNBFyRb6kjTWLtvlEa++lkqTWSAucziHk2OJX8bOReeSRJeUAtYdQnY6HuZVA7ogmnGzA1K7V+Xd+DTOnuC1JSPtmvn3pk7Pz8uwRBBNAFZgt7NQFu0UnlfX9gujHDTmU5RinFTPNIj2GfPJV2jzjzfV6TmaxNCsjgmBMzN0dcw/xSS68VSKp4klIKFNaneQMdXoj5dpSN1f5paWOt7EB18eu35g5cDSg3nDllthoF9hmWxt17kPV8VAb1pvtg/cxD3K9fr5t0+y8zf+6Ch/neWH9M6rH9i3JMfkijOIkdABRmcU1YzIolrCto6IUoFc2a5lcpe/d9lGi6pmLQ5M4E10SpQ+n64oi/bd5ZGMPHodc00BJ/DRt4rtRTCEDjXJo4eNZ1lU5cqwxGhVqXe1zhnsv/+322MunNG32s7zhyjt8Q780oHeGmtMmvjDn3oQNaDo6rKZh44qF3DZ6fdhNVyswFeMn0C1wYVKVrMcaufGDK1n7Y1WEFsOl7MZlNk3YcyS7NMioNt+QwIr9qvOfM0g9gAHjsFGGZp7rWdgKrYN7Wxbt16dzmJhx50ZLaCaRJQ39IwAC3laW4jEx76f8mFBdJmTt6ZrA3+1Il0hZ26zGA4FQmy1svt9A9VBQbXI9FwfpfPsYWnXQUx6FRNWSI7Y4bcjwSSxAsayHJR8bmZzcCa/p4oTmi/2qA9m6hiFza9Sl7StB8aTuhh5zWpN/a8H1iCoVU8b9Otp6kP+Wz4n4D3vJiRTv89voFA59E4Ts+pJGYXyqdSsjd+DHB1df6LbFni4G8YuIkD6Ih/DHaQEe8X/xqJ0ASM40O+NfeFcm9EJ7mZFHBEANJPr/3iHa/95n9KgwfEh/35zZc/M+G/T2Al/6E3/3Fxe2rk+BYmByTofzSyFw9nlE3iFeIpt4m392dd43xa/rfrEbWSEegDMPLuq14rp3IeVfyMik1X+RG3weirWkXCdZmKbNfAtzn1D+c7xwH/kB3+U50cDgJcVVJZr7tVl8CXLU7pG5Cp48TDCNQTEOOrfikacC0zeUby4meQKrLI4crcVdJZS3ykm0QOOIEFAPukqts12a0jy5UWHv1uz2pPT4zf0a4SXeoF0XrSNJgF/IDzTMngRNLQM3vxf0yZbtvlACUhZ5rgtN4zlCbBBcTebfsVw3gGfkyBryEC0xrP6oycRb2U6feJl/uMLFHE0X6fKe6Cm8EMVbfaBBTscrIxhh45eiKyR1Hxsa29C0zWsr8UYgslizIGTOGBFpSJo9Qzghs94Tg0Jnf2G8B0x3sDR5+d3aujva0Os7/laPI/6noy6dp0bjMp8AkxFU5kxgKDbgUK+Jzshv4+PxjWyxndl3JCdP8FBg9Rg5adH4J/GJIc+Aj4QZIlux18BnwDt+g828RIQdIMd0WJSvo/ZfSUL4D/JBnvpjAQ2yWEQPoN+FpZmsSnxDIXR8H4FfgR7xJwEfrO0X0rMf1JIjHb9QzTB+Bz8TuXqpPucmjNTQ7/7V4ehL40fiVwUMDkH9SmHR7OWfZXR5pOwPNrLZCjZO/nyKY62mWdoR1mALDpxs0vN1/b9GjT95gB+J1BLlRggQmBwgRA7LSqFfMCfwW/Ojti0Hu9BXCUAePAUSaKs0dBLnDrqAlrgxy1R2phR1ksLOXIkJNW83AkgOXWdRQJyMZ06lhsQRdEbOd9E8PaLuBSRDHVlJJLDgZMeEOHevS8SfGkbE9MsdUL4SWjhV6mPQmlbXpezMiU8NiUW93gG6VFwk8R1nqx44VBMX4OuYWY06+DTKYWRg62elHXX0A2+sUrxMTWKaDmrLLpWHgEHoeiFj3L04Wzrn0NzSUTBmSo+8PCESDWBTOT5NM2EoRgmVaOxEAgIxuMXLyV3gaX3wh3DJ3qZR643yi23CCr1AssOFLPyRWLiSYeuUx2kS6XImsWVmgkCAvjJTd4l5kD+j1Jb4NO/G6e3ptPHpWOKjfxKcgRfilymD+HHOKvIwf516oBnk3o5/GRG7kJfRwIspfzndHDDkV2s0uRXex1ZCd7DtnBzkG2s9aRbZEg5LORMGQLc/2had7maNrWadyu0bCvU7/PUbfHUrteo2Zdp3pZp2rhUzmnUTHPUT6vYxo9kWXjCKXjOiVWKZ7soileo3BiYRzWKejblPzezuuEktvZhi5fkNPFom+xjux2nay2lMwWi9TVfoK2XiejsdMbnpJW2amVUDSlrc6gqDJbWcgERdGEPF9Kdq6Tc5DGjpIS28lxNiEpTiMxsSXpYovTpoKEhIYURVDiQzvOw441Z4HQNBHj4rbASSXaroP/8pHMSDFRkTJCQ14TBQWmEUj1QWla84+vroYugw5FozQVmoakohBICkkMmeSNJKHWIRLmIPhHIv3J3l6ayNsJ6YdcB+cVC9Y3CInxxSB9vdbxoaGQahHmxf/R6wg3ek8U02v94zQnaUCLJETqpIQo7enLpUmXdLlSylXABL2SOdu/Q2MZfUk66CW1dKQrlUvKUn5vvaoNeOgBQCMHsXZ/BQCuxmyhOUmD7tJJ6cpVGxL8lZuzY3766RZmIzb4Xgoh7oTVetJFCzaSaxbMut7DJK/msxJVC5a03QDp1WrFITOz/KvaN24EeJ4ZtzCYrDhsDngWKA9t2KB7RUIBmuc4ZQDCN0b1MgsUZjAjrwwqzfbkDjA4slzCg4UBIGTGY3bDJ5y0ma1bA3ASngs3AAAA) format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* open-sans-cyrillic-700-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* open-sans-greek-ext-700-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+1F00-1FFF;
+}
+/* open-sans-greek-700-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0370-03FF;
+}
+/* open-sans-hebrew-700-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0590-05FF, U+20AA, U+25CC, U+FB1D-FB4F;
+}
+/* open-sans-vietnamese-700-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
+}
+/* open-sans-latin-ext-700-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* open-sans-latin-700-italic*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: italic;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+/*!******************************************************************************************************!*\
+ !*** css ../../node_modules/css-loader/dist/cjs.js!../../node_modules/@fontsource/open-sans/400.css ***!
+ \******************************************************************************************************/
+/* open-sans-cyrillic-ext-400-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* open-sans-cyrillic-400-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* open-sans-greek-ext-400-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+1F00-1FFF;
+}
+/* open-sans-greek-400-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0370-03FF;
+}
+/* open-sans-hebrew-400-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0590-05FF, U+20AA, U+25CC, U+FB1D-FB4F;
+}
+/* open-sans-vietnamese-400-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
+}
+/* open-sans-latin-ext-400-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* open-sans-latin-400-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 400;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+/*!******************************************************************************************************!*\
+ !*** css ../../node_modules/css-loader/dist/cjs.js!../../node_modules/@fontsource/open-sans/700.css ***!
+ \******************************************************************************************************/
+/* open-sans-cyrillic-ext-700-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* open-sans-cyrillic-700-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* open-sans-greek-ext-700-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+1F00-1FFF;
+}
+/* open-sans-greek-700-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0370-03FF;
+}
+/* open-sans-hebrew-700-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0590-05FF, U+20AA, U+25CC, U+FB1D-FB4F;
+}
+/* open-sans-vietnamese-700-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
+}
+/* open-sans-latin-ext-700-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* open-sans-latin-700-normal*/
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-display: swap;
+ font-weight: 700;
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+/*!*****************************************************************************************************************!*\
+ !*** css ../../node_modules/css-loader/dist/cjs.js!../../node_modules/sass-loader/dist/cjs.js!./src/index.scss ***!
+ \*****************************************************************************************************************/
+/*
+ * CSS for jsPsych experiments.
+ *
+ * This stylesheet provides minimal styling to make jsPsych
+ * experiments look polished without any additional styles.
+ */
+/* Container holding jsPsych content */
+.jspsych-display-element {
+ display: flex;
+ flex-direction: column;
+ overflow-y: auto;
+}
+
+.jspsych-display-element:focus {
+ outline: none;
+}
+
+.jspsych-content-wrapper {
+ display: flex;
+ margin: auto;
+ flex: 1 1 100%;
+ width: 100%;
+}
+
+.jspsych-content {
+ max-width: 95%;
+ /* this is mainly an IE 10-11 fix */
+ text-align: center;
+ margin: auto;
+ /* this is for overflowing content */
+}
+
+.jspsych-top {
+ align-items: flex-start;
+}
+
+.jspsych-middle {
+ align-items: center;
+}
+
+/* fonts and type */
+.jspsych-display-element {
+ font-family: "Open Sans", "Arial", sans-serif;
+ font-size: 18px;
+ line-height: 1.6em;
+}
+
+/* Form elements like input fields and buttons */
+.jspsych-display-element input[type=text] {
+ font-family: "Open Sans", "Arial", sans-serif;
+ font-size: 14px;
+}
+
+/* borrowing Bootstrap style for btn elements, but combining styles a bit */
+.jspsych-btn {
+ display: inline-block;
+ padding: 6px 12px;
+ margin: 0px;
+ font-size: 14px;
+ font-weight: 400;
+ font-family: "Open Sans", "Arial", sans-serif;
+ cursor: pointer;
+ line-height: 1.4;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ background-image: none;
+ border: 1px solid transparent;
+ border-radius: 4px;
+ color: #333;
+ background-color: #fff;
+ border-color: #ccc;
+}
+
+/* only apply the hover style on devices with a mouse/pointer that can hover - issue #977 */
+@media (hover: hover) {
+ .jspsych-btn:hover {
+ background-color: #ddd;
+ border-color: #aaa;
+ }
+}
+.jspsych-btn:active {
+ background-color: #ddd;
+ border-color: #000000;
+}
+
+.jspsych-btn:disabled {
+ background-color: #eee;
+ color: #aaa;
+ border-color: #ccc;
+ cursor: not-allowed;
+}
+
+/* custom style for input[type="range] (slider) to improve alignment between positions and labels */
+.jspsych-slider {
+ appearance: none;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ width: 100%;
+ background: transparent;
+}
+
+.jspsych-slider:focus {
+ outline: none;
+}
+
+/* track */
+.jspsych-slider::-webkit-slider-runnable-track {
+ appearance: none;
+ -webkit-appearance: none;
+ width: 100%;
+ height: 8px;
+ cursor: pointer;
+ background: #eee;
+ box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d;
+ border-radius: 2px;
+ border: 1px solid #aaa;
+}
+
+.jspsych-slider::-moz-range-track {
+ appearance: none;
+ width: 100%;
+ height: 8px;
+ cursor: pointer;
+ background: #eee;
+ box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d;
+ border-radius: 2px;
+ border: 1px solid #aaa;
+}
+
+.jspsych-slider::-ms-track {
+ appearance: none;
+ width: 99%;
+ height: 14px;
+ cursor: pointer;
+ background: #eee;
+ box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d;
+ border-radius: 2px;
+ border: 1px solid #aaa;
+}
+
+/* thumb */
+.jspsych-slider::-webkit-slider-thumb {
+ border: 1px solid #666;
+ height: 24px;
+ width: 15px;
+ border-radius: 5px;
+ background: #ffffff;
+ cursor: pointer;
+ -webkit-appearance: none;
+ margin-top: -9px;
+}
+
+.jspsych-slider::-moz-range-thumb {
+ border: 1px solid #666;
+ height: 24px;
+ width: 15px;
+ border-radius: 5px;
+ background: #ffffff;
+ cursor: pointer;
+}
+
+.jspsych-slider::-ms-thumb {
+ border: 1px solid #666;
+ height: 20px;
+ width: 15px;
+ border-radius: 5px;
+ background: #ffffff;
+ cursor: pointer;
+ margin-top: -2px;
+}
+
+/* jsPsych progress bar */
+#jspsych-progressbar-container {
+ color: #555;
+ border-bottom: 1px solid #dedede;
+ background-color: #f9f9f9;
+ margin-bottom: 1em;
+ text-align: center;
+ padding: 8px 0px;
+ width: 100%;
+ line-height: 1em;
+}
+
+#jspsych-progressbar-container span {
+ font-size: 14px;
+ padding-right: 14px;
+}
+
+#jspsych-progressbar-outer {
+ background-color: #eee;
+ width: 50%;
+ margin: auto;
+ height: 14px;
+ display: inline-block;
+ vertical-align: middle;
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+
+#jspsych-progressbar-inner {
+ background-color: #aaa;
+ width: 0%;
+ height: 100%;
+}
+
+/* Control appearance of jsPsych.data.displayData() */
+#jspsych-data-display {
+ text-align: left;
+}
diff --git a/app/static/lib/jspsych-7.2.1/extensions/extension-mouse-tracking.js b/app/static/lib/jspsych-7.2.1/extensions/extension-mouse-tracking.js
new file mode 100644
index 00000000..15e455fa
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/extensions/extension-mouse-tracking.js
@@ -0,0 +1,112 @@
+var jsPsychExtensionMouseTracking = (function () {
+ 'use strict';
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+
+ function __awaiter(thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+ }
+
+ class MouseTrackingExtension {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ this.initialize = ({ minimum_sample_time = 0 }) => __awaiter(this, void 0, void 0, function* () {
+ this.domObserver = new MutationObserver(this.mutationObserverCallback);
+ this.minimumSampleTime = minimum_sample_time;
+ });
+ this.on_start = (params) => {
+ params = params || {};
+ this.currentTrialData = [];
+ this.currentTrialTargets = new Map();
+ this.currentTrialSelectors = params.targets || [];
+ this.lastSampleTime = null;
+ this.eventsToTrack = params.events || ["mousemove"];
+ this.domObserver.observe(this.jsPsych.getDisplayElement(), { childList: true });
+ };
+ this.on_load = () => {
+ // set current trial start time
+ this.currentTrialStartTime = performance.now();
+ // start data collection
+ if (this.eventsToTrack.includes("mousemove")) {
+ window.addEventListener("mousemove", this.mouseMoveEventHandler);
+ }
+ if (this.eventsToTrack.includes("mousedown")) {
+ window.addEventListener("mousedown", this.mouseDownEventHandler);
+ }
+ if (this.eventsToTrack.includes("mouseup")) {
+ window.addEventListener("mouseup", this.mouseUpEventHandler);
+ }
+ };
+ this.on_finish = () => {
+ this.domObserver.disconnect();
+ if (this.eventsToTrack.includes("mousemove")) {
+ window.removeEventListener("mousemove", this.mouseMoveEventHandler);
+ }
+ if (this.eventsToTrack.includes("mousedown")) {
+ window.removeEventListener("mousedown", this.mouseDownEventHandler);
+ }
+ if (this.eventsToTrack.includes("mouseup")) {
+ window.removeEventListener("mouseup", this.mouseUpEventHandler);
+ }
+ return {
+ mouse_tracking_data: this.currentTrialData,
+ mouse_tracking_targets: Object.fromEntries(this.currentTrialTargets.entries()),
+ };
+ };
+ this.mouseMoveEventHandler = ({ clientX: x, clientY: y }) => {
+ const event_time = performance.now();
+ const t = Math.round(event_time - this.currentTrialStartTime);
+ if (this.lastSampleTime === null ||
+ event_time - this.lastSampleTime >= this.minimumSampleTime) {
+ this.lastSampleTime = event_time;
+ this.currentTrialData.push({ x, y, t, event: "mousemove" });
+ }
+ };
+ this.mouseUpEventHandler = ({ clientX: x, clientY: y }) => {
+ const event_time = performance.now();
+ const t = Math.round(event_time - this.currentTrialStartTime);
+ this.currentTrialData.push({ x, y, t, event: "mouseup" });
+ };
+ this.mouseDownEventHandler = ({ clientX: x, clientY: y }) => {
+ const event_time = performance.now();
+ const t = Math.round(event_time - this.currentTrialStartTime);
+ this.currentTrialData.push({ x, y, t, event: "mousedown" });
+ };
+ this.mutationObserverCallback = (mutationsList, observer) => {
+ for (const selector of this.currentTrialSelectors) {
+ if (!this.currentTrialTargets.has(selector)) {
+ const target = this.jsPsych.getDisplayElement().querySelector(selector);
+ if (target) {
+ this.currentTrialTargets.set(selector, target.getBoundingClientRect());
+ }
+ }
+ }
+ };
+ }
+ }
+ MouseTrackingExtension.info = {
+ name: "mouse-tracking",
+ };
+
+ return MouseTrackingExtension;
+
+})();
diff --git a/app/static/lib/jspsych-7.2.1/extensions/extension-webgazer.js b/app/static/lib/jspsych-7.2.1/extensions/extension-webgazer.js
new file mode 100644
index 00000000..6801af49
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/extensions/extension-webgazer.js
@@ -0,0 +1,232 @@
+var jsPsychExtensionWebgazer = (function () {
+ 'use strict';
+
+ class WebGazerExtension {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ // private state for the extension
+ // extension authors can define public functions to interact
+ // with the state. recommend not exposing state directly
+ // so that state manipulations are checked.
+ this.currentTrialData = [];
+ this.currentTrialTargets = {};
+ this.initialized = false;
+ this.activeTrial = false;
+ this.initialize = ({ round_predictions = true, auto_initialize = false, sampling_interval = 34, webgazer, }) => {
+ // set initial state of the extension
+ this.round_predictions = round_predictions;
+ this.sampling_interval = sampling_interval;
+ this.gazeUpdateCallbacks = [];
+ this.domObserver = new MutationObserver(this.mutationObserverCallback);
+ return new Promise((resolve, reject) => {
+ if (typeof webgazer === "undefined") {
+ if (window.webgazer) {
+ this.webgazer = window.webgazer;
+ }
+ else {
+ reject(new Error("Webgazer extension failed to initialize. webgazer.js not loaded. Load webgazer.js before calling initJsPsych()"));
+ }
+ }
+ else {
+ this.webgazer = webgazer;
+ }
+ // sets up event handler for webgazer data
+ // this.webgazer.setGazeListener(this.handleGazeDataUpdate);
+ // default to threadedRidge regression
+ // NEVER MIND... kalman filter is too useful.
+ //state.webgazer.workerScriptURL = 'js/webgazer/ridgeWorker.mjs';
+ //state.webgazer.setRegression('threadedRidge');
+ //state.webgazer.applyKalmanFilter(false); // kalman filter doesn't seem to work yet with threadedridge.
+ // hide video by default
+ this.hideVideo();
+ // hide predictions by default
+ this.hidePredictions();
+ if (auto_initialize) {
+ // starts webgazer, and once it initializes we stop mouseCalibration and
+ // pause webgazer data.
+ this.webgazer
+ .begin()
+ .then(() => {
+ this.initialized = true;
+ this.stopMouseCalibration();
+ this.pause();
+ resolve();
+ })
+ .catch((error) => {
+ console.error(error);
+ reject(error);
+ });
+ }
+ else {
+ resolve();
+ }
+ });
+ };
+ this.on_start = (params) => {
+ this.currentTrialData = [];
+ this.currentTrialTargets = {};
+ this.currentTrialSelectors = params.targets;
+ this.domObserver.observe(this.jsPsych.getDisplayElement(), { childList: true });
+ };
+ this.on_load = () => {
+ // set current trial start time
+ this.currentTrialStart = performance.now();
+ // resume data collection
+ // state.webgazer.resume();
+ this.startSampleInterval();
+ // set internal flag
+ this.activeTrial = true;
+ };
+ this.on_finish = () => {
+ // pause the eye tracker
+ this.stopSampleInterval();
+ // stop watching the DOM
+ this.domObserver.disconnect();
+ // state.webgazer.pause();
+ // set internal flag
+ this.activeTrial = false;
+ // send back the gazeData
+ return {
+ webgazer_data: this.currentTrialData,
+ webgazer_targets: this.currentTrialTargets,
+ };
+ };
+ this.start = () => {
+ return new Promise((resolve, reject) => {
+ if (typeof this.webgazer == "undefined") {
+ const error = "Failed to start webgazer. Things to check: Is webgazer.js loaded? Is the webgazer extension included in initJsPsych?";
+ console.error(error);
+ reject(error);
+ }
+ this.webgazer
+ .begin()
+ .then(() => {
+ this.initialized = true;
+ this.stopMouseCalibration();
+ this.pause();
+ resolve();
+ })
+ .catch((error) => {
+ console.error(error);
+ reject(error);
+ });
+ });
+ };
+ this.startSampleInterval = (interval = this.sampling_interval) => {
+ this.gazeInterval = setInterval(() => {
+ this.webgazer.getCurrentPrediction().then(this.handleGazeDataUpdate);
+ }, interval);
+ // repeat the call here so that we get one immediate execution. above will not
+ // start until state.sampling_interval is reached the first time.
+ this.webgazer.getCurrentPrediction().then(this.handleGazeDataUpdate);
+ };
+ this.stopSampleInterval = () => {
+ clearInterval(this.gazeInterval);
+ };
+ this.isInitialized = () => {
+ return this.initialized;
+ };
+ this.faceDetected = () => {
+ return this.webgazer.getTracker().predictionReady;
+ };
+ this.showPredictions = () => {
+ this.webgazer.showPredictionPoints(true);
+ };
+ this.hidePredictions = () => {
+ this.webgazer.showPredictionPoints(false);
+ };
+ this.showVideo = () => {
+ this.webgazer.showVideo(true);
+ this.webgazer.showFaceOverlay(true);
+ this.webgazer.showFaceFeedbackBox(true);
+ };
+ this.hideVideo = () => {
+ this.webgazer.showVideo(false);
+ this.webgazer.showFaceOverlay(false);
+ this.webgazer.showFaceFeedbackBox(false);
+ };
+ this.resume = () => {
+ this.webgazer.resume();
+ };
+ this.pause = () => {
+ this.webgazer.pause();
+ // sometimes gaze dot will show and freeze after pause?
+ if (document.querySelector("#webgazerGazeDot")) {
+ document.querySelector("#webgazerGazeDot").style.display = "none";
+ }
+ };
+ this.resetCalibration = () => {
+ this.webgazer.clearData();
+ };
+ this.stopMouseCalibration = () => {
+ this.webgazer.removeMouseEventListeners();
+ };
+ this.startMouseCalibration = () => {
+ this.webgazer.addMouseEventListeners();
+ };
+ this.calibratePoint = (x, y) => {
+ this.webgazer.recordScreenPosition(x, y, "click");
+ };
+ this.setRegressionType = (regression_type) => {
+ var valid_regression_models = ["ridge", "weightedRidge", "threadedRidge"];
+ if (valid_regression_models.includes(regression_type)) {
+ this.webgazer.setRegression(regression_type);
+ }
+ else {
+ console.warn("Invalid regression_type parameter for webgazer.setRegressionType. Valid options are ridge, weightedRidge, and threadedRidge.");
+ }
+ };
+ this.getCurrentPrediction = () => {
+ return this.webgazer.getCurrentPrediction();
+ };
+ this.onGazeUpdate = (callback) => {
+ this.gazeUpdateCallbacks.push(callback);
+ return () => {
+ this.gazeUpdateCallbacks = this.gazeUpdateCallbacks.filter((item) => {
+ return item !== callback;
+ });
+ };
+ };
+ this.handleGazeDataUpdate = (gazeData, elapsedTime) => {
+ if (gazeData !== null) {
+ var d = {
+ x: this.round_predictions ? Math.round(gazeData.x) : gazeData.x,
+ y: this.round_predictions ? Math.round(gazeData.y) : gazeData.y,
+ t: gazeData.t,
+ };
+ if (this.activeTrial) {
+ //console.log(`handleUpdate: t = ${Math.round(gazeData.t)}, now = ${Math.round(performance.now())}`);
+ d.t = Math.round(gazeData.t - this.currentTrialStart);
+ this.currentTrialData.push(d); // add data to current trial's data
+ }
+ this.currentGaze = d;
+ for (var i = 0; i < this.gazeUpdateCallbacks.length; i++) {
+ this.gazeUpdateCallbacks[i](d);
+ }
+ }
+ else {
+ this.currentGaze = null;
+ }
+ };
+ this.mutationObserverCallback = (mutationsList, observer) => {
+ for (const selector of this.currentTrialSelectors) {
+ if (!this.currentTrialTargets[selector]) {
+ if (this.jsPsych.getDisplayElement().querySelector(selector)) {
+ var coords = this.jsPsych
+ .getDisplayElement()
+ .querySelector(selector)
+ .getBoundingClientRect();
+ this.currentTrialTargets[selector] = coords;
+ }
+ }
+ }
+ };
+ }
+ }
+ WebGazerExtension.info = {
+ name: "webgazer",
+ };
+
+ return WebGazerExtension;
+
+})();
diff --git a/app/static/lib/jspsych-7.2.1/jspsych.js b/app/static/lib/jspsych-7.2.1/jspsych.js
new file mode 100644
index 00000000..e4ab3f51
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/jspsych.js
@@ -0,0 +1,3308 @@
+var jsPsychModule = (function (exports) {
+ 'use strict';
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+
+ function __awaiter(thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+ }
+
+ var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+ // Gets all non-builtin properties up the prototype chain
+ const getAllProperties = object => {
+ const properties = new Set();
+
+ do {
+ for (const key of Reflect.ownKeys(object)) {
+ properties.add([object, key]);
+ }
+ } while ((object = Reflect.getPrototypeOf(object)) && object !== Object.prototype);
+
+ return properties;
+ };
+
+ var autoBind = (self, {include, exclude} = {}) => {
+ const filter = key => {
+ const match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key);
+
+ if (include) {
+ return include.some(match);
+ }
+
+ if (exclude) {
+ return !exclude.some(match);
+ }
+
+ return true;
+ };
+
+ for (const [object, key] of getAllProperties(self.constructor.prototype)) {
+ if (key === 'constructor' || !filter(key)) {
+ continue;
+ }
+
+ const descriptor = Reflect.getOwnPropertyDescriptor(object, key);
+ if (descriptor && typeof descriptor.value === 'function') {
+ self[key] = self[key].bind(self);
+ }
+ }
+
+ return self;
+ };
+
+ var version = "7.2.1";
+
+ class MigrationError extends Error {
+ constructor(message = "The global `jsPsych` variable is no longer available in jsPsych v7.") {
+ super(`${message} Please follow the migration guide at https://www.jspsych.org/7.0/support/migration-v7/ to update your experiment.`);
+ this.name = "MigrationError";
+ }
+ }
+ // Define a global jsPsych object to handle invocations on it with migration errors
+ window.jsPsych = {
+ get init() {
+ throw new MigrationError("`jsPsych.init()` was replaced by `initJsPsych()` in jsPsych v7.");
+ },
+ get data() {
+ throw new MigrationError();
+ },
+ get randomization() {
+ throw new MigrationError();
+ },
+ get turk() {
+ throw new MigrationError();
+ },
+ get pluginAPI() {
+ throw new MigrationError();
+ },
+ get ALL_KEYS() {
+ throw new MigrationError('jsPsych.ALL_KEYS was replaced by the "ALL_KEYS" string in jsPsych v7.');
+ },
+ get NO_KEYS() {
+ throw new MigrationError('jsPsych.NO_KEYS was replaced by the "NO_KEYS" string in jsPsych v7.');
+ },
+ };
+
+ /**
+ * Finds all of the unique items in an array.
+ * @param arr The array to extract unique values from
+ * @returns An array with one copy of each unique item in `arr`
+ */
+ function unique(arr) {
+ return [...new Set(arr)];
+ }
+ function deepCopy(obj) {
+ if (!obj)
+ return obj;
+ let out;
+ if (Array.isArray(obj)) {
+ out = [];
+ for (const x of obj) {
+ out.push(deepCopy(x));
+ }
+ return out;
+ }
+ else if (typeof obj === "object" && obj !== null) {
+ out = {};
+ for (const key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ out[key] = deepCopy(obj[key]);
+ }
+ }
+ return out;
+ }
+ else {
+ return obj;
+ }
+ }
+
+ var utils = /*#__PURE__*/Object.freeze({
+ __proto__: null,
+ unique: unique,
+ deepCopy: deepCopy
+ });
+
+ class DataColumn {
+ constructor(values = []) {
+ this.values = values;
+ }
+ sum() {
+ let s = 0;
+ for (const v of this.values) {
+ s += v;
+ }
+ return s;
+ }
+ mean() {
+ return this.sum() / this.count();
+ }
+ median() {
+ if (this.values.length === 0) {
+ return undefined;
+ }
+ const numbers = this.values.slice(0).sort(function (a, b) {
+ return a - b;
+ });
+ const middle = Math.floor(numbers.length / 2);
+ const isEven = numbers.length % 2 === 0;
+ return isEven ? (numbers[middle] + numbers[middle - 1]) / 2 : numbers[middle];
+ }
+ min() {
+ return Math.min.apply(null, this.values);
+ }
+ max() {
+ return Math.max.apply(null, this.values);
+ }
+ count() {
+ return this.values.length;
+ }
+ variance() {
+ const mean = this.mean();
+ let sum_square_error = 0;
+ for (const x of this.values) {
+ sum_square_error += Math.pow(x - mean, 2);
+ }
+ const mse = sum_square_error / (this.values.length - 1);
+ return mse;
+ }
+ sd() {
+ const mse = this.variance();
+ const rmse = Math.sqrt(mse);
+ return rmse;
+ }
+ frequencies() {
+ const unique = {};
+ for (const x of this.values) {
+ if (typeof unique[x] === "undefined") {
+ unique[x] = 1;
+ }
+ else {
+ unique[x]++;
+ }
+ }
+ return unique;
+ }
+ all(eval_fn) {
+ for (const x of this.values) {
+ if (!eval_fn(x)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ subset(eval_fn) {
+ const out = [];
+ for (const x of this.values) {
+ if (eval_fn(x)) {
+ out.push(x);
+ }
+ }
+ return new DataColumn(out);
+ }
+ }
+
+ // private function to save text file on local drive
+ function saveTextToFile(textstr, filename) {
+ const blobToSave = new Blob([textstr], {
+ type: "text/plain",
+ });
+ let blobURL = "";
+ if (typeof window.webkitURL !== "undefined") {
+ blobURL = window.webkitURL.createObjectURL(blobToSave);
+ }
+ else {
+ blobURL = window.URL.createObjectURL(blobToSave);
+ }
+ const link = document.createElement("a");
+ link.id = "jspsych-download-as-text-link";
+ link.style.display = "none";
+ link.download = filename;
+ link.href = blobURL;
+ link.click();
+ }
+ // this function based on code suggested by StackOverflow users:
+ // http://stackoverflow.com/users/64741/zachary
+ // http://stackoverflow.com/users/317/joseph-sturtevant
+ function JSON2CSV(objArray) {
+ const array = typeof objArray != "object" ? JSON.parse(objArray) : objArray;
+ let line = "";
+ let result = "";
+ const columns = [];
+ for (const row of array) {
+ for (const key in row) {
+ let keyString = key + "";
+ keyString = '"' + keyString.replace(/"/g, '""') + '",';
+ if (!columns.includes(key)) {
+ columns.push(key);
+ line += keyString;
+ }
+ }
+ }
+ line = line.slice(0, -1); // removes last comma
+ result += line + "\r\n";
+ for (const row of array) {
+ line = "";
+ for (const col of columns) {
+ let value = typeof row[col] === "undefined" ? "" : row[col];
+ if (typeof value == "object") {
+ value = JSON.stringify(value);
+ }
+ const valueString = value + "";
+ line += '"' + valueString.replace(/"/g, '""') + '",';
+ }
+ line = line.slice(0, -1);
+ result += line + "\r\n";
+ }
+ return result;
+ }
+ // this function is modified from StackOverflow:
+ // http://stackoverflow.com/posts/3855394
+ function getQueryString() {
+ const a = window.location.search.substr(1).split("&");
+ const b = {};
+ for (let i = 0; i < a.length; ++i) {
+ const p = a[i].split("=", 2);
+ if (p.length == 1)
+ b[p[0]] = "";
+ else
+ b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
+ }
+ return b;
+ }
+
+ class DataCollection {
+ constructor(data = []) {
+ this.trials = data;
+ }
+ push(new_data) {
+ this.trials.push(new_data);
+ return this;
+ }
+ join(other_data_collection) {
+ this.trials = this.trials.concat(other_data_collection.values());
+ return this;
+ }
+ top() {
+ if (this.trials.length <= 1) {
+ return this;
+ }
+ else {
+ return new DataCollection([this.trials[this.trials.length - 1]]);
+ }
+ }
+ /**
+ * Queries the first n elements in a collection of trials.
+ *
+ * @param n A positive integer of elements to return. A value of
+ * n that is less than 1 will throw an error.
+ *
+ * @return First n objects of a collection of trials. If fewer than
+ * n trials are available, the trials.length elements will
+ * be returned.
+ *
+ */
+ first(n = 1) {
+ if (n < 1) {
+ throw `You must query with a positive nonzero integer. Please use a
+ different value for n.`;
+ }
+ if (this.trials.length === 0)
+ return new DataCollection();
+ if (n > this.trials.length)
+ n = this.trials.length;
+ return new DataCollection(this.trials.slice(0, n));
+ }
+ /**
+ * Queries the last n elements in a collection of trials.
+ *
+ * @param n A positive integer of elements to return. A value of
+ * n that is less than 1 will throw an error.
+ *
+ * @return Last n objects of a collection of trials. If fewer than
+ * n trials are available, the trials.length elements will
+ * be returned.
+ *
+ */
+ last(n = 1) {
+ if (n < 1) {
+ throw `You must query with a positive nonzero integer. Please use a
+ different value for n.`;
+ }
+ if (this.trials.length === 0)
+ return new DataCollection();
+ if (n > this.trials.length)
+ n = this.trials.length;
+ return new DataCollection(this.trials.slice(this.trials.length - n, this.trials.length));
+ }
+ values() {
+ return this.trials;
+ }
+ count() {
+ return this.trials.length;
+ }
+ readOnly() {
+ return new DataCollection(deepCopy(this.trials));
+ }
+ addToAll(properties) {
+ for (const trial of this.trials) {
+ Object.assign(trial, properties);
+ }
+ return this;
+ }
+ addToLast(properties) {
+ if (this.trials.length != 0) {
+ Object.assign(this.trials[this.trials.length - 1], properties);
+ }
+ return this;
+ }
+ filter(filters) {
+ // [{p1: v1, p2:v2}, {p1:v2}]
+ // {p1: v1}
+ let f;
+ if (!Array.isArray(filters)) {
+ f = deepCopy([filters]);
+ }
+ else {
+ f = deepCopy(filters);
+ }
+ const filtered_data = [];
+ for (const trial of this.trials) {
+ let keep = false;
+ for (const filter of f) {
+ let match = true;
+ for (const key of Object.keys(filter)) {
+ if (typeof trial[key] !== "undefined" && trial[key] === filter[key]) ;
+ else {
+ match = false;
+ }
+ }
+ if (match) {
+ keep = true;
+ break;
+ } // can break because each filter is OR.
+ }
+ if (keep) {
+ filtered_data.push(trial);
+ }
+ }
+ return new DataCollection(filtered_data);
+ }
+ filterCustom(fn) {
+ return new DataCollection(this.trials.filter(fn));
+ }
+ filterColumns(columns) {
+ return new DataCollection(this.trials.map((trial) => Object.fromEntries(columns.filter((key) => key in trial).map((key) => [key, trial[key]]))));
+ }
+ select(column) {
+ const values = [];
+ for (const trial of this.trials) {
+ if (typeof trial[column] !== "undefined") {
+ values.push(trial[column]);
+ }
+ }
+ return new DataColumn(values);
+ }
+ ignore(columns) {
+ if (!Array.isArray(columns)) {
+ columns = [columns];
+ }
+ const o = deepCopy(this.trials);
+ for (const trial of o) {
+ for (const delete_key of columns) {
+ delete trial[delete_key];
+ }
+ }
+ return new DataCollection(o);
+ }
+ uniqueNames() {
+ const names = [];
+ for (const trial of this.trials) {
+ for (const key of Object.keys(trial)) {
+ if (!names.includes(key)) {
+ names.push(key);
+ }
+ }
+ }
+ return names;
+ }
+ csv() {
+ return JSON2CSV(this.trials);
+ }
+ json(pretty = false) {
+ if (pretty) {
+ return JSON.stringify(this.trials, null, "\t");
+ }
+ return JSON.stringify(this.trials);
+ }
+ localSave(format, filename) {
+ format = format.toLowerCase();
+ let data_string;
+ if (format === "json") {
+ data_string = this.json();
+ }
+ else if (format === "csv") {
+ data_string = this.csv();
+ }
+ else {
+ throw new Error('Invalid format specified for localSave. Must be "json" or "csv".');
+ }
+ saveTextToFile(data_string, filename);
+ }
+ }
+
+ class JsPsychData {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ // data properties for all trials
+ this.dataProperties = {};
+ this.reset();
+ }
+ reset() {
+ this.allData = new DataCollection();
+ this.interactionData = new DataCollection();
+ }
+ get() {
+ return this.allData;
+ }
+ getInteractionData() {
+ return this.interactionData;
+ }
+ write(data_object) {
+ const progress = this.jsPsych.getProgress();
+ const trial = this.jsPsych.getCurrentTrial();
+ //var trial_opt_data = typeof trial.data == 'function' ? trial.data() : trial.data;
+ const default_data = {
+ trial_type: trial.type.info.name,
+ trial_index: progress.current_trial_global,
+ time_elapsed: this.jsPsych.getTotalTime(),
+ internal_node_id: this.jsPsych.getCurrentTimelineNodeID(),
+ };
+ this.allData.push(Object.assign(Object.assign(Object.assign(Object.assign({}, data_object), trial.data), default_data), this.dataProperties));
+ }
+ addProperties(properties) {
+ // first, add the properties to all data that's already stored
+ this.allData.addToAll(properties);
+ // now add to list so that it gets appended to all future data
+ this.dataProperties = Object.assign({}, this.dataProperties, properties);
+ }
+ addDataToLastTrial(data) {
+ this.allData.addToLast(data);
+ }
+ getDataByTimelineNode(node_id) {
+ return this.allData.filterCustom((x) => x.internal_node_id.slice(0, node_id.length) === node_id);
+ }
+ getLastTrialData() {
+ return this.allData.top();
+ }
+ getLastTimelineData() {
+ const lasttrial = this.getLastTrialData();
+ const node_id = lasttrial.select("internal_node_id").values[0];
+ if (typeof node_id === "undefined") {
+ return new DataCollection();
+ }
+ else {
+ const parent_node_id = node_id.substr(0, node_id.lastIndexOf("-"));
+ const lastnodedata = this.getDataByTimelineNode(parent_node_id);
+ return lastnodedata;
+ }
+ }
+ displayData(format = "json") {
+ format = format.toLowerCase();
+ if (format != "json" && format != "csv") {
+ console.log("Invalid format declared for displayData function. Using json as default.");
+ format = "json";
+ }
+ const data_string = format === "json" ? this.allData.json(true) : this.allData.csv();
+ const display_element = this.jsPsych.getDisplayElement();
+ display_element.innerHTML = '';
+ document.getElementById("jspsych-data-display").textContent = data_string;
+ }
+ urlVariables() {
+ if (typeof this.query_string == "undefined") {
+ this.query_string = getQueryString();
+ }
+ return this.query_string;
+ }
+ getURLVariable(whichvar) {
+ return this.urlVariables()[whichvar];
+ }
+ createInteractionListeners() {
+ // blur event capture
+ window.addEventListener("blur", () => {
+ const data = {
+ event: "blur",
+ trial: this.jsPsych.getProgress().current_trial_global,
+ time: this.jsPsych.getTotalTime(),
+ };
+ this.interactionData.push(data);
+ this.jsPsych.getInitSettings().on_interaction_data_update(data);
+ });
+ // focus event capture
+ window.addEventListener("focus", () => {
+ const data = {
+ event: "focus",
+ trial: this.jsPsych.getProgress().current_trial_global,
+ time: this.jsPsych.getTotalTime(),
+ };
+ this.interactionData.push(data);
+ this.jsPsych.getInitSettings().on_interaction_data_update(data);
+ });
+ // fullscreen change capture
+ const fullscreenchange = () => {
+ const data = {
+ event:
+ // @ts-expect-error
+ document.isFullScreen ||
+ // @ts-expect-error
+ document.webkitIsFullScreen ||
+ // @ts-expect-error
+ document.mozIsFullScreen ||
+ document.fullscreenElement
+ ? "fullscreenenter"
+ : "fullscreenexit",
+ trial: this.jsPsych.getProgress().current_trial_global,
+ time: this.jsPsych.getTotalTime(),
+ };
+ this.interactionData.push(data);
+ this.jsPsych.getInitSettings().on_interaction_data_update(data);
+ };
+ document.addEventListener("fullscreenchange", fullscreenchange);
+ document.addEventListener("mozfullscreenchange", fullscreenchange);
+ document.addEventListener("webkitfullscreenchange", fullscreenchange);
+ }
+ // public methods for testing purposes. not recommended for use.
+ _customInsert(data) {
+ this.allData = new DataCollection(data);
+ }
+ _fullreset() {
+ this.reset();
+ this.dataProperties = {};
+ }
+ }
+
+ class HardwareAPI {
+ constructor() {
+ /**
+ * Indicates whether this instance of jspsych has opened a hardware connection through our browser
+ * extension
+ **/
+ this.hardwareConnected = false;
+ //it might be useful to open up a line of communication from the extension back to this page
+ //script, again, this will have to pass through DOM events. For now speed is of no concern so I
+ //will use jQuery
+ document.addEventListener("jspsych-activate", (evt) => {
+ this.hardwareConnected = true;
+ });
+ }
+ /**
+ * Allows communication with user hardware through our custom Google Chrome extension + native C++ program
+ * @param mess The message to be passed to our extension, see its documentation for the expected members of this object.
+ * @author Daniel Rivas
+ *
+ */
+ hardware(mess) {
+ //since Chrome extension content-scripts do not share the javascript environment with the page
+ //script that loaded jspsych, we will need to use hacky methods like communicating through DOM
+ //events.
+ const jspsychEvt = new CustomEvent("jspsych", { detail: mess });
+ document.dispatchEvent(jspsychEvt);
+ //And voila! it will be the job of the content script injected by the extension to listen for
+ //the event and do the appropriate actions.
+ }
+ }
+
+ class KeyboardListenerAPI {
+ constructor(getRootElement, areResponsesCaseSensitive = false, minimumValidRt = 0) {
+ this.getRootElement = getRootElement;
+ this.areResponsesCaseSensitive = areResponsesCaseSensitive;
+ this.minimumValidRt = minimumValidRt;
+ this.listeners = new Set();
+ this.heldKeys = new Set();
+ this.areRootListenersRegistered = false;
+ autoBind(this);
+ this.registerRootListeners();
+ }
+ /**
+ * If not previously done and `this.getRootElement()` returns an element, adds the root key
+ * listeners to that element.
+ */
+ registerRootListeners() {
+ if (!this.areRootListenersRegistered) {
+ const rootElement = this.getRootElement();
+ if (rootElement) {
+ rootElement.addEventListener("keydown", this.rootKeydownListener);
+ rootElement.addEventListener("keyup", this.rootKeyupListener);
+ this.areRootListenersRegistered = true;
+ }
+ }
+ }
+ rootKeydownListener(e) {
+ // Iterate over a static copy of the listeners set because listeners might add other listeners
+ // that we do not want to be included in the loop
+ for (const listener of Array.from(this.listeners)) {
+ listener(e);
+ }
+ this.heldKeys.add(this.toLowerCaseIfInsensitive(e.key));
+ }
+ toLowerCaseIfInsensitive(string) {
+ return this.areResponsesCaseSensitive ? string : string.toLowerCase();
+ }
+ rootKeyupListener(e) {
+ this.heldKeys.delete(this.toLowerCaseIfInsensitive(e.key));
+ }
+ isResponseValid(validResponses, allowHeldKey, key) {
+ // check if key was already held down
+ if (!allowHeldKey && this.heldKeys.has(key)) {
+ return false;
+ }
+ if (validResponses === "ALL_KEYS") {
+ return true;
+ }
+ if (validResponses === "NO_KEYS") {
+ return false;
+ }
+ return validResponses.includes(key);
+ }
+ getKeyboardResponse({ callback_function, valid_responses = "ALL_KEYS", rt_method = "performance", persist, audio_context, audio_context_start_time, allow_held_key = false, minimum_valid_rt = this.minimumValidRt, }) {
+ if (rt_method !== "performance" && rt_method !== "audio") {
+ console.log('Invalid RT method specified in getKeyboardResponse. Defaulting to "performance" method.');
+ rt_method = "performance";
+ }
+ const usePerformanceRt = rt_method === "performance";
+ const startTime = usePerformanceRt ? performance.now() : audio_context_start_time * 1000;
+ this.registerRootListeners();
+ if (!this.areResponsesCaseSensitive && typeof valid_responses !== "string") {
+ valid_responses = valid_responses.map((r) => r.toLowerCase());
+ }
+ const listener = (e) => {
+ const rt = Math.round((rt_method == "performance" ? performance.now() : audio_context.currentTime * 1000) -
+ startTime);
+ if (rt < minimum_valid_rt) {
+ return;
+ }
+ const key = this.toLowerCaseIfInsensitive(e.key);
+ if (this.isResponseValid(valid_responses, allow_held_key, key)) {
+ // if this is a valid response, then we don't want the key event to trigger other actions
+ // like scrolling via the spacebar.
+ e.preventDefault();
+ if (!persist) {
+ // remove keyboard listener if it exists
+ this.cancelKeyboardResponse(listener);
+ }
+ callback_function({ key, rt });
+ }
+ };
+ this.listeners.add(listener);
+ return listener;
+ }
+ cancelKeyboardResponse(listener) {
+ // remove the listener from the set of listeners if it is contained
+ this.listeners.delete(listener);
+ }
+ cancelAllKeyboardResponses() {
+ this.listeners.clear();
+ }
+ compareKeys(key1, key2) {
+ if ((typeof key1 !== "string" && key1 !== null) ||
+ (typeof key2 !== "string" && key2 !== null)) {
+ console.error("Error in jsPsych.pluginAPI.compareKeys: arguments must be key strings or null.");
+ return undefined;
+ }
+ if (typeof key1 === "string" && typeof key2 === "string") {
+ // if both values are strings, then check whether or not letter case should be converted before comparing (case_sensitive_responses in initJsPsych)
+ return this.areResponsesCaseSensitive
+ ? key1 === key2
+ : key1.toLowerCase() === key2.toLowerCase();
+ }
+ return key1 === null && key2 === null;
+ }
+ }
+
+ /**
+ * Parameter types for plugins
+ */
+ exports.ParameterType = void 0;
+ (function (ParameterType) {
+ ParameterType[ParameterType["BOOL"] = 0] = "BOOL";
+ ParameterType[ParameterType["STRING"] = 1] = "STRING";
+ ParameterType[ParameterType["INT"] = 2] = "INT";
+ ParameterType[ParameterType["FLOAT"] = 3] = "FLOAT";
+ ParameterType[ParameterType["FUNCTION"] = 4] = "FUNCTION";
+ ParameterType[ParameterType["KEY"] = 5] = "KEY";
+ ParameterType[ParameterType["KEYS"] = 6] = "KEYS";
+ ParameterType[ParameterType["SELECT"] = 7] = "SELECT";
+ ParameterType[ParameterType["HTML_STRING"] = 8] = "HTML_STRING";
+ ParameterType[ParameterType["IMAGE"] = 9] = "IMAGE";
+ ParameterType[ParameterType["AUDIO"] = 10] = "AUDIO";
+ ParameterType[ParameterType["VIDEO"] = 11] = "VIDEO";
+ ParameterType[ParameterType["OBJECT"] = 12] = "OBJECT";
+ ParameterType[ParameterType["COMPLEX"] = 13] = "COMPLEX";
+ ParameterType[ParameterType["TIMELINE"] = 14] = "TIMELINE";
+ })(exports.ParameterType || (exports.ParameterType = {}));
+ const universalPluginParameters = {
+ /**
+ * Data to add to this trial (key-value pairs)
+ */
+ data: {
+ type: exports.ParameterType.OBJECT,
+ pretty_name: "Data",
+ default: {},
+ },
+ /**
+ * Function to execute when trial begins
+ */
+ on_start: {
+ type: exports.ParameterType.FUNCTION,
+ pretty_name: "On start",
+ default: function () {
+ return;
+ },
+ },
+ /**
+ * Function to execute when trial is finished
+ */
+ on_finish: {
+ type: exports.ParameterType.FUNCTION,
+ pretty_name: "On finish",
+ default: function () {
+ return;
+ },
+ },
+ /**
+ * Function to execute after the trial has loaded
+ */
+ on_load: {
+ type: exports.ParameterType.FUNCTION,
+ pretty_name: "On load",
+ default: function () {
+ return;
+ },
+ },
+ /**
+ * Length of gap between the end of this trial and the start of the next trial
+ */
+ post_trial_gap: {
+ type: exports.ParameterType.INT,
+ pretty_name: "Post trial gap",
+ default: null,
+ },
+ /**
+ * A list of CSS classes to add to the jsPsych display element for the duration of this trial
+ */
+ css_classes: {
+ type: exports.ParameterType.STRING,
+ pretty_name: "Custom CSS classes",
+ default: null,
+ },
+ /**
+ * Options to control simulation mode for the trial.
+ */
+ simulation_options: {
+ type: exports.ParameterType.COMPLEX,
+ default: null,
+ },
+ };
+
+ const preloadParameterTypes = [
+ exports.ParameterType.AUDIO,
+ exports.ParameterType.IMAGE,
+ exports.ParameterType.VIDEO,
+ ];
+ class MediaAPI {
+ constructor(useWebaudio, webaudioContext) {
+ this.useWebaudio = useWebaudio;
+ this.webaudioContext = webaudioContext;
+ // video //
+ this.video_buffers = {};
+ // audio //
+ this.context = null;
+ this.audio_buffers = [];
+ // preloading stimuli //
+ this.preload_requests = [];
+ this.img_cache = {};
+ this.preloadMap = new Map();
+ this.microphone_recorder = null;
+ }
+ getVideoBuffer(videoID) {
+ return this.video_buffers[videoID];
+ }
+ initAudio() {
+ this.context = this.useWebaudio ? this.webaudioContext : null;
+ }
+ audioContext() {
+ if (this.context !== null) {
+ if (this.context.state !== "running") {
+ this.context.resume();
+ }
+ }
+ return this.context;
+ }
+ getAudioBuffer(audioID) {
+ return new Promise((resolve, reject) => {
+ // check whether audio file already preloaded
+ if (typeof this.audio_buffers[audioID] == "undefined" ||
+ this.audio_buffers[audioID] == "tmp") {
+ // if audio is not already loaded, try to load it
+ this.preloadAudio([audioID], () => {
+ resolve(this.audio_buffers[audioID]);
+ }, () => { }, (e) => {
+ reject(e.error);
+ });
+ }
+ else {
+ // audio is already loaded
+ resolve(this.audio_buffers[audioID]);
+ }
+ });
+ }
+ preloadAudio(files, callback_complete = () => { }, callback_load = (filepath) => { }, callback_error = (error_msg) => { }) {
+ files = unique(files.flat());
+ let n_loaded = 0;
+ if (files.length == 0) {
+ callback_complete();
+ return;
+ }
+ const load_audio_file_webaudio = (source, count = 1) => {
+ const request = new XMLHttpRequest();
+ request.open("GET", source, true);
+ request.responseType = "arraybuffer";
+ request.onload = () => {
+ this.context.decodeAudioData(request.response, (buffer) => {
+ this.audio_buffers[source] = buffer;
+ n_loaded++;
+ callback_load(source);
+ if (n_loaded == files.length) {
+ callback_complete();
+ }
+ }, (e) => {
+ callback_error({ source: source, error: e });
+ });
+ };
+ request.onerror = function (e) {
+ let err = e;
+ if (this.status == 404) {
+ err = "404";
+ }
+ callback_error({ source: source, error: err });
+ };
+ request.onloadend = function (e) {
+ if (this.status == 404) {
+ callback_error({ source: source, error: "404" });
+ }
+ };
+ request.send();
+ this.preload_requests.push(request);
+ };
+ const load_audio_file_html5audio = (source, count = 1) => {
+ const audio = new Audio();
+ const handleCanPlayThrough = () => {
+ this.audio_buffers[source] = audio;
+ n_loaded++;
+ callback_load(source);
+ if (n_loaded == files.length) {
+ callback_complete();
+ }
+ audio.removeEventListener("canplaythrough", handleCanPlayThrough);
+ };
+ audio.addEventListener("canplaythrough", handleCanPlayThrough);
+ audio.addEventListener("error", function handleError(e) {
+ callback_error({ source: audio.src, error: e });
+ audio.removeEventListener("error", handleError);
+ });
+ audio.addEventListener("abort", function handleAbort(e) {
+ callback_error({ source: audio.src, error: e });
+ audio.removeEventListener("abort", handleAbort);
+ });
+ audio.src = source;
+ this.preload_requests.push(audio);
+ };
+ for (const file of files) {
+ if (typeof this.audio_buffers[file] !== "undefined") {
+ n_loaded++;
+ callback_load(file);
+ if (n_loaded == files.length) {
+ callback_complete();
+ }
+ }
+ else {
+ this.audio_buffers[file] = "tmp";
+ if (this.audioContext() !== null) {
+ load_audio_file_webaudio(file);
+ }
+ else {
+ load_audio_file_html5audio(file);
+ }
+ }
+ }
+ }
+ preloadImages(images, callback_complete = () => { }, callback_load = (filepath) => { }, callback_error = (error_msg) => { }) {
+ // flatten the images array
+ images = unique(images.flat());
+ var n_loaded = 0;
+ if (images.length === 0) {
+ callback_complete();
+ return;
+ }
+ for (var i = 0; i < images.length; i++) {
+ var img = new Image();
+ img.onload = function () {
+ n_loaded++;
+ callback_load(img.src);
+ if (n_loaded === images.length) {
+ callback_complete();
+ }
+ };
+ img.onerror = function (e) {
+ callback_error({ source: img.src, error: e });
+ };
+ img.src = images[i];
+ this.img_cache[images[i]] = img;
+ this.preload_requests.push(img);
+ }
+ }
+ preloadVideo(videos, callback_complete = () => { }, callback_load = (filepath) => { }, callback_error = (error_msg) => { }) {
+ // flatten the video array
+ videos = unique(videos.flat());
+ let n_loaded = 0;
+ if (videos.length === 0) {
+ callback_complete();
+ return;
+ }
+ for (const video of videos) {
+ const video_buffers = this.video_buffers;
+ //based on option 4 here: http://dinbror.dk/blog/how-to-preload-entire-html5-video-before-play-solved/
+ const request = new XMLHttpRequest();
+ request.open("GET", video, true);
+ request.responseType = "blob";
+ request.onload = function () {
+ if (this.status === 200 || this.status === 0) {
+ const videoBlob = this.response;
+ video_buffers[video] = URL.createObjectURL(videoBlob); // IE10+
+ n_loaded++;
+ callback_load(video);
+ if (n_loaded === videos.length) {
+ callback_complete();
+ }
+ }
+ };
+ request.onerror = function (e) {
+ let err = e;
+ if (this.status == 404) {
+ err = "404";
+ }
+ callback_error({ source: video, error: err });
+ };
+ request.onloadend = function (e) {
+ if (this.status == 404) {
+ callback_error({ source: video, error: "404" });
+ }
+ };
+ request.send();
+ this.preload_requests.push(request);
+ }
+ }
+ getAutoPreloadList(timeline_description) {
+ /** Map each preload parameter type to a set of paths to be preloaded */
+ const preloadPaths = Object.fromEntries(preloadParameterTypes.map((type) => [type, new Set()]));
+ const traverseTimeline = (node, inheritedTrialType) => {
+ var _a, _b, _c, _d;
+ const isTimeline = typeof node.timeline !== "undefined";
+ if (isTimeline) {
+ for (const childNode of node.timeline) {
+ traverseTimeline(childNode, (_a = node.type) !== null && _a !== void 0 ? _a : inheritedTrialType);
+ }
+ }
+ else if ((_c = ((_b = node.type) !== null && _b !== void 0 ? _b : inheritedTrialType)) === null || _c === void 0 ? void 0 : _c.info) {
+ // node is a trial with type.info set
+ // Get the plugin name and parameters object from the info object
+ const { name: pluginName, parameters } = ((_d = node.type) !== null && _d !== void 0 ? _d : inheritedTrialType).info;
+ // Extract parameters to be preloaded and their types from parameter info if this has not
+ // yet been done for `pluginName`
+ if (!this.preloadMap.has(pluginName)) {
+ this.preloadMap.set(pluginName, Object.fromEntries(Object.entries(parameters)
+ // Filter out parameter entries with media types and a non-false `preload` option
+ .filter(([_name, { type, preload }]) => preloadParameterTypes.includes(type) && (preload !== null && preload !== void 0 ? preload : true))
+ // Map each entry's value to its parameter type
+ .map(([name, { type }]) => [name, type])));
+ }
+ // Add preload paths from this trial
+ for (const [parameterName, parameterType] of Object.entries(this.preloadMap.get(pluginName))) {
+ const parameterValue = node[parameterName];
+ const elements = preloadPaths[parameterType];
+ if (typeof parameterValue === "string") {
+ elements.add(parameterValue);
+ }
+ else if (Array.isArray(parameterValue)) {
+ for (const element of parameterValue.flat()) {
+ if (typeof element === "string") {
+ elements.add(element);
+ }
+ }
+ }
+ }
+ }
+ };
+ traverseTimeline({ timeline: timeline_description });
+ return {
+ images: [...preloadPaths[exports.ParameterType.IMAGE]],
+ audio: [...preloadPaths[exports.ParameterType.AUDIO]],
+ video: [...preloadPaths[exports.ParameterType.VIDEO]],
+ };
+ }
+ cancelPreloads() {
+ for (const request of this.preload_requests) {
+ request.onload = () => { };
+ request.onerror = () => { };
+ request.oncanplaythrough = () => { };
+ request.onabort = () => { };
+ }
+ this.preload_requests = [];
+ }
+ initializeMicrophoneRecorder(stream) {
+ const recorder = new MediaRecorder(stream);
+ this.microphone_recorder = recorder;
+ }
+ getMicrophoneRecorder() {
+ return this.microphone_recorder;
+ }
+ }
+
+ class SimulationAPI {
+ dispatchEvent(event) {
+ document.body.dispatchEvent(event);
+ }
+ /**
+ * Dispatches a `keydown` event for the specified key
+ * @param key Character code (`.key` property) for the key to press.
+ */
+ keyDown(key) {
+ this.dispatchEvent(new KeyboardEvent("keydown", { key }));
+ }
+ /**
+ * Dispatches a `keyup` event for the specified key
+ * @param key Character code (`.key` property) for the key to press.
+ */
+ keyUp(key) {
+ this.dispatchEvent(new KeyboardEvent("keyup", { key }));
+ }
+ /**
+ * Dispatches a `keydown` and `keyup` event in sequence to simulate pressing a key.
+ * @param key Character code (`.key` property) for the key to press.
+ * @param delay Length of time to wait (ms) before executing action
+ */
+ pressKey(key, delay = 0) {
+ if (delay > 0) {
+ setTimeout(() => {
+ this.keyDown(key);
+ this.keyUp(key);
+ }, delay);
+ }
+ else {
+ this.keyDown(key);
+ this.keyUp(key);
+ }
+ }
+ /**
+ * Dispatches `mousedown`, `mouseup`, and `click` events on the target element
+ * @param target The element to click
+ * @param delay Length of time to wait (ms) before executing action
+ */
+ clickTarget(target, delay = 0) {
+ if (delay > 0) {
+ setTimeout(() => {
+ target.dispatchEvent(new MouseEvent("mousedown", { bubbles: true }));
+ target.dispatchEvent(new MouseEvent("mouseup", { bubbles: true }));
+ target.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ }, delay);
+ }
+ else {
+ target.dispatchEvent(new MouseEvent("mousedown", { bubbles: true }));
+ target.dispatchEvent(new MouseEvent("mouseup", { bubbles: true }));
+ target.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ }
+ }
+ /**
+ * Sets the value of a target text input
+ * @param target A text input element to fill in
+ * @param text Text to input
+ * @param delay Length of time to wait (ms) before executing action
+ */
+ fillTextInput(target, text, delay = 0) {
+ if (delay > 0) {
+ setTimeout(() => {
+ target.value = text;
+ }, delay);
+ }
+ else {
+ target.value = text;
+ }
+ }
+ /**
+ * Picks a valid key from `choices`, taking into account jsPsych-specific
+ * identifiers like "NO_KEYS" and "ALL_KEYS".
+ * @param choices Which keys are valid.
+ * @returns A key selected at random from the valid keys.
+ */
+ getValidKey(choices) {
+ const possible_keys = [
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ " ",
+ ];
+ let key;
+ if (choices == "NO_KEYS") {
+ key = null;
+ }
+ else if (choices == "ALL_KEYS") {
+ key = possible_keys[Math.floor(Math.random() * possible_keys.length)];
+ }
+ else {
+ const flat_choices = choices.flat();
+ key = flat_choices[Math.floor(Math.random() * flat_choices.length)];
+ }
+ return key;
+ }
+ mergeSimulationData(default_data, simulation_options) {
+ // override any data with data from simulation object
+ return Object.assign(Object.assign({}, default_data), simulation_options === null || simulation_options === void 0 ? void 0 : simulation_options.data);
+ }
+ ensureSimulationDataConsistency(trial, data) {
+ // All RTs must be rounded
+ if (data.rt) {
+ data.rt = Math.round(data.rt);
+ }
+ // If a trial_duration and rt exist, make sure that the RT is not longer than the trial.
+ if (trial.trial_duration && data.rt && data.rt > trial.trial_duration) {
+ data.rt = null;
+ if (data.response) {
+ data.response = null;
+ }
+ if (data.correct) {
+ data.correct = false;
+ }
+ }
+ // If trial.choices is NO_KEYS make sure that response and RT are null
+ if (trial.choices && trial.choices == "NO_KEYS") {
+ if (data.rt) {
+ data.rt = null;
+ }
+ if (data.response) {
+ data.response = null;
+ }
+ }
+ // If response is not allowed before stimulus display complete, ensure RT
+ // is longer than display time.
+ if (trial.allow_response_before_complete) {
+ if (trial.sequence_reps && trial.frame_time) {
+ const min_time = trial.sequence_reps * trial.frame_time * trial.stimuli.length;
+ if (data.rt < min_time) {
+ data.rt = null;
+ data.response = null;
+ }
+ }
+ }
+ }
+ }
+
+ class TimeoutAPI {
+ constructor() {
+ this.timeout_handlers = [];
+ }
+ setTimeout(callback, delay) {
+ const handle = window.setTimeout(callback, delay);
+ this.timeout_handlers.push(handle);
+ return handle;
+ }
+ clearAllTimeouts() {
+ for (const handler of this.timeout_handlers) {
+ clearTimeout(handler);
+ }
+ this.timeout_handlers = [];
+ }
+ }
+
+ function createJointPluginAPIObject(jsPsych) {
+ const settings = jsPsych.getInitSettings();
+ return Object.assign({}, ...[
+ new KeyboardListenerAPI(jsPsych.getDisplayContainerElement, settings.case_sensitive_responses, settings.minimum_valid_rt),
+ new TimeoutAPI(),
+ new MediaAPI(settings.use_webaudio, jsPsych.webaudio_context),
+ new HardwareAPI(),
+ new SimulationAPI(),
+ ].map((object) => autoBind(object)));
+ }
+
+ var wordList = [
+ // Borrowed from xkcd password generator which borrowed it from wherever
+ "ability","able","aboard","about","above","accept","accident","according",
+ "account","accurate","acres","across","act","action","active","activity",
+ "actual","actually","add","addition","additional","adjective","adult","adventure",
+ "advice","affect","afraid","after","afternoon","again","against","age",
+ "ago","agree","ahead","aid","air","airplane","alike","alive",
+ "all","allow","almost","alone","along","aloud","alphabet","already",
+ "also","although","am","among","amount","ancient","angle","angry",
+ "animal","announced","another","answer","ants","any","anybody","anyone",
+ "anything","anyway","anywhere","apart","apartment","appearance","apple","applied",
+ "appropriate","are","area","arm","army","around","arrange","arrangement",
+ "arrive","arrow","art","article","as","aside","ask","asleep",
+ "at","ate","atmosphere","atom","atomic","attached","attack","attempt",
+ "attention","audience","author","automobile","available","average","avoid","aware",
+ "away","baby","back","bad","badly","bag","balance","ball",
+ "balloon","band","bank","bar","bare","bark","barn","base",
+ "baseball","basic","basis","basket","bat","battle","be","bean",
+ "bear","beat","beautiful","beauty","became","because","become","becoming",
+ "bee","been","before","began","beginning","begun","behavior","behind",
+ "being","believed","bell","belong","below","belt","bend","beneath",
+ "bent","beside","best","bet","better","between","beyond","bicycle",
+ "bigger","biggest","bill","birds","birth","birthday","bit","bite",
+ "black","blank","blanket","blew","blind","block","blood","blow",
+ "blue","board","boat","body","bone","book","border","born",
+ "both","bottle","bottom","bound","bow","bowl","box","boy",
+ "brain","branch","brass","brave","bread","break","breakfast","breath",
+ "breathe","breathing","breeze","brick","bridge","brief","bright","bring",
+ "broad","broke","broken","brother","brought","brown","brush","buffalo",
+ "build","building","built","buried","burn","burst","bus","bush",
+ "business","busy","but","butter","buy","by","cabin","cage",
+ "cake","call","calm","came","camera","camp","can","canal",
+ "cannot","cap","capital","captain","captured","car","carbon","card",
+ "care","careful","carefully","carried","carry","case","cast","castle",
+ "cat","catch","cattle","caught","cause","cave","cell","cent",
+ "center","central","century","certain","certainly","chain","chair","chamber",
+ "chance","change","changing","chapter","character","characteristic","charge","chart",
+ "check","cheese","chemical","chest","chicken","chief","child","children",
+ "choice","choose","chose","chosen","church","circle","circus","citizen",
+ "city","class","classroom","claws","clay","clean","clear","clearly",
+ "climate","climb","clock","close","closely","closer","cloth","clothes",
+ "clothing","cloud","club","coach","coal","coast","coat","coffee",
+ "cold","collect","college","colony","color","column","combination","combine",
+ "come","comfortable","coming","command","common","community","company","compare",
+ "compass","complete","completely","complex","composed","composition","compound","concerned",
+ "condition","congress","connected","consider","consist","consonant","constantly","construction",
+ "contain","continent","continued","contrast","control","conversation","cook","cookies",
+ "cool","copper","copy","corn","corner","correct","correctly","cost",
+ "cotton","could","count","country","couple","courage","course","court",
+ "cover","cow","cowboy","crack","cream","create","creature","crew",
+ "crop","cross","crowd","cry","cup","curious","current","curve",
+ "customs","cut","cutting","daily","damage","dance","danger","dangerous",
+ "dark","darkness","date","daughter","dawn","day","dead","deal",
+ "dear","death","decide","declared","deep","deeply","deer","definition",
+ "degree","depend","depth","describe","desert","design","desk","detail",
+ "determine","develop","development","diagram","diameter","did","die","differ",
+ "difference","different","difficult","difficulty","dig","dinner","direct","direction",
+ "directly","dirt","dirty","disappear","discover","discovery","discuss","discussion",
+ "disease","dish","distance","distant","divide","division","do","doctor",
+ "does","dog","doing","doll","dollar","done","donkey","door",
+ "dot","double","doubt","down","dozen","draw","drawn","dream",
+ "dress","drew","dried","drink","drive","driven","driver","driving",
+ "drop","dropped","drove","dry","duck","due","dug","dull",
+ "during","dust","duty","each","eager","ear","earlier","early",
+ "earn","earth","easier","easily","east","easy","eat","eaten",
+ "edge","education","effect","effort","egg","eight","either","electric",
+ "electricity","element","elephant","eleven","else","empty","end","enemy",
+ "energy","engine","engineer","enjoy","enough","enter","entire","entirely",
+ "environment","equal","equally","equator","equipment","escape","especially","essential",
+ "establish","even","evening","event","eventually","ever","every","everybody",
+ "everyone","everything","everywhere","evidence","exact","exactly","examine","example",
+ "excellent","except","exchange","excited","excitement","exciting","exclaimed","exercise",
+ "exist","expect","experience","experiment","explain","explanation","explore","express",
+ "expression","extra","eye","face","facing","fact","factor","factory",
+ "failed","fair","fairly","fall","fallen","familiar","family","famous",
+ "far","farm","farmer","farther","fast","fastened","faster","fat",
+ "father","favorite","fear","feathers","feature","fed","feed","feel",
+ "feet","fell","fellow","felt","fence","few","fewer","field",
+ "fierce","fifteen","fifth","fifty","fight","fighting","figure","fill",
+ "film","final","finally","find","fine","finest","finger","finish",
+ "fire","fireplace","firm","first","fish","five","fix","flag",
+ "flame","flat","flew","flies","flight","floating","floor","flow",
+ "flower","fly","fog","folks","follow","food","foot","football",
+ "for","force","foreign","forest","forget","forgot","forgotten","form",
+ "former","fort","forth","forty","forward","fought","found","four",
+ "fourth","fox","frame","free","freedom","frequently","fresh","friend",
+ "friendly","frighten","frog","from","front","frozen","fruit","fuel",
+ "full","fully","fun","function","funny","fur","furniture","further",
+ "future","gain","game","garage","garden","gas","gasoline","gate",
+ "gather","gave","general","generally","gentle","gently","get","getting",
+ "giant","gift","girl","give","given","giving","glad","glass",
+ "globe","go","goes","gold","golden","gone","good","goose",
+ "got","government","grabbed","grade","gradually","grain","grandfather","grandmother",
+ "graph","grass","gravity","gray","great","greater","greatest","greatly",
+ "green","grew","ground","group","grow","grown","growth","guard",
+ "guess","guide","gulf","gun","habit","had","hair","half",
+ "halfway","hall","hand","handle","handsome","hang","happen","happened",
+ "happily","happy","harbor","hard","harder","hardly","has","hat",
+ "have","having","hay","he","headed","heading","health","heard",
+ "hearing","heart","heat","heavy","height","held","hello","help",
+ "helpful","her","herd","here","herself","hidden","hide","high",
+ "higher","highest","highway","hill","him","himself","his","history",
+ "hit","hold","hole","hollow","home","honor","hope","horn",
+ "horse","hospital","hot","hour","house","how","however","huge",
+ "human","hundred","hung","hungry","hunt","hunter","hurried","hurry",
+ "hurt","husband","ice","idea","identity","if","ill","image",
+ "imagine","immediately","importance","important","impossible","improve","in","inch",
+ "include","including","income","increase","indeed","independent","indicate","individual",
+ "industrial","industry","influence","information","inside","instance","instant","instead",
+ "instrument","interest","interior","into","introduced","invented","involved","iron",
+ "is","island","it","its","itself","jack","jar","jet",
+ "job","join","joined","journey","joy","judge","jump","jungle",
+ "just","keep","kept","key","kids","kill","kind","kitchen",
+ "knew","knife","know","knowledge","known","label","labor","lack",
+ "lady","laid","lake","lamp","land","language","large","larger",
+ "largest","last","late","later","laugh","law","lay","layers",
+ "lead","leader","leaf","learn","least","leather","leave","leaving",
+ "led","left","leg","length","lesson","let","letter","level",
+ "library","lie","life","lift","light","like","likely","limited",
+ "line","lion","lips","liquid","list","listen","little","live",
+ "living","load","local","locate","location","log","lonely","long",
+ "longer","look","loose","lose","loss","lost","lot","loud",
+ "love","lovely","low","lower","luck","lucky","lunch","lungs",
+ "lying","machine","machinery","mad","made","magic","magnet","mail",
+ "main","mainly","major","make","making","man","managed","manner",
+ "manufacturing","many","map","mark","market","married","mass","massage",
+ "master","material","mathematics","matter","may","maybe","me","meal",
+ "mean","means","meant","measure","meat","medicine","meet","melted",
+ "member","memory","men","mental","merely","met","metal","method",
+ "mice","middle","might","mighty","mile","military","milk","mill",
+ "mind","mine","minerals","minute","mirror","missing","mission","mistake",
+ "mix","mixture","model","modern","molecular","moment","money","monkey",
+ "month","mood","moon","more","morning","most","mostly","mother",
+ "motion","motor","mountain","mouse","mouth","move","movement","movie",
+ "moving","mud","muscle","music","musical","must","my","myself",
+ "mysterious","nails","name","nation","national","native","natural","naturally",
+ "nature","near","nearby","nearer","nearest","nearly","necessary","neck",
+ "needed","needle","needs","negative","neighbor","neighborhood","nervous","nest",
+ "never","new","news","newspaper","next","nice","night","nine",
+ "no","nobody","nodded","noise","none","noon","nor","north",
+ "nose","not","note","noted","nothing","notice","noun","now",
+ "number","numeral","nuts","object","observe","obtain","occasionally","occur",
+ "ocean","of","off","offer","office","officer","official","oil",
+ "old","older","oldest","on","once","one","only","onto",
+ "open","operation","opinion","opportunity","opposite","or","orange","orbit",
+ "order","ordinary","organization","organized","origin","original","other","ought",
+ "our","ourselves","out","outer","outline","outside","over","own",
+ "owner","oxygen","pack","package","page","paid","pain","paint",
+ "pair","palace","pale","pan","paper","paragraph","parallel","parent",
+ "park","part","particles","particular","particularly","partly","parts","party",
+ "pass","passage","past","path","pattern","pay","peace","pen",
+ "pencil","people","per","percent","perfect","perfectly","perhaps","period",
+ "person","personal","pet","phrase","physical","piano","pick","picture",
+ "pictured","pie","piece","pig","pile","pilot","pine","pink",
+ "pipe","pitch","place","plain","plan","plane","planet","planned",
+ "planning","plant","plastic","plate","plates","play","pleasant","please",
+ "pleasure","plenty","plural","plus","pocket","poem","poet","poetry",
+ "point","pole","police","policeman","political","pond","pony","pool",
+ "poor","popular","population","porch","port","position","positive","possible",
+ "possibly","post","pot","potatoes","pound","pour","powder","power",
+ "powerful","practical","practice","prepare","present","president","press","pressure",
+ "pretty","prevent","previous","price","pride","primitive","principal","principle",
+ "printed","private","prize","probably","problem","process","produce","product",
+ "production","program","progress","promised","proper","properly","property","protection",
+ "proud","prove","provide","public","pull","pupil","pure","purple",
+ "purpose","push","put","putting","quarter","queen","question","quick",
+ "quickly","quiet","quietly","quite","rabbit","race","radio","railroad",
+ "rain","raise","ran","ranch","range","rapidly","rate","rather",
+ "raw","rays","reach","read","reader","ready","real","realize",
+ "rear","reason","recall","receive","recent","recently","recognize","record",
+ "red","refer","refused","region","regular","related","relationship","religious",
+ "remain","remarkable","remember","remove","repeat","replace","replied","report",
+ "represent","require","research","respect","rest","result","return","review",
+ "rhyme","rhythm","rice","rich","ride","riding","right","ring",
+ "rise","rising","river","road","roar","rock","rocket","rocky",
+ "rod","roll","roof","room","root","rope","rose","rough",
+ "round","route","row","rubbed","rubber","rule","ruler","run",
+ "running","rush","sad","saddle","safe","safety","said","sail",
+ "sale","salmon","salt","same","sand","sang","sat","satellites",
+ "satisfied","save","saved","saw","say","scale","scared","scene",
+ "school","science","scientific","scientist","score","screen","sea","search",
+ "season","seat","second","secret","section","see","seed","seeing",
+ "seems","seen","seldom","select","selection","sell","send","sense",
+ "sent","sentence","separate","series","serious","serve","service","sets",
+ "setting","settle","settlers","seven","several","shade","shadow","shake",
+ "shaking","shall","shallow","shape","share","sharp","she","sheep",
+ "sheet","shelf","shells","shelter","shine","shinning","ship","shirt",
+ "shoe","shoot","shop","shore","short","shorter","shot","should",
+ "shoulder","shout","show","shown","shut","sick","sides","sight",
+ "sign","signal","silence","silent","silk","silly","silver","similar",
+ "simple","simplest","simply","since","sing","single","sink","sister",
+ "sit","sitting","situation","six","size","skill","skin","sky",
+ "slabs","slave","sleep","slept","slide","slight","slightly","slip",
+ "slipped","slope","slow","slowly","small","smaller","smallest","smell",
+ "smile","smoke","smooth","snake","snow","so","soap","social",
+ "society","soft","softly","soil","solar","sold","soldier","solid",
+ "solution","solve","some","somebody","somehow","someone","something","sometime",
+ "somewhere","son","song","soon","sort","sound","source","south",
+ "southern","space","speak","special","species","specific","speech","speed",
+ "spell","spend","spent","spider","spin","spirit","spite","split",
+ "spoken","sport","spread","spring","square","stage","stairs","stand",
+ "standard","star","stared","start","state","statement","station","stay",
+ "steady","steam","steel","steep","stems","step","stepped","stick",
+ "stiff","still","stock","stomach","stone","stood","stop","stopped",
+ "store","storm","story","stove","straight","strange","stranger","straw",
+ "stream","street","strength","stretch","strike","string","strip","strong",
+ "stronger","struck","structure","struggle","stuck","student","studied","studying",
+ "subject","substance","success","successful","such","sudden","suddenly","sugar",
+ "suggest","suit","sum","summer","sun","sunlight","supper","supply",
+ "support","suppose","sure","surface","surprise","surrounded","swam","sweet",
+ "swept","swim","swimming","swing","swung","syllable","symbol","system",
+ "table","tail","take","taken","tales","talk","tall","tank",
+ "tape","task","taste","taught","tax","tea","teach","teacher",
+ "team","tears","teeth","telephone","television","tell","temperature","ten",
+ "tent","term","terrible","test","than","thank","that","thee",
+ "them","themselves","then","theory","there","therefore","these","they",
+ "thick","thin","thing","think","third","thirty","this","those",
+ "thou","though","thought","thousand","thread","three","threw","throat",
+ "through","throughout","throw","thrown","thumb","thus","thy","tide",
+ "tie","tight","tightly","till","time","tin","tiny","tip",
+ "tired","title","to","tobacco","today","together","told","tomorrow",
+ "tone","tongue","tonight","too","took","tool","top","topic",
+ "torn","total","touch","toward","tower","town","toy","trace",
+ "track","trade","traffic","trail","train","transportation","trap","travel",
+ "treated","tree","triangle","tribe","trick","tried","trip","troops",
+ "tropical","trouble","truck","trunk","truth","try","tube","tune",
+ "turn","twelve","twenty","twice","two","type","typical","uncle",
+ "under","underline","understanding","unhappy","union","unit","universe","unknown",
+ "unless","until","unusual","up","upon","upper","upward","us",
+ "use","useful","using","usual","usually","valley","valuable","value",
+ "vapor","variety","various","vast","vegetable","verb","vertical","very",
+ "vessels","victory","view","village","visit","visitor","voice","volume",
+ "vote","vowel","voyage","wagon","wait","walk","wall","want",
+ "war","warm","warn","was","wash","waste","watch","water",
+ "wave","way","we","weak","wealth","wear","weather","week",
+ "weigh","weight","welcome","well","went","were","west","western",
+ "wet","whale","what","whatever","wheat","wheel","when","whenever",
+ "where","wherever","whether","which","while","whispered","whistle","white",
+ "who","whole","whom","whose","why","wide","widely","wife",
+ "wild","will","willing","win","wind","window","wing","winter",
+ "wire","wise","wish","with","within","without","wolf","women",
+ "won","wonder","wonderful","wood","wooden","wool","word","wore",
+ "work","worker","world","worried","worry","worse","worth","would",
+ "wrapped","write","writer","writing","written","wrong","wrote","yard",
+ "year","yellow","yes","yesterday","yet","you","young","younger",
+ "your","yourself","youth","zero","zebra","zipper","zoo","zulu"
+ ];
+
+ function words(options) {
+
+ function word() {
+ if (options && options.maxLength > 1) {
+ return generateWordWithMaxLength();
+ } else {
+ return generateRandomWord();
+ }
+ }
+
+ function generateWordWithMaxLength() {
+ var rightSize = false;
+ var wordUsed;
+ while (!rightSize) {
+ wordUsed = generateRandomWord();
+ if(wordUsed.length <= options.maxLength) {
+ rightSize = true;
+ }
+
+ }
+ return wordUsed;
+ }
+
+ function generateRandomWord() {
+ return wordList[randInt(wordList.length)];
+ }
+
+ function randInt(lessThan) {
+ return Math.floor(Math.random() * lessThan);
+ }
+
+ // No arguments = generate one word
+ if (typeof(options) === 'undefined') {
+ return word();
+ }
+
+ // Just a number = return that many words
+ if (typeof(options) === 'number') {
+ options = { exactly: options };
+ }
+
+ // options supported: exactly, min, max, join
+ if (options.exactly) {
+ options.min = options.exactly;
+ options.max = options.exactly;
+ }
+
+ // not a number = one word par string
+ if (typeof(options.wordsPerString) !== 'number') {
+ options.wordsPerString = 1;
+ }
+
+ //not a function = returns the raw word
+ if (typeof(options.formatter) !== 'function') {
+ options.formatter = (word) => word;
+ }
+
+ //not a string = separator is a space
+ if (typeof(options.separator) !== 'string') {
+ options.separator = ' ';
+ }
+
+ var total = options.min + randInt(options.max + 1 - options.min);
+ var results = [];
+ var token = '';
+ var relativeIndex = 0;
+
+ for (var i = 0; (i < total * options.wordsPerString); i++) {
+ if (relativeIndex === options.wordsPerString - 1) {
+ token += options.formatter(word(), relativeIndex);
+ }
+ else {
+ token += options.formatter(word(), relativeIndex) + options.separator;
+ }
+ relativeIndex++;
+ if ((i + 1) % options.wordsPerString === 0) {
+ results.push(token);
+ token = '';
+ relativeIndex = 0;
+ }
+
+ }
+ if (typeof options.join === 'string') {
+ results = results.join(options.join);
+ }
+
+ return results;
+ }
+
+ var randomWords$1 = words;
+ // Export the word list as it is often useful
+ words.wordList = wordList;
+
+ var alea = {exports: {}};
+
+ (function (module) {
+ // A port of an algorithm by Johannes Baagøe , 2010
+ // http://baagoe.com/en/RandomMusings/javascript/
+ // https://github.com/nquinlan/better-random-numbers-for-javascript-mirror
+ // Original work is under MIT license -
+
+ // Copyright (C) 2010 by Johannes Baagøe
+ //
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
+ // of this software and associated documentation files (the "Software"), to deal
+ // in the Software without restriction, including without limitation the rights
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ // copies of the Software, and to permit persons to whom the Software is
+ // furnished to do so, subject to the following conditions:
+ //
+ // The above copyright notice and this permission notice shall be included in
+ // all copies or substantial portions of the Software.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ // THE SOFTWARE.
+
+
+
+ (function(global, module, define) {
+
+ function Alea(seed) {
+ var me = this, mash = Mash();
+
+ me.next = function() {
+ var t = 2091639 * me.s0 + me.c * 2.3283064365386963e-10; // 2^-32
+ me.s0 = me.s1;
+ me.s1 = me.s2;
+ return me.s2 = t - (me.c = t | 0);
+ };
+
+ // Apply the seeding algorithm from Baagoe.
+ me.c = 1;
+ me.s0 = mash(' ');
+ me.s1 = mash(' ');
+ me.s2 = mash(' ');
+ me.s0 -= mash(seed);
+ if (me.s0 < 0) { me.s0 += 1; }
+ me.s1 -= mash(seed);
+ if (me.s1 < 0) { me.s1 += 1; }
+ me.s2 -= mash(seed);
+ if (me.s2 < 0) { me.s2 += 1; }
+ mash = null;
+ }
+
+ function copy(f, t) {
+ t.c = f.c;
+ t.s0 = f.s0;
+ t.s1 = f.s1;
+ t.s2 = f.s2;
+ return t;
+ }
+
+ function impl(seed, opts) {
+ var xg = new Alea(seed),
+ state = opts && opts.state,
+ prng = xg.next;
+ prng.int32 = function() { return (xg.next() * 0x100000000) | 0; };
+ prng.double = function() {
+ return prng() + (prng() * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53
+ };
+ prng.quick = prng;
+ if (state) {
+ if (typeof(state) == 'object') copy(state, xg);
+ prng.state = function() { return copy(xg, {}); };
+ }
+ return prng;
+ }
+
+ function Mash() {
+ var n = 0xefc8249d;
+
+ var mash = function(data) {
+ data = String(data);
+ for (var i = 0; i < data.length; i++) {
+ n += data.charCodeAt(i);
+ var h = 0.02519603282416938 * n;
+ n = h >>> 0;
+ h -= n;
+ h *= n;
+ n = h >>> 0;
+ h -= n;
+ n += h * 0x100000000; // 2^32
+ }
+ return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
+ };
+
+ return mash;
+ }
+
+
+ if (module && module.exports) {
+ module.exports = impl;
+ } else if (define && define.amd) {
+ define(function() { return impl; });
+ } else {
+ this.alea = impl;
+ }
+
+ })(
+ commonjsGlobal,
+ module, // present in node.js
+ (typeof undefined) == 'function' // present with an AMD loader
+ );
+ }(alea));
+
+ var seedrandom = alea.exports;
+
+ /**
+ * Uses the `seedrandom` package to replace Math.random() with a seedable PRNG.
+ *
+ * @param seed An optional seed. If none is given, a random seed will be generated.
+ * @returns The seed value.
+ */
+ function setSeed(seed = Math.random().toString()) {
+ Math.random = seedrandom(seed);
+ return seed;
+ }
+ function repeat(array, repetitions, unpack = false) {
+ const arr_isArray = Array.isArray(array);
+ const rep_isArray = Array.isArray(repetitions);
+ // if array is not an array, then we just repeat the item
+ if (!arr_isArray) {
+ if (!rep_isArray) {
+ array = [array];
+ repetitions = [repetitions];
+ }
+ else {
+ repetitions = [repetitions[0]];
+ console.log("Unclear parameters given to randomization.repeat. Multiple set sizes specified, but only one item exists to sample. Proceeding using the first set size.");
+ }
+ }
+ else {
+ // if repetitions is not an array, but array is, then we
+ // repeat repetitions for each entry in array
+ if (!rep_isArray) {
+ let reps = [];
+ for (let i = 0; i < array.length; i++) {
+ reps.push(repetitions);
+ }
+ repetitions = reps;
+ }
+ else {
+ if (array.length != repetitions.length) {
+ console.warn("Unclear parameters given to randomization.repeat. Items and repetitions are unequal lengths. Behavior may not be as expected.");
+ // throw warning if repetitions is too short, use first rep ONLY.
+ if (repetitions.length < array.length) {
+ let reps = [];
+ for (let i = 0; i < array.length; i++) {
+ reps.push(repetitions);
+ }
+ repetitions = reps;
+ }
+ else {
+ // throw warning if too long, and then use the first N
+ repetitions = repetitions.slice(0, array.length);
+ }
+ }
+ }
+ }
+ // should be clear at this point to assume that array and repetitions are arrays with == length
+ let allsamples = [];
+ for (let i = 0; i < array.length; i++) {
+ for (let j = 0; j < repetitions[i]; j++) {
+ if (array[i] == null || typeof array[i] != "object") {
+ allsamples.push(array[i]);
+ }
+ else {
+ allsamples.push(Object.assign({}, array[i]));
+ }
+ }
+ }
+ let out = shuffle(allsamples);
+ if (unpack) {
+ out = unpackArray(out);
+ }
+ return out;
+ }
+ function shuffle(array) {
+ if (!Array.isArray(array)) {
+ console.error("Argument to shuffle() must be an array.");
+ }
+ const copy_array = array.slice(0);
+ let m = copy_array.length, t, i;
+ // While there remain elements to shuffle…
+ while (m) {
+ // Pick a remaining element…
+ i = Math.floor(Math.random() * m--);
+ // And swap it with the current element.
+ t = copy_array[m];
+ copy_array[m] = copy_array[i];
+ copy_array[i] = t;
+ }
+ return copy_array;
+ }
+ function shuffleNoRepeats(arr, equalityTest) {
+ if (!Array.isArray(arr)) {
+ console.error("First argument to shuffleNoRepeats() must be an array.");
+ }
+ if (typeof equalityTest !== "undefined" && typeof equalityTest !== "function") {
+ console.error("Second argument to shuffleNoRepeats() must be a function.");
+ }
+ // define a default equalityTest
+ if (typeof equalityTest == "undefined") {
+ equalityTest = function (a, b) {
+ if (a === b) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ };
+ }
+ const random_shuffle = shuffle(arr);
+ for (let i = 0; i < random_shuffle.length - 1; i++) {
+ if (equalityTest(random_shuffle[i], random_shuffle[i + 1])) {
+ // neighbors are equal, pick a new random neighbor to swap (not the first or last element, to avoid edge cases)
+ let random_pick = Math.floor(Math.random() * (random_shuffle.length - 2)) + 1;
+ // test to make sure the new neighbor isn't equal to the old one
+ while (equalityTest(random_shuffle[i + 1], random_shuffle[random_pick]) ||
+ equalityTest(random_shuffle[i + 1], random_shuffle[random_pick + 1]) ||
+ equalityTest(random_shuffle[i + 1], random_shuffle[random_pick - 1])) {
+ random_pick = Math.floor(Math.random() * (random_shuffle.length - 2)) + 1;
+ }
+ const new_neighbor = random_shuffle[random_pick];
+ random_shuffle[random_pick] = random_shuffle[i + 1];
+ random_shuffle[i + 1] = new_neighbor;
+ }
+ }
+ return random_shuffle;
+ }
+ function shuffleAlternateGroups(arr_groups, random_group_order = false) {
+ const n_groups = arr_groups.length;
+ if (n_groups == 1) {
+ console.warn("shuffleAlternateGroups() was called with only one group. Defaulting to simple shuffle.");
+ return shuffle(arr_groups[0]);
+ }
+ let group_order = [];
+ for (let i = 0; i < n_groups; i++) {
+ group_order.push(i);
+ }
+ if (random_group_order) {
+ group_order = shuffle(group_order);
+ }
+ const randomized_groups = [];
+ let min_length = null;
+ for (let i = 0; i < n_groups; i++) {
+ min_length =
+ min_length === null ? arr_groups[i].length : Math.min(min_length, arr_groups[i].length);
+ randomized_groups.push(shuffle(arr_groups[i]));
+ }
+ const out = [];
+ for (let i = 0; i < min_length; i++) {
+ for (let j = 0; j < group_order.length; j++) {
+ out.push(randomized_groups[group_order[j]][i]);
+ }
+ }
+ return out;
+ }
+ function sampleWithoutReplacement(arr, size) {
+ if (!Array.isArray(arr)) {
+ console.error("First argument to sampleWithoutReplacement() must be an array");
+ }
+ if (size > arr.length) {
+ console.error("Cannot take a sample larger than the size of the set of items to sample.");
+ }
+ return shuffle(arr).slice(0, size);
+ }
+ function sampleWithReplacement(arr, size, weights) {
+ if (!Array.isArray(arr)) {
+ console.error("First argument to sampleWithReplacement() must be an array");
+ }
+ const normalized_weights = [];
+ if (typeof weights !== "undefined") {
+ if (weights.length !== arr.length) {
+ console.error("The length of the weights array must equal the length of the array " +
+ "to be sampled from.");
+ }
+ let weight_sum = 0;
+ for (const weight of weights) {
+ weight_sum += weight;
+ }
+ for (const weight of weights) {
+ normalized_weights.push(weight / weight_sum);
+ }
+ }
+ else {
+ for (let i = 0; i < arr.length; i++) {
+ normalized_weights.push(1 / arr.length);
+ }
+ }
+ const cumulative_weights = [normalized_weights[0]];
+ for (let i = 1; i < normalized_weights.length; i++) {
+ cumulative_weights.push(normalized_weights[i] + cumulative_weights[i - 1]);
+ }
+ const samp = [];
+ for (let i = 0; i < size; i++) {
+ const rnd = Math.random();
+ let index = 0;
+ while (rnd > cumulative_weights[index]) {
+ index++;
+ }
+ samp.push(arr[index]);
+ }
+ return samp;
+ }
+ function factorial(factors, repetitions = 1, unpack = false) {
+ let design = [{}];
+ for (const [factorName, factor] of Object.entries(factors)) {
+ const new_design = [];
+ for (const level of factor) {
+ for (const cell of design) {
+ new_design.push(Object.assign(Object.assign({}, cell), { [factorName]: level }));
+ }
+ }
+ design = new_design;
+ }
+ return repeat(design, repetitions, unpack);
+ }
+ function randomID(length = 32) {
+ let result = "";
+ const chars = "0123456789abcdefghjklmnopqrstuvwxyz";
+ for (let i = 0; i < length; i++) {
+ result += chars[Math.floor(Math.random() * chars.length)];
+ }
+ return result;
+ }
+ /**
+ * Generate a random integer from `lower` to `upper`, inclusive of both end points.
+ * @param lower The lowest value it is possible to generate
+ * @param upper The highest value it is possible to generate
+ * @returns A random integer
+ */
+ function randomInt(lower, upper) {
+ if (upper < lower) {
+ throw new Error("Upper boundary must be less than or equal to lower boundary");
+ }
+ return lower + Math.floor(Math.random() * (upper - lower + 1));
+ }
+ /**
+ * Generates a random sample from a Bernoulli distribution.
+ * @param p The probability of sampling 1.
+ * @returns 0, with probability 1-p, or 1, with probability p.
+ */
+ function sampleBernoulli(p) {
+ return Math.random() <= p ? 1 : 0;
+ }
+ function sampleNormal(mean, standard_deviation) {
+ return randn_bm() * standard_deviation + mean;
+ }
+ function sampleExponential(rate) {
+ return -Math.log(Math.random()) / rate;
+ }
+ function sampleExGaussian(mean, standard_deviation, rate, positive = false) {
+ let s = sampleNormal(mean, standard_deviation) + sampleExponential(rate);
+ if (positive) {
+ while (s <= 0) {
+ s = sampleNormal(mean, standard_deviation) + sampleExponential(rate);
+ }
+ }
+ return s;
+ }
+ /**
+ * Generate one or more random words.
+ *
+ * This is a wrapper function for the {@link https://www.npmjs.com/package/random-words `random-words` npm package}.
+ *
+ * @param opts An object with optional properties `min`, `max`, `exactly`,
+ * `join`, `maxLength`, `wordsPerString`, `separator`, and `formatter`.
+ *
+ * @returns An array of words or a single string, depending on parameter choices.
+ */
+ function randomWords(opts) {
+ return randomWords$1(opts);
+ }
+ // Box-Muller transformation for a random sample from normal distribution with mean = 0, std = 1
+ // https://stackoverflow.com/a/36481059/3726673
+ function randn_bm() {
+ var u = 0, v = 0;
+ while (u === 0)
+ u = Math.random(); //Converting [0,1) to (0,1)
+ while (v === 0)
+ v = Math.random();
+ return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
+ }
+ function unpackArray(array) {
+ const out = {};
+ for (const x of array) {
+ for (const key of Object.keys(x)) {
+ if (typeof out[key] === "undefined") {
+ out[key] = [];
+ }
+ out[key].push(x[key]);
+ }
+ }
+ return out;
+ }
+
+ var randomization = /*#__PURE__*/Object.freeze({
+ __proto__: null,
+ setSeed: setSeed,
+ repeat: repeat,
+ shuffle: shuffle,
+ shuffleNoRepeats: shuffleNoRepeats,
+ shuffleAlternateGroups: shuffleAlternateGroups,
+ sampleWithoutReplacement: sampleWithoutReplacement,
+ sampleWithReplacement: sampleWithReplacement,
+ factorial: factorial,
+ randomID: randomID,
+ randomInt: randomInt,
+ sampleBernoulli: sampleBernoulli,
+ sampleNormal: sampleNormal,
+ sampleExponential: sampleExponential,
+ sampleExGaussian: sampleExGaussian,
+ randomWords: randomWords
+ });
+
+ /**
+ * Gets information about the Mechanical Turk Environment, HIT, Assignment, and Worker
+ * by parsing the URL variables that Mechanical Turk generates.
+ * @returns An object containing information about the Mechanical Turk Environment, HIT, Assignment, and Worker.
+ */
+ function turkInfo() {
+ const turk = {
+ previewMode: false,
+ outsideTurk: false,
+ hitId: "INVALID_URL_PARAMETER",
+ assignmentId: "INVALID_URL_PARAMETER",
+ workerId: "INVALID_URL_PARAMETER",
+ turkSubmitTo: "INVALID_URL_PARAMETER",
+ };
+ const param = function (url, name) {
+ name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
+ const regexS = "[\\?&]" + name + "=([^]*)";
+ const regex = new RegExp(regexS);
+ const results = regex.exec(url);
+ return results == null ? "" : results[1];
+ };
+ const src = param(window.location.href, "assignmentId")
+ ? window.location.href
+ : document.referrer;
+ const keys = ["assignmentId", "hitId", "workerId", "turkSubmitTo"];
+ keys.map(function (key) {
+ turk[key] = unescape(param(src, key));
+ });
+ turk.previewMode = turk.assignmentId == "ASSIGNMENT_ID_NOT_AVAILABLE";
+ turk.outsideTurk =
+ !turk.previewMode && turk.hitId === "" && turk.assignmentId == "" && turk.workerId == "";
+ return turk;
+ }
+ /**
+ * Send data to Mechnical Turk for storage.
+ * @param data An object containing `key:value` pairs to send to Mechanical Turk. Values
+ * cannot contain nested objects, arrays, or functions.
+ * @returns Nothing
+ */
+ function submitToTurk(data) {
+ const turk = turkInfo();
+ const assignmentId = turk.assignmentId;
+ const turkSubmitTo = turk.turkSubmitTo;
+ if (!assignmentId || !turkSubmitTo)
+ return;
+ const form = document.createElement("form");
+ form.method = "POST";
+ form.action = turkSubmitTo + "/mturk/externalSubmit?assignmentId=" + assignmentId;
+ for (const key in data) {
+ if (data.hasOwnProperty(key)) {
+ const hiddenField = document.createElement("input");
+ hiddenField.type = "hidden";
+ hiddenField.name = key;
+ hiddenField.id = key;
+ hiddenField.value = data[key];
+ form.appendChild(hiddenField);
+ }
+ }
+ document.body.appendChild(form);
+ form.submit();
+ }
+
+ var turk = /*#__PURE__*/Object.freeze({
+ __proto__: null,
+ turkInfo: turkInfo,
+ submitToTurk: submitToTurk
+ });
+
+ class TimelineNode {
+ // constructor
+ constructor(jsPsych, parameters, parent, relativeID) {
+ this.jsPsych = jsPsych;
+ // track progress through the node
+ this.progress = {
+ current_location: -1,
+ current_variable_set: 0,
+ current_repetition: 0,
+ current_iteration: 0,
+ done: false,
+ };
+ // store a link to the parent of this node
+ this.parent_node = parent;
+ // create the ID for this node
+ this.relative_id = typeof parent === "undefined" ? 0 : relativeID;
+ // check if there is a timeline parameter
+ // if there is, then this node has its own timeline
+ if (typeof parameters.timeline !== "undefined") {
+ // create timeline properties
+ this.timeline_parameters = {
+ timeline: [],
+ loop_function: parameters.loop_function,
+ conditional_function: parameters.conditional_function,
+ sample: parameters.sample,
+ randomize_order: typeof parameters.randomize_order == "undefined" ? false : parameters.randomize_order,
+ repetitions: typeof parameters.repetitions == "undefined" ? 1 : parameters.repetitions,
+ timeline_variables: typeof parameters.timeline_variables == "undefined"
+ ? [{}]
+ : parameters.timeline_variables,
+ on_timeline_finish: parameters.on_timeline_finish,
+ on_timeline_start: parameters.on_timeline_start,
+ };
+ this.setTimelineVariablesOrder();
+ // extract all of the node level data and parameters
+ // but remove all of the timeline-level specific information
+ // since this will be used to copy things down hierarchically
+ var node_data = Object.assign({}, parameters);
+ delete node_data.timeline;
+ delete node_data.conditional_function;
+ delete node_data.loop_function;
+ delete node_data.randomize_order;
+ delete node_data.repetitions;
+ delete node_data.timeline_variables;
+ delete node_data.sample;
+ delete node_data.on_timeline_start;
+ delete node_data.on_timeline_finish;
+ this.node_trial_data = node_data; // store for later...
+ // create a TimelineNode for each element in the timeline
+ for (var i = 0; i < parameters.timeline.length; i++) {
+ // merge parameters
+ var merged_parameters = Object.assign({}, node_data, parameters.timeline[i]);
+ // merge any data from the parent node into child nodes
+ if (typeof node_data.data == "object" && typeof parameters.timeline[i].data == "object") {
+ var merged_data = Object.assign({}, node_data.data, parameters.timeline[i].data);
+ merged_parameters.data = merged_data;
+ }
+ this.timeline_parameters.timeline.push(new TimelineNode(this.jsPsych, merged_parameters, this, i));
+ }
+ }
+ // if there is no timeline parameter, then this node is a trial node
+ else {
+ // check to see if a valid trial type is defined
+ if (typeof parameters.type === "undefined") {
+ console.error('Trial level node is missing the "type" parameter. The parameters for the node are: ' +
+ JSON.stringify(parameters));
+ }
+ // create a deep copy of the parameters for the trial
+ this.trial_parameters = Object.assign({}, parameters);
+ }
+ }
+ // recursively get the next trial to run.
+ // if this node is a leaf (trial), then return the trial.
+ // otherwise, recursively find the next trial in the child timeline.
+ trial() {
+ if (typeof this.timeline_parameters == "undefined") {
+ // returns a clone of the trial_parameters to
+ // protect functions.
+ return deepCopy(this.trial_parameters);
+ }
+ else {
+ if (this.progress.current_location >= this.timeline_parameters.timeline.length) {
+ return null;
+ }
+ else {
+ return this.timeline_parameters.timeline[this.progress.current_location].trial();
+ }
+ }
+ }
+ markCurrentTrialComplete() {
+ if (typeof this.timeline_parameters === "undefined") {
+ this.progress.done = true;
+ }
+ else {
+ this.timeline_parameters.timeline[this.progress.current_location].markCurrentTrialComplete();
+ }
+ }
+ nextRepetiton() {
+ this.setTimelineVariablesOrder();
+ this.progress.current_location = -1;
+ this.progress.current_variable_set = 0;
+ this.progress.current_repetition++;
+ for (var i = 0; i < this.timeline_parameters.timeline.length; i++) {
+ this.timeline_parameters.timeline[i].reset();
+ }
+ }
+ // set the order for going through the timeline variables array
+ setTimelineVariablesOrder() {
+ const timeline_parameters = this.timeline_parameters;
+ // check to make sure this node has variables
+ if (typeof timeline_parameters === "undefined" ||
+ typeof timeline_parameters.timeline_variables === "undefined") {
+ return;
+ }
+ var order = [];
+ for (var i = 0; i < timeline_parameters.timeline_variables.length; i++) {
+ order.push(i);
+ }
+ if (typeof timeline_parameters.sample !== "undefined") {
+ if (timeline_parameters.sample.type == "custom") {
+ order = timeline_parameters.sample.fn(order);
+ }
+ else if (timeline_parameters.sample.type == "with-replacement") {
+ order = sampleWithReplacement(order, timeline_parameters.sample.size, timeline_parameters.sample.weights);
+ }
+ else if (timeline_parameters.sample.type == "without-replacement") {
+ order = sampleWithoutReplacement(order, timeline_parameters.sample.size);
+ }
+ else if (timeline_parameters.sample.type == "fixed-repetitions") {
+ order = repeat(order, timeline_parameters.sample.size, false);
+ }
+ else if (timeline_parameters.sample.type == "alternate-groups") {
+ order = shuffleAlternateGroups(timeline_parameters.sample.groups, timeline_parameters.sample.randomize_group_order);
+ }
+ else {
+ console.error('Invalid type in timeline sample parameters. Valid options for type are "custom", "with-replacement", "without-replacement", "fixed-repetitions", and "alternate-groups"');
+ }
+ }
+ if (timeline_parameters.randomize_order) {
+ order = shuffle(order);
+ }
+ this.progress.order = order;
+ }
+ // next variable set
+ nextSet() {
+ this.progress.current_location = -1;
+ this.progress.current_variable_set++;
+ for (var i = 0; i < this.timeline_parameters.timeline.length; i++) {
+ this.timeline_parameters.timeline[i].reset();
+ }
+ }
+ // update the current trial node to be completed
+ // returns true if the node is complete after advance (all subnodes are also complete)
+ // returns false otherwise
+ advance() {
+ const progress = this.progress;
+ const timeline_parameters = this.timeline_parameters;
+ const internal = this.jsPsych.internal;
+ // first check to see if done
+ if (progress.done) {
+ return true;
+ }
+ // if node has not started yet (progress.current_location == -1),
+ // then try to start the node.
+ if (progress.current_location == -1) {
+ // check for on_timeline_start and conditonal function on nodes with timelines
+ if (typeof timeline_parameters !== "undefined") {
+ // only run the conditional function if this is the first repetition of the timeline when
+ // repetitions > 1, and only when on the first variable set
+ if (typeof timeline_parameters.conditional_function !== "undefined" &&
+ progress.current_repetition == 0 &&
+ progress.current_variable_set == 0) {
+ internal.call_immediate = true;
+ var conditional_result = timeline_parameters.conditional_function();
+ internal.call_immediate = false;
+ // if the conditional_function() returns false, then the timeline
+ // doesn't run and is marked as complete.
+ if (conditional_result == false) {
+ progress.done = true;
+ return true;
+ }
+ }
+ // if we reach this point then the node has its own timeline and will start
+ // so we need to check if there is an on_timeline_start function if we are on the first variable set
+ if (typeof timeline_parameters.on_timeline_start !== "undefined" &&
+ progress.current_variable_set == 0) {
+ timeline_parameters.on_timeline_start();
+ }
+ }
+ // if we reach this point, then either the node doesn't have a timeline of the
+ // conditional function returned true and it can start
+ progress.current_location = 0;
+ // call advance again on this node now that it is pointing to a new location
+ return this.advance();
+ }
+ // if this node has a timeline, propogate down to the current trial.
+ if (typeof timeline_parameters !== "undefined") {
+ var have_node_to_run = false;
+ // keep incrementing the location in the timeline until one of the nodes reached is incomplete
+ while (progress.current_location < timeline_parameters.timeline.length &&
+ have_node_to_run == false) {
+ // check to see if the node currently pointed at is done
+ var target_complete = timeline_parameters.timeline[progress.current_location].advance();
+ if (!target_complete) {
+ have_node_to_run = true;
+ return false;
+ }
+ else {
+ progress.current_location++;
+ }
+ }
+ // if we've reached the end of the timeline (which, if the code is here, we have)
+ // there are a few steps to see what to do next...
+ // first, check the timeline_variables to see if we need to loop through again
+ // with a new set of variables
+ if (progress.current_variable_set < progress.order.length - 1) {
+ // reset the progress of the node to be with the new set
+ this.nextSet();
+ // then try to advance this node again.
+ return this.advance();
+ }
+ // if we're all done with the timeline_variables, then check to see if there are more repetitions
+ else if (progress.current_repetition < timeline_parameters.repetitions - 1) {
+ this.nextRepetiton();
+ // check to see if there is an on_timeline_finish function
+ if (typeof timeline_parameters.on_timeline_finish !== "undefined") {
+ timeline_parameters.on_timeline_finish();
+ }
+ return this.advance();
+ }
+ // if we're all done with the repetitions...
+ else {
+ // check to see if there is an on_timeline_finish function
+ if (typeof timeline_parameters.on_timeline_finish !== "undefined") {
+ timeline_parameters.on_timeline_finish();
+ }
+ // if we're all done with the repetitions, check if there is a loop function.
+ if (typeof timeline_parameters.loop_function !== "undefined") {
+ internal.call_immediate = true;
+ if (timeline_parameters.loop_function(this.generatedData())) {
+ this.reset();
+ internal.call_immediate = false;
+ return this.parent_node.advance();
+ }
+ else {
+ progress.done = true;
+ internal.call_immediate = false;
+ return true;
+ }
+ }
+ }
+ // no more loops on this timeline, we're done!
+ progress.done = true;
+ return true;
+ }
+ }
+ // check the status of the done flag
+ isComplete() {
+ return this.progress.done;
+ }
+ // getter method for timeline variables
+ getTimelineVariableValue(variable_name) {
+ if (typeof this.timeline_parameters == "undefined") {
+ return undefined;
+ }
+ var v = this.timeline_parameters.timeline_variables[this.progress.order[this.progress.current_variable_set]][variable_name];
+ return v;
+ }
+ // recursive upward search for timeline variables
+ findTimelineVariable(variable_name) {
+ var v = this.getTimelineVariableValue(variable_name);
+ if (typeof v == "undefined") {
+ if (typeof this.parent_node !== "undefined") {
+ return this.parent_node.findTimelineVariable(variable_name);
+ }
+ else {
+ return undefined;
+ }
+ }
+ else {
+ return v;
+ }
+ }
+ // recursive downward search for active trial to extract timeline variable
+ timelineVariable(variable_name) {
+ if (typeof this.timeline_parameters == "undefined") {
+ return this.findTimelineVariable(variable_name);
+ }
+ else {
+ // if progress.current_location is -1, then the timeline variable is being evaluated
+ // in a function that runs prior to the trial starting, so we should treat that trial
+ // as being the active trial for purposes of finding the value of the timeline variable
+ var loc = Math.max(0, this.progress.current_location);
+ // if loc is greater than the number of elements on this timeline, then the timeline
+ // variable is being evaluated in a function that runs after the trial on the timeline
+ // are complete but before advancing to the next (like a loop_function).
+ // treat the last active trial as the active trial for this purpose.
+ if (loc == this.timeline_parameters.timeline.length) {
+ loc = loc - 1;
+ }
+ // now find the variable
+ return this.timeline_parameters.timeline[loc].timelineVariable(variable_name);
+ }
+ }
+ // recursively get all the timeline variables for this trial
+ allTimelineVariables() {
+ var all_tvs = this.allTimelineVariablesNames();
+ var all_tvs_vals = {};
+ for (var i = 0; i < all_tvs.length; i++) {
+ all_tvs_vals[all_tvs[i]] = this.timelineVariable(all_tvs[i]);
+ }
+ return all_tvs_vals;
+ }
+ // helper to get all the names at this stage.
+ allTimelineVariablesNames(so_far = []) {
+ if (typeof this.timeline_parameters !== "undefined") {
+ so_far = so_far.concat(Object.keys(this.timeline_parameters.timeline_variables[this.progress.order[this.progress.current_variable_set]]));
+ // if progress.current_location is -1, then the timeline variable is being evaluated
+ // in a function that runs prior to the trial starting, so we should treat that trial
+ // as being the active trial for purposes of finding the value of the timeline variable
+ var loc = Math.max(0, this.progress.current_location);
+ // if loc is greater than the number of elements on this timeline, then the timeline
+ // variable is being evaluated in a function that runs after the trial on the timeline
+ // are complete but before advancing to the next (like a loop_function).
+ // treat the last active trial as the active trial for this purpose.
+ if (loc == this.timeline_parameters.timeline.length) {
+ loc = loc - 1;
+ }
+ // now find the variable
+ return this.timeline_parameters.timeline[loc].allTimelineVariablesNames(so_far);
+ }
+ if (typeof this.timeline_parameters == "undefined") {
+ return so_far;
+ }
+ }
+ // recursively get the number of **trials** contained in the timeline
+ // assuming that while loops execute exactly once and if conditionals
+ // always run
+ length() {
+ var length = 0;
+ if (typeof this.timeline_parameters !== "undefined") {
+ for (var i = 0; i < this.timeline_parameters.timeline.length; i++) {
+ length += this.timeline_parameters.timeline[i].length();
+ }
+ }
+ else {
+ return 1;
+ }
+ return length;
+ }
+ // return the percentage of trials completed, grouped at the first child level
+ // counts a set of trials as complete when the child node is done
+ percentComplete() {
+ var total_trials = this.length();
+ var completed_trials = 0;
+ for (var i = 0; i < this.timeline_parameters.timeline.length; i++) {
+ if (this.timeline_parameters.timeline[i].isComplete()) {
+ completed_trials += this.timeline_parameters.timeline[i].length();
+ }
+ }
+ return (completed_trials / total_trials) * 100;
+ }
+ // resets the node and all subnodes to original state
+ // but increments the current_iteration counter
+ reset() {
+ this.progress.current_location = -1;
+ this.progress.current_repetition = 0;
+ this.progress.current_variable_set = 0;
+ this.progress.current_iteration++;
+ this.progress.done = false;
+ this.setTimelineVariablesOrder();
+ if (typeof this.timeline_parameters != "undefined") {
+ for (var i = 0; i < this.timeline_parameters.timeline.length; i++) {
+ this.timeline_parameters.timeline[i].reset();
+ }
+ }
+ }
+ // mark this node as finished
+ end() {
+ this.progress.done = true;
+ }
+ // recursively end whatever sub-node is running the current trial
+ endActiveNode() {
+ if (typeof this.timeline_parameters == "undefined") {
+ this.end();
+ this.parent_node.end();
+ }
+ else {
+ this.timeline_parameters.timeline[this.progress.current_location].endActiveNode();
+ }
+ }
+ // get a unique ID associated with this node
+ // the ID reflects the current iteration through this node.
+ ID() {
+ var id = "";
+ if (typeof this.parent_node == "undefined") {
+ return "0." + this.progress.current_iteration;
+ }
+ else {
+ id += this.parent_node.ID() + "-";
+ id += this.relative_id + "." + this.progress.current_iteration;
+ return id;
+ }
+ }
+ // get the ID of the active trial
+ activeID() {
+ if (typeof this.timeline_parameters == "undefined") {
+ return this.ID();
+ }
+ else {
+ return this.timeline_parameters.timeline[this.progress.current_location].activeID();
+ }
+ }
+ // get all the data generated within this node
+ generatedData() {
+ return this.jsPsych.data.getDataByTimelineNode(this.ID());
+ }
+ // get all the trials of a particular type
+ trialsOfType(type) {
+ if (typeof this.timeline_parameters == "undefined") {
+ if (this.trial_parameters.type == type) {
+ return this.trial_parameters;
+ }
+ else {
+ return [];
+ }
+ }
+ else {
+ var trials = [];
+ for (var i = 0; i < this.timeline_parameters.timeline.length; i++) {
+ var t = this.timeline_parameters.timeline[i].trialsOfType(type);
+ trials = trials.concat(t);
+ }
+ return trials;
+ }
+ }
+ // add new trials to end of this timeline
+ insert(parameters) {
+ if (typeof this.timeline_parameters === "undefined") {
+ console.error("Cannot add new trials to a trial-level node.");
+ }
+ else {
+ this.timeline_parameters.timeline.push(new TimelineNode(this.jsPsych, Object.assign(Object.assign({}, this.node_trial_data), parameters), this, this.timeline_parameters.timeline.length));
+ }
+ }
+ }
+
+ function delay(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+ class JsPsych {
+ constructor(options) {
+ this.extensions = {};
+ this.turk = turk;
+ this.randomization = randomization;
+ this.utils = utils;
+ //
+ // private variables
+ //
+ /**
+ * options
+ */
+ this.opts = {};
+ // flow control
+ this.global_trial_index = 0;
+ this.current_trial = {};
+ this.current_trial_finished = false;
+ /**
+ * is the experiment paused?
+ */
+ this.paused = false;
+ this.waiting = false;
+ /**
+ * is the page retrieved directly via file:// protocol (true) or hosted on a server (false)?
+ */
+ this.file_protocol = false;
+ /**
+ * is the experiment running in `simulate()` mode
+ */
+ this.simulation_mode = null;
+ // storing a single webaudio context to prevent problems with multiple inits
+ // of jsPsych
+ this.webaudio_context = null;
+ this.internal = {
+ /**
+ * this flag is used to determine whether we are in a scope where
+ * jsPsych.timelineVariable() should be executed immediately or
+ * whether it should return a function to access the variable later.
+ *
+ **/
+ call_immediate: false,
+ };
+ this.progress_bar_amount = 0;
+ // override default options if user specifies an option
+ options = Object.assign({ display_element: undefined, on_finish: () => { }, on_trial_start: () => { }, on_trial_finish: () => { }, on_data_update: () => { }, on_interaction_data_update: () => { }, on_close: () => { }, use_webaudio: true, exclusions: {}, show_progress_bar: false, message_progress_bar: "Completion Progress", auto_update_progress_bar: true, default_iti: 0, minimum_valid_rt: 0, experiment_width: null, override_safe_mode: false, case_sensitive_responses: false, extensions: [] }, options);
+ this.opts = options;
+ autoBind(this); // so we can pass JsPsych methods as callbacks and `this` remains the JsPsych instance
+ this.webaudio_context =
+ typeof window !== "undefined" && typeof window.AudioContext !== "undefined"
+ ? new AudioContext()
+ : null;
+ // detect whether page is running in browser as a local file, and if so, disable web audio and video preloading to prevent CORS issues
+ if (window.location.protocol == "file:" &&
+ (options.override_safe_mode === false || typeof options.override_safe_mode === "undefined")) {
+ options.use_webaudio = false;
+ this.file_protocol = true;
+ console.warn("jsPsych detected that it is running via the file:// protocol and not on a web server. " +
+ "To prevent issues with cross-origin requests, Web Audio and video preloading have been disabled. " +
+ "If you would like to override this setting, you can set 'override_safe_mode' to 'true' in initJsPsych. " +
+ "For more information, see: https://www.jspsych.org/overview/running-experiments");
+ }
+ // initialize modules
+ this.data = new JsPsychData(this);
+ this.pluginAPI = createJointPluginAPIObject(this);
+ // create instances of extensions
+ for (const extension of options.extensions) {
+ this.extensions[extension.type.info.name] = new extension.type(this);
+ }
+ // initialize audio context based on options and browser capabilities
+ this.pluginAPI.initAudio();
+ }
+ version() {
+ return version;
+ }
+ /**
+ * Starts an experiment using the provided timeline and returns a promise that is resolved when
+ * the experiment is finished.
+ *
+ * @param timeline The timeline to be run
+ */
+ run(timeline) {
+ return __awaiter(this, void 0, void 0, function* () {
+ if (typeof timeline === "undefined") {
+ console.error("No timeline declared in jsPsych.run. Cannot start experiment.");
+ }
+ if (timeline.length === 0) {
+ console.error("No trials have been added to the timeline (the timeline is an empty array). Cannot start experiment.");
+ }
+ // create experiment timeline
+ this.timelineDescription = timeline;
+ this.timeline = new TimelineNode(this, { timeline });
+ yield this.prepareDom();
+ yield this.checkExclusions(this.opts.exclusions);
+ yield this.loadExtensions(this.opts.extensions);
+ document.documentElement.setAttribute("jspsych", "present");
+ this.startExperiment();
+ yield this.finished;
+ });
+ }
+ simulate(timeline, simulation_mode = "data-only", simulation_options = {}) {
+ return __awaiter(this, void 0, void 0, function* () {
+ this.simulation_mode = simulation_mode;
+ this.simulation_options = simulation_options;
+ yield this.run(timeline);
+ });
+ }
+ getProgress() {
+ return {
+ total_trials: typeof this.timeline === "undefined" ? undefined : this.timeline.length(),
+ current_trial_global: this.global_trial_index,
+ percent_complete: typeof this.timeline === "undefined" ? 0 : this.timeline.percentComplete(),
+ };
+ }
+ getStartTime() {
+ return this.exp_start_time;
+ }
+ getTotalTime() {
+ if (typeof this.exp_start_time === "undefined") {
+ return 0;
+ }
+ return new Date().getTime() - this.exp_start_time.getTime();
+ }
+ getDisplayElement() {
+ return this.DOM_target;
+ }
+ getDisplayContainerElement() {
+ return this.DOM_container;
+ }
+ finishTrial(data = {}) {
+ if (this.current_trial_finished) {
+ return;
+ }
+ this.current_trial_finished = true;
+ // remove any CSS classes that were added to the DOM via css_classes parameter
+ if (typeof this.current_trial.css_classes !== "undefined" &&
+ Array.isArray(this.current_trial.css_classes)) {
+ this.DOM_target.classList.remove(...this.current_trial.css_classes);
+ }
+ // write the data from the trial
+ this.data.write(data);
+ // get back the data with all of the defaults in
+ const trial_data = this.data.get().filter({ trial_index: this.global_trial_index });
+ // for trial-level callbacks, we just want to pass in a reference to the values
+ // of the DataCollection, for easy access and editing.
+ const trial_data_values = trial_data.values()[0];
+ const current_trial = this.current_trial;
+ if (typeof current_trial.save_trial_parameters === "object") {
+ for (const key of Object.keys(current_trial.save_trial_parameters)) {
+ const key_val = current_trial.save_trial_parameters[key];
+ if (key_val === true) {
+ if (typeof current_trial[key] === "undefined") {
+ console.warn(`Invalid parameter specified in save_trial_parameters. Trial has no property called "${key}".`);
+ }
+ else if (typeof current_trial[key] === "function") {
+ trial_data_values[key] = current_trial[key].toString();
+ }
+ else {
+ trial_data_values[key] = current_trial[key];
+ }
+ }
+ if (key_val === false) {
+ // we don't allow internal_node_id or trial_index to be deleted because it would break other things
+ if (key !== "internal_node_id" && key !== "trial_index") {
+ delete trial_data_values[key];
+ }
+ }
+ }
+ }
+ // handle extension callbacks
+ if (Array.isArray(current_trial.extensions)) {
+ for (const extension of current_trial.extensions) {
+ const ext_data_values = this.extensions[extension.type.info.name].on_finish(extension.params);
+ Object.assign(trial_data_values, ext_data_values);
+ }
+ }
+ // about to execute lots of callbacks, so switch context.
+ this.internal.call_immediate = true;
+ // handle callback at plugin level
+ if (typeof current_trial.on_finish === "function") {
+ current_trial.on_finish(trial_data_values);
+ }
+ // handle callback at whole-experiment level
+ this.opts.on_trial_finish(trial_data_values);
+ // after the above callbacks are complete, then the data should be finalized
+ // for this trial. call the on_data_update handler, passing in the same
+ // data object that just went through the trial's finish handlers.
+ this.opts.on_data_update(trial_data_values);
+ // done with callbacks
+ this.internal.call_immediate = false;
+ // wait for iti
+ if (this.simulation_mode === "data-only") {
+ this.nextTrial();
+ }
+ else if (typeof current_trial.post_trial_gap === null ||
+ typeof current_trial.post_trial_gap === "undefined") {
+ if (this.opts.default_iti > 0) {
+ setTimeout(this.nextTrial, this.opts.default_iti);
+ }
+ else {
+ this.nextTrial();
+ }
+ }
+ else {
+ if (current_trial.post_trial_gap > 0) {
+ setTimeout(this.nextTrial, current_trial.post_trial_gap);
+ }
+ else {
+ this.nextTrial();
+ }
+ }
+ }
+ endExperiment(end_message = "", data = {}) {
+ this.timeline.end_message = end_message;
+ this.timeline.end();
+ this.pluginAPI.cancelAllKeyboardResponses();
+ this.pluginAPI.clearAllTimeouts();
+ this.finishTrial(data);
+ }
+ endCurrentTimeline() {
+ this.timeline.endActiveNode();
+ }
+ getCurrentTrial() {
+ return this.current_trial;
+ }
+ getInitSettings() {
+ return this.opts;
+ }
+ getCurrentTimelineNodeID() {
+ return this.timeline.activeID();
+ }
+ timelineVariable(varname, immediate = false) {
+ if (this.internal.call_immediate || immediate === true) {
+ return this.timeline.timelineVariable(varname);
+ }
+ else {
+ return {
+ timelineVariablePlaceholder: true,
+ timelineVariableFunction: () => this.timeline.timelineVariable(varname),
+ };
+ }
+ }
+ getAllTimelineVariables() {
+ return this.timeline.allTimelineVariables();
+ }
+ addNodeToEndOfTimeline(new_timeline, preload_callback) {
+ this.timeline.insert(new_timeline);
+ }
+ pauseExperiment() {
+ this.paused = true;
+ }
+ resumeExperiment() {
+ this.paused = false;
+ if (this.waiting) {
+ this.waiting = false;
+ this.nextTrial();
+ }
+ }
+ loadFail(message) {
+ message = message || "
The experiment failed to load.
";
+ this.DOM_target.innerHTML = message;
+ }
+ getSafeModeStatus() {
+ return this.file_protocol;
+ }
+ getTimeline() {
+ return this.timelineDescription;
+ }
+ prepareDom() {
+ return __awaiter(this, void 0, void 0, function* () {
+ // Wait until the document is ready
+ if (document.readyState !== "complete") {
+ yield new Promise((resolve) => {
+ window.addEventListener("load", resolve);
+ });
+ }
+ const options = this.opts;
+ // set DOM element where jsPsych will render content
+ // if undefined, then jsPsych will use the tag and the entire page
+ if (typeof options.display_element === "undefined") {
+ // check if there is a body element on the page
+ const body = document.querySelector("body");
+ if (body === null) {
+ document.documentElement.appendChild(document.createElement("body"));
+ }
+ // using the full page, so we need the HTML element to
+ // have 100% height, and body to be full width and height with
+ // no margin
+ document.querySelector("html").style.height = "100%";
+ document.querySelector("body").style.margin = "0px";
+ document.querySelector("body").style.height = "100%";
+ document.querySelector("body").style.width = "100%";
+ options.display_element = document.querySelector("body");
+ }
+ else {
+ // make sure that the display element exists on the page
+ const display = options.display_element instanceof Element
+ ? options.display_element
+ : document.querySelector("#" + options.display_element);
+ if (display === null) {
+ console.error("The display_element specified in initJsPsych() does not exist in the DOM.");
+ }
+ else {
+ options.display_element = display;
+ }
+ }
+ options.display_element.innerHTML =
+ '
';
+ this.DOM_container = options.display_element;
+ this.DOM_target = document.querySelector("#jspsych-content");
+ // set experiment_width if not null
+ if (options.experiment_width !== null) {
+ this.DOM_target.style.width = options.experiment_width + "px";
+ }
+ // add tabIndex attribute to scope event listeners
+ options.display_element.tabIndex = 0;
+ // add CSS class to DOM_target
+ if (options.display_element.className.indexOf("jspsych-display-element") === -1) {
+ options.display_element.className += " jspsych-display-element";
+ }
+ this.DOM_target.className += "jspsych-content";
+ // create listeners for user browser interaction
+ this.data.createInteractionListeners();
+ // add event for closing window
+ window.addEventListener("beforeunload", options.on_close);
+ });
+ }
+ loadExtensions(extensions) {
+ return __awaiter(this, void 0, void 0, function* () {
+ // run the .initialize method of any extensions that are in use
+ // these should return a Promise to indicate when loading is complete
+ try {
+ yield Promise.all(extensions.map((extension) => this.extensions[extension.type.info.name].initialize(extension.params || {})));
+ }
+ catch (error_message) {
+ console.error(error_message);
+ throw new Error(error_message);
+ }
+ });
+ }
+ startExperiment() {
+ this.finished = new Promise((resolve) => {
+ this.resolveFinishedPromise = resolve;
+ });
+ // show progress bar if requested
+ if (this.opts.show_progress_bar === true) {
+ this.drawProgressBar(this.opts.message_progress_bar);
+ }
+ // record the start time
+ this.exp_start_time = new Date();
+ // begin!
+ this.timeline.advance();
+ this.doTrial(this.timeline.trial());
+ }
+ finishExperiment() {
+ const finish_result = this.opts.on_finish(this.data.get());
+ const done_handler = () => {
+ if (typeof this.timeline.end_message !== "undefined") {
+ this.DOM_target.innerHTML = this.timeline.end_message;
+ }
+ this.resolveFinishedPromise();
+ };
+ if (finish_result) {
+ Promise.resolve(finish_result).then(done_handler);
+ }
+ else {
+ done_handler();
+ }
+ }
+ nextTrial() {
+ // if experiment is paused, don't do anything.
+ if (this.paused) {
+ this.waiting = true;
+ return;
+ }
+ this.global_trial_index++;
+ // advance timeline
+ this.timeline.markCurrentTrialComplete();
+ const complete = this.timeline.advance();
+ // update progress bar if shown
+ if (this.opts.show_progress_bar === true && this.opts.auto_update_progress_bar === true) {
+ this.updateProgressBar();
+ }
+ // check if experiment is over
+ if (complete) {
+ this.finishExperiment();
+ return;
+ }
+ this.doTrial(this.timeline.trial());
+ }
+ doTrial(trial) {
+ this.current_trial = trial;
+ this.current_trial_finished = false;
+ // process all timeline variables for this trial
+ this.evaluateTimelineVariables(trial);
+ if (typeof trial.type === "string") {
+ throw new MigrationError("A string was provided as the trial's `type` parameter. Since jsPsych v7, the `type` parameter needs to be a plugin object.");
+ }
+ // instantiate the plugin for this trial
+ trial.type = Object.assign(Object.assign({}, autoBind(new trial.type(this))), { info: trial.type.info });
+ // evaluate variables that are functions
+ this.evaluateFunctionParameters(trial);
+ // get default values for parameters
+ this.setDefaultValues(trial);
+ // about to execute callbacks
+ this.internal.call_immediate = true;
+ // call experiment wide callback
+ this.opts.on_trial_start(trial);
+ // call trial specific callback if it exists
+ if (typeof trial.on_start === "function") {
+ trial.on_start(trial);
+ }
+ // call any on_start functions for extensions
+ if (Array.isArray(trial.extensions)) {
+ for (const extension of trial.extensions) {
+ this.extensions[extension.type.info.name].on_start(extension.params);
+ }
+ }
+ // apply the focus to the element containing the experiment.
+ this.DOM_container.focus();
+ // reset the scroll on the DOM target
+ this.DOM_target.scrollTop = 0;
+ // add CSS classes to the DOM_target if they exist in trial.css_classes
+ if (typeof trial.css_classes !== "undefined") {
+ if (!Array.isArray(trial.css_classes) && typeof trial.css_classes === "string") {
+ trial.css_classes = [trial.css_classes];
+ }
+ if (Array.isArray(trial.css_classes)) {
+ this.DOM_target.classList.add(...trial.css_classes);
+ }
+ }
+ // setup on_load event callback
+ const load_callback = () => {
+ if (typeof trial.on_load === "function") {
+ trial.on_load();
+ }
+ // call any on_load functions for extensions
+ if (Array.isArray(trial.extensions)) {
+ for (const extension of trial.extensions) {
+ this.extensions[extension.type.info.name].on_load(extension.params);
+ }
+ }
+ };
+ let trial_complete;
+ if (!this.simulation_mode) {
+ trial_complete = trial.type.trial(this.DOM_target, trial, load_callback);
+ }
+ if (this.simulation_mode) {
+ // check if the trial supports simulation
+ if (trial.type.simulate) {
+ let trial_sim_opts;
+ if (!trial.simulation_options) {
+ trial_sim_opts = this.simulation_options.default;
+ }
+ if (trial.simulation_options) {
+ if (typeof trial.simulation_options == "string") {
+ if (this.simulation_options[trial.simulation_options]) {
+ trial_sim_opts = this.simulation_options[trial.simulation_options];
+ }
+ else if (this.simulation_options.default) {
+ console.log(`No matching simulation options found for "${trial.simulation_options}". Using "default" options.`);
+ trial_sim_opts = this.simulation_options.default;
+ }
+ else {
+ console.log(`No matching simulation options found for "${trial.simulation_options}" and no "default" options provided. Using the default values provided by the plugin.`);
+ trial_sim_opts = {};
+ }
+ }
+ else {
+ trial_sim_opts = trial.simulation_options;
+ }
+ }
+ trial_sim_opts = this.utils.deepCopy(trial_sim_opts);
+ trial_sim_opts = this.replaceFunctionsWithValues(trial_sim_opts, null);
+ if ((trial_sim_opts === null || trial_sim_opts === void 0 ? void 0 : trial_sim_opts.simulate) === false) {
+ trial_complete = trial.type.trial(this.DOM_target, trial, load_callback);
+ }
+ else {
+ trial_complete = trial.type.simulate(trial, (trial_sim_opts === null || trial_sim_opts === void 0 ? void 0 : trial_sim_opts.mode) || this.simulation_mode, trial_sim_opts, load_callback);
+ }
+ }
+ else {
+ // trial doesn't have a simulate method, so just run as usual
+ trial_complete = trial.type.trial(this.DOM_target, trial, load_callback);
+ }
+ }
+ // see if trial_complete is a Promise by looking for .then() function
+ const is_promise = trial_complete && typeof trial_complete.then == "function";
+ // in simulation mode we let the simulate function call the load_callback always.
+ if (!is_promise && !this.simulation_mode) {
+ load_callback();
+ }
+ // done with callbacks
+ this.internal.call_immediate = false;
+ }
+ evaluateTimelineVariables(trial) {
+ for (const key of Object.keys(trial)) {
+ // timeline variables on the root level
+ if (typeof trial[key] === "object" &&
+ trial[key] !== null &&
+ typeof trial[key].timelineVariablePlaceholder !== "undefined") {
+ /*trial[key].toString().replace(/\s/g, "") ==
+ "function(){returntimeline.timelineVariable(varname);}"
+ )*/ trial[key] = trial[key].timelineVariableFunction();
+ }
+ // timeline variables that are nested in objects
+ if (typeof trial[key] === "object" && trial[key] !== null) {
+ this.evaluateTimelineVariables(trial[key]);
+ }
+ }
+ }
+ evaluateFunctionParameters(trial) {
+ // set a flag so that jsPsych.timelineVariable() is immediately executed in this context
+ this.internal.call_immediate = true;
+ // iterate over each parameter
+ for (const key of Object.keys(trial)) {
+ // check to make sure parameter is not "type", since that was eval'd above.
+ if (key !== "type") {
+ // this if statement is checking to see if the parameter type is expected to be a function, in which case we should NOT evaluate it.
+ // the first line checks if the parameter is defined in the universalPluginParameters set
+ // the second line checks the plugin-specific parameters
+ if (typeof universalPluginParameters[key] !== "undefined" &&
+ universalPluginParameters[key].type !== exports.ParameterType.FUNCTION) {
+ trial[key] = this.replaceFunctionsWithValues(trial[key], null);
+ }
+ if (typeof trial.type.info.parameters[key] !== "undefined" &&
+ trial.type.info.parameters[key].type !== exports.ParameterType.FUNCTION) {
+ trial[key] = this.replaceFunctionsWithValues(trial[key], trial.type.info.parameters[key]);
+ }
+ }
+ }
+ // reset so jsPsych.timelineVariable() is no longer immediately executed
+ this.internal.call_immediate = false;
+ }
+ replaceFunctionsWithValues(obj, info) {
+ // null typeof is 'object' (?!?!), so need to run this first!
+ if (obj === null) {
+ return obj;
+ }
+ // arrays
+ else if (Array.isArray(obj)) {
+ for (let i = 0; i < obj.length; i++) {
+ obj[i] = this.replaceFunctionsWithValues(obj[i], info);
+ }
+ }
+ // objects
+ else if (typeof obj === "object") {
+ if (info === null || !info.nested) {
+ for (const key of Object.keys(obj)) {
+ if (key === "type") {
+ // Ignore the object's `type` field because it contains a plugin and we do not want to
+ // call plugin functions
+ continue;
+ }
+ obj[key] = this.replaceFunctionsWithValues(obj[key], null);
+ }
+ }
+ else {
+ for (const key of Object.keys(obj)) {
+ if (typeof info.nested[key] === "object" &&
+ info.nested[key].type !== exports.ParameterType.FUNCTION) {
+ obj[key] = this.replaceFunctionsWithValues(obj[key], info.nested[key]);
+ }
+ }
+ }
+ }
+ else if (typeof obj === "function") {
+ return obj();
+ }
+ return obj;
+ }
+ setDefaultValues(trial) {
+ for (const param in trial.type.info.parameters) {
+ // check if parameter is complex with nested defaults
+ if (trial.type.info.parameters[param].type === exports.ParameterType.COMPLEX) {
+ if (trial.type.info.parameters[param].array === true) {
+ // iterate over each entry in the array
+ trial[param].forEach(function (ip, i) {
+ // check each parameter in the plugin description
+ for (const p in trial.type.info.parameters[param].nested) {
+ if (typeof trial[param][i][p] === "undefined" || trial[param][i][p] === null) {
+ if (typeof trial.type.info.parameters[param].nested[p].default === "undefined") {
+ console.error("You must specify a value for the " +
+ p +
+ " parameter (nested in the " +
+ param +
+ " parameter) in the " +
+ trial.type +
+ " plugin.");
+ }
+ else {
+ trial[param][i][p] = trial.type.info.parameters[param].nested[p].default;
+ }
+ }
+ }
+ });
+ }
+ }
+ // if it's not nested, checking is much easier and do that here:
+ else if (typeof trial[param] === "undefined" || trial[param] === null) {
+ if (typeof trial.type.info.parameters[param].default === "undefined") {
+ console.error("You must specify a value for the " +
+ param +
+ " parameter in the " +
+ trial.type.info.name +
+ " plugin.");
+ }
+ else {
+ trial[param] = trial.type.info.parameters[param].default;
+ }
+ }
+ }
+ }
+ checkExclusions(exclusions) {
+ return __awaiter(this, void 0, void 0, function* () {
+ if (exclusions.min_width || exclusions.min_height || exclusions.audio) {
+ console.warn("The exclusions option in `initJsPsych()` is deprecated and will be removed in a future version. We recommend using the browser-check plugin instead. See https://www.jspsych.org/latest/plugins/browser-check/.");
+ }
+ // MINIMUM SIZE
+ if (exclusions.min_width || exclusions.min_height) {
+ const mw = exclusions.min_width || 0;
+ const mh = exclusions.min_height || 0;
+ if (window.innerWidth < mw || window.innerHeight < mh) {
+ this.getDisplayElement().innerHTML =
+ "
Your browser window is too small to complete this experiment. " +
+ "Please maximize the size of your browser window. If your browser window is already maximized, " +
+ "you will not be able to complete this experiment.
" +
+ "
The minimum width is " +
+ mw +
+ "px. Your current width is " +
+ window.innerWidth +
+ "px.
" +
+ "
The minimum height is " +
+ mh +
+ "px. Your current height is " +
+ window.innerHeight +
+ "px.
";
+ // Wait for window size to increase
+ while (window.innerWidth < mw || window.innerHeight < mh) {
+ yield delay(100);
+ }
+ this.getDisplayElement().innerHTML = "";
+ }
+ }
+ // WEB AUDIO API
+ if (typeof exclusions.audio !== "undefined" && exclusions.audio) {
+ if (!window.hasOwnProperty("AudioContext") && !window.hasOwnProperty("webkitAudioContext")) {
+ this.getDisplayElement().innerHTML =
+ "
Your browser does not support the WebAudio API, which means that you will not " +
+ "be able to complete the experiment.
Browsers that support the WebAudio API include " +
+ "Chrome, Firefox, Safari, and Edge.
");
+ }
+ updateProgressBar() {
+ this.setProgressBar(this.getProgress().percent_complete / 100);
+ }
+ setProgressBar(proportion_complete) {
+ proportion_complete = Math.max(Math.min(1, proportion_complete), 0);
+ document.querySelector("#jspsych-progressbar-inner").style.width =
+ proportion_complete * 100 + "%";
+ this.progress_bar_amount = proportion_complete;
+ }
+ getProgressBarCompleted() {
+ return this.progress_bar_amount;
+ }
+ }
+
+ // temporary patch for Safari
+ if (typeof window !== "undefined" &&
+ window.hasOwnProperty("webkitAudioContext") &&
+ !window.hasOwnProperty("AudioContext")) {
+ // @ts-expect-error
+ window.AudioContext = webkitAudioContext;
+ }
+ // end patch
+ // The following function provides a uniform interface to initialize jsPsych, no matter whether a
+ // browser supports ES6 classes or not (and whether the ES6 build or the Babel build is used).
+ /**
+ * Creates a new JsPsych instance using the provided options.
+ *
+ * @param options The options to pass to the JsPsych constructor
+ * @returns A new JsPsych instance
+ */
+ function initJsPsych(options) {
+ const jsPsych = new JsPsych(options);
+ // Handle invocations of non-existent v6 methods with migration errors
+ const migrationMessages = {
+ init: "`jsPsych.init()` was replaced by `initJsPsych()` in jsPsych v7.",
+ ALL_KEYS: 'jsPsych.ALL_KEYS was replaced by the "ALL_KEYS" string in jsPsych v7.',
+ NO_KEYS: 'jsPsych.NO_KEYS was replaced by the "NO_KEYS" string in jsPsych v7.',
+ // Getter functions that were renamed
+ currentTimelineNodeID: "`currentTimelineNodeID()` was renamed to `getCurrentTimelineNodeID()` in jsPsych v7.",
+ progress: "`progress()` was renamed to `getProgress()` in jsPsych v7.",
+ startTime: "`startTime()` was renamed to `getStartTime()` in jsPsych v7.",
+ totalTime: "`totalTime()` was renamed to `getTotalTime()` in jsPsych v7.",
+ currentTrial: "`currentTrial()` was renamed to `getCurrentTrial()` in jsPsych v7.",
+ initSettings: "`initSettings()` was renamed to `getInitSettings()` in jsPsych v7.",
+ allTimelineVariables: "`allTimelineVariables()` was renamed to `getAllTimelineVariables()` in jsPsych v7.",
+ };
+ Object.defineProperties(jsPsych, Object.fromEntries(Object.entries(migrationMessages).map(([key, message]) => [
+ key,
+ {
+ get() {
+ throw new MigrationError(message);
+ },
+ },
+ ])));
+ return jsPsych;
+ }
+
+ exports.JsPsych = JsPsych;
+ exports.initJsPsych = initJsPsych;
+ exports.universalPluginParameters = universalPluginParameters;
+
+ Object.defineProperty(exports, '__esModule', { value: true });
+
+ return exports;
+
+})({});
+var initJsPsych = jsPsychModule.initJsPsych;
diff --git a/app/static/lib/jspsych-6.3.1/license.txt b/app/static/lib/jspsych-7.2.1/license.txt
similarity index 94%
rename from app/static/lib/jspsych-6.3.1/license.txt
rename to app/static/lib/jspsych-7.2.1/license.txt
index eae22335..cb651e0c 100644
--- a/app/static/lib/jspsych-6.3.1/license.txt
+++ b/app/static/lib/jspsych-7.2.1/license.txt
@@ -1,21 +1,21 @@
-The MIT License (MIT)
-
-Copyright (c) 2014-2018 Joshua R. de Leeuw
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+The MIT License (MIT)
+
+Copyright (c) 2014-2021 Joshua R. de Leeuw
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-animation.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-animation.js
new file mode 100644
index 00000000..f6bb1ff3
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-animation.js
@@ -0,0 +1,252 @@
+var jsPsychAnimation = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "animation",
+ parameters: {
+ /** Array containing the image(s) to be displayed. */
+ stimuli: {
+ type: jspsych.ParameterType.IMAGE,
+ pretty_name: "Stimuli",
+ default: undefined,
+ array: true,
+ },
+ /** Duration to display each image. */
+ frame_time: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Frame time",
+ default: 250,
+ },
+ /** Length of gap to be shown between each image. */
+ frame_isi: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Frame gap",
+ default: 0,
+ },
+ /** Number of times to show entire sequence */
+ sequence_reps: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Sequence repetitions",
+ default: 1,
+ },
+ /** Array containing the key(s) the subject is allowed to press to respond to the stimuli. */
+ choices: {
+ type: jspsych.ParameterType.KEYS,
+ pretty_name: "Choices",
+ default: "ALL_KEYS",
+ },
+ /** Any content here will be displayed below stimulus. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /**
+ * If true, the images will be drawn onto a canvas element (prevents blank screen between consecutive images in some browsers).
+ * If false, the image will be shown via an img element.
+ */
+ render_on_canvas: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Render on canvas",
+ default: true,
+ },
+ },
+ };
+ /**
+ * **animation**
+ *
+ * jsPsych plugin for showing animations and recording keyboard responses
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-animation/ animation plugin documentation on jspsych.org}
+ */
+ class AnimationPlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ var interval_time = trial.frame_time + trial.frame_isi;
+ var animate_frame = 0;
+ var reps = 0;
+ var startTime = performance.now();
+ var animation_sequence = [];
+ var responses = [];
+ var current_stim = "";
+ if (trial.render_on_canvas) {
+ // first clear the display element (because the render_on_canvas method appends to display_element instead of overwriting it with .innerHTML)
+ if (display_element.hasChildNodes()) {
+ // can't loop through child list because the list will be modified by .removeChild()
+ while (display_element.firstChild) {
+ display_element.removeChild(display_element.firstChild);
+ }
+ }
+ var canvas = document.createElement("canvas");
+ canvas.id = "jspsych-animation-image";
+ canvas.style.margin = "0";
+ canvas.style.padding = "0";
+ display_element.insertBefore(canvas, null);
+ var ctx = canvas.getContext("2d");
+ }
+ const endTrial = () => {
+ this.jsPsych.pluginAPI.cancelKeyboardResponse(response_listener);
+ var trial_data = {
+ animation_sequence: animation_sequence,
+ response: responses,
+ };
+ this.jsPsych.finishTrial(trial_data);
+ };
+ var animate_interval = setInterval(() => {
+ var showImage = true;
+ if (!trial.render_on_canvas) {
+ display_element.innerHTML = ""; // clear everything
+ }
+ animate_frame++;
+ if (animate_frame == trial.stimuli.length) {
+ animate_frame = 0;
+ reps++;
+ if (reps >= trial.sequence_reps) {
+ endTrial();
+ clearInterval(animate_interval);
+ showImage = false;
+ }
+ }
+ if (showImage) {
+ show_next_frame();
+ }
+ }, interval_time);
+ const show_next_frame = () => {
+ if (trial.render_on_canvas) {
+ display_element.querySelector("#jspsych-animation-image").style.visibility =
+ "visible";
+ var img = new Image();
+ img.src = trial.stimuli[animate_frame];
+ canvas.height = img.naturalHeight;
+ canvas.width = img.naturalWidth;
+ ctx.drawImage(img, 0, 0);
+ if (trial.prompt !== null && animate_frame == 0 && reps == 0) {
+ display_element.insertAdjacentHTML("beforeend", trial.prompt);
+ }
+ }
+ else {
+ // show image
+ display_element.innerHTML =
+ '';
+ if (trial.prompt !== null) {
+ display_element.innerHTML += trial.prompt;
+ }
+ }
+ current_stim = trial.stimuli[animate_frame];
+ // record when image was shown
+ animation_sequence.push({
+ stimulus: trial.stimuli[animate_frame],
+ time: Math.round(performance.now() - startTime),
+ });
+ if (trial.frame_isi > 0) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ display_element.querySelector("#jspsych-animation-image").style.visibility =
+ "hidden";
+ current_stim = "blank";
+ // record when blank image was shown
+ animation_sequence.push({
+ stimulus: "blank",
+ time: Math.round(performance.now() - startTime),
+ });
+ }, trial.frame_time);
+ }
+ };
+ var after_response = (info) => {
+ responses.push({
+ key_press: info.key,
+ rt: info.rt,
+ stimulus: current_stim,
+ });
+ // after a valid response, the stimulus will have the CSS class 'responded'
+ // which can be used to provide visual feedback that a response was recorded
+ display_element.querySelector("#jspsych-animation-image").className += " responded";
+ };
+ // hold the jspsych response listener object in memory
+ // so that we can turn off the response collection when
+ // the trial ends
+ var response_listener = this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: after_response,
+ valid_responses: trial.choices,
+ rt_method: "performance",
+ persist: true,
+ allow_held_key: false,
+ });
+ // show the first frame immediately
+ show_next_frame();
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const fake_animation_sequence = [];
+ const fake_responses = [];
+ let t = 0;
+ const check_if_fake_response_generated = () => {
+ return this.jsPsych.randomization.sampleWithReplacement([true, false], 1, [1, 10])[0];
+ };
+ for (let i = 0; i < trial.sequence_reps; i++) {
+ for (const frame of trial.stimuli) {
+ fake_animation_sequence.push({
+ stimulus: frame,
+ time: t,
+ });
+ if (check_if_fake_response_generated()) {
+ fake_responses.push({
+ key_press: this.jsPsych.pluginAPI.getValidKey(trial.choices),
+ rt: t + this.jsPsych.randomization.randomInt(0, trial.frame_time - 1),
+ current_stim: frame,
+ });
+ }
+ t += trial.frame_time;
+ if (trial.frame_isi > 0) {
+ fake_animation_sequence.push({
+ stimulus: "blank",
+ time: t,
+ });
+ if (check_if_fake_response_generated()) {
+ fake_responses.push({
+ key_press: this.jsPsych.pluginAPI.getValidKey(trial.choices),
+ rt: t + this.jsPsych.randomization.randomInt(0, trial.frame_isi - 1),
+ current_stim: "blank",
+ });
+ }
+ t += trial.frame_isi;
+ }
+ }
+ }
+ const default_data = {
+ animation_sequence: fake_animation_sequence,
+ response: fake_responses,
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ for (const response of data.response) {
+ this.jsPsych.pluginAPI.pressKey(response.key_press, response.rt);
+ }
+ }
+ }
+ AnimationPlugin.info = info;
+
+ return AnimationPlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-audio-button-response.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-audio-button-response.js
new file mode 100644
index 00000000..7cb9dcd5
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-audio-button-response.js
@@ -0,0 +1,304 @@
+var jsPsychAudioButtonResponse = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "audio-button-response",
+ parameters: {
+ /** The audio to be played. */
+ stimulus: {
+ type: jspsych.ParameterType.AUDIO,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /** Array containing the label(s) for the button(s). */
+ choices: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Choices",
+ default: undefined,
+ array: true,
+ },
+ /** The HTML for creating button. Can create own style. Use the "%choice%" string to indicate where the label from the choices parameter should be inserted. */
+ button_html: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Button HTML",
+ default: '',
+ array: true,
+ },
+ /** Any content here will be displayed below the stimulus. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /** The maximum duration to wait for a response. */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /** Vertical margin of button. */
+ margin_vertical: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Margin vertical",
+ default: "0px",
+ },
+ /** Horizontal margin of button. */
+ margin_horizontal: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Margin horizontal",
+ default: "8px",
+ },
+ /** If true, the trial will end when user makes a response. */
+ response_ends_trial: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response ends trial",
+ default: true,
+ },
+ /** If true, then the trial will end as soon as the audio file finishes playing. */
+ trial_ends_after_audio: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Trial ends after audio",
+ default: false,
+ },
+ /**
+ * If true, then responses are allowed while the audio is playing.
+ * If false, then the audio must finish playing before a response is accepted.
+ */
+ response_allowed_while_playing: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response allowed while playing",
+ default: true,
+ },
+ },
+ };
+ /**
+ * **audio-button-response**
+ *
+ * jsPsych plugin for playing an audio file and getting a button response
+ *
+ * @author Kristin Diep
+ * @see {@link https://www.jspsych.org/plugins/jspsych-audio-button-response/ audio-button-response plugin documentation on jspsych.org}
+ */
+ class AudioButtonResponsePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial, on_load) {
+ // hold the .resolve() function from the Promise that ends the trial
+ let trial_complete;
+ // setup stimulus
+ var context = this.jsPsych.pluginAPI.audioContext();
+ // store response
+ var response = {
+ rt: null,
+ button: null,
+ };
+ // record webaudio context start time
+ var startTime;
+ // load audio file
+ this.jsPsych.pluginAPI
+ .getAudioBuffer(trial.stimulus)
+ .then((buffer) => {
+ if (context !== null) {
+ this.audio = context.createBufferSource();
+ this.audio.buffer = buffer;
+ this.audio.connect(context.destination);
+ }
+ else {
+ this.audio = buffer;
+ this.audio.currentTime = 0;
+ }
+ setupTrial();
+ })
+ .catch((err) => {
+ console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
+ console.error(err);
+ });
+ const setupTrial = () => {
+ // set up end event if trial needs it
+ if (trial.trial_ends_after_audio) {
+ this.audio.addEventListener("ended", end_trial);
+ }
+ // enable buttons after audio ends if necessary
+ if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {
+ this.audio.addEventListener("ended", enable_buttons);
+ }
+ //display buttons
+ var buttons = [];
+ if (Array.isArray(trial.button_html)) {
+ if (trial.button_html.length == trial.choices.length) {
+ buttons = trial.button_html;
+ }
+ else {
+ console.error("Error in audio-button-response plugin. The length of the button_html array does not equal the length of the choices array");
+ }
+ }
+ else {
+ for (var i = 0; i < trial.choices.length; i++) {
+ buttons.push(trial.button_html);
+ }
+ }
+ var html = '
';
+ for (var i = 0; i < trial.choices.length; i++) {
+ var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
+ html +=
+ '
' +
+ str +
+ "
";
+ }
+ html += "
";
+ //show prompt if there is one
+ if (trial.prompt !== null) {
+ html += trial.prompt;
+ }
+ display_element.innerHTML = html;
+ if (trial.response_allowed_while_playing) {
+ enable_buttons();
+ }
+ else {
+ disable_buttons();
+ }
+ // start time
+ startTime = performance.now();
+ // start audio
+ if (context !== null) {
+ startTime = context.currentTime;
+ this.audio.start(startTime);
+ }
+ else {
+ this.audio.play();
+ }
+ // end trial if time limit is set
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ end_trial();
+ }, trial.trial_duration);
+ }
+ on_load();
+ };
+ // function to handle responses by the subject
+ function after_response(choice) {
+ // measure rt
+ var endTime = performance.now();
+ var rt = Math.round(endTime - startTime);
+ if (context !== null) {
+ endTime = context.currentTime;
+ rt = Math.round((endTime - startTime) * 1000);
+ }
+ response.button = parseInt(choice);
+ response.rt = rt;
+ // disable all the buttons after a response
+ disable_buttons();
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ }
+ // function to end trial when it is time
+ const end_trial = () => {
+ // kill any remaining setTimeout handlers
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // stop the audio file if it is playing
+ // remove end event listeners if they exist
+ if (context !== null) {
+ this.audio.stop();
+ }
+ else {
+ this.audio.pause();
+ }
+ this.audio.removeEventListener("ended", end_trial);
+ this.audio.removeEventListener("ended", enable_buttons);
+ // gather the data to store for the trial
+ var trial_data = {
+ rt: response.rt,
+ stimulus: trial.stimulus,
+ response: response.button,
+ };
+ // clear the display
+ display_element.innerHTML = "";
+ // move on to the next trial
+ this.jsPsych.finishTrial(trial_data);
+ trial_complete();
+ };
+ function button_response(e) {
+ var choice = e.currentTarget.getAttribute("data-choice"); // don't use dataset for jsdom compatibility
+ after_response(choice);
+ }
+ function disable_buttons() {
+ var btns = document.querySelectorAll(".jspsych-audio-button-response-button");
+ for (var i = 0; i < btns.length; i++) {
+ var btn_el = btns[i].querySelector("button");
+ if (btn_el) {
+ btn_el.disabled = true;
+ }
+ btns[i].removeEventListener("click", button_response);
+ }
+ }
+ function enable_buttons() {
+ var btns = document.querySelectorAll(".jspsych-audio-button-response-button");
+ for (var i = 0; i < btns.length; i++) {
+ var btn_el = btns[i].querySelector("button");
+ if (btn_el) {
+ btn_el.disabled = false;
+ }
+ btns[i].addEventListener("click", button_response);
+ }
+ }
+ return new Promise((resolve) => {
+ trial_complete = resolve;
+ });
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ stimulus: trial.stimulus,
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ response: this.jsPsych.randomization.randomInt(0, trial.choices.length - 1),
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ const respond = () => {
+ if (data.rt !== null) {
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector(`div[data-choice="${data.response}"] button`), data.rt);
+ }
+ };
+ this.trial(display_element, trial, () => {
+ load_callback();
+ if (!trial.response_allowed_while_playing) {
+ this.audio.addEventListener("ended", respond);
+ }
+ else {
+ respond();
+ }
+ });
+ }
+ }
+ AudioButtonResponsePlugin.info = info;
+
+ return AudioButtonResponsePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-audio-keyboard-response.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-audio-keyboard-response.js
new file mode 100644
index 00000000..842ed15a
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-audio-keyboard-response.js
@@ -0,0 +1,237 @@
+var jsPsychAudioKeyboardResponse = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "audio-keyboard-response",
+ parameters: {
+ /** The audio file to be played. */
+ stimulus: {
+ type: jspsych.ParameterType.AUDIO,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
+ choices: {
+ type: jspsych.ParameterType.KEYS,
+ pretty_name: "Choices",
+ default: "ALL_KEYS",
+ },
+ /** Any content here will be displayed below the stimulus. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /** The maximum duration to wait for a response. */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /** If true, the trial will end when user makes a response. */
+ response_ends_trial: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response ends trial",
+ default: true,
+ },
+ /** If true, then the trial will end as soon as the audio file finishes playing. */
+ trial_ends_after_audio: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Trial ends after audio",
+ default: false,
+ },
+ /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */
+ response_allowed_while_playing: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response allowed while playing",
+ default: true,
+ },
+ },
+ };
+ /**
+ * **audio-keyboard-response**
+ *
+ * jsPsych plugin for playing an audio file and getting a keyboard response
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}
+ */
+ class AudioKeyboardResponsePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial, on_load) {
+ // hold the .resolve() function from the Promise that ends the trial
+ let trial_complete;
+ // setup stimulus
+ var context = this.jsPsych.pluginAPI.audioContext();
+ // store response
+ var response = {
+ rt: null,
+ key: null,
+ };
+ // record webaudio context start time
+ var startTime;
+ // load audio file
+ this.jsPsych.pluginAPI
+ .getAudioBuffer(trial.stimulus)
+ .then((buffer) => {
+ if (context !== null) {
+ this.audio = context.createBufferSource();
+ this.audio.buffer = buffer;
+ this.audio.connect(context.destination);
+ }
+ else {
+ this.audio = buffer;
+ this.audio.currentTime = 0;
+ }
+ setupTrial();
+ })
+ .catch((err) => {
+ console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
+ console.error(err);
+ });
+ const setupTrial = () => {
+ // set up end event if trial needs it
+ if (trial.trial_ends_after_audio) {
+ this.audio.addEventListener("ended", end_trial);
+ }
+ // show prompt if there is one
+ if (trial.prompt !== null) {
+ display_element.innerHTML = trial.prompt;
+ }
+ // start audio
+ if (context !== null) {
+ startTime = context.currentTime;
+ this.audio.start(startTime);
+ }
+ else {
+ this.audio.play();
+ }
+ // start keyboard listener when trial starts or sound ends
+ if (trial.response_allowed_while_playing) {
+ setup_keyboard_listener();
+ }
+ else if (!trial.trial_ends_after_audio) {
+ this.audio.addEventListener("ended", setup_keyboard_listener);
+ }
+ // end trial if time limit is set
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ end_trial();
+ }, trial.trial_duration);
+ }
+ on_load();
+ };
+ // function to end trial when it is time
+ const end_trial = () => {
+ // kill any remaining setTimeout handlers
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // stop the audio file if it is playing
+ // remove end event listeners if they exist
+ if (context !== null) {
+ this.audio.stop();
+ }
+ else {
+ this.audio.pause();
+ }
+ this.audio.removeEventListener("ended", end_trial);
+ this.audio.removeEventListener("ended", setup_keyboard_listener);
+ // kill keyboard listeners
+ this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
+ // gather the data to store for the trial
+ var trial_data = {
+ rt: response.rt,
+ stimulus: trial.stimulus,
+ response: response.key,
+ };
+ // clear the display
+ display_element.innerHTML = "";
+ // move on to the next trial
+ this.jsPsych.finishTrial(trial_data);
+ trial_complete();
+ };
+ // function to handle responses by the subject
+ function after_response(info) {
+ // only record the first response
+ if (response.key == null) {
+ response = info;
+ }
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ }
+ const setup_keyboard_listener = () => {
+ // start the response listener
+ if (context !== null) {
+ this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: after_response,
+ valid_responses: trial.choices,
+ rt_method: "audio",
+ persist: false,
+ allow_held_key: false,
+ audio_context: context,
+ audio_context_start_time: startTime,
+ });
+ }
+ else {
+ this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: after_response,
+ valid_responses: trial.choices,
+ rt_method: "performance",
+ persist: false,
+ allow_held_key: false,
+ });
+ }
+ };
+ return new Promise((resolve) => {
+ trial_complete = resolve;
+ });
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ const respond = () => {
+ if (data.rt !== null) {
+ this.jsPsych.pluginAPI.pressKey(data.response, data.rt);
+ }
+ };
+ this.trial(display_element, trial, () => {
+ load_callback();
+ if (!trial.response_allowed_while_playing) {
+ this.audio.addEventListener("ended", respond);
+ }
+ else {
+ respond();
+ }
+ });
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ stimulus: trial.stimulus,
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ response: this.jsPsych.pluginAPI.getValidKey(trial.choices),
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ }
+ AudioKeyboardResponsePlugin.info = info;
+
+ return AudioKeyboardResponsePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-audio-slider-response.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-audio-slider-response.js
new file mode 100644
index 00000000..554e351c
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-audio-slider-response.js
@@ -0,0 +1,355 @@
+var jsPsychAudioSliderResponse = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "audio-slider-response",
+ parameters: {
+ /** The audio file to be played. */
+ stimulus: {
+ type: jspsych.ParameterType.AUDIO,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /** Sets the minimum value of the slider. */
+ min: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Min slider",
+ default: 0,
+ },
+ /** Sets the maximum value of the slider */
+ max: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Max slider",
+ default: 100,
+ },
+ /** Sets the starting value of the slider */
+ slider_start: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Slider starting value",
+ default: 50,
+ },
+ /** Sets the step of the slider */
+ step: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Step",
+ default: 1,
+ },
+ /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */
+ labels: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Labels",
+ default: [],
+ array: true,
+ },
+ /** Width of the slider in pixels. */
+ slider_width: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Slider width",
+ default: null,
+ },
+ /** Label of the button to advance. */
+ button_label: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Button label",
+ default: "Continue",
+ array: false,
+ },
+ /** If true, the participant will have to move the slider before continuing. */
+ require_movement: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Require movement",
+ default: false,
+ },
+ /** Any content here will be displayed below the slider. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /** How long to show the trial. */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /** If true, trial will end when user makes a response. */
+ response_ends_trial: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response ends trial",
+ default: true,
+ },
+ /** If true, then the trial will end as soon as the audio file finishes playing. */
+ trial_ends_after_audio: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Trial ends after audio",
+ default: false,
+ },
+ /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */
+ response_allowed_while_playing: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response allowed while playing",
+ default: true,
+ },
+ },
+ };
+ /**
+ * **audio-slider-response**
+ *
+ * jsPsych plugin for playing audio and getting a slider response
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}
+ */
+ class AudioSliderResponsePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial, on_load) {
+ // hold the .resolve() function from the Promise that ends the trial
+ let trial_complete;
+ // half of the thumb width value from jspsych.css, used to adjust the label positions
+ var half_thumb_width = 7.5;
+ // setup stimulus
+ var context = this.jsPsych.pluginAPI.audioContext();
+ // record webaudio context start time
+ var startTime;
+ // for storing data related to response
+ var response;
+ // load audio file
+ this.jsPsych.pluginAPI
+ .getAudioBuffer(trial.stimulus)
+ .then((buffer) => {
+ if (context !== null) {
+ this.audio = context.createBufferSource();
+ this.audio.buffer = buffer;
+ this.audio.connect(context.destination);
+ }
+ else {
+ this.audio = buffer;
+ this.audio.currentTime = 0;
+ }
+ setupTrial();
+ })
+ .catch((err) => {
+ console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
+ console.error(err);
+ });
+ const setupTrial = () => {
+ // set up end event if trial needs it
+ if (trial.trial_ends_after_audio) {
+ this.audio.addEventListener("ended", end_trial);
+ }
+ // enable slider after audio ends if necessary
+ if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {
+ this.audio.addEventListener("ended", enable_slider);
+ }
+ var html = '
';
+ html +=
+ '
';
+ html +=
+ '
";
+ for (var j = 0; j < trial.labels.length; j++) {
+ var label_width_perc = 100 / (trial.labels.length - 1);
+ var percent_of_range = j * (100 / (trial.labels.length - 1));
+ var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;
+ var offset = (percent_dist_from_center * half_thumb_width) / 100;
+ html +=
+ '
';
+ html += '' + trial.labels[j] + "";
+ html += "
";
+ }
+ html += "
";
+ html += "
";
+ html += "
";
+ if (trial.prompt !== null) {
+ html += trial.prompt;
+ }
+ // add submit button
+ var next_disabled_attribute = "";
+ if (trial.require_movement || !trial.response_allowed_while_playing) {
+ next_disabled_attribute = "disabled";
+ }
+ html +=
+ '";
+ display_element.innerHTML = html;
+ response = {
+ rt: null,
+ response: null,
+ };
+ if (!trial.response_allowed_while_playing) {
+ display_element.querySelector("#jspsych-audio-slider-response-response").disabled = true;
+ display_element.querySelector("#jspsych-audio-slider-response-next").disabled = true;
+ }
+ if (trial.require_movement) {
+ const enable_button = () => {
+ display_element.querySelector("#jspsych-audio-slider-response-next").disabled = false;
+ };
+ display_element
+ .querySelector("#jspsych-audio-slider-response-response")
+ .addEventListener("mousedown", enable_button);
+ display_element
+ .querySelector("#jspsych-audio-slider-response-response")
+ .addEventListener("touchstart", enable_button);
+ display_element
+ .querySelector("#jspsych-audio-slider-response-response")
+ .addEventListener("change", enable_button);
+ }
+ display_element
+ .querySelector("#jspsych-audio-slider-response-next")
+ .addEventListener("click", () => {
+ // measure response time
+ var endTime = performance.now();
+ var rt = Math.round(endTime - startTime);
+ if (context !== null) {
+ endTime = context.currentTime;
+ rt = Math.round((endTime - startTime) * 1000);
+ }
+ response.rt = rt;
+ response.response = display_element.querySelector("#jspsych-audio-slider-response-response").valueAsNumber;
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ else {
+ display_element.querySelector("#jspsych-audio-slider-response-next").disabled = true;
+ }
+ });
+ startTime = performance.now();
+ // start audio
+ if (context !== null) {
+ startTime = context.currentTime;
+ this.audio.start(startTime);
+ }
+ else {
+ this.audio.play();
+ }
+ // end trial if trial_duration is set
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ end_trial();
+ }, trial.trial_duration);
+ }
+ on_load();
+ };
+ // function to enable slider after audio ends
+ function enable_slider() {
+ document.querySelector("#jspsych-audio-slider-response-response").disabled =
+ false;
+ if (!trial.require_movement) {
+ document.querySelector("#jspsych-audio-slider-response-next").disabled =
+ false;
+ }
+ }
+ const end_trial = () => {
+ // kill any remaining setTimeout handlers
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // stop the audio file if it is playing
+ // remove end event listeners if they exist
+ if (context !== null) {
+ this.audio.stop();
+ }
+ else {
+ this.audio.pause();
+ }
+ this.audio.removeEventListener("ended", end_trial);
+ this.audio.removeEventListener("ended", enable_slider);
+ // save data
+ var trialdata = {
+ rt: response.rt,
+ stimulus: trial.stimulus,
+ slider_start: trial.slider_start,
+ response: response.response,
+ };
+ display_element.innerHTML = "";
+ // next trial
+ this.jsPsych.finishTrial(trialdata);
+ trial_complete();
+ };
+ return new Promise((resolve) => {
+ trial_complete = resolve;
+ });
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ stimulus: trial.stimulus,
+ slider_start: trial.slider_start,
+ response: this.jsPsych.randomization.randomInt(trial.min, trial.max),
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ const respond = () => {
+ if (data.rt !== null) {
+ const el = display_element.querySelector("input[type='range']");
+ setTimeout(() => {
+ this.jsPsych.pluginAPI.clickTarget(el);
+ el.valueAsNumber = data.response;
+ }, data.rt / 2);
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("button"), data.rt);
+ }
+ };
+ this.trial(display_element, trial, () => {
+ load_callback();
+ if (!trial.response_allowed_while_playing) {
+ this.audio.addEventListener("ended", respond);
+ }
+ else {
+ respond();
+ }
+ });
+ }
+ }
+ AudioSliderResponsePlugin.info = info;
+
+ return AudioSliderResponsePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-browser-check.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-browser-check.js
new file mode 100644
index 00000000..a1846372
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-browser-check.js
@@ -0,0 +1,648 @@
+var jsPsychBrowserCheck = (function (jspsych) {
+ 'use strict';
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+
+ function __awaiter(thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+ }
+
+ var __spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
+ if (ar || !(i in from)) {
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
+ ar[i] = from[i];
+ }
+ }
+ return to.concat(ar || Array.prototype.slice.call(from));
+ };
+ var BrowserInfo = /** @class */ (function () {
+ function BrowserInfo(name, version, os) {
+ this.name = name;
+ this.version = version;
+ this.os = os;
+ this.type = 'browser';
+ }
+ return BrowserInfo;
+ }());
+ var NodeInfo = /** @class */ (function () {
+ function NodeInfo(version) {
+ this.version = version;
+ this.type = 'node';
+ this.name = 'node';
+ this.os = process.platform;
+ }
+ return NodeInfo;
+ }());
+ var SearchBotDeviceInfo = /** @class */ (function () {
+ function SearchBotDeviceInfo(name, version, os, bot) {
+ this.name = name;
+ this.version = version;
+ this.os = os;
+ this.bot = bot;
+ this.type = 'bot-device';
+ }
+ return SearchBotDeviceInfo;
+ }());
+ var BotInfo = /** @class */ (function () {
+ function BotInfo() {
+ this.type = 'bot';
+ this.bot = true; // NOTE: deprecated test name instead
+ this.name = 'bot';
+ this.version = null;
+ this.os = null;
+ }
+ return BotInfo;
+ }());
+ var ReactNativeInfo = /** @class */ (function () {
+ function ReactNativeInfo() {
+ this.type = 'react-native';
+ this.name = 'react-native';
+ this.version = null;
+ this.os = null;
+ }
+ return ReactNativeInfo;
+ }());
+ // tslint:disable-next-line:max-line-length
+ var SEARCHBOX_UA_REGEX = /alexa|bot|crawl(er|ing)|facebookexternalhit|feedburner|google web preview|nagios|postrank|pingdom|slurp|spider|yahoo!|yandex/;
+ var SEARCHBOT_OS_REGEX = /(nuhk|curl|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask\ Jeeves\/Teoma|ia_archiver)/;
+ var REQUIRED_VERSION_PARTS = 3;
+ var userAgentRules = [
+ ['aol', /AOLShield\/([0-9\._]+)/],
+ ['edge', /Edge\/([0-9\._]+)/],
+ ['edge-ios', /EdgiOS\/([0-9\._]+)/],
+ ['yandexbrowser', /YaBrowser\/([0-9\._]+)/],
+ ['kakaotalk', /KAKAOTALK\s([0-9\.]+)/],
+ ['samsung', /SamsungBrowser\/([0-9\.]+)/],
+ ['silk', /\bSilk\/([0-9._-]+)\b/],
+ ['miui', /MiuiBrowser\/([0-9\.]+)$/],
+ ['beaker', /BeakerBrowser\/([0-9\.]+)/],
+ ['edge-chromium', /EdgA?\/([0-9\.]+)/],
+ [
+ 'chromium-webview',
+ /(?!Chrom.*OPR)wv\).*Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/,
+ ],
+ ['chrome', /(?!Chrom.*OPR)Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/],
+ ['phantomjs', /PhantomJS\/([0-9\.]+)(:?\s|$)/],
+ ['crios', /CriOS\/([0-9\.]+)(:?\s|$)/],
+ ['firefox', /Firefox\/([0-9\.]+)(?:\s|$)/],
+ ['fxios', /FxiOS\/([0-9\.]+)/],
+ ['opera-mini', /Opera Mini.*Version\/([0-9\.]+)/],
+ ['opera', /Opera\/([0-9\.]+)(?:\s|$)/],
+ ['opera', /OPR\/([0-9\.]+)(:?\s|$)/],
+ ['ie', /Trident\/7\.0.*rv\:([0-9\.]+).*\).*Gecko$/],
+ ['ie', /MSIE\s([0-9\.]+);.*Trident\/[4-7].0/],
+ ['ie', /MSIE\s(7\.0)/],
+ ['bb10', /BB10;\sTouch.*Version\/([0-9\.]+)/],
+ ['android', /Android\s([0-9\.]+)/],
+ ['ios', /Version\/([0-9\._]+).*Mobile.*Safari.*/],
+ ['safari', /Version\/([0-9\._]+).*Safari/],
+ ['facebook', /FB[AS]V\/([0-9\.]+)/],
+ ['instagram', /Instagram\s([0-9\.]+)/],
+ ['ios-webview', /AppleWebKit\/([0-9\.]+).*Mobile/],
+ ['ios-webview', /AppleWebKit\/([0-9\.]+).*Gecko\)$/],
+ ['curl', /^curl\/([0-9\.]+)$/],
+ ['searchbot', SEARCHBOX_UA_REGEX],
+ ];
+ var operatingSystemRules = [
+ ['iOS', /iP(hone|od|ad)/],
+ ['Android OS', /Android/],
+ ['BlackBerry OS', /BlackBerry|BB10/],
+ ['Windows Mobile', /IEMobile/],
+ ['Amazon OS', /Kindle/],
+ ['Windows 3.11', /Win16/],
+ ['Windows 95', /(Windows 95)|(Win95)|(Windows_95)/],
+ ['Windows 98', /(Windows 98)|(Win98)/],
+ ['Windows 2000', /(Windows NT 5.0)|(Windows 2000)/],
+ ['Windows XP', /(Windows NT 5.1)|(Windows XP)/],
+ ['Windows Server 2003', /(Windows NT 5.2)/],
+ ['Windows Vista', /(Windows NT 6.0)/],
+ ['Windows 7', /(Windows NT 6.1)/],
+ ['Windows 8', /(Windows NT 6.2)/],
+ ['Windows 8.1', /(Windows NT 6.3)/],
+ ['Windows 10', /(Windows NT 10.0)/],
+ ['Windows ME', /Windows ME/],
+ ['Open BSD', /OpenBSD/],
+ ['Sun OS', /SunOS/],
+ ['Chrome OS', /CrOS/],
+ ['Linux', /(Linux)|(X11)/],
+ ['Mac OS', /(Mac_PowerPC)|(Macintosh)/],
+ ['QNX', /QNX/],
+ ['BeOS', /BeOS/],
+ ['OS/2', /OS\/2/],
+ ];
+ function detect(userAgent) {
+ if (!!userAgent) {
+ return parseUserAgent(userAgent);
+ }
+ if (typeof document === 'undefined' &&
+ typeof navigator !== 'undefined' &&
+ navigator.product === 'ReactNative') {
+ return new ReactNativeInfo();
+ }
+ if (typeof navigator !== 'undefined') {
+ return parseUserAgent(navigator.userAgent);
+ }
+ return getNodeVersion();
+ }
+ function matchUserAgent(ua) {
+ // opted for using reduce here rather than Array#first with a regex.test call
+ // this is primarily because using the reduce we only perform the regex
+ // execution once rather than once for the test and for the exec again below
+ // probably something that needs to be benchmarked though
+ return (ua !== '' &&
+ userAgentRules.reduce(function (matched, _a) {
+ var browser = _a[0], regex = _a[1];
+ if (matched) {
+ return matched;
+ }
+ var uaMatch = regex.exec(ua);
+ return !!uaMatch && [browser, uaMatch];
+ }, false));
+ }
+ function parseUserAgent(ua) {
+ var matchedRule = matchUserAgent(ua);
+ if (!matchedRule) {
+ return null;
+ }
+ var name = matchedRule[0], match = matchedRule[1];
+ if (name === 'searchbot') {
+ return new BotInfo();
+ }
+ // Do not use RegExp for split operation as some browser do not support it (See: http://blog.stevenlevithan.com/archives/cross-browser-split)
+ var versionParts = match[1] && match[1].split('.').join('_').split('_').slice(0, 3);
+ if (versionParts) {
+ if (versionParts.length < REQUIRED_VERSION_PARTS) {
+ versionParts = __spreadArray(__spreadArray([], versionParts, true), createVersionParts(REQUIRED_VERSION_PARTS - versionParts.length), true);
+ }
+ }
+ else {
+ versionParts = [];
+ }
+ var version = versionParts.join('.');
+ var os = detectOS(ua);
+ var searchBotMatch = SEARCHBOT_OS_REGEX.exec(ua);
+ if (searchBotMatch && searchBotMatch[1]) {
+ return new SearchBotDeviceInfo(name, version, os, searchBotMatch[1]);
+ }
+ return new BrowserInfo(name, version, os);
+ }
+ function detectOS(ua) {
+ for (var ii = 0, count = operatingSystemRules.length; ii < count; ii++) {
+ var _a = operatingSystemRules[ii], os = _a[0], regex = _a[1];
+ var match = regex.exec(ua);
+ if (match) {
+ return os;
+ }
+ }
+ return null;
+ }
+ function getNodeVersion() {
+ var isNode = typeof process !== 'undefined' && process.version;
+ return isNode ? new NodeInfo(process.version.slice(1)) : null;
+ }
+ function createVersionParts(count) {
+ var output = [];
+ for (var ii = 0; ii < count; ii++) {
+ output.push('0');
+ }
+ return output;
+ }
+
+ const info = {
+ name: "browser-check",
+ parameters: {
+ /**
+ * List of features to check and record in the data
+ */
+ features: {
+ type: jspsych.ParameterType.STRING,
+ array: true,
+ default: [
+ "width",
+ "height",
+ "webaudio",
+ "browser",
+ "browser_version",
+ "mobile",
+ "os",
+ "fullscreen",
+ "vsync_rate",
+ "webcam",
+ "microphone",
+ ],
+ },
+ /**
+ * Any features listed here will be skipped, even if they appear in `features`. Useful for
+ * when you want to run most of the defaults.
+ */
+ skip_features: {
+ type: jspsych.ParameterType.STRING,
+ array: true,
+ default: [],
+ },
+ /**
+ * The number of animation frames to sample when calculating vsync_rate.
+ */
+ vsync_frame_count: {
+ type: jspsych.ParameterType.INT,
+ default: 60,
+ },
+ /**
+ * If `true`, show a message when window size is too small to allow the user
+ * to adjust if their screen allows for it.
+ */
+ allow_window_resize: {
+ type: jspsych.ParameterType.BOOL,
+ default: true,
+ },
+ /**
+ * When `allow_window_resize` is `true`, this is the minimum width (px) that the window
+ * needs to be before the experiment will continue.
+ */
+ minimum_width: {
+ type: jspsych.ParameterType.INT,
+ default: 0,
+ },
+ /**
+ * When `allow_window_resize` is `true`, this is the minimum height (px) that the window
+ * needs to be before the experiment will continue.
+ */
+ minimum_height: {
+ type: jspsych.ParameterType.INT,
+ default: 0,
+ },
+ /**
+ * Message to display during interactive window resizing.
+ */
+ window_resize_message: {
+ type: jspsych.ParameterType.HTML_STRING,
+ default: `
Your browser window is too small to complete this experiment. Please maximize the size of your browser window.
+ If your browser window is already maximized, you will not be able to complete this experiment.
+
The minimum window width is px.
+
Your current window width is px.
+
The minimum window height is px.
+
Your current window height is px.
`,
+ },
+ /**
+ * During the interactive resize, a button with this text will be displayed below the
+ * `window_resize_message` for the participant to click if the window cannot meet the
+ * minimum size needed. When the button is clicked, the experiment will end and
+ * `exclusion_message` will be displayed.
+ */
+ resize_fail_button_text: {
+ type: jspsych.ParameterType.STRING,
+ default: "I cannot make the window any larger",
+ },
+ /**
+ * A function that evaluates to `true` if the browser meets all of the inclusion criteria
+ * for the experiment, and `false` otherwise. The first argument to the function will be
+ * an object containing key value pairs with the measured features of the browser. The
+ * keys will be the same as those listed in `features`.
+ */
+ inclusion_function: {
+ type: jspsych.ParameterType.FUNCTION,
+ default: () => {
+ return true;
+ },
+ },
+ /**
+ * The message to display if `inclusion_function` returns `false`
+ */
+ exclusion_message: {
+ type: jspsych.ParameterType.FUNCTION,
+ default: () => {
+ return `
Your browser does not meet the requirements to participate in this experiment.
`;
+ },
+ },
+ },
+ };
+ /**
+ * **browser-check**
+ *
+ * jsPsych plugin for checking features of the browser and validating against a set of inclusion criteria.
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-browser-check/ browser-check plugin documentation on jspsych.org}
+ */
+ class BrowserCheckPlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ this.end_flag = false;
+ }
+ delay(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+ trial(display_element, trial) {
+ this.t = trial;
+ const featureCheckFunctionsMap = this.create_feature_fn_map(trial);
+ const features_to_check = trial.features.filter((x) => !trial.skip_features.includes(x));
+ this.run_trial(featureCheckFunctionsMap, features_to_check);
+ }
+ run_trial(fnMap, features) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const feature_data = yield this.measure_features(fnMap, features);
+ const include = yield this.inclusion_check(this.t.inclusion_function, feature_data);
+ if (include) {
+ this.end_trial(feature_data);
+ }
+ else {
+ this.end_experiment(feature_data);
+ }
+ });
+ }
+ create_feature_fn_map(trial) {
+ return new Map(Object.entries({
+ width: () => {
+ return window.innerWidth;
+ },
+ height: () => {
+ return window.innerHeight;
+ },
+ webaudio: () => {
+ if (window.AudioContext ||
+ // @ts-ignore because prefixed not in document type
+ window.webkitAudioContext ||
+ // @ts-ignore because prefixed not in document type
+ window.mozAudioContext ||
+ // @ts-ignore because prefixed not in document type
+ window.oAudioContext ||
+ // @ts-ignore because prefixed not in document type
+ window.msAudioContext) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ },
+ browser: () => {
+ return detect().name;
+ },
+ browser_version: () => {
+ return detect().version;
+ },
+ mobile: () => {
+ return /Mobi/i.test(window.navigator.userAgent);
+ },
+ os: () => {
+ return detect().os;
+ },
+ fullscreen: () => {
+ if (document.exitFullscreen ||
+ // @ts-ignore because prefixed not in document type
+ document.webkitExitFullscreen ||
+ // @ts-ignore because prefixed not in document type
+ document.msExitFullscreen) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ },
+ vsync_rate: () => {
+ return new Promise((resolve) => {
+ let t0 = performance.now();
+ let deltas = [];
+ let framesToRun = trial.vsync_frame_count;
+ const finish = () => {
+ let sum = 0;
+ for (const v of deltas) {
+ sum += v;
+ }
+ const frame_rate = 1000.0 / (sum / deltas.length);
+ const frame_rate_two_sig_dig = Math.round(frame_rate * 100) / 100;
+ resolve(frame_rate_two_sig_dig);
+ };
+ const nextFrame = () => {
+ let t1 = performance.now();
+ deltas.push(t1 - t0);
+ t0 = t1;
+ framesToRun--;
+ if (framesToRun > 0) {
+ requestAnimationFrame(nextFrame);
+ }
+ else {
+ finish();
+ }
+ };
+ const start = () => {
+ t0 = performance.now();
+ requestAnimationFrame(nextFrame);
+ };
+ requestAnimationFrame(start);
+ });
+ },
+ webcam: () => {
+ return new Promise((resolve, reject) => {
+ if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
+ resolve(false);
+ }
+ navigator.mediaDevices.enumerateDevices().then((devices) => {
+ const webcams = devices.filter((d) => {
+ return d.kind == "videoinput";
+ });
+ if (webcams.length > 0) {
+ resolve(true);
+ }
+ else {
+ resolve(false);
+ }
+ });
+ });
+ },
+ microphone: () => {
+ return new Promise((resolve, reject) => {
+ if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
+ resolve(false);
+ }
+ navigator.mediaDevices.enumerateDevices().then((devices) => {
+ const microphones = devices.filter((d) => {
+ return d.kind == "audioinput";
+ });
+ if (microphones.length > 0) {
+ resolve(true);
+ }
+ else {
+ resolve(false);
+ }
+ });
+ });
+ },
+ }));
+ }
+ measure_features(fnMap, features_to_check) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const feature_data = new Map();
+ const feature_checks = [];
+ for (const feature of features_to_check) {
+ // this allows for feature check functions to be sync or async
+ feature_checks.push(Promise.resolve(fnMap.get(feature)()));
+ }
+ const results = yield Promise.allSettled(feature_checks);
+ for (let i = 0; i < features_to_check.length; i++) {
+ if (results[i].status === "fulfilled") {
+ // @ts-expect-error because .value isn't recognized for some reason
+ feature_data.set(features_to_check[i], results[i].value);
+ }
+ else {
+ feature_data.set(features_to_check[i], null);
+ }
+ }
+ return feature_data;
+ });
+ }
+ inclusion_check(fn, data) {
+ return __awaiter(this, void 0, void 0, function* () {
+ yield this.check_allow_resize(data);
+ // screen was too small
+ if (this.end_flag) {
+ return false;
+ }
+ return fn(Object.fromEntries(data));
+ });
+ }
+ check_allow_resize(feature_data) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const display_element = this.jsPsych.getDisplayElement();
+ const w = feature_data.get("width");
+ const h = feature_data.get("height");
+ if (this.t.allow_window_resize &&
+ (w || h) &&
+ (this.t.minimum_width > 0 || this.t.minimum_height > 0)) {
+ display_element.innerHTML =
+ this.t.window_resize_message +
+ ``;
+ display_element.querySelector("#browser-check-max-size-btn").addEventListener("click", () => {
+ display_element.innerHTML = "";
+ this.end_flag = true;
+ });
+ const min_width_el = display_element.querySelector("#browser-check-min-width");
+ const min_height_el = display_element.querySelector("#browser-check-min-height");
+ const actual_height_el = display_element.querySelector("#browser-check-actual-height");
+ const actual_width_el = display_element.querySelector("#browser-check-actual-width");
+ while (!this.end_flag &&
+ (window.innerWidth < this.t.minimum_width || window.innerHeight < this.t.minimum_height)) {
+ if (min_width_el) {
+ min_width_el.innerHTML = this.t.minimum_width.toString();
+ }
+ if (min_height_el) {
+ min_height_el.innerHTML = this.t.minimum_height.toString();
+ }
+ if (actual_height_el) {
+ actual_height_el.innerHTML = window.innerHeight.toString();
+ }
+ if (actual_width_el) {
+ actual_width_el.innerHTML = window.innerWidth.toString();
+ }
+ yield this.delay(100);
+ feature_data.set("width", window.innerWidth);
+ feature_data.set("height", window.innerHeight);
+ }
+ }
+ });
+ }
+ end_trial(feature_data) {
+ this.jsPsych.getDisplayElement().innerHTML = "";
+ const trial_data = Object.assign({}, Object.fromEntries(feature_data));
+ this.jsPsych.finishTrial(trial_data);
+ }
+ end_experiment(feature_data) {
+ this.jsPsych.getDisplayElement().innerHTML = "";
+ const trial_data = Object.assign({}, Object.fromEntries(feature_data));
+ this.jsPsych.endExperiment(this.t.exclusion_message(trial_data), trial_data);
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const featureCheckFunctionsMap = this.create_feature_fn_map(trial);
+ // measure everything except vsync, which we just fake.
+ const features_to_check = trial.features.filter((x) => !trial.skip_features.includes(x));
+ const feature_data = yield this.measure_features(featureCheckFunctionsMap, features_to_check.filter((x) => x !== "vsync_rate"));
+ if (features_to_check.includes("vsync_rate")) {
+ feature_data.set("vsync_rate", 60);
+ }
+ const default_data = Object.fromEntries(feature_data);
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ // don't think this is necessary for this plugin...
+ // this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ });
+ }
+ simulate_data_only(trial, simulation_options) {
+ this.create_simulation_data(trial, simulation_options).then((data) => {
+ if (trial.allow_window_resize) {
+ if (data.width < trial.minimum_width) {
+ data.width = trial.minimum_width;
+ }
+ if (data.height < trial.minimum_height) {
+ data.height = trial.minimum_height;
+ }
+ }
+ // check inclusion function
+ if (trial.inclusion_function(data)) {
+ this.jsPsych.finishTrial(data);
+ }
+ else {
+ this.jsPsych.endExperiment(trial.exclusion_message(data), data);
+ }
+ });
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ this.t = trial;
+ load_callback();
+ this.create_simulation_data(trial, simulation_options).then((data) => {
+ const feature_data = new Map(Object.entries(data));
+ // run inclusion_check
+ // if the window size is big enough or the user resizes it within 3 seconds,
+ // then the plugin's trial code will finish up the trial.
+ // otherwise we simulate clicking the button and then the code above should
+ // finish it up too.
+ setTimeout(() => {
+ const btn = document.querySelector("#browser-check-max-size-btn");
+ if (btn) {
+ this.jsPsych.pluginAPI.clickTarget(btn);
+ }
+ }, 3000);
+ this.inclusion_check(this.t.inclusion_function, feature_data).then((include) => {
+ if (include) {
+ this.end_trial(feature_data);
+ }
+ else {
+ this.end_experiment(feature_data);
+ }
+ });
+ });
+ }
+ }
+ BrowserCheckPlugin.info = info;
+
+ return BrowserCheckPlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-call-function.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-call-function.js
new file mode 100644
index 00000000..24a6760f
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-call-function.js
@@ -0,0 +1,59 @@
+var jsPsychCallFunction = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "call-function",
+ parameters: {
+ /** Function to call */
+ func: {
+ type: jspsych.ParameterType.FUNCTION,
+ pretty_name: "Function",
+ default: undefined,
+ },
+ /** Is the function call asynchronous? */
+ async: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Asynchronous",
+ default: false,
+ },
+ },
+ };
+ /**
+ * **call-function**
+ *
+ * jsPsych plugin for calling an arbitrary function during a jsPsych experiment
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-call-function/ call-function plugin documentation on jspsych.org}
+ */
+ class CallFunctionPlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ //trial.post_trial_gap = 0; // TO DO: TS error: number not assignable to type any[]. I don't think this param should be an array..?
+ let return_val;
+ const end_trial = () => {
+ const trial_data = {
+ value: return_val,
+ };
+ this.jsPsych.finishTrial(trial_data);
+ };
+ if (trial.async) {
+ const done = (data) => {
+ return_val = data;
+ end_trial();
+ };
+ trial.func(done);
+ }
+ else {
+ return_val = trial.func();
+ end_trial();
+ }
+ }
+ }
+ CallFunctionPlugin.info = info;
+
+ return CallFunctionPlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-canvas-button-response.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-canvas-button-response.js
new file mode 100644
index 00000000..e0446318
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-canvas-button-response.js
@@ -0,0 +1,234 @@
+var jsPsychCanvasButtonResponse = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "canvas-button-response",
+ parameters: {
+ /** The drawing function to apply to the canvas. Should take the canvas object as argument. */
+ stimulus: {
+ type: jspsych.ParameterType.FUNCTION,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /** Array containing the label(s) for the button(s). */
+ choices: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Choices",
+ default: undefined,
+ array: true,
+ },
+ /** The html of the button. Can create own style. */
+ button_html: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Button HTML",
+ default: '',
+ array: true,
+ },
+ /** Any content here will be displayed under the button. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /** How long to hide the stimulus. */
+ stimulus_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Stimulus duration",
+ default: null,
+ },
+ /** How long to show the trial. */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /** The vertical margin of the button. */
+ margin_vertical: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Margin vertical",
+ default: "0px",
+ },
+ /** The horizontal margin of the button. */
+ margin_horizontal: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Margin horizontal",
+ default: "8px",
+ },
+ /** If true, then trial will end when user responds. */
+ response_ends_trial: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response ends trial",
+ default: true,
+ },
+ /** Array containing the height (first value) and width (second value) of the canvas element. */
+ canvas_size: {
+ type: jspsych.ParameterType.INT,
+ array: true,
+ pretty_name: "Canvas size",
+ default: [500, 500],
+ },
+ },
+ };
+ /**
+ * **canvas-button-response**
+ *
+ * jsPsych plugin for displaying a canvas stimulus and getting a button response
+ *
+ * @author Chris Jungerius (modified from Josh de Leeuw)
+ * @see {@link https://www.jspsych.org/plugins/jspsych-canvas-button-response/ canvas-button-response plugin documentation on jspsych.org}
+ */
+ class CanvasButtonResponsePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ // create canvas
+ var html = '
' +
+ '' +
+ "
";
+ //display buttons
+ var buttons = [];
+ if (Array.isArray(trial.button_html)) {
+ if (trial.button_html.length == trial.choices.length) {
+ buttons = trial.button_html;
+ }
+ else {
+ console.error("Error in canvas-button-response plugin. The length of the button_html array does not equal the length of the choices array");
+ }
+ }
+ else {
+ for (var i = 0; i < trial.choices.length; i++) {
+ buttons.push(trial.button_html);
+ }
+ }
+ html += '
';
+ for (var i = 0; i < trial.choices.length; i++) {
+ var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
+ html +=
+ '
' +
+ str +
+ "
";
+ }
+ html += "
";
+ //show prompt if there is one
+ if (trial.prompt !== null) {
+ html += trial.prompt;
+ }
+ display_element.innerHTML = html;
+ //draw
+ let c = document.getElementById("jspsych-canvas-stimulus");
+ trial.stimulus(c);
+ // start time
+ var start_time = performance.now();
+ // add event listeners to buttons
+ for (var i = 0; i < trial.choices.length; i++) {
+ display_element
+ .querySelector("#jspsych-canvas-button-response-button-" + i)
+ .addEventListener("click", (e) => {
+ var btn_el = e.currentTarget;
+ var choice = btn_el.getAttribute("data-choice"); // don't use dataset for jsdom compatibility
+ after_response(choice);
+ });
+ }
+ // store response
+ var response = {
+ rt: null,
+ button: null,
+ };
+ // function to end trial when it is time
+ const end_trial = () => {
+ // kill any remaining setTimeout handlers
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // gather the data to store for the trial
+ var trial_data = {
+ rt: response.rt,
+ response: response.button,
+ };
+ // clear the display
+ display_element.innerHTML = "";
+ // move on to the next trial
+ this.jsPsych.finishTrial(trial_data);
+ };
+ // function to handle responses by the subject
+ function after_response(choice) {
+ // measure rt
+ var end_time = performance.now();
+ var rt = Math.round(end_time - start_time);
+ response.button = parseInt(choice);
+ response.rt = rt;
+ // after a valid response, the stimulus will have the CSS class 'responded'
+ // which can be used to provide visual feedback that a response was recorded
+ display_element.querySelector("#jspsych-canvas-button-response-stimulus").className +=
+ " responded";
+ // disable all the buttons after a response
+ var btns = document.querySelectorAll(".jspsych-canvas-button-response-button button");
+ for (var i = 0; i < btns.length; i++) {
+ //btns[i].removeEventListener('click');
+ btns[i].setAttribute("disabled", "disabled");
+ }
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ }
+ // hide image if timing is set
+ if (trial.stimulus_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ display_element.querySelector("#jspsych-canvas-button-response-stimulus").style.visibility = "hidden";
+ }, trial.stimulus_duration);
+ }
+ // end trial if time limit is set
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ end_trial();
+ }, trial.trial_duration);
+ }
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ response: this.jsPsych.randomization.randomInt(0, trial.choices.length - 1),
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ if (data.rt !== null) {
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector(`div[data-choice="${data.response}"] button`), data.rt);
+ }
+ }
+ }
+ CanvasButtonResponsePlugin.info = info;
+
+ return CanvasButtonResponsePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-canvas-keyboard-response.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-canvas-keyboard-response.js
new file mode 100644
index 00000000..d988a5b9
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-canvas-keyboard-response.js
@@ -0,0 +1,176 @@
+var jsPsychCanvasKeyboardResponse = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "canvas-keyboard-response",
+ parameters: {
+ /** The drawing function to apply to the canvas. Should take the canvas object as argument. */
+ stimulus: {
+ type: jspsych.ParameterType.FUNCTION,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
+ choices: {
+ type: jspsych.ParameterType.KEYS,
+ pretty_name: "Choices",
+ default: "ALL_KEYS",
+ },
+ /** Any content here will be displayed below the stimulus. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /** How long to show the stimulus. */
+ stimulus_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Stimulus duration",
+ default: null,
+ },
+ /** How long to show trial before it ends. */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /** If true, trial will end when subject makes a response. */
+ response_ends_trial: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response ends trial",
+ default: true,
+ },
+ /** Array containing the height (first value) and width (second value) of the canvas element. */
+ canvas_size: {
+ type: jspsych.ParameterType.INT,
+ array: true,
+ pretty_name: "Canvas size",
+ default: [500, 500],
+ },
+ },
+ };
+ /**
+ * **canvas-keyboard-response**
+ *
+ * jsPsych plugin for displaying a canvas stimulus and getting a keyboard response
+ *
+ * @author Chris Jungerius (modified from Josh de Leeuw)
+ * @see {@link https://www.jspsych.org/plugins/jspsych-canvas-keyboard-response/ canvas-keyboard-response plugin documentation on jspsych.org}
+ */
+ class CanvasKeyboardResponsePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ var new_html = '
' +
+ '' +
+ "
";
+ // add prompt
+ if (trial.prompt !== null) {
+ new_html += trial.prompt;
+ }
+ // draw
+ display_element.innerHTML = new_html;
+ let c = document.getElementById("jspsych-canvas-stimulus");
+ trial.stimulus(c);
+ // store response
+ var response = {
+ rt: null,
+ key: null,
+ };
+ // function to end trial when it is time
+ const end_trial = () => {
+ // kill any remaining setTimeout handlers
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // kill keyboard listeners
+ if (typeof keyboardListener !== "undefined") {
+ this.jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
+ }
+ // gather the data to store for the trial
+ var trial_data = {
+ rt: response.rt,
+ response: response.key,
+ };
+ // clear the display
+ display_element.innerHTML = "";
+ // move on to the next trial
+ this.jsPsych.finishTrial(trial_data);
+ };
+ // function to handle responses by the subject
+ var after_response = (info) => {
+ // after a valid response, the stimulus will have the CSS class 'responded'
+ // which can be used to provide visual feedback that a response was recorded
+ display_element.querySelector("#jspsych-canvas-keyboard-response-stimulus").className +=
+ " responded";
+ // only record the first response
+ if (response.key == null) {
+ response = info;
+ }
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ };
+ // start the response listener
+ if (trial.choices != "NO_KEYS") {
+ var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: after_response,
+ valid_responses: trial.choices,
+ rt_method: "performance",
+ persist: false,
+ allow_held_key: false,
+ });
+ }
+ // hide stimulus if stimulus_duration is set
+ if (trial.stimulus_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ display_element.querySelector("#jspsych-canvas-keyboard-response-stimulus").style.visibility = "hidden";
+ }, trial.stimulus_duration);
+ }
+ // end trial if trial_duration is set
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ end_trial();
+ }, trial.trial_duration);
+ }
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ if (data.rt !== null) {
+ this.jsPsych.pluginAPI.pressKey(data.response, data.rt);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ response: this.jsPsych.pluginAPI.getValidKey(trial.choices),
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ }
+ CanvasKeyboardResponsePlugin.info = info;
+
+ return CanvasKeyboardResponsePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-canvas-slider-response.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-canvas-slider-response.js
new file mode 100644
index 00000000..f29a7ef7
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-canvas-slider-response.js
@@ -0,0 +1,263 @@
+var jsPsychCanvasSliderResponse = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "canvas-slider-response",
+ parameters: {
+ /** The drawing function to apply to the canvas. Should take the canvas object as argument. */
+ stimulus: {
+ type: jspsych.ParameterType.FUNCTION,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /** Sets the minimum value of the slider. */
+ min: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Min slider",
+ default: 0,
+ },
+ /** Sets the maximum value of the slider */
+ max: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Max slider",
+ default: 100,
+ },
+ /** Sets the starting value of the slider */
+ slider_start: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Slider starting value",
+ default: 50,
+ },
+ /** Sets the step of the slider */
+ step: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Step",
+ default: 1,
+ },
+ /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */
+ labels: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Labels",
+ default: [],
+ array: true,
+ },
+ /** Width of the slider in pixels. */
+ slider_width: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Slider width",
+ default: null,
+ },
+ /** Label of the button to advance. */
+ button_label: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Button label",
+ default: "Continue",
+ array: false,
+ },
+ /** If true, the participant will have to move the slider before continuing. */
+ require_movement: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Require movement",
+ default: false,
+ },
+ /** Any content here will be displayed below the slider */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /** How long to show the stimulus. */
+ stimulus_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Stimulus duration",
+ default: null,
+ },
+ /** How long to show the trial. */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /** If true, trial will end when user makes a response. */
+ response_ends_trial: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response ends trial",
+ default: true,
+ },
+ /** Array containing the height (first value) and width (second value) of the canvas element. */
+ canvas_size: {
+ type: jspsych.ParameterType.INT,
+ array: true,
+ pretty_name: "Canvas size",
+ default: [500, 500],
+ },
+ },
+ };
+ /**
+ * **canvas-slider-response**
+ *
+ * jsPsych plugin for displaying a canvas stimulus and getting a slider response
+ *
+ * @author Chris Jungerius (modified from Josh de Leeuw)
+ * @see {@link https://www.jspsych.org/plugins/jspsych-canvas-slider-response/ canvas-slider-response plugin documentation on jspsych.org}
+ */
+ class CanvasSliderResponsePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ var html = '
';
+ html += '' + trial.labels[j] + "";
+ html += "
";
+ }
+ html += "
";
+ html += "
";
+ html += "
";
+ if (trial.prompt !== null) {
+ html += trial.prompt;
+ }
+ // add submit button
+ html +=
+ '";
+ display_element.innerHTML = html;
+ // draw
+ let c = document.getElementById("jspsych-canvas-stimulus");
+ trial.stimulus(c);
+ var response = {
+ rt: null,
+ response: null,
+ };
+ const end_trial = () => {
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // save data
+ var trialdata = {
+ rt: response.rt,
+ response: response.response,
+ slider_start: trial.slider_start,
+ };
+ display_element.innerHTML = "";
+ // next trial
+ this.jsPsych.finishTrial(trialdata);
+ };
+ if (trial.require_movement) {
+ const enable_button = () => {
+ display_element.querySelector("#jspsych-canvas-slider-response-next").disabled = false;
+ };
+ display_element
+ .querySelector("#jspsych-canvas-slider-response-response")
+ .addEventListener("mousedown", enable_button);
+ display_element
+ .querySelector("#jspsych-canvas-slider-response-response")
+ .addEventListener("touchstart", enable_button);
+ display_element
+ .querySelector("#jspsych-canvas-slider-response-response")
+ .addEventListener("change", enable_button);
+ }
+ display_element
+ .querySelector("#jspsych-canvas-slider-response-next")
+ .addEventListener("click", () => {
+ // measure response time
+ var endTime = performance.now();
+ response.rt = Math.round(endTime - startTime);
+ response.response = display_element.querySelector("#jspsych-canvas-slider-response-response").valueAsNumber;
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ else {
+ display_element.querySelector("#jspsych-canvas-slider-response-next").disabled = true;
+ }
+ });
+ if (trial.stimulus_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ display_element.querySelector("#jspsych-canvas-slider-response-stimulus").style.visibility = "hidden";
+ }, trial.stimulus_duration);
+ }
+ // end trial if trial_duration is set
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(end_trial, trial.trial_duration);
+ }
+ var startTime = performance.now();
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ response: this.jsPsych.randomization.randomInt(trial.min, trial.max),
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ if (data.rt !== null) {
+ const el = display_element.querySelector("input[type='range']");
+ setTimeout(() => {
+ this.jsPsych.pluginAPI.clickTarget(el);
+ el.valueAsNumber = data.response;
+ }, data.rt / 2);
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("button"), data.rt);
+ }
+ }
+ }
+ CanvasSliderResponsePlugin.info = info;
+
+ return CanvasSliderResponsePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-categorize-animation.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-categorize-animation.js
new file mode 100644
index 00000000..e5c3974b
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-categorize-animation.js
@@ -0,0 +1,317 @@
+var jsPsychCategorizeAnimation = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "categorize-animation",
+ parameters: {
+ /** Array of paths to image files. */
+ stimuli: {
+ type: jspsych.ParameterType.IMAGE,
+ pretty_name: "Stimuli",
+ default: undefined,
+ array: true,
+ },
+ /** The key to indicate correct response */
+ key_answer: {
+ type: jspsych.ParameterType.KEY,
+ pretty_name: "Key answer",
+ default: undefined,
+ },
+ /** Array containing the key(s) the subject is allowed to press to respond to the stimuli. */
+ choices: {
+ type: jspsych.ParameterType.KEYS,
+ pretty_name: "Choices",
+ default: "ALL_KEYS",
+ },
+ /** Text to describe correct answer. */
+ text_answer: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Text answer",
+ default: null,
+ },
+ /** String to show when subject gives correct answer */
+ correct_text: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Correct text",
+ default: "Correct.",
+ },
+ /** String to show when subject gives incorrect answer. */
+ incorrect_text: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Incorrect text",
+ default: "Wrong.",
+ },
+ /** Duration to display each image. */
+ frame_time: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Frame time",
+ default: 500,
+ },
+ /** How many times to display entire sequence. */
+ sequence_reps: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Sequence repetitions",
+ default: 1,
+ },
+ /** If true, subject can response before the animation sequence finishes */
+ allow_response_before_complete: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Allow response before complete",
+ default: false,
+ },
+ /** How long to show feedback */
+ feedback_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Feedback duration",
+ default: 2000,
+ },
+ /** Any content here will be displayed below the stimulus. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /**
+ * If true, the images will be drawn onto a canvas element (prevents blank screen between consecutive images in some browsers).
+ * If false, the image will be shown via an img element.
+ */
+ render_on_canvas: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Render on canvas",
+ default: true,
+ },
+ },
+ };
+ /**
+ * **categorize-animation**
+ *
+ * jsPsych plugin for categorization trials with feedback and animated stimuli
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-categorize-animation/ categorize-animation plugin documentation on jspsych.org}
+ */
+ class CategorizeAnimationPlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ var animate_frame = 0;
+ var reps = 0;
+ var showAnimation = true;
+ var responded = false;
+ var timeoutSet = false;
+ var correct;
+ if (trial.render_on_canvas) {
+ // first clear the display element (because the render_on_canvas method appends to display_element instead of overwriting it with .innerHTML)
+ if (display_element.hasChildNodes()) {
+ // can't loop through child list because the list will be modified by .removeChild()
+ while (display_element.firstChild) {
+ display_element.removeChild(display_element.firstChild);
+ }
+ }
+ var canvas = document.createElement("canvas");
+ canvas.id = "jspsych-categorize-animation-stimulus";
+ canvas.style.margin = "0";
+ canvas.style.padding = "0";
+ display_element.insertBefore(canvas, null);
+ var ctx = canvas.getContext("2d");
+ if (trial.prompt !== null) {
+ var prompt_div = document.createElement("div");
+ prompt_div.id = "jspsych-categorize-animation-prompt";
+ prompt_div.style.visibility = "hidden";
+ prompt_div.innerHTML = trial.prompt;
+ display_element.insertBefore(prompt_div, canvas.nextElementSibling);
+ }
+ var feedback_div = document.createElement("div");
+ display_element.insertBefore(feedback_div, display_element.nextElementSibling);
+ }
+ const update_display = () => {
+ if (showAnimation) {
+ if (trial.render_on_canvas) {
+ display_element.querySelector("#jspsych-categorize-animation-stimulus").style.visibility = "visible";
+ var img = new Image();
+ img.src = trial.stimuli[animate_frame];
+ canvas.height = img.naturalHeight;
+ canvas.width = img.naturalWidth;
+ ctx.drawImage(img, 0, 0);
+ }
+ else {
+ display_element.innerHTML +=
+ '';
+ }
+ }
+ if (!responded && trial.allow_response_before_complete) {
+ // in here if the user can respond before the animation is done
+ if (trial.prompt !== null) {
+ if (trial.render_on_canvas) {
+ prompt_div.style.visibility = "visible";
+ }
+ else {
+ display_element.innerHTML += trial.prompt;
+ }
+ }
+ if (trial.render_on_canvas) {
+ if (!showAnimation) {
+ canvas.remove();
+ }
+ }
+ }
+ else if (!responded) {
+ // in here if the user has to wait to respond until animation is done.
+ // if this is the case, don't show the prompt until the animation is over.
+ if (!showAnimation) {
+ if (trial.prompt !== null) {
+ if (trial.render_on_canvas) {
+ prompt_div.style.visibility = "visible";
+ }
+ else {
+ display_element.innerHTML += trial.prompt;
+ }
+ }
+ if (trial.render_on_canvas) {
+ canvas.remove();
+ }
+ }
+ }
+ else {
+ // user has responded if we get here.
+ // show feedback
+ var feedback_text = "";
+ if (correct) {
+ feedback_text = trial.correct_text.replace("%ANS%", trial.text_answer);
+ }
+ else {
+ feedback_text = trial.incorrect_text.replace("%ANS%", trial.text_answer);
+ }
+ if (trial.render_on_canvas) {
+ if (trial.prompt !== null) {
+ prompt_div.remove();
+ }
+ feedback_div.innerHTML = feedback_text;
+ }
+ else {
+ display_element.innerHTML += feedback_text;
+ }
+ // set timeout to clear feedback
+ if (!timeoutSet) {
+ timeoutSet = true;
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ endTrial();
+ }, trial.feedback_duration);
+ }
+ }
+ };
+ // show animation
+ var animate_interval = setInterval(() => {
+ if (!trial.render_on_canvas) {
+ display_element.innerHTML = ""; // clear everything
+ }
+ animate_frame++;
+ if (animate_frame == trial.stimuli.length) {
+ animate_frame = 0;
+ reps++;
+ // check if reps complete //
+ if (trial.sequence_reps != -1 && reps >= trial.sequence_reps) {
+ // done with animation
+ showAnimation = false;
+ }
+ }
+ update_display();
+ }, trial.frame_time);
+ update_display();
+ const endTrial = () => {
+ clearInterval(animate_interval); // stop animation!
+ display_element.innerHTML = ""; // clear everything
+ this.jsPsych.finishTrial(trial_data);
+ };
+ var keyboard_listener;
+ var trial_data = {};
+ // @ts-expect-error Error is: Unreachable code detected: Not all code paths return a value
+ const after_response = (info) => {
+ // ignore the response if animation is playing and subject
+ // not allowed to respond before it is complete
+ if (!trial.allow_response_before_complete && showAnimation) {
+ return false;
+ }
+ correct = false;
+ if (this.jsPsych.pluginAPI.compareKeys(trial.key_answer, info.key)) {
+ correct = true;
+ }
+ responded = true;
+ trial_data = {
+ stimulus: trial.stimuli,
+ rt: info.rt,
+ correct: correct,
+ response: info.key,
+ };
+ this.jsPsych.pluginAPI.cancelKeyboardResponse(keyboard_listener);
+ };
+ keyboard_listener = this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: after_response,
+ valid_responses: trial.choices,
+ rt_method: "performance",
+ persist: true,
+ allow_held_key: false,
+ });
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const animation_length = trial.sequence_reps * trial.frame_time * trial.stimuli.length;
+ const key = this.jsPsych.pluginAPI.getValidKey(trial.choices);
+ const default_data = {
+ stimulus: trial.stimuli,
+ rt: animation_length + this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ response: key,
+ correct: key == trial.key_answer,
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ if (data.rt == null || data.response == null) {
+ throw new Error(`
+ Simulated response for categorize-animation plugin was invalid.
+ This could be because the response RT was too fast and generated
+ before the animation finished when the allow_response_before_complete
+ parameter is false. In a real experiment this would cause the experiment
+ to pause indefinitely.`);
+ }
+ else {
+ this.jsPsych.finishTrial(data);
+ }
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ if (data.rt !== null) {
+ this.jsPsych.pluginAPI.pressKey(data.response, data.rt);
+ }
+ else {
+ throw new Error(`
+ Simulated response for categorize-animation plugin was invalid.
+ This could be because the response RT was too fast and generated
+ before the animation finished when the allow_response_before_complete
+ parameter is false. In a real experiment this would cause the experiment
+ to pause indefinitely.`);
+ }
+ }
+ }
+ CategorizeAnimationPlugin.info = info;
+
+ return CategorizeAnimationPlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-categorize-html.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-categorize-html.js
new file mode 100644
index 00000000..8268c9e5
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-categorize-html.js
@@ -0,0 +1,246 @@
+var jsPsychCategorizeHtml = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "categorize-html",
+ parameters: {
+ /** The HTML content to be displayed. */
+ stimulus: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /** The key to indicate the correct response. */
+ key_answer: {
+ type: jspsych.ParameterType.KEY,
+ pretty_name: "Key answer",
+ default: undefined,
+ },
+ /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
+ choices: {
+ type: jspsych.ParameterType.KEYS,
+ pretty_name: "Choices",
+ default: "ALL_KEYS",
+ },
+ /** Label that is associated with the correct answer. */
+ text_answer: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Text answer",
+ default: null,
+ },
+ /** String to show when correct answer is given. */
+ correct_text: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Correct text",
+ default: "
Correct
",
+ },
+ /** String to show when incorrect answer is given. */
+ incorrect_text: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Incorrect text",
+ default: "
Incorrect
",
+ },
+ /** Any content here will be displayed below the stimulus. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /** If set to true, then the subject must press the correct response key after feedback in order to advance to next trial. */
+ force_correct_button_press: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Force correct button press",
+ default: false,
+ },
+ /** If true, stimulus will be shown during feedback. If false, only the text feedback will be displayed during feedback. */
+ show_stim_with_feedback: {
+ type: jspsych.ParameterType.BOOL,
+ default: true,
+ no_function: false,
+ },
+ /** Whether or not to show feedback following a response timeout. */
+ show_feedback_on_timeout: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Show feedback on timeout",
+ default: false,
+ },
+ /** The message displayed on a timeout non-response. */
+ timeout_message: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Timeout message",
+ default: "
Please respond faster.
",
+ },
+ /** How long to show the stimulus. */
+ stimulus_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Stimulus duration",
+ default: null,
+ },
+ /** How long to show trial */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /** How long to show feedback. */
+ feedback_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Feedback duration",
+ default: 2000,
+ },
+ },
+ };
+ /**
+ * **categorize-html**
+ *
+ * jsPsych plugin for categorization trials with feedback
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-categorize-html/ categorize-html plugin documentation on jspsych.org}
+ */
+ class CategorizeHtmlPlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ display_element.innerHTML =
+ '
' +
+ trial.stimulus +
+ "
";
+ // hide image after time if the timing parameter is set
+ if (trial.stimulus_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ display_element.querySelector("#jspsych-categorize-html-stimulus").style.visibility = "hidden";
+ }, trial.stimulus_duration);
+ }
+ // if prompt is set, show prompt
+ if (trial.prompt !== null) {
+ display_element.innerHTML += trial.prompt;
+ }
+ var trial_data = {};
+ // create response function
+ const after_response = (info) => {
+ // kill any remaining setTimeout handlers
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // clear keyboard listener
+ this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
+ var correct = false;
+ if (this.jsPsych.pluginAPI.compareKeys(trial.key_answer, info.key)) {
+ correct = true;
+ }
+ // save data
+ trial_data = {
+ rt: info.rt,
+ correct: correct,
+ stimulus: trial.stimulus,
+ response: info.key,
+ };
+ display_element.innerHTML = "";
+ var timeout = info.rt == null;
+ doFeedback(correct, timeout);
+ };
+ this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: after_response,
+ valid_responses: trial.choices,
+ rt_method: "performance",
+ persist: false,
+ allow_held_key: false,
+ });
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ after_response({
+ key: null,
+ rt: null,
+ });
+ }, trial.trial_duration);
+ }
+ const endTrial = () => {
+ display_element.innerHTML = "";
+ this.jsPsych.finishTrial(trial_data);
+ };
+ const doFeedback = (correct, timeout) => {
+ if (timeout && !trial.show_feedback_on_timeout) {
+ display_element.innerHTML += trial.timeout_message;
+ }
+ else {
+ // show image during feedback if flag is set
+ if (trial.show_stim_with_feedback) {
+ display_element.innerHTML =
+ '
",
+ },
+ /** String to show when incorrect answer is given. */
+ incorrect_text: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Incorrect text",
+ default: "
Wrong
",
+ },
+ /** Any content here will be displayed below the stimulus. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /** If set to true, then the subject must press the correct response key after feedback in order to advance to next trial. */
+ force_correct_button_press: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Force correct button press",
+ default: false,
+ },
+ /** Whether or not the stimulus should be shown on the feedback screen. */
+ show_stim_with_feedback: {
+ type: jspsych.ParameterType.BOOL,
+ default: true,
+ no_function: false,
+ },
+ /** If true, stimulus will be shown during feedback. If false, only the text feedback will be displayed during feedback. */
+ show_feedback_on_timeout: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Show feedback on timeout",
+ default: false,
+ },
+ /** The message displayed on a timeout non-response. */
+ timeout_message: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Timeout message",
+ default: "
Please respond faster.
",
+ },
+ /** How long to show the stimulus. */
+ stimulus_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Stimulus duration",
+ default: null,
+ },
+ /** How long to show the trial. */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /** How long to show the feedback. */
+ feedback_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Feedback duration",
+ default: 2000,
+ },
+ },
+ };
+ /**
+ * **categorize-image**
+ *
+ * jsPsych plugin for image categorization trials with feedback
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-categorize-image/ categorize-image plugin documentation on jspsych.org}
+ */
+ class CategorizeImagePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ display_element.innerHTML =
+ '';
+ // hide image after time if the timing parameter is set
+ if (trial.stimulus_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ display_element.querySelector("#jspsych-categorize-image-stimulus").style.visibility = "hidden";
+ }, trial.stimulus_duration);
+ }
+ // if prompt is set, show prompt
+ if (trial.prompt !== null) {
+ display_element.innerHTML += trial.prompt;
+ }
+ var trial_data = {};
+ // create response function
+ const after_response = (info) => {
+ // kill any remaining setTimeout handlers
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // clear keyboard listener
+ this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
+ var correct = false;
+ if (this.jsPsych.pluginAPI.compareKeys(trial.key_answer, info.key)) {
+ correct = true;
+ }
+ // save data
+ trial_data = {
+ rt: info.rt,
+ correct: correct,
+ stimulus: trial.stimulus,
+ response: info.key,
+ };
+ display_element.innerHTML = "";
+ var timeout = info.rt == null;
+ doFeedback(correct, timeout);
+ };
+ this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: after_response,
+ valid_responses: trial.choices,
+ rt_method: "performance",
+ persist: false,
+ allow_held_key: false,
+ });
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ after_response({
+ key: null,
+ rt: null,
+ });
+ }, trial.trial_duration);
+ }
+ const endTrial = () => {
+ display_element.innerHTML = "";
+ this.jsPsych.finishTrial(trial_data);
+ };
+ const doFeedback = (correct, timeout) => {
+ if (timeout && !trial.show_feedback_on_timeout) {
+ display_element.innerHTML += trial.timeout_message;
+ }
+ else {
+ // show image during feedback if flag is set
+ if (trial.show_stim_with_feedback) {
+ display_element.innerHTML =
+ '';
+ }
+ // substitute answer in feedback string.
+ var atext = "";
+ if (correct) {
+ atext = trial.correct_text.replace("%ANS%", trial.text_answer);
+ }
+ else {
+ atext = trial.incorrect_text.replace("%ANS%", trial.text_answer);
+ }
+ // show the feedback
+ display_element.innerHTML += atext;
+ }
+ // check if force correct button press is set
+ if (trial.force_correct_button_press &&
+ correct === false &&
+ ((timeout && trial.show_feedback_on_timeout) || !timeout)) {
+ var after_forced_response = (info) => {
+ endTrial();
+ };
+ this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: after_forced_response,
+ valid_responses: [trial.key_answer],
+ rt_method: "performance",
+ persist: false,
+ allow_held_key: false,
+ });
+ }
+ else {
+ this.jsPsych.pluginAPI.setTimeout(endTrial, trial.feedback_duration);
+ }
+ };
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const key = this.jsPsych.pluginAPI.getValidKey(trial.choices);
+ const default_data = {
+ stimulus: trial.stimulus,
+ response: key,
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ correct: key == trial.key_answer,
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ if (data.rt !== null) {
+ this.jsPsych.pluginAPI.pressKey(data.response, data.rt);
+ }
+ if (trial.force_correct_button_press && !data.correct) {
+ this.jsPsych.pluginAPI.pressKey(trial.key_answer, data.rt + trial.feedback_duration / 2);
+ }
+ }
+ }
+ CategorizeImagePlugin.info = info;
+
+ return CategorizeImagePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-cloze.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-cloze.js
new file mode 100644
index 00000000..3a1b692e
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-cloze.js
@@ -0,0 +1,153 @@
+var jsPsychCloze = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "cloze",
+ parameters: {
+ /** The cloze text to be displayed. Blanks are indicated by %% signs and automatically replaced by input fields. If there is a correct answer you want the system to check against, it must be typed between the two percentage signs (i.e. %solution%). */
+ text: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Cloze text",
+ default: undefined,
+ },
+ /** Text of the button participants have to press for finishing the cloze test. */
+ button_text: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Button text",
+ default: "OK",
+ },
+ /** Boolean value indicating if the answers given by participants should be compared against a correct solution given in the text (between % signs) after the button was clicked. */
+ check_answers: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Check answers",
+ default: false,
+ },
+ /** Function called if check_answers is set to TRUE and there is a difference between the participants answers and the correct solution provided in the text. */
+ mistake_fn: {
+ type: jspsych.ParameterType.FUNCTION,
+ pretty_name: "Mistake function",
+ default: () => { },
+ },
+ },
+ };
+ /**
+ * **cloze**
+ *
+ * jsPsych plugin for displaying a cloze test and checking participants answers against a correct solution
+ *
+ * @author Philipp Sprengholz
+ * @see {@link https://www.jspsych.org/plugins/jspsych-cloze/ cloze plugin documentation on jspsych.org}
+ */
+ class ClozePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ var html = '
';
+ var elements = trial.text.split("%");
+ const solutions = this.getSolutions(trial.text);
+ let solution_counter = 0;
+ for (var i = 0; i < elements.length; i++) {
+ if (i % 2 === 0) {
+ html += elements[i];
+ }
+ else {
+ html += ``;
+ solution_counter++;
+ }
+ }
+ html += "
";
+ display_element.innerHTML = html;
+ const check = () => {
+ var answers = [];
+ var answers_correct = true;
+ for (var i = 0; i < solutions.length; i++) {
+ var field = document.getElementById("input" + i);
+ answers.push(field.value.trim());
+ if (trial.check_answers) {
+ if (answers[i] !== solutions[i]) {
+ field.style.color = "red";
+ answers_correct = false;
+ }
+ else {
+ field.style.color = "black";
+ }
+ }
+ }
+ if (!trial.check_answers || (trial.check_answers && answers_correct)) {
+ var trial_data = {
+ response: answers,
+ };
+ display_element.innerHTML = "";
+ this.jsPsych.finishTrial(trial_data);
+ }
+ else {
+ trial.mistake_fn();
+ }
+ };
+ display_element.innerHTML +=
+ ' ";
+ display_element.querySelector("#finish_cloze_button").addEventListener("click", check);
+ }
+ getSolutions(text) {
+ const solutions = [];
+ const elements = text.split("%");
+ for (let i = 0; i < elements.length; i++) {
+ if (i % 2 == 1) {
+ solutions.push(elements[i].trim());
+ }
+ }
+ return solutions;
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const solutions = this.getSolutions(trial.text);
+ const responses = [];
+ for (const word of solutions) {
+ if (word == "") {
+ responses.push(this.jsPsych.randomization.randomWords({ exactly: 1 }));
+ }
+ else {
+ responses.push(word);
+ }
+ }
+ const default_data = {
+ response: responses,
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ //this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ const inputs = display_element.querySelectorAll('input[type="text"]');
+ let rt = this.jsPsych.randomization.sampleExGaussian(750, 200, 0.01, true);
+ for (let i = 0; i < data.response.length; i++) {
+ this.jsPsych.pluginAPI.fillTextInput(inputs[i], data.response[i], rt);
+ rt += this.jsPsych.randomization.sampleExGaussian(750, 200, 0.01, true);
+ }
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("#finish_cloze_button"), rt);
+ }
+ }
+ ClozePlugin.info = info;
+
+ return ClozePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-external-html.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-external-html.js
new file mode 100644
index 00000000..91c63272
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-external-html.js
@@ -0,0 +1,181 @@
+var jsPsychExternalHtml = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "external-html",
+ parameters: {
+ /** The url of the external html page */
+ url: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "URL",
+ default: undefined,
+ },
+ /** The key to continue to the next page. */
+ cont_key: {
+ type: jspsych.ParameterType.KEY,
+ pretty_name: "Continue key",
+ default: null,
+ },
+ /** The button to continue to the next page. */
+ cont_btn: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Continue button",
+ default: null,
+ },
+ /** Function to check whether user is allowed to continue after clicking cont_key or clicking cont_btn */
+ check_fn: {
+ type: jspsych.ParameterType.FUNCTION,
+ pretty_name: "Check function",
+ default: () => true,
+ },
+ /** Whether or not to force a page refresh. */
+ force_refresh: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Force refresh",
+ default: false,
+ },
+ /** If execute_Script == true, then all JavasScript code on the external page will be executed. */
+ execute_script: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Execute scripts",
+ default: false,
+ },
+ },
+ };
+ /**
+ * **external-html**
+ *
+ * jsPsych plugin to load and display an external html page. To proceed to the next trial, the
+ * user might either press a button on the page or a specific key. Afterwards, the page will be hidden and
+ * the experiment will continue.
+ *
+ * @author Erik Weitnauer
+ * @see {@link https://www.jspsych.org/plugins/jspsych-external-html/ external-html plugin documentation on jspsych.org}
+ */
+ class ExternalHtmlPlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial, on_load) {
+ // hold the .resolve() function from the Promise that ends the trial
+ let trial_complete;
+ var url = trial.url;
+ if (trial.force_refresh) {
+ url = trial.url + "?t=" + performance.now();
+ }
+ fetch(url)
+ .then((response) => {
+ return response.text();
+ })
+ .then((html) => {
+ display_element.innerHTML = html;
+ on_load();
+ var t0 = performance.now();
+ const key_listener = (e) => {
+ if (this.jsPsych.pluginAPI.compareKeys(e.key, trial.cont_key)) {
+ finish();
+ }
+ };
+ const finish = () => {
+ if (trial.check_fn && !trial.check_fn(display_element)) {
+ return;
+ }
+ if (trial.cont_key) {
+ display_element.removeEventListener("keydown", key_listener);
+ }
+ var trial_data = {
+ rt: Math.round(performance.now() - t0),
+ url: trial.url,
+ };
+ display_element.innerHTML = "";
+ this.jsPsych.finishTrial(trial_data);
+ trial_complete();
+ };
+ // by default, scripts on the external page are not executed with XMLHttpRequest().
+ // To activate their content through DOM manipulation, we need to relocate all script tags
+ if (trial.execute_script) {
+ // changed for..of getElementsByTagName("script") here to for i loop due to TS error:
+ // Type 'HTMLCollectionOf' must have a '[Symbol.iterator]()' method that returns an iterator.ts(2488)
+ var all_scripts = display_element.getElementsByTagName("script");
+ for (var i = 0; i < all_scripts.length; i++) {
+ const relocatedScript = document.createElement("script");
+ const curr_script = all_scripts[i];
+ relocatedScript.text = curr_script.text;
+ curr_script.parentNode.replaceChild(relocatedScript, curr_script);
+ }
+ }
+ if (trial.cont_btn) {
+ display_element.querySelector("#" + trial.cont_btn).addEventListener("click", finish);
+ }
+ if (trial.cont_key) {
+ display_element.addEventListener("keydown", key_listener);
+ }
+ })
+ .catch((err) => {
+ console.error(`Something went wrong with fetch() in plugin-external-html.`, err);
+ });
+ // helper to load via XMLHttpRequest
+ /*const load = (element, file, callback) => {
+ var xmlhttp = new XMLHttpRequest();
+ xmlhttp.open("GET", file, true);
+ xmlhttp.onload = () => {
+ console.log(`loaded ${xmlhttp.status}`)
+ if (xmlhttp.status == 200 || xmlhttp.status == 0) {
+ //Check if loaded
+ element.innerHTML = xmlhttp.responseText;
+ console.log(`made it ${xmlhttp.responseText}`);
+ callback();
+ }
+ };
+ xmlhttp.send();
+ };
+
+ load(display_element, url, () => {
+
+ });
+ */
+ return new Promise((resolve) => {
+ trial_complete = resolve;
+ });
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ url: trial.url,
+ rt: this.jsPsych.randomization.sampleExGaussian(2000, 200, 1 / 200, true),
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial, () => {
+ load_callback();
+ if (trial.cont_key) {
+ this.jsPsych.pluginAPI.pressKey(trial.cont_key, data.rt);
+ }
+ else if (trial.cont_btn) {
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("#" + trial.cont_btn), data.rt);
+ }
+ });
+ }
+ }
+ ExternalHtmlPlugin.info = info;
+
+ return ExternalHtmlPlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-free-sort.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-free-sort.js
new file mode 100644
index 00000000..c6744160
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-free-sort.js
@@ -0,0 +1,510 @@
+var jsPsychFreeSort = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "free-sort",
+ parameters: {
+ /** Array of images to be displayed and sorted. */
+ stimuli: {
+ type: jspsych.ParameterType.IMAGE,
+ pretty_name: "Stimuli",
+ default: undefined,
+ array: true,
+ },
+ /** Height of items in pixels. */
+ stim_height: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Stimulus height",
+ default: 100,
+ },
+ /** Width of items in pixels */
+ stim_width: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Stimulus width",
+ default: 100,
+ },
+ /** How much larger to make the stimulus while moving (1 = no scaling) */
+ scale_factor: {
+ type: jspsych.ParameterType.FLOAT,
+ pretty_name: "Stimulus scaling factor",
+ default: 1.5,
+ },
+ /** The height in pixels of the container that subjects can move the stimuli in. */
+ sort_area_height: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Sort area height",
+ default: 700,
+ },
+ /** The width in pixels of the container that subjects can move the stimuli in. */
+ sort_area_width: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Sort area width",
+ default: 700,
+ },
+ /** The shape of the sorting area */
+ sort_area_shape: {
+ type: jspsych.ParameterType.SELECT,
+ pretty_name: "Sort area shape",
+ options: ["square", "ellipse"],
+ default: "ellipse",
+ },
+ /** HTML to display above/below the sort area. It can be used to provide a reminder about the action the subject is supposed to take. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: "",
+ },
+ /** Indicates whether to show prompt "above" or "below" the sorting area. */
+ prompt_location: {
+ type: jspsych.ParameterType.SELECT,
+ pretty_name: "Prompt location",
+ options: ["above", "below"],
+ default: "above",
+ },
+ /** The text that appears on the button to continue to the next trial. */
+ button_label: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Button label",
+ default: "Continue",
+ },
+ /**
+ * If true, the sort area border color will change while items are being moved in and out of the sort area,
+ * and the background color will change once all items have been moved into the sort area.
+ * If false, the border will remain black and the background will remain white throughout the trial.
+ */
+ change_border_background_color: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Change border background color",
+ default: true,
+ },
+ /**
+ * If change_border_background_color is true, the sort area border will change to this color
+ * when an item is being moved into the sort area, and the background will change to this color
+ * when all of the items have been moved into the sort area.
+ */
+ border_color_in: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Border color - in",
+ default: "#a1d99b",
+ },
+ /**
+ * If change_border_background_color is true, this will be the color of the sort area border
+ * when there are one or more items that still need to be moved into the sort area.
+ */
+ border_color_out: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Border color - out",
+ default: "#fc9272",
+ },
+ /** The width in pixels of the border around the sort area. If null, the border width defaults to 3% of the sort area height. */
+ border_width: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Border width",
+ default: null,
+ },
+ /**
+ * Text to display when there are one or more items that still need to be placed in the sort area.
+ * If "%n%" is included in the string, it will be replaced with the number of items that still need to be moved inside.
+ * If "%s%" is included in the string, a "s" will be included when the number of items remaining is greater than one.
+ * */
+ counter_text_unfinished: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Counter text unfinished",
+ default: "You still need to place %n% item%s% inside the sort area.",
+ },
+ /** Text that will take the place of the counter_text_unfinished text when all items have been moved inside the sort area. */
+ counter_text_finished: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Counter text finished",
+ default: "All items placed. Feel free to reposition items if necessary.",
+ },
+ /**
+ * If false, the images will be positioned to the left and right of the sort area when the trial loads.
+ * If true, the images will be positioned at random locations inside the sort area when the trial loads.
+ */
+ stim_starts_inside: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Stim starts inside",
+ default: false,
+ },
+ /**
+ * When the images appear outside the sort area, this determines the x-axis spread of the image columns.
+ * Default value is 1. Values less than 1 will compress the image columns along the x-axis, and values greater than 1 will spread them farther apart.
+ */
+ column_spread_factor: {
+ type: jspsych.ParameterType.FLOAT,
+ pretty_name: "column spread factor",
+ default: 1,
+ },
+ },
+ };
+ /**
+ * **free-sort**
+ *
+ * jsPsych plugin for drag-and-drop sorting of a collection of images
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-free-sort/ free-sort plugin documentation on jspsych.org}
+ */
+ class FreeSortPlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ var start_time = performance.now();
+ // can't change trial properties (const), so create new variables for properties that might need to be changed
+ var border_color_out = trial.border_color_out;
+ var border_width = trial.border_width;
+ var stimuli = trial.stimuli;
+ if (trial.change_border_background_color == false) {
+ border_color_out = "#000000";
+ }
+ if (trial.border_width == null) {
+ border_width = trial.sort_area_height * 0.03;
+ }
+ let html = "
';
+ // another div for border
+ html +=
+ "';
+ }
+ else {
+ html += 'webkit-border-radius: 0%; moz-border-radius: 0%; border-radius: 0%">
';
+ }
+ // variable that has the prompt text and counter
+ const html_text = '
' +
+ trial.prompt +
+ '
' +
+ get_counter_text(stimuli.length) +
+ "
";
+ // position prompt above or below
+ if (trial.prompt_location == "below") {
+ html += html_text;
+ }
+ else {
+ html = html_text + html;
+ }
+ // add button
+ html +=
+ '";
+ display_element.innerHTML = html;
+ // store initial location data
+ let init_locations = [];
+ if (!trial.stim_starts_inside) {
+ // determine number of rows and colums, must be a even number
+ let num_rows = Math.ceil(Math.sqrt(stimuli.length));
+ if (num_rows % 2 != 0) {
+ num_rows = num_rows + 1;
+ }
+ // compute coords for left and right side of arena
+ var r_coords = [];
+ var l_coords = [];
+ for (const x of make_arr(0, trial.sort_area_width - trial.stim_width, num_rows)) {
+ for (const y of make_arr(0, trial.sort_area_height - trial.stim_height, num_rows)) {
+ if (x > (trial.sort_area_width - trial.stim_width) * 0.5) {
+ //r_coords.push({ x:x, y:y } )
+ r_coords.push({
+ x: x + trial.sort_area_width * (0.5 * trial.column_spread_factor),
+ y: y,
+ });
+ }
+ else {
+ l_coords.push({
+ x: x - trial.sort_area_width * (0.5 * trial.column_spread_factor),
+ y: y,
+ });
+ //l_coords.push({ x:x, y:y } )
+ }
+ }
+ }
+ // repeat coordinates until you have enough coords (may be obsolete)
+ while (r_coords.length + l_coords.length < stimuli.length) {
+ r_coords = r_coords.concat(r_coords);
+ l_coords = l_coords.concat(l_coords);
+ }
+ // reverse left coords, so that coords closest to arena is used first
+ l_coords = l_coords.reverse();
+ // shuffle stimuli, so that starting positions are random
+ stimuli = shuffle(stimuli);
+ }
+ let inside = [];
+ for (let i = 0; i < stimuli.length; i++) {
+ var coords;
+ if (trial.stim_starts_inside) {
+ coords = random_coordinate(trial.sort_area_width - trial.stim_width, trial.sort_area_height - trial.stim_height);
+ }
+ else {
+ if (i % 2 == 0) {
+ coords = r_coords[Math.floor(i * 0.5)];
+ }
+ else {
+ coords = l_coords[Math.floor(i * 0.5)];
+ }
+ }
+ display_element.querySelector("#jspsych-free-sort-arena").innerHTML +=
+ "' +
+ "";
+ init_locations.push({
+ src: stimuli[i],
+ x: coords.x,
+ y: coords.y,
+ });
+ if (trial.stim_starts_inside) {
+ inside.push(true);
+ }
+ else {
+ inside.push(false);
+ }
+ }
+ // moves within a trial
+ let moves = [];
+ // are objects currently inside
+ let cur_in = false;
+ // draggable items
+ const draggables = display_element.querySelectorAll(".jspsych-free-sort-draggable");
+ // button (will show when all items are inside) and border (will change color)
+ const border = display_element.querySelector("#jspsych-free-sort-border");
+ const button = display_element.querySelector("#jspsych-free-sort-done-btn");
+ // when trial starts, modify text and border/background if all items are inside (stim_starts_inside: true)
+ if (inside.some(Boolean) && trial.change_border_background_color) {
+ border.style.borderColor = trial.border_color_in;
+ }
+ if (inside.every(Boolean)) {
+ if (trial.change_border_background_color) {
+ border.style.background = trial.border_color_in;
+ }
+ button.style.visibility = "visible";
+ display_element.querySelector("#jspsych-free-sort-counter").innerHTML =
+ trial.counter_text_finished;
+ }
+ let start_event_name = "mousedown";
+ let move_event_name = "mousemove";
+ let end_event_name = "mouseup";
+ if (typeof document.ontouchend !== "undefined") {
+ // for touch devices
+ start_event_name = "touchstart";
+ move_event_name = "touchmove";
+ end_event_name = "touchend";
+ }
+ for (let i = 0; i < draggables.length; i++) {
+ draggables[i].addEventListener(start_event_name, (event) => {
+ let pageX;
+ let pageY;
+ if (event instanceof MouseEvent) {
+ pageX = event.pageX;
+ pageY = event.pageY;
+ }
+ //if (typeof document.ontouchend !== "undefined") {
+ if (event instanceof TouchEvent) {
+ // for touch devices
+ event.preventDefault();
+ const touchObject = event.changedTouches[0];
+ pageX = touchObject.pageX;
+ pageY = touchObject.pageY;
+ }
+ let elem = event.currentTarget;
+ let x = pageX - elem.offsetLeft;
+ let y = pageY - elem.offsetTop - window.scrollY;
+ elem.style.transform = "scale(" + trial.scale_factor + "," + trial.scale_factor + ")";
+ let move_event = (e) => {
+ let clientX = e.clientX;
+ let clientY = e.clientY;
+ if (typeof document.ontouchend !== "undefined") {
+ // for touch devices
+ const touchObject = e.changedTouches[0];
+ clientX = touchObject.clientX;
+ clientY = touchObject.clientY;
+ }
+ cur_in = inside_ellipse(clientX - x, clientY - y, trial.sort_area_width * 0.5 - trial.stim_width * 0.5, trial.sort_area_height * 0.5 - trial.stim_height * 0.5, trial.sort_area_width * 0.5, trial.sort_area_height * 0.5, trial.sort_area_shape == "square");
+ elem.style.top =
+ Math.min(trial.sort_area_height - trial.stim_height * 0.5, Math.max(-trial.stim_height * 0.5, clientY - y)) + "px";
+ elem.style.left =
+ Math.min(trial.sort_area_width * 1.5 - trial.stim_width, Math.max(-trial.sort_area_width * 0.5, clientX - x)) + "px";
+ // modify border while items is being moved
+ if (trial.change_border_background_color) {
+ if (cur_in) {
+ border.style.borderColor = trial.border_color_in;
+ border.style.background = "None";
+ }
+ else {
+ border.style.borderColor = border_color_out;
+ border.style.background = "None";
+ }
+ }
+ // replace in overall array, grab index from item id
+ var elem_number = parseInt(elem.id.split("jspsych-free-sort-draggable-")[1], 10);
+ inside.splice(elem_number, 1, cur_in);
+ // modify text and background if all items are inside
+ if (inside.every(Boolean)) {
+ if (trial.change_border_background_color) {
+ border.style.background = trial.border_color_in;
+ }
+ button.style.visibility = "visible";
+ display_element.querySelector("#jspsych-free-sort-counter").innerHTML =
+ trial.counter_text_finished;
+ }
+ else {
+ border.style.background = "none";
+ button.style.visibility = "hidden";
+ display_element.querySelector("#jspsych-free-sort-counter").innerHTML =
+ get_counter_text(inside.length - inside.filter(Boolean).length);
+ }
+ };
+ document.addEventListener(move_event_name, move_event);
+ var end_event = (e) => {
+ document.removeEventListener(move_event_name, move_event);
+ elem.style.transform = "scale(1, 1)";
+ if (trial.change_border_background_color) {
+ if (inside.every(Boolean)) {
+ border.style.background = trial.border_color_in;
+ border.style.borderColor = trial.border_color_in;
+ }
+ else {
+ border.style.background = "none";
+ border.style.borderColor = border_color_out;
+ }
+ }
+ moves.push({
+ src: elem.dataset.src,
+ x: elem.offsetLeft,
+ y: elem.offsetTop,
+ });
+ document.removeEventListener(end_event_name, end_event);
+ };
+ document.addEventListener(end_event_name, end_event);
+ });
+ }
+ display_element.querySelector("#jspsych-free-sort-done-btn").addEventListener("click", () => {
+ if (inside.every(Boolean)) {
+ const end_time = performance.now();
+ const rt = Math.round(end_time - start_time);
+ // gather data
+ const items = display_element.querySelectorAll(".jspsych-free-sort-draggable");
+ // get final position of all items
+ let final_locations = [];
+ for (let i = 0; i < items.length; i++) {
+ final_locations.push({
+ src: items[i].dataset.src,
+ x: parseInt(items[i].style.left),
+ y: parseInt(items[i].style.top),
+ });
+ }
+ const trial_data = {
+ init_locations: init_locations,
+ moves: moves,
+ final_locations: final_locations,
+ rt: rt,
+ };
+ // advance to next part
+ display_element.innerHTML = "";
+ this.jsPsych.finishTrial(trial_data);
+ }
+ });
+ function get_counter_text(n) {
+ var text_out = "";
+ var text_bits = trial.counter_text_unfinished.split("%");
+ for (var i = 0; i < text_bits.length; i++) {
+ if (i % 2 === 0) {
+ text_out += text_bits[i];
+ }
+ else {
+ if (text_bits[i] == "n") {
+ text_out += n.toString();
+ }
+ else if (text_bits[i] == "s" && n > 1) {
+ text_out += "s";
+ }
+ }
+ }
+ return text_out;
+ }
+ // helper functions
+ function shuffle(array) {
+ // define three variables
+ let cur_idx = array.length, tmp_val, rand_idx;
+ // While there remain elements to shuffle...
+ while (0 !== cur_idx) {
+ // Pick a remaining element...
+ rand_idx = Math.floor(Math.random() * cur_idx);
+ cur_idx -= 1;
+ // And swap it with the current element.
+ tmp_val = array[cur_idx];
+ array[cur_idx] = array[rand_idx];
+ array[rand_idx] = tmp_val;
+ }
+ return array;
+ }
+ function make_arr(startValue, stopValue, cardinality) {
+ const step = (stopValue - startValue) / (cardinality - 1);
+ let arr = [];
+ for (let i = 0; i < cardinality; i++) {
+ arr.push(startValue + step * i);
+ }
+ return arr;
+ }
+ function inside_ellipse(x, y, x0, y0, rx, ry, square = false) {
+ if (square) {
+ return Math.abs(x - x0) <= rx && Math.abs(y - y0) <= ry;
+ }
+ else {
+ return ((x - x0) * (x - x0) * (ry * ry) + (y - y0) * (y - y0) * (rx * rx) <= rx * rx * (ry * ry));
+ }
+ }
+ function random_coordinate(max_width, max_height) {
+ const rnd_x = Math.floor(Math.random() * (max_width - 1));
+ const rnd_y = Math.floor(Math.random() * (max_height - 1));
+ return {
+ x: rnd_x,
+ y: rnd_y,
+ };
+ }
+ }
+ }
+ FreeSortPlugin.info = info;
+
+ return FreeSortPlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-fullscreen.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-fullscreen.js
new file mode 100644
index 00000000..8c1926cd
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-fullscreen.js
@@ -0,0 +1,156 @@
+var jsPsychFullscreen = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "fullscreen",
+ parameters: {
+ /** If true, experiment will enter fullscreen mode. If false, the browser will exit fullscreen mode. */
+ fullscreen_mode: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Fullscreen mode",
+ default: true,
+ array: false,
+ },
+ /** HTML content to display above the button to enter fullscreen mode */
+ message: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Message",
+ default: "
The experiment will switch to full screen mode when you press the button below
",
+ array: false,
+ },
+ /** The text that appears on the button to enter fullscreen */
+ button_label: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Button label",
+ default: "Continue",
+ array: false,
+ },
+ /** The length of time to delay after entering fullscreen mode before ending the trial. */
+ delay_after: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Delay after",
+ default: 1000,
+ array: false,
+ },
+ },
+ };
+ /**
+ * **fullscreen**
+ *
+ * jsPsych plugin for toggling fullscreen mode in the browser
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-fullscreen/ fullscreen plugin documentation on jspsych.org}
+ */
+ class FullscreenPlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ // check if keys are allowed in fullscreen mode
+ var keyboardNotAllowed = typeof Element !== "undefined" && "ALLOW_KEYBOARD_INPUT" in Element;
+ if (keyboardNotAllowed) {
+ // This is Safari, and keyboard events will be disabled. Don't allow fullscreen here.
+ // do something else?
+ this.endTrial(display_element, false, trial);
+ }
+ else {
+ if (trial.fullscreen_mode) {
+ this.showDisplay(display_element, trial);
+ }
+ else {
+ this.exitFullScreen();
+ this.endTrial(display_element, true, trial);
+ }
+ }
+ }
+ showDisplay(display_element, trial) {
+ display_element.innerHTML = `
+ ${trial.message}
+
+ `;
+ display_element.querySelector("#jspsych-fullscreen-btn").addEventListener("click", () => {
+ this.enterFullScreen();
+ this.endTrial(display_element, true, trial);
+ });
+ }
+ endTrial(display_element, success, trial) {
+ display_element.innerHTML = "";
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ var trial_data = {
+ success: success,
+ };
+ this.jsPsych.finishTrial(trial_data);
+ }, trial.delay_after);
+ }
+ enterFullScreen() {
+ var element = document.documentElement;
+ if (element.requestFullscreen) {
+ element.requestFullscreen();
+ }
+ else if (element["mozRequestFullScreen"]) {
+ element["mozRequestFullScreen"]();
+ }
+ else if (element["webkitRequestFullscreen"]) {
+ element["webkitRequestFullscreen"]();
+ }
+ else if (element["msRequestFullscreen"]) {
+ element["msRequestFullscreen"]();
+ }
+ }
+ exitFullScreen() {
+ if (document.fullscreenElement ||
+ document["mozFullScreenElement"] ||
+ document["webkitFullscreenElement"]) {
+ if (document.exitFullscreen) {
+ document.exitFullscreen();
+ }
+ else if (document["msExitFullscreen"]) {
+ document["msExitFullscreen"]();
+ }
+ else if (document["mozCancelFullScreen"]) {
+ document["mozCancelFullScreen"]();
+ }
+ else if (document["webkitExitFullscreen"]) {
+ document["webkitExitFullscreen"]();
+ }
+ }
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ success: true,
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ if (data.success === false) {
+ this.endTrial(display_element, false, trial);
+ }
+ else {
+ this.trial(display_element, trial);
+ load_callback();
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("#jspsych-fullscreen-btn"), this.jsPsych.randomization.sampleExGaussian(1000, 100, 1 / 200, true));
+ }
+ }
+ }
+ FullscreenPlugin.info = info;
+
+ return FullscreenPlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-html-audio-response.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-html-audio-response.js
new file mode 100644
index 00000000..d1f8f924
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-html-audio-response.js
@@ -0,0 +1,208 @@
+var jsPsychHtmlAudioResponse = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "html-audio-response",
+ parameters: {
+ /** The HTML string to be displayed */
+ stimulus: {
+ type: jspsych.ParameterType.HTML_STRING,
+ default: undefined,
+ },
+ /** How long to show the stimulus. */
+ stimulus_duration: {
+ type: jspsych.ParameterType.INT,
+ default: null,
+ },
+ /** How long to show the trial. */
+ recording_duration: {
+ type: jspsych.ParameterType.INT,
+ default: 2000,
+ },
+ show_done_button: {
+ type: jspsych.ParameterType.BOOL,
+ default: true,
+ },
+ done_button_label: {
+ type: jspsych.ParameterType.STRING,
+ default: "Continue",
+ },
+ record_again_button_label: {
+ type: jspsych.ParameterType.STRING,
+ default: "Record again",
+ },
+ accept_button_label: {
+ type: jspsych.ParameterType.STRING,
+ default: "Continue",
+ },
+ allow_playback: {
+ type: jspsych.ParameterType.BOOL,
+ default: false,
+ },
+ save_audio_url: {
+ type: jspsych.ParameterType.BOOL,
+ default: false,
+ },
+ },
+ };
+ /**
+ * html-audio-response
+ * jsPsych plugin for displaying a stimulus and recording an audio response through a microphone
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-html-audio-response/ html-audio-response plugin documentation on jspsych.org}
+ */
+ class HtmlAudioResponsePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ this.rt = null;
+ this.recorded_data_chunks = [];
+ }
+ trial(display_element, trial) {
+ this.recorder = this.jsPsych.pluginAPI.getMicrophoneRecorder();
+ this.setupRecordingEvents(display_element, trial);
+ this.startRecording();
+ }
+ showDisplay(display_element, trial) {
+ const ro = new ResizeObserver((entries, observer) => {
+ this.stimulus_start_time = performance.now();
+ observer.unobserve(display_element);
+ //observer.disconnect();
+ });
+ ro.observe(display_element);
+ let html = `
${trial.stimulus}
`;
+ if (trial.show_done_button) {
+ html += ``;
+ }
+ display_element.innerHTML = html;
+ }
+ hideStimulus(display_element) {
+ const el = display_element.querySelector("#jspsych-html-audio-response-stimulus");
+ if (el) {
+ el.style.visibility = "hidden";
+ }
+ }
+ addButtonEvent(display_element, trial) {
+ const btn = display_element.querySelector("#finish-trial");
+ if (btn) {
+ btn.addEventListener("click", () => {
+ const end_time = performance.now();
+ this.rt = Math.round(end_time - this.stimulus_start_time);
+ this.stopRecording().then(() => {
+ if (trial.allow_playback) {
+ this.showPlaybackControls(display_element, trial);
+ }
+ else {
+ this.endTrial(display_element, trial);
+ }
+ });
+ });
+ }
+ }
+ setupRecordingEvents(display_element, trial) {
+ this.data_available_handler = (e) => {
+ if (e.data.size > 0) {
+ this.recorded_data_chunks.push(e.data);
+ }
+ };
+ this.stop_event_handler = () => {
+ const data = new Blob(this.recorded_data_chunks, { type: "audio/webm" });
+ this.audio_url = URL.createObjectURL(data);
+ const reader = new FileReader();
+ reader.addEventListener("load", () => {
+ const base64 = reader.result.split(",")[1];
+ this.response = base64;
+ this.load_resolver();
+ });
+ reader.readAsDataURL(data);
+ };
+ this.start_event_handler = (e) => {
+ // resets the recorded data
+ this.recorded_data_chunks.length = 0;
+ this.recorder_start_time = e.timeStamp;
+ this.showDisplay(display_element, trial);
+ this.addButtonEvent(display_element, trial);
+ // setup timer for hiding the stimulus
+ if (trial.stimulus_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ this.hideStimulus(display_element);
+ }, trial.stimulus_duration);
+ }
+ // setup timer for ending the trial
+ if (trial.recording_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ // this check is necessary for cases where the
+ // done_button is clicked before the timer expires
+ if (this.recorder.state !== "inactive") {
+ this.stopRecording().then(() => {
+ if (trial.allow_playback) {
+ this.showPlaybackControls(display_element, trial);
+ }
+ else {
+ this.endTrial(display_element, trial);
+ }
+ });
+ }
+ }, trial.recording_duration);
+ }
+ };
+ this.recorder.addEventListener("dataavailable", this.data_available_handler);
+ this.recorder.addEventListener("stop", this.stop_event_handler);
+ this.recorder.addEventListener("start", this.start_event_handler);
+ }
+ startRecording() {
+ this.recorder.start();
+ }
+ stopRecording() {
+ this.recorder.stop();
+ return new Promise((resolve) => {
+ this.load_resolver = resolve;
+ });
+ }
+ showPlaybackControls(display_element, trial) {
+ display_element.innerHTML = `
+
+
+
+ `;
+ display_element.querySelector("#record-again").addEventListener("click", () => {
+ // release object url to save memory
+ URL.revokeObjectURL(this.audio_url);
+ this.startRecording();
+ });
+ display_element.querySelector("#continue").addEventListener("click", () => {
+ this.endTrial(display_element, trial);
+ });
+ // const audio = display_element.querySelector('#playback');
+ // audio.src =
+ }
+ endTrial(display_element, trial) {
+ // clear recordering event handler
+ this.recorder.removeEventListener("dataavailable", this.data_available_handler);
+ this.recorder.removeEventListener("start", this.start_event_handler);
+ this.recorder.removeEventListener("stop", this.stop_event_handler);
+ // kill any remaining setTimeout handlers
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // gather the data to store for the trial
+ var trial_data = {
+ rt: this.rt,
+ stimulus: trial.stimulus,
+ response: this.response,
+ estimated_stimulus_onset: Math.round(this.stimulus_start_time - this.recorder_start_time),
+ };
+ if (trial.save_audio_url) {
+ trial_data.audio_url = this.audio_url;
+ }
+ else {
+ URL.revokeObjectURL(this.audio_url);
+ }
+ // clear the display
+ display_element.innerHTML = "";
+ // move on to the next trial
+ this.jsPsych.finishTrial(trial_data);
+ }
+ }
+ HtmlAudioResponsePlugin.info = info;
+
+ return HtmlAudioResponsePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-html-button-response.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-html-button-response.js
new file mode 100644
index 00000000..08cd3765
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-html-button-response.js
@@ -0,0 +1,216 @@
+var jsPsychHtmlButtonResponse = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "html-button-response",
+ parameters: {
+ /** The HTML string to be displayed */
+ stimulus: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /** Array containing the label(s) for the button(s). */
+ choices: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Choices",
+ default: undefined,
+ array: true,
+ },
+ /** The HTML for creating button. Can create own style. Use the "%choice%" string to indicate where the label from the choices parameter should be inserted. */
+ button_html: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Button HTML",
+ default: '',
+ array: true,
+ },
+ /** Any content here will be displayed under the button(s). */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /** How long to show the stimulus. */
+ stimulus_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Stimulus duration",
+ default: null,
+ },
+ /** How long to show the trial. */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /** The vertical margin of the button. */
+ margin_vertical: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Margin vertical",
+ default: "0px",
+ },
+ /** The horizontal margin of the button. */
+ margin_horizontal: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Margin horizontal",
+ default: "8px",
+ },
+ /** If true, then trial will end when user responds. */
+ response_ends_trial: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response ends trial",
+ default: true,
+ },
+ },
+ };
+ /**
+ * html-button-response
+ * jsPsych plugin for displaying a stimulus and getting a button response
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-html-button-response/ html-button-response plugin documentation on jspsych.org}
+ */
+ class HtmlButtonResponsePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ // display stimulus
+ var html = '
' + trial.stimulus + "
";
+ //display buttons
+ var buttons = [];
+ if (Array.isArray(trial.button_html)) {
+ if (trial.button_html.length == trial.choices.length) {
+ buttons = trial.button_html;
+ }
+ else {
+ console.error("Error in html-button-response plugin. The length of the button_html array does not equal the length of the choices array");
+ }
+ }
+ else {
+ for (var i = 0; i < trial.choices.length; i++) {
+ buttons.push(trial.button_html);
+ }
+ }
+ html += '
';
+ for (var i = 0; i < trial.choices.length; i++) {
+ var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
+ html +=
+ '
' +
+ str +
+ "
";
+ }
+ html += "
";
+ //show prompt if there is one
+ if (trial.prompt !== null) {
+ html += trial.prompt;
+ }
+ display_element.innerHTML = html;
+ // start time
+ var start_time = performance.now();
+ // add event listeners to buttons
+ for (var i = 0; i < trial.choices.length; i++) {
+ display_element
+ .querySelector("#jspsych-html-button-response-button-" + i)
+ .addEventListener("click", (e) => {
+ var btn_el = e.currentTarget;
+ var choice = btn_el.getAttribute("data-choice"); // don't use dataset for jsdom compatibility
+ after_response(choice);
+ });
+ }
+ // store response
+ var response = {
+ rt: null,
+ button: null,
+ };
+ // function to end trial when it is time
+ const end_trial = () => {
+ // kill any remaining setTimeout handlers
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // gather the data to store for the trial
+ var trial_data = {
+ rt: response.rt,
+ stimulus: trial.stimulus,
+ response: response.button,
+ };
+ // clear the display
+ display_element.innerHTML = "";
+ // move on to the next trial
+ this.jsPsych.finishTrial(trial_data);
+ };
+ // function to handle responses by the subject
+ function after_response(choice) {
+ // measure rt
+ var end_time = performance.now();
+ var rt = Math.round(end_time - start_time);
+ response.button = parseInt(choice);
+ response.rt = rt;
+ // after a valid response, the stimulus will have the CSS class 'responded'
+ // which can be used to provide visual feedback that a response was recorded
+ display_element.querySelector("#jspsych-html-button-response-stimulus").className +=
+ " responded";
+ // disable all the buttons after a response
+ var btns = document.querySelectorAll(".jspsych-html-button-response-button button");
+ for (var i = 0; i < btns.length; i++) {
+ //btns[i].removeEventListener('click');
+ btns[i].setAttribute("disabled", "disabled");
+ }
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ }
+ // hide image if timing is set
+ if (trial.stimulus_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ display_element.querySelector("#jspsych-html-button-response-stimulus").style.visibility = "hidden";
+ }, trial.stimulus_duration);
+ }
+ // end trial if time limit is set
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(end_trial, trial.trial_duration);
+ }
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ stimulus: trial.stimulus,
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ response: this.jsPsych.randomization.randomInt(0, trial.choices.length - 1),
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ if (data.rt !== null) {
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector(`div[data-choice="${data.response}"] button`), data.rt);
+ }
+ }
+ }
+ HtmlButtonResponsePlugin.info = info;
+
+ return HtmlButtonResponsePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-html-keyboard-response.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-html-keyboard-response.js
new file mode 100644
index 00000000..c1e319b5
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-html-keyboard-response.js
@@ -0,0 +1,173 @@
+var jsPsychHtmlKeyboardResponse = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "html-keyboard-response",
+ parameters: {
+ /**
+ * The HTML string to be displayed.
+ */
+ stimulus: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /**
+ * Array containing the key(s) the subject is allowed to press to respond to the stimulus.
+ */
+ choices: {
+ type: jspsych.ParameterType.KEYS,
+ pretty_name: "Choices",
+ default: "ALL_KEYS",
+ },
+ /**
+ * Any content here will be displayed below the stimulus.
+ */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /**
+ * How long to show the stimulus.
+ */
+ stimulus_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Stimulus duration",
+ default: null,
+ },
+ /**
+ * How long to show trial before it ends.
+ */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /**
+ * If true, trial will end when subject makes a response.
+ */
+ response_ends_trial: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response ends trial",
+ default: true,
+ },
+ },
+ };
+ /**
+ * **html-keyboard-response**
+ *
+ * jsPsych plugin for displaying a stimulus and getting a keyboard response
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-html-keyboard-response/ html-keyboard-response plugin documentation on jspsych.org}
+ */
+ class HtmlKeyboardResponsePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ var new_html = '
' + trial.stimulus + "
";
+ // add prompt
+ if (trial.prompt !== null) {
+ new_html += trial.prompt;
+ }
+ // draw
+ display_element.innerHTML = new_html;
+ // store response
+ var response = {
+ rt: null,
+ key: null,
+ };
+ // function to end trial when it is time
+ const end_trial = () => {
+ // kill any remaining setTimeout handlers
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // kill keyboard listeners
+ if (typeof keyboardListener !== "undefined") {
+ this.jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
+ }
+ // gather the data to store for the trial
+ var trial_data = {
+ rt: response.rt,
+ stimulus: trial.stimulus,
+ response: response.key,
+ };
+ // clear the display
+ display_element.innerHTML = "";
+ // move on to the next trial
+ this.jsPsych.finishTrial(trial_data);
+ };
+ // function to handle responses by the subject
+ var after_response = (info) => {
+ // after a valid response, the stimulus will have the CSS class 'responded'
+ // which can be used to provide visual feedback that a response was recorded
+ display_element.querySelector("#jspsych-html-keyboard-response-stimulus").className +=
+ " responded";
+ // only record the first response
+ if (response.key == null) {
+ response = info;
+ }
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ };
+ // start the response listener
+ if (trial.choices != "NO_KEYS") {
+ var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: after_response,
+ valid_responses: trial.choices,
+ rt_method: "performance",
+ persist: false,
+ allow_held_key: false,
+ });
+ }
+ // hide stimulus if stimulus_duration is set
+ if (trial.stimulus_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ display_element.querySelector("#jspsych-html-keyboard-response-stimulus").style.visibility = "hidden";
+ }, trial.stimulus_duration);
+ }
+ // end trial if trial_duration is set
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(end_trial, trial.trial_duration);
+ }
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ stimulus: trial.stimulus,
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ response: this.jsPsych.pluginAPI.getValidKey(trial.choices),
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ if (data.rt !== null) {
+ this.jsPsych.pluginAPI.pressKey(data.response, data.rt);
+ }
+ }
+ }
+ HtmlKeyboardResponsePlugin.info = info;
+
+ return HtmlKeyboardResponsePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-html-slider-response.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-html-slider-response.js
new file mode 100644
index 00000000..00f429a9
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-html-slider-response.js
@@ -0,0 +1,258 @@
+var jsPsychHtmlSliderResponse = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "html-slider-response",
+ parameters: {
+ /** The HTML string to be displayed */
+ stimulus: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /** Sets the minimum value of the slider. */
+ min: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Min slider",
+ default: 0,
+ },
+ /** Sets the maximum value of the slider */
+ max: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Max slider",
+ default: 100,
+ },
+ /** Sets the starting value of the slider */
+ slider_start: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Slider starting value",
+ default: 50,
+ },
+ /** Sets the step of the slider */
+ step: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Step",
+ default: 1,
+ },
+ /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */
+ labels: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Labels",
+ default: [],
+ array: true,
+ },
+ /** Width of the slider in pixels. */
+ slider_width: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Slider width",
+ default: null,
+ },
+ /** Label of the button to advance. */
+ button_label: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Button label",
+ default: "Continue",
+ array: false,
+ },
+ /** If true, the participant will have to move the slider before continuing. */
+ require_movement: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Require movement",
+ default: false,
+ },
+ /** Any content here will be displayed below the slider. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /** How long to show the stimulus. */
+ stimulus_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Stimulus duration",
+ default: null,
+ },
+ /** How long to show the trial. */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /** If true, trial will end when user makes a response. */
+ response_ends_trial: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response ends trial",
+ default: true,
+ },
+ },
+ };
+ /**
+ * **html-slider-response**
+ *
+ * jsPsych plugin for showing an HTML stimulus and collecting a slider response
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-html-slider-response/ html-slider-response plugin documentation on jspsych.org}
+ */
+ class HtmlSliderResponsePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ // half of the thumb width value from jspsych.css, used to adjust the label positions
+ var half_thumb_width = 7.5;
+ var html = '
';
+ html += '
' + trial.stimulus + "
";
+ html +=
+ '
';
+ html +=
+ '';
+ html += "
";
+ for (var j = 0; j < trial.labels.length; j++) {
+ var label_width_perc = 100 / (trial.labels.length - 1);
+ var percent_of_range = j * (100 / (trial.labels.length - 1));
+ var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;
+ var offset = (percent_dist_from_center * half_thumb_width) / 100;
+ html +=
+ '
';
+ html += '' + trial.labels[j] + "";
+ html += "
";
+ }
+ html += "
";
+ html += "
";
+ html += "
";
+ if (trial.prompt !== null) {
+ html += trial.prompt;
+ }
+ // add submit button
+ html +=
+ '";
+ display_element.innerHTML = html;
+ var response = {
+ rt: null,
+ response: null,
+ };
+ if (trial.require_movement) {
+ const enable_button = () => {
+ display_element.querySelector("#jspsych-html-slider-response-next").disabled = false;
+ };
+ display_element
+ .querySelector("#jspsych-html-slider-response-response")
+ .addEventListener("mousedown", enable_button);
+ display_element
+ .querySelector("#jspsych-html-slider-response-response")
+ .addEventListener("touchstart", enable_button);
+ display_element
+ .querySelector("#jspsych-html-slider-response-response")
+ .addEventListener("change", enable_button);
+ }
+ const end_trial = () => {
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // save data
+ var trialdata = {
+ rt: response.rt,
+ stimulus: trial.stimulus,
+ slider_start: trial.slider_start,
+ response: response.response,
+ };
+ display_element.innerHTML = "";
+ // next trial
+ this.jsPsych.finishTrial(trialdata);
+ };
+ display_element
+ .querySelector("#jspsych-html-slider-response-next")
+ .addEventListener("click", () => {
+ // measure response time
+ var endTime = performance.now();
+ response.rt = Math.round(endTime - startTime);
+ response.response = display_element.querySelector("#jspsych-html-slider-response-response").valueAsNumber;
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ else {
+ display_element.querySelector("#jspsych-html-slider-response-next").disabled = true;
+ }
+ });
+ if (trial.stimulus_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ display_element.querySelector("#jspsych-html-slider-response-stimulus").style.visibility = "hidden";
+ }, trial.stimulus_duration);
+ }
+ // end trial if trial_duration is set
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(end_trial, trial.trial_duration);
+ }
+ var startTime = performance.now();
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ stimulus: trial.stimulus,
+ slider_start: trial.slider_start,
+ response: this.jsPsych.randomization.randomInt(trial.min, trial.max),
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ if (data.rt !== null) {
+ const el = display_element.querySelector("input[type='range']");
+ setTimeout(() => {
+ this.jsPsych.pluginAPI.clickTarget(el);
+ el.valueAsNumber = data.response;
+ }, data.rt / 2);
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("button"), data.rt);
+ }
+ }
+ }
+ HtmlSliderResponsePlugin.info = info;
+
+ return HtmlSliderResponsePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-iat-html.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-iat-html.js
new file mode 100644
index 00000000..eb2f7b5a
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-iat-html.js
@@ -0,0 +1,340 @@
+var jsPsychIatHtml = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "iat-html",
+ parameters: {
+ /** The HTML string to be displayed. */
+ stimulus: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /** Key press that is associated with the left category label.*/
+ left_category_key: {
+ type: jspsych.ParameterType.KEY,
+ pretty_name: "Left category key",
+ default: "e",
+ },
+ /** Key press that is associated with the right category label. */
+ right_category_key: {
+ type: jspsych.ParameterType.KEY,
+ pretty_name: "Right category key",
+ default: "i",
+ },
+ /** The label that is associated with the stimulus. Aligned to the left side of page */
+ left_category_label: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Left category label",
+ array: true,
+ default: ["left"],
+ },
+ /** The label that is associated with the stimulus. Aligned to the right side of the page. */
+ right_category_label: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Right category label",
+ array: true,
+ default: ["right"],
+ },
+ /** Array containing the key(s) that allow the user to advance to the next trial if their key press was incorrect. */
+ key_to_move_forward: {
+ type: jspsych.ParameterType.KEYS,
+ pretty_name: "Key to move forward",
+ default: "ALL_KEYS",
+ },
+ /** If true, then html when wrong will be displayed when user makes an incorrect key press. */
+ display_feedback: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Display feedback",
+ default: false,
+ },
+ /** The HTML to display when a user presses the wrong key. */
+ html_when_wrong: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "HTML when wrong",
+ default: 'X',
+ },
+ /** Instructions shown at the bottom of the page. */
+ bottom_instructions: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Bottom instructions",
+ default: "
If you press the wrong key, a red X will appear. Press any key to continue.
",
+ },
+ /** If true, in order to advance to the next trial after a wrong key press the user will be forced to press the correct key. */
+ force_correct_key_press: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Force correct key press",
+ default: false,
+ },
+ /** Stimulus will be associated with either "left" or "right". */
+ stim_key_association: {
+ type: jspsych.ParameterType.SELECT,
+ pretty_name: "Stimulus key association",
+ options: ["left", "right"],
+ default: undefined,
+ },
+ /** If true, trial will end when user makes a response. */
+ response_ends_trial: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response ends trial",
+ default: true,
+ },
+ /** How long to show the trial. */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ },
+ };
+ /**
+ * **iat-html**
+ *
+ * jsPsych plugin for running an IAT (Implicit Association Test) with an HTML-formatted stimulus
+ *
+ * @author Kristin Diep
+ * @see {@link https://www.jspsych.org/plugins/jspsych-iat-html/ iat-html plugin documentation on jspsych.org}
+ */
+ class IatHtmlPlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ var html_str = "";
+ html_str +=
+ "
";
+ display_element.innerHTML = html_str;
+ // store response
+ var response = {
+ rt: null,
+ key: null,
+ correct: false,
+ };
+ // function to end trial when it is time
+ const end_trial = () => {
+ // kill any remaining setTimeout handlers
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // kill keyboard listeners
+ if (typeof keyboardListener !== "undefined") {
+ this.jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
+ }
+ // gather the data to store for the trial
+ var trial_data = {
+ rt: response.rt,
+ stimulus: trial.stimulus,
+ response: response.key,
+ correct: response.correct,
+ };
+ // clears the display
+ display_element.innerHTML = "";
+ // move on to the next trial
+ this.jsPsych.finishTrial(trial_data);
+ };
+ var leftKeyCode = trial.left_category_key;
+ var rightKeyCode = trial.right_category_key;
+ // function to handle responses by the subject
+ const after_response = (info) => {
+ var wImg = document.getElementById("wrongImgContainer");
+ // after a valid response, the stimulus will have the CSS class 'responded'
+ // which can be used to provide visual feedback that a response was recorded
+ display_element.querySelector("#jspsych-iat-stim").className += " responded";
+ // only record the first response
+ if (response.key == null) {
+ response.key = info.key;
+ response.rt = info.rt;
+ }
+ if (trial.stim_key_association == "right") {
+ if (response.rt !== null &&
+ this.jsPsych.pluginAPI.compareKeys(response.key, rightKeyCode)) {
+ response.correct = true;
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ }
+ else {
+ response.correct = false;
+ if (!trial.response_ends_trial && trial.display_feedback == true) {
+ wImg.style.visibility = "visible";
+ }
+ if (trial.response_ends_trial && trial.display_feedback == true) {
+ wImg.style.visibility = "visible";
+ if (trial.force_correct_key_press) {
+ this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: end_trial,
+ valid_responses: [trial.right_category_key],
+ });
+ }
+ else {
+ this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: end_trial,
+ valid_responses: trial.key_to_move_forward,
+ });
+ }
+ }
+ else if (trial.response_ends_trial && trial.display_feedback != true) {
+ end_trial();
+ }
+ else if (!trial.response_ends_trial && trial.display_feedback != true) ;
+ }
+ }
+ else if (trial.stim_key_association == "left") {
+ if (response.rt !== null && this.jsPsych.pluginAPI.compareKeys(response.key, leftKeyCode)) {
+ response.correct = true;
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ }
+ else {
+ response.correct = false;
+ if (!trial.response_ends_trial && trial.display_feedback == true) {
+ wImg.style.visibility = "visible";
+ }
+ if (trial.response_ends_trial && trial.display_feedback == true) {
+ wImg.style.visibility = "visible";
+ if (trial.force_correct_key_press) {
+ this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: end_trial,
+ valid_responses: [trial.left_category_key],
+ });
+ }
+ else {
+ this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: end_trial,
+ valid_responses: trial.key_to_move_forward,
+ });
+ }
+ }
+ else if (trial.response_ends_trial && trial.display_feedback != true) {
+ end_trial();
+ }
+ else if (!trial.response_ends_trial && trial.display_feedback != true) ;
+ }
+ }
+ };
+ // start the response listener
+ if (trial.left_category_key != "NO_KEYS" && trial.right_category_key != "NO_KEYS") {
+ var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: after_response,
+ valid_responses: [trial.left_category_key, trial.right_category_key],
+ rt_method: "performance",
+ persist: false,
+ allow_held_key: false,
+ });
+ }
+ // end trial if time limit is set
+ if (trial.trial_duration !== null && trial.response_ends_trial != true) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ end_trial();
+ }, trial.trial_duration);
+ }
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const key = this.jsPsych.pluginAPI.getValidKey([
+ trial.left_category_key,
+ trial.right_category_key,
+ ]);
+ const correct = trial.stim_key_association == "left"
+ ? key == trial.left_category_key
+ : key == trial.right_category_key;
+ const default_data = {
+ stimulus: trial.stimulus,
+ response: key,
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ correct: correct,
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ if (data.response !== null) {
+ this.jsPsych.pluginAPI.pressKey(data.response, data.rt);
+ }
+ const cont_rt = data.rt == null ? trial.trial_duration : data.rt;
+ if (trial.force_correct_key_press) {
+ if (!data.correct) {
+ this.jsPsych.pluginAPI.pressKey(trial.stim_key_association == "left" ? trial.left_category_key : trial.right_category_key, cont_rt + this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true));
+ }
+ }
+ else {
+ this.jsPsych.pluginAPI.pressKey(this.jsPsych.pluginAPI.getValidKey(trial.key_to_move_forward), cont_rt + this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true));
+ }
+ }
+ }
+ IatImagePlugin.info = info;
+
+ return IatImagePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-image-button-response.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-image-button-response.js
new file mode 100644
index 00000000..b8e25a3f
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-image-button-response.js
@@ -0,0 +1,377 @@
+var jsPsychImageButtonResponse = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "image-button-response",
+ parameters: {
+ /** The image to be displayed */
+ stimulus: {
+ type: jspsych.ParameterType.IMAGE,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /** Set the image height in pixels */
+ stimulus_height: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Image height",
+ default: null,
+ },
+ /** Set the image width in pixels */
+ stimulus_width: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Image width",
+ default: null,
+ },
+ /** Maintain the aspect ratio after setting width or height */
+ maintain_aspect_ratio: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Maintain aspect ratio",
+ default: true,
+ },
+ /** Array containing the label(s) for the button(s). */
+ choices: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Choices",
+ default: undefined,
+ array: true,
+ },
+ /** The HTML for creating button. Can create own style. Use the "%choice%" string to indicate where the label from the choices parameter should be inserted. */
+ button_html: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Button HTML",
+ default: '',
+ array: true,
+ },
+ /** Any content here will be displayed under the button. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /** How long to show the stimulus. */
+ stimulus_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Stimulus duration",
+ default: null,
+ },
+ /** How long to show the trial. */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /** The vertical margin of the button. */
+ margin_vertical: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Margin vertical",
+ default: "0px",
+ },
+ /** The horizontal margin of the button. */
+ margin_horizontal: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Margin horizontal",
+ default: "8px",
+ },
+ /** If true, then trial will end when user responds. */
+ response_ends_trial: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response ends trial",
+ default: true,
+ },
+ /**
+ * If true, the image will be drawn onto a canvas element (prevents blank screen between consecutive images in some browsers).
+ * If false, the image will be shown via an img element.
+ */
+ render_on_canvas: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Render on canvas",
+ default: true,
+ },
+ },
+ };
+ /**
+ * **image-button-response**
+ *
+ * jsPsych plugin for displaying an image stimulus and getting a button response
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-image-button-response/ image-button-response plugin documentation on jspsych.org}
+ */
+ class ImageButtonResponsePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ var height, width;
+ var html;
+ if (trial.render_on_canvas) {
+ var image_drawn = false;
+ // first clear the display element (because the render_on_canvas method appends to display_element instead of overwriting it with .innerHTML)
+ if (display_element.hasChildNodes()) {
+ // can't loop through child list because the list will be modified by .removeChild()
+ while (display_element.firstChild) {
+ display_element.removeChild(display_element.firstChild);
+ }
+ }
+ // create canvas element and image
+ var canvas = document.createElement("canvas");
+ canvas.id = "jspsych-image-button-response-stimulus";
+ canvas.style.margin = "0";
+ canvas.style.padding = "0";
+ var ctx = canvas.getContext("2d");
+ var img = new Image();
+ img.onload = () => {
+ // if image wasn't preloaded, then it will need to be drawn whenever it finishes loading
+ if (!image_drawn) {
+ getHeightWidth(); // only possible to get width/height after image loads
+ ctx.drawImage(img, 0, 0, width, height);
+ }
+ };
+ img.src = trial.stimulus;
+ // get/set image height and width - this can only be done after image loads because uses image's naturalWidth/naturalHeight properties
+ const getHeightWidth = () => {
+ if (trial.stimulus_height !== null) {
+ height = trial.stimulus_height;
+ if (trial.stimulus_width == null && trial.maintain_aspect_ratio) {
+ width = img.naturalWidth * (trial.stimulus_height / img.naturalHeight);
+ }
+ }
+ else {
+ height = img.naturalHeight;
+ }
+ if (trial.stimulus_width !== null) {
+ width = trial.stimulus_width;
+ if (trial.stimulus_height == null && trial.maintain_aspect_ratio) {
+ height = img.naturalHeight * (trial.stimulus_width / img.naturalWidth);
+ }
+ }
+ else if (!(trial.stimulus_height !== null && trial.maintain_aspect_ratio)) {
+ // if stimulus width is null, only use the image's natural width if the width value wasn't set
+ // in the if statement above, based on a specified height and maintain_aspect_ratio = true
+ width = img.naturalWidth;
+ }
+ canvas.height = height;
+ canvas.width = width;
+ };
+ getHeightWidth(); // call now, in case image loads immediately (is cached)
+ // create buttons
+ var buttons = [];
+ if (Array.isArray(trial.button_html)) {
+ if (trial.button_html.length == trial.choices.length) {
+ buttons = trial.button_html;
+ }
+ else {
+ console.error("Error in image-button-response plugin. The length of the button_html array does not equal the length of the choices array");
+ }
+ }
+ else {
+ for (var i = 0; i < trial.choices.length; i++) {
+ buttons.push(trial.button_html);
+ }
+ }
+ var btngroup_div = document.createElement("div");
+ btngroup_div.id = "jspsych-image-button-response-btngroup";
+ html = "";
+ for (var i = 0; i < trial.choices.length; i++) {
+ var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
+ html +=
+ '
' +
+ str +
+ "
";
+ }
+ btngroup_div.innerHTML = html;
+ // add canvas to screen and draw image
+ display_element.insertBefore(canvas, null);
+ if (img.complete && Number.isFinite(width) && Number.isFinite(height)) {
+ // if image has loaded and width/height have been set, then draw it now
+ // (don't rely on img onload function to draw image when image is in the cache, because that causes a delay in the image presentation)
+ ctx.drawImage(img, 0, 0, width, height);
+ image_drawn = true;
+ }
+ // add buttons to screen
+ display_element.insertBefore(btngroup_div, canvas.nextElementSibling);
+ // add prompt if there is one
+ if (trial.prompt !== null) {
+ display_element.insertAdjacentHTML("beforeend", trial.prompt);
+ }
+ }
+ else {
+ // display stimulus as an image element
+ html = '';
+ //display buttons
+ var buttons = [];
+ if (Array.isArray(trial.button_html)) {
+ if (trial.button_html.length == trial.choices.length) {
+ buttons = trial.button_html;
+ }
+ else {
+ console.error("Error in image-button-response plugin. The length of the button_html array does not equal the length of the choices array");
+ }
+ }
+ else {
+ for (var i = 0; i < trial.choices.length; i++) {
+ buttons.push(trial.button_html);
+ }
+ }
+ html += '
';
+ for (var i = 0; i < trial.choices.length; i++) {
+ var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
+ html +=
+ '
' +
+ str +
+ "
";
+ }
+ html += "
";
+ // add prompt
+ if (trial.prompt !== null) {
+ html += trial.prompt;
+ }
+ // update the page content
+ display_element.innerHTML = html;
+ // set image dimensions after image has loaded (so that we have access to naturalHeight/naturalWidth)
+ var img = display_element.querySelector("#jspsych-image-button-response-stimulus");
+ if (trial.stimulus_height !== null) {
+ height = trial.stimulus_height;
+ if (trial.stimulus_width == null && trial.maintain_aspect_ratio) {
+ width = img.naturalWidth * (trial.stimulus_height / img.naturalHeight);
+ }
+ }
+ else {
+ height = img.naturalHeight;
+ }
+ if (trial.stimulus_width !== null) {
+ width = trial.stimulus_width;
+ if (trial.stimulus_height == null && trial.maintain_aspect_ratio) {
+ height = img.naturalHeight * (trial.stimulus_width / img.naturalWidth);
+ }
+ }
+ else if (!(trial.stimulus_height !== null && trial.maintain_aspect_ratio)) {
+ // if stimulus width is null, only use the image's natural width if the width value wasn't set
+ // in the if statement above, based on a specified height and maintain_aspect_ratio = true
+ width = img.naturalWidth;
+ }
+ img.style.height = height.toString() + "px";
+ img.style.width = width.toString() + "px";
+ }
+ // start timing
+ var start_time = performance.now();
+ for (var i = 0; i < trial.choices.length; i++) {
+ display_element
+ .querySelector("#jspsych-image-button-response-button-" + i)
+ .addEventListener("click", (e) => {
+ var btn_el = e.currentTarget;
+ var choice = btn_el.getAttribute("data-choice"); // don't use dataset for jsdom compatibility
+ after_response(choice);
+ });
+ }
+ // store response
+ var response = {
+ rt: null,
+ button: null,
+ };
+ // function to end trial when it is time
+ const end_trial = () => {
+ // kill any remaining setTimeout handlers
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // gather the data to store for the trial
+ var trial_data = {
+ rt: response.rt,
+ stimulus: trial.stimulus,
+ response: response.button,
+ };
+ // clear the display
+ display_element.innerHTML = "";
+ // move on to the next trial
+ this.jsPsych.finishTrial(trial_data);
+ };
+ // function to handle responses by the subject
+ function after_response(choice) {
+ // measure rt
+ var end_time = performance.now();
+ var rt = Math.round(end_time - start_time);
+ response.button = parseInt(choice);
+ response.rt = rt;
+ // after a valid response, the stimulus will have the CSS class 'responded'
+ // which can be used to provide visual feedback that a response was recorded
+ display_element.querySelector("#jspsych-image-button-response-stimulus").className +=
+ " responded";
+ // disable all the buttons after a response
+ var btns = document.querySelectorAll(".jspsych-image-button-response-button button");
+ for (var i = 0; i < btns.length; i++) {
+ //btns[i].removeEventListener('click');
+ btns[i].setAttribute("disabled", "disabled");
+ }
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ }
+ // hide image if timing is set
+ if (trial.stimulus_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ display_element.querySelector("#jspsych-image-button-response-stimulus").style.visibility = "hidden";
+ }, trial.stimulus_duration);
+ }
+ // end trial if time limit is set
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ end_trial();
+ }, trial.trial_duration);
+ }
+ else if (trial.response_ends_trial === false) {
+ console.warn("The experiment may be deadlocked. Try setting a trial duration or set response_ends_trial to true.");
+ }
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ stimulus: trial.stimulus,
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ response: this.jsPsych.randomization.randomInt(0, trial.choices.length - 1),
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ if (data.rt !== null) {
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector(`div[data-choice="${data.response}"] button`), data.rt);
+ }
+ }
+ }
+ ImageButtonResponsePlugin.info = info;
+
+ return ImageButtonResponsePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-image-keyboard-response.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-image-keyboard-response.js
new file mode 100644
index 00000000..e4b96f02
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-image-keyboard-response.js
@@ -0,0 +1,284 @@
+var jsPsychImageKeyboardResponse = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "image-keyboard-response",
+ parameters: {
+ /** The image to be displayed */
+ stimulus: {
+ type: jspsych.ParameterType.IMAGE,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /** Set the image height in pixels */
+ stimulus_height: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Image height",
+ default: null,
+ },
+ /** Set the image width in pixels */
+ stimulus_width: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Image width",
+ default: null,
+ },
+ /** Maintain the aspect ratio after setting width or height */
+ maintain_aspect_ratio: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Maintain aspect ratio",
+ default: true,
+ },
+ /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
+ choices: {
+ type: jspsych.ParameterType.KEYS,
+ pretty_name: "Choices",
+ default: "ALL_KEYS",
+ },
+ /** Any content here will be displayed below the stimulus. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /** How long to show the stimulus. */
+ stimulus_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Stimulus duration",
+ default: null,
+ },
+ /** How long to show trial before it ends */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /** If true, trial will end when subject makes a response. */
+ response_ends_trial: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response ends trial",
+ default: true,
+ },
+ /**
+ * If true, the image will be drawn onto a canvas element (prevents blank screen between consecutive images in some browsers).
+ * If false, the image will be shown via an img element.
+ */
+ render_on_canvas: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Render on canvas",
+ default: true,
+ },
+ },
+ };
+ /**
+ * **image-keyboard-response**
+ *
+ * jsPsych plugin for displaying an image stimulus and getting a keyboard response
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-image-keyboard-response/ image-keyboard-response plugin documentation on jspsych.org}
+ */
+ class ImageKeyboardResponsePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ var height, width;
+ if (trial.render_on_canvas) {
+ var image_drawn = false;
+ // first clear the display element (because the render_on_canvas method appends to display_element instead of overwriting it with .innerHTML)
+ if (display_element.hasChildNodes()) {
+ // can't loop through child list because the list will be modified by .removeChild()
+ while (display_element.firstChild) {
+ display_element.removeChild(display_element.firstChild);
+ }
+ }
+ // create canvas element and image
+ var canvas = document.createElement("canvas");
+ canvas.id = "jspsych-image-keyboard-response-stimulus";
+ canvas.style.margin = "0";
+ canvas.style.padding = "0";
+ var ctx = canvas.getContext("2d");
+ var img = new Image();
+ img.onload = () => {
+ // if image wasn't preloaded, then it will need to be drawn whenever it finishes loading
+ if (!image_drawn) {
+ getHeightWidth(); // only possible to get width/height after image loads
+ ctx.drawImage(img, 0, 0, width, height);
+ }
+ };
+ img.src = trial.stimulus;
+ // get/set image height and width - this can only be done after image loads because uses image's naturalWidth/naturalHeight properties
+ const getHeightWidth = () => {
+ if (trial.stimulus_height !== null) {
+ height = trial.stimulus_height;
+ if (trial.stimulus_width == null && trial.maintain_aspect_ratio) {
+ width = img.naturalWidth * (trial.stimulus_height / img.naturalHeight);
+ }
+ }
+ else {
+ height = img.naturalHeight;
+ }
+ if (trial.stimulus_width !== null) {
+ width = trial.stimulus_width;
+ if (trial.stimulus_height == null && trial.maintain_aspect_ratio) {
+ height = img.naturalHeight * (trial.stimulus_width / img.naturalWidth);
+ }
+ }
+ else if (!(trial.stimulus_height !== null && trial.maintain_aspect_ratio)) {
+ // if stimulus width is null, only use the image's natural width if the width value wasn't set
+ // in the if statement above, based on a specified height and maintain_aspect_ratio = true
+ width = img.naturalWidth;
+ }
+ canvas.height = height;
+ canvas.width = width;
+ };
+ getHeightWidth(); // call now, in case image loads immediately (is cached)
+ // add canvas and draw image
+ display_element.insertBefore(canvas, null);
+ if (img.complete && Number.isFinite(width) && Number.isFinite(height)) {
+ // if image has loaded and width/height have been set, then draw it now
+ // (don't rely on img onload function to draw image when image is in the cache, because that causes a delay in the image presentation)
+ ctx.drawImage(img, 0, 0, width, height);
+ image_drawn = true;
+ }
+ // add prompt if there is one
+ if (trial.prompt !== null) {
+ display_element.insertAdjacentHTML("beforeend", trial.prompt);
+ }
+ }
+ else {
+ // display stimulus as an image element
+ var html = '';
+ // add prompt
+ if (trial.prompt !== null) {
+ html += trial.prompt;
+ }
+ // update the page content
+ display_element.innerHTML = html;
+ // set image dimensions after image has loaded (so that we have access to naturalHeight/naturalWidth)
+ var img = display_element.querySelector("#jspsych-image-keyboard-response-stimulus");
+ if (trial.stimulus_height !== null) {
+ height = trial.stimulus_height;
+ if (trial.stimulus_width == null && trial.maintain_aspect_ratio) {
+ width = img.naturalWidth * (trial.stimulus_height / img.naturalHeight);
+ }
+ }
+ else {
+ height = img.naturalHeight;
+ }
+ if (trial.stimulus_width !== null) {
+ width = trial.stimulus_width;
+ if (trial.stimulus_height == null && trial.maintain_aspect_ratio) {
+ height = img.naturalHeight * (trial.stimulus_width / img.naturalWidth);
+ }
+ }
+ else if (!(trial.stimulus_height !== null && trial.maintain_aspect_ratio)) {
+ // if stimulus width is null, only use the image's natural width if the width value wasn't set
+ // in the if statement above, based on a specified height and maintain_aspect_ratio = true
+ width = img.naturalWidth;
+ }
+ img.style.height = height.toString() + "px";
+ img.style.width = width.toString() + "px";
+ }
+ // store response
+ var response = {
+ rt: null,
+ key: null,
+ };
+ // function to end trial when it is time
+ const end_trial = () => {
+ // kill any remaining setTimeout handlers
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // kill keyboard listeners
+ if (typeof keyboardListener !== "undefined") {
+ this.jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
+ }
+ // gather the data to store for the trial
+ var trial_data = {
+ rt: response.rt,
+ stimulus: trial.stimulus,
+ response: response.key,
+ };
+ // clear the display
+ display_element.innerHTML = "";
+ // move on to the next trial
+ this.jsPsych.finishTrial(trial_data);
+ };
+ // function to handle responses by the subject
+ var after_response = (info) => {
+ // after a valid response, the stimulus will have the CSS class 'responded'
+ // which can be used to provide visual feedback that a response was recorded
+ display_element.querySelector("#jspsych-image-keyboard-response-stimulus").className +=
+ " responded";
+ // only record the first response
+ if (response.key == null) {
+ response = info;
+ }
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ };
+ // start the response listener
+ if (trial.choices != "NO_KEYS") {
+ var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({
+ callback_function: after_response,
+ valid_responses: trial.choices,
+ rt_method: "performance",
+ persist: false,
+ allow_held_key: false,
+ });
+ }
+ // hide stimulus if stimulus_duration is set
+ if (trial.stimulus_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ display_element.querySelector("#jspsych-image-keyboard-response-stimulus").style.visibility = "hidden";
+ }, trial.stimulus_duration);
+ }
+ // end trial if trial_duration is set
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ end_trial();
+ }, trial.trial_duration);
+ }
+ else if (trial.response_ends_trial === false) {
+ console.warn("The experiment may be deadlocked. Try setting a trial duration or set response_ends_trial to true.");
+ }
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ if (data.rt !== null) {
+ this.jsPsych.pluginAPI.pressKey(data.response, data.rt);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ stimulus: trial.stimulus,
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ response: this.jsPsych.pluginAPI.getValidKey(trial.choices),
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ }
+ ImageKeyboardResponsePlugin.info = info;
+
+ return ImageKeyboardResponsePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-image-slider-response.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-image-slider-response.js
new file mode 100644
index 00000000..47068c0c
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-image-slider-response.js
@@ -0,0 +1,447 @@
+var jsPsychImageSliderResponse = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "image-slider-response",
+ parameters: {
+ /** The image to be displayed */
+ stimulus: {
+ type: jspsych.ParameterType.IMAGE,
+ pretty_name: "Stimulus",
+ default: undefined,
+ },
+ /** Set the image height in pixels */
+ stimulus_height: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Image height",
+ default: null,
+ },
+ /** Set the image width in pixels */
+ stimulus_width: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Image width",
+ default: null,
+ },
+ /** Maintain the aspect ratio after setting width or height */
+ maintain_aspect_ratio: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Maintain aspect ratio",
+ default: true,
+ },
+ /** Sets the minimum value of the slider. */
+ min: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Min slider",
+ default: 0,
+ },
+ /** Sets the maximum value of the slider */
+ max: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Max slider",
+ default: 100,
+ },
+ /** Sets the starting value of the slider */
+ slider_start: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Slider starting value",
+ default: 50,
+ },
+ /** Sets the step of the slider */
+ step: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Step",
+ default: 1,
+ },
+ /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */
+ labels: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Labels",
+ default: [],
+ array: true,
+ },
+ /** Width of the slider in pixels. */
+ slider_width: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Slider width",
+ default: null,
+ },
+ /** Label of the button to advance. */
+ button_label: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Button label",
+ default: "Continue",
+ array: false,
+ },
+ /** If true, the participant will have to move the slider before continuing. */
+ require_movement: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Require movement",
+ default: false,
+ },
+ /** Any content here will be displayed below the slider. */
+ prompt: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Prompt",
+ default: null,
+ },
+ /** How long to show the stimulus. */
+ stimulus_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Stimulus duration",
+ default: null,
+ },
+ /** How long to show the trial. */
+ trial_duration: {
+ type: jspsych.ParameterType.INT,
+ pretty_name: "Trial duration",
+ default: null,
+ },
+ /** If true, trial will end when user makes a response. */
+ response_ends_trial: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Response ends trial",
+ default: true,
+ },
+ /**
+ * If true, the image will be drawn onto a canvas element (prevents blank screen between consecutive images in some browsers).
+ * If false, the image will be shown via an img element.
+ */
+ render_on_canvas: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Render on canvas",
+ default: true,
+ },
+ },
+ };
+ /**
+ * **image-slider-response**
+ *
+ * jsPsych plugin for showing an image stimulus and getting a slider response
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-image-slider-response/ image-slider-response plugin documentation on jspsych.org}
+ */
+ class ImageSliderResponsePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ var height, width;
+ var html;
+ // half of the thumb width value from jspsych.css, used to adjust the label positions
+ var half_thumb_width = 7.5;
+ if (trial.render_on_canvas) {
+ var image_drawn = false;
+ // first clear the display element (because the render_on_canvas method appends to display_element instead of overwriting it with .innerHTML)
+ if (display_element.hasChildNodes()) {
+ // can't loop through child list because the list will be modified by .removeChild()
+ while (display_element.firstChild) {
+ display_element.removeChild(display_element.firstChild);
+ }
+ }
+ // create wrapper div, canvas element and image
+ var content_wrapper = document.createElement("div");
+ content_wrapper.id = "jspsych-image-slider-response-wrapper";
+ content_wrapper.style.margin = "100px 0px";
+ var canvas = document.createElement("canvas");
+ canvas.id = "jspsych-image-slider-response-stimulus";
+ canvas.style.margin = "0";
+ canvas.style.padding = "0";
+ var ctx = canvas.getContext("2d");
+ var img = new Image();
+ img.onload = () => {
+ // if image wasn't preloaded, then it will need to be drawn whenever it finishes loading
+ if (!image_drawn) {
+ getHeightWidth(); // only possible to get width/height after image loads
+ ctx.drawImage(img, 0, 0, width, height);
+ }
+ };
+ img.src = trial.stimulus;
+ // get/set image height and width - this can only be done after image loads because uses image's naturalWidth/naturalHeight properties
+ const getHeightWidth = () => {
+ if (trial.stimulus_height !== null) {
+ height = trial.stimulus_height;
+ if (trial.stimulus_width == null && trial.maintain_aspect_ratio) {
+ width = img.naturalWidth * (trial.stimulus_height / img.naturalHeight);
+ }
+ }
+ else {
+ height = img.naturalHeight;
+ }
+ if (trial.stimulus_width !== null) {
+ width = trial.stimulus_width;
+ if (trial.stimulus_height == null && trial.maintain_aspect_ratio) {
+ height = img.naturalHeight * (trial.stimulus_width / img.naturalWidth);
+ }
+ }
+ else if (!(trial.stimulus_height !== null && trial.maintain_aspect_ratio)) {
+ // if stimulus width is null, only use the image's natural width if the width value wasn't set
+ // in the if statement above, based on a specified height and maintain_aspect_ratio = true
+ width = img.naturalWidth;
+ }
+ canvas.height = height;
+ canvas.width = width;
+ };
+ getHeightWidth(); // call now, in case image loads immediately (is cached)
+ // create container with slider and labels
+ var slider_container = document.createElement("div");
+ slider_container.classList.add("jspsych-image-slider-response-container");
+ slider_container.style.position = "relative";
+ slider_container.style.margin = "0 auto 3em auto";
+ if (trial.slider_width !== null) {
+ slider_container.style.width = trial.slider_width.toString() + "px";
+ }
+ // create html string with slider and labels, and add to slider container
+ html =
+ '';
+ html += "
";
+ for (var j = 0; j < trial.labels.length; j++) {
+ var label_width_perc = 100 / (trial.labels.length - 1);
+ var percent_of_range = j * (100 / (trial.labels.length - 1));
+ var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;
+ var offset = (percent_dist_from_center * half_thumb_width) / 100;
+ html +=
+ '
';
+ html += '' + trial.labels[j] + "";
+ html += "
";
+ }
+ html += "
";
+ slider_container.innerHTML = html;
+ // add canvas and slider to content wrapper div
+ content_wrapper.insertBefore(canvas, content_wrapper.firstElementChild);
+ content_wrapper.insertBefore(slider_container, canvas.nextElementSibling);
+ // add content wrapper div to screen and draw image on canvas
+ display_element.insertBefore(content_wrapper, null);
+ if (img.complete && Number.isFinite(width) && Number.isFinite(height)) {
+ // if image has loaded and width/height have been set, then draw it now
+ // (don't rely on img onload function to draw image when image is in the cache, because that causes a delay in the image presentation)
+ ctx.drawImage(img, 0, 0, width, height);
+ image_drawn = true;
+ }
+ // add prompt if there is one
+ if (trial.prompt !== null) {
+ display_element.insertAdjacentHTML("beforeend", trial.prompt);
+ }
+ // add submit button
+ var submit_btn = document.createElement("button");
+ submit_btn.id = "jspsych-image-slider-response-next";
+ submit_btn.classList.add("jspsych-btn");
+ submit_btn.disabled = trial.require_movement ? true : false;
+ submit_btn.innerHTML = trial.button_label;
+ display_element.insertBefore(submit_btn, display_element.nextElementSibling);
+ }
+ else {
+ html = '
';
+ html += '
';
+ html += '';
+ html += "
";
+ html +=
+ '
';
+ html +=
+ '';
+ html += "
";
+ for (var j = 0; j < trial.labels.length; j++) {
+ var label_width_perc = 100 / (trial.labels.length - 1);
+ var percent_of_range = j * (100 / (trial.labels.length - 1));
+ var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;
+ var offset = (percent_dist_from_center * half_thumb_width) / 100;
+ html +=
+ '
';
+ html += '' + trial.labels[j] + "";
+ html += "
";
+ }
+ html += "
";
+ html += "
";
+ html += "
";
+ if (trial.prompt !== null) {
+ html += trial.prompt;
+ }
+ // add submit button
+ html +=
+ '";
+ display_element.innerHTML = html;
+ // set image dimensions after image has loaded (so that we have access to naturalHeight/naturalWidth)
+ var img = display_element.querySelector("img");
+ if (trial.stimulus_height !== null) {
+ height = trial.stimulus_height;
+ if (trial.stimulus_width == null && trial.maintain_aspect_ratio) {
+ width = img.naturalWidth * (trial.stimulus_height / img.naturalHeight);
+ }
+ }
+ else {
+ height = img.naturalHeight;
+ }
+ if (trial.stimulus_width !== null) {
+ width = trial.stimulus_width;
+ if (trial.stimulus_height == null && trial.maintain_aspect_ratio) {
+ height = img.naturalHeight * (trial.stimulus_width / img.naturalWidth);
+ }
+ }
+ else if (!(trial.stimulus_height !== null && trial.maintain_aspect_ratio)) {
+ // if stimulus width is null, only use the image's natural width if the width value wasn't set
+ // in the if statement above, based on a specified height and maintain_aspect_ratio = true
+ width = img.naturalWidth;
+ }
+ img.style.height = height.toString() + "px";
+ img.style.width = width.toString() + "px";
+ }
+ var response = {
+ rt: null,
+ response: null,
+ };
+ if (trial.require_movement) {
+ const enable_button = () => {
+ display_element.querySelector("#jspsych-image-slider-response-next").disabled = false;
+ };
+ display_element
+ .querySelector("#jspsych-image-slider-response-response")
+ .addEventListener("mousedown", enable_button);
+ display_element
+ .querySelector("#jspsych-image-slider-response-response")
+ .addEventListener("touchstart", enable_button);
+ display_element
+ .querySelector("#jspsych-image-slider-response-response")
+ .addEventListener("change", enable_button);
+ }
+ const end_trial = () => {
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ // save data
+ var trialdata = {
+ rt: response.rt,
+ stimulus: trial.stimulus,
+ slider_start: trial.slider_start,
+ response: response.response,
+ };
+ display_element.innerHTML = "";
+ // next trial
+ this.jsPsych.finishTrial(trialdata);
+ };
+ display_element
+ .querySelector("#jspsych-image-slider-response-next")
+ .addEventListener("click", () => {
+ // measure response time
+ var endTime = performance.now();
+ response.rt = Math.round(endTime - startTime);
+ response.response = display_element.querySelector("#jspsych-image-slider-response-response").valueAsNumber;
+ if (trial.response_ends_trial) {
+ end_trial();
+ }
+ else {
+ display_element.querySelector("#jspsych-image-slider-response-next").disabled = true;
+ }
+ });
+ if (trial.stimulus_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ display_element.querySelector("#jspsych-image-slider-response-stimulus").style.visibility = "hidden";
+ }, trial.stimulus_duration);
+ }
+ // end trial if trial_duration is set
+ if (trial.trial_duration !== null) {
+ this.jsPsych.pluginAPI.setTimeout(() => {
+ end_trial();
+ }, trial.trial_duration);
+ }
+ var startTime = performance.now();
+ }
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
+ if (simulation_mode == "data-only") {
+ load_callback();
+ this.simulate_data_only(trial, simulation_options);
+ }
+ if (simulation_mode == "visual") {
+ this.simulate_visual(trial, simulation_options, load_callback);
+ }
+ }
+ create_simulation_data(trial, simulation_options) {
+ const default_data = {
+ stimulus: trial.stimulus,
+ slider_start: trial.slider_start,
+ response: this.jsPsych.randomization.randomInt(trial.min, trial.max),
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
+ };
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
+ return data;
+ }
+ simulate_data_only(trial, simulation_options) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ this.jsPsych.finishTrial(data);
+ }
+ simulate_visual(trial, simulation_options, load_callback) {
+ const data = this.create_simulation_data(trial, simulation_options);
+ const display_element = this.jsPsych.getDisplayElement();
+ this.trial(display_element, trial);
+ load_callback();
+ if (data.rt !== null) {
+ const el = display_element.querySelector("input[type='range']");
+ setTimeout(() => {
+ this.jsPsych.pluginAPI.clickTarget(el);
+ el.valueAsNumber = data.response;
+ }, data.rt / 2);
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("button"), data.rt);
+ }
+ }
+ }
+ ImageSliderResponsePlugin.info = info;
+
+ return ImageSliderResponsePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-initialize-microphone.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-initialize-microphone.js
new file mode 100644
index 00000000..e5ace86f
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-initialize-microphone.js
@@ -0,0 +1,119 @@
+var jsPsychInitializeMicrophone = (function (jspsych) {
+ 'use strict';
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+
+ function __awaiter(thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+ }
+
+ const info = {
+ name: "initialize-microphone",
+ parameters: {
+ /** Function to call */
+ device_select_message: {
+ type: jspsych.ParameterType.HTML_STRING,
+ default: `
Please select the microphone you would like to use.
`,
+ },
+ /** Is the function call asynchronous? */
+ button_label: {
+ type: jspsych.ParameterType.STRING,
+ default: "Use this microphone",
+ },
+ },
+ };
+ /**
+ * **initialize-microphone**
+ *
+ * jsPsych plugin for getting permission to initialize a microphone
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-initialize-microphone/ initialize-microphone plugin documentation on jspsych.org}
+ */
+ class InitializeMicrophonePlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ this.run_trial(display_element, trial).then((id) => {
+ this.jsPsych.finishTrial({
+ device_id: id,
+ });
+ });
+ }
+ run_trial(display_element, trial) {
+ return __awaiter(this, void 0, void 0, function* () {
+ yield this.askForPermission();
+ this.showMicrophoneSelection(display_element, trial);
+ this.updateDeviceList(display_element);
+ navigator.mediaDevices.ondevicechange = (e) => {
+ this.updateDeviceList(display_element);
+ };
+ const mic_id = yield this.waitForSelection(display_element);
+ const stream = yield navigator.mediaDevices.getUserMedia({ audio: { deviceId: mic_id } });
+ this.jsPsych.pluginAPI.initializeMicrophoneRecorder(stream);
+ return mic_id;
+ });
+ }
+ askForPermission() {
+ return __awaiter(this, void 0, void 0, function* () {
+ const stream = yield navigator.mediaDevices.getUserMedia({ audio: true, video: false });
+ return stream;
+ });
+ }
+ showMicrophoneSelection(display_element, trial) {
+ let html = `
+ ${trial.device_select_message}
+
+ `;
+ display_element.innerHTML = html;
+ }
+ waitForSelection(display_element) {
+ return new Promise((resolve) => {
+ display_element.querySelector("#btn-select-mic").addEventListener("click", () => {
+ const mic = display_element.querySelector("#which-mic").value;
+ resolve(mic);
+ });
+ });
+ }
+ updateDeviceList(display_element) {
+ navigator.mediaDevices.enumerateDevices().then((devices) => {
+ const mics = devices.filter((d) => d.kind === "audioinput" && d.deviceId !== "default" && d.deviceId !== "communications");
+ // remove entries with duplicate groupID
+ const unique_mics = mics.filter((mic, index, arr) => arr.findIndex((v) => v.groupId == mic.groupId) == index);
+ // reset the list by clearing all current options
+ display_element.querySelector("#which-mic").innerHTML = "";
+ unique_mics.forEach((d) => {
+ let el = document.createElement("option");
+ el.value = d.deviceId;
+ el.innerHTML = d.label;
+ display_element.querySelector("#which-mic").appendChild(el);
+ });
+ });
+ }
+ }
+ InitializeMicrophonePlugin.info = info;
+
+ return InitializeMicrophonePlugin;
+
+})(jsPsychModule);
diff --git a/app/static/lib/jspsych-7.2.1/plugins/plugin-instructions.js b/app/static/lib/jspsych-7.2.1/plugins/plugin-instructions.js
new file mode 100644
index 00000000..315828a3
--- /dev/null
+++ b/app/static/lib/jspsych-7.2.1/plugins/plugin-instructions.js
@@ -0,0 +1,300 @@
+var jsPsychInstructions = (function (jspsych) {
+ 'use strict';
+
+ const info = {
+ name: "instructions",
+ parameters: {
+ /** Each element of the array is the HTML-formatted content for a single page. */
+ pages: {
+ type: jspsych.ParameterType.HTML_STRING,
+ pretty_name: "Pages",
+ default: undefined,
+ array: true,
+ },
+ /** The key the subject can press in order to advance to the next page. */
+ key_forward: {
+ type: jspsych.ParameterType.KEY,
+ pretty_name: "Key forward",
+ default: "ArrowRight",
+ },
+ /** The key that the subject can press to return to the previous page. */
+ key_backward: {
+ type: jspsych.ParameterType.KEY,
+ pretty_name: "Key backward",
+ default: "ArrowLeft",
+ },
+ /** If true, the subject can return to the previous page of the instructions. */
+ allow_backward: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Allow backward",
+ default: true,
+ },
+ /** If true, the subject can use keyboard keys to navigate the pages. */
+ allow_keys: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Allow keys",
+ default: true,
+ },
+ /** If true, then a "Previous" and "Next" button will be displayed beneath the instructions. */
+ show_clickable_nav: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Show clickable nav",
+ default: false,
+ },
+ /** If true, and clickable navigation is enabled, then Page x/y will be shown between the nav buttons. */
+ show_page_number: {
+ type: jspsych.ParameterType.BOOL,
+ pretty_name: "Show page number",
+ default: false,
+ },
+ /** The text that appears before x/y (current/total) pages displayed with show_page_number. */
+ page_label: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Page label",
+ default: "Page",
+ },
+ /** The text that appears on the button to go backwards. */
+ button_label_previous: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Button label previous",
+ default: "Previous",
+ },
+ /** The text that appears on the button to go forwards. */
+ button_label_next: {
+ type: jspsych.ParameterType.STRING,
+ pretty_name: "Button label next",
+ default: "Next",
+ },
+ },
+ };
+ /**
+ * **instructions**
+ *
+ * jsPsych plugin to display text (including HTML-formatted strings) during the experiment.
+ * Use it to show a set of pages that participants can move forward/backward through.
+ * Page numbers can be displayed to help with navigation by setting show_page_number to true.
+ *
+ * @author Josh de Leeuw
+ * @see {@link https://www.jspsych.org/plugins/jspsych-instructions/ instructions plugin documentation on jspsych.org}
+ */
+ class InstructionsPlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+ trial(display_element, trial) {
+ var current_page = 0;
+ var view_history = [];
+ var start_time = performance.now();
+ var last_page_update_time = start_time;
+ function btnListener(evt) {
+ evt.target.removeEventListener("click", btnListener);
+ if (this.id === "jspsych-instructions-back") {
+ back();
+ }
+ else if (this.id === "jspsych-instructions-next") {
+ next();
+ }
+ }
+ function show_current_page() {
+ var html = trial.pages[current_page];
+ var pagenum_display = "";
+ if (trial.show_page_number) {
+ pagenum_display =
+ "" +
+ trial.page_label +
+ " " +
+ (current_page + 1) +
+ "/" +
+ trial.pages.length +
+ "";
+ }
+ if (trial.show_clickable_nav) {
+ var nav_html = "