mirror of
https://github.com/jspsych/jsPsych.git
synced 2025-05-11 16:18:11 +00:00
Merge branch 'master' of https://github.com/jodeleeuw/jsPsych
Need to update local repo
This commit is contained in:
commit
3272be54a6
@ -1,6 +1,6 @@
|
||||

|
||||
|
||||
jsPsych is a JavaScript library for creating and running behavioral experiments in a web browser. jsPsych simplifies the process of coding browser-based experiments by providing a set of flexible plugins that define different kinds of tasks a subject could complete during an experiment. By assembling different plugins together and customizing the parameters of each, it is possible to create many different types of experiments.
|
||||
jsPsych is a JavaScript library for creating behavioral experiments that run in a web browser. jsPsych creates a framework for defining experiments and provides a set of flexible plugins that create different kinds of tasks a subject could complete during an experiment. By assembling different plugins together and customizing the parameters of each, it is possible to create many different types of experiments.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
@ -27,10 +27,10 @@ de Leeuw, J.R. (2015). jsPsych: A JavaScript library for creating behavioral exp
|
||||
Response times
|
||||
--------------
|
||||
|
||||
Wondering if jsPsych can be used for research that depends on accurate response time measurement? In general, the answer is yes. Response time measurements in jsPsych (and JavaScript in general) are comparable to those taken in standard lab software like Psychophysics Toolbox and E-Prime. Response times measured in JavaScript tend to be a little bit longer (10-40ms), but have similar variance. See the following references for extensive work on this topic.
|
||||
Wondering if jsPsych can be used for research that depends on accurate response time measurement? For most purposes, the answer is yes. Response time measurements in jsPsych (and JavaScript in general) are comparable to those taken in standard lab software like Psychophysics Toolbox and E-Prime. Response times measured in JavaScript tend to be a little bit longer (10-40ms), but have similar variance. See the following references for extensive work on this topic.
|
||||
|
||||
* [de Leeuw, J. R., & Motz, B. A. (2016). Psychophysics in a Web browser? Comparing response times collected with JavaScript and Psychophysics Toolbox in a visual search task. *Behavior Research Methods*, *48*(1), 1-12.](http://link.springer.com/article/10.3758%2Fs13428-015-0567-2)
|
||||
* [Hilbig, B. E. (in press). Reaction time effects in lab- versus web-based research: Experimental evidence. *Behavior Research Methods*.](http://dx.doi.org/10.3758/s13428-015-0678-9)
|
||||
* [Hilbig, B. E. (2016). Reaction time effects in lab- versus web-based research: Experimental evidence. *Behavior Research Methods*, *48*(4), 1718-1724.](http://dx.doi.org/10.3758/s13428-015-0678-9)
|
||||
* [Pinet, S., Zielinski, C., Mathôt, S. et al. (in press). Measuring sequences of keystrokes with jsPsych: Reliability of response times and interkeystroke intervals. *Behavior Research Methods*.](http://link.springer.com/article/10.3758/s13428-016-0776-3)
|
||||
* [Reimers, S., & Stewart, N. (2015). Presentation and response time accuracy in Adobe Flash and HTML5/JavaScript Web experiments. *Behavior Research Methods*, *47*(2), 309-327.](http://link.springer.com/article/10.3758%2Fs13428-014-0471-1)
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
The following people have contributed to the development of jsPsych by writing code, documentation, and/or suggesting major improvements (in alphabetical order):
|
||||
The following people have contributed to the development of jsPsych by writing code, documentation, and/or suggesting improvements (in alphabetical order):
|
||||
* Xiaolu Bai - https://github.com/lbai001
|
||||
* Jason Carpenter
|
||||
* Josh de Leeuw - https://github.com/jodeleeuw
|
||||
* Steve Chao - https://github.com/stchao
|
||||
* Jana Klaus - https://github.com/janakl4us
|
||||
* Jonas Lambers
|
||||
* Shane Martin - https://github.com/shamrt
|
||||
* Adrian Oesch - https://github.com/adrianoesch
|
||||
* Junyan Qi - https://github.com/GavinQ1
|
||||
* Dan Rivas - https://github.com/rivasd
|
||||
* Marian Sauter - https://github.com/mariansauter
|
||||
* Tim Vergenz - https://github.com/vergenzt
|
||||
* Matteo Visconti di Oleggio Castello - https://github.com/mvdoc
|
||||
* Wolfgang Walther - https://github.com/wolfgangwalther
|
||||
* Erik Weitnauer - https://github.com/eweitnauer
|
||||
* Rob Wilkinson - https://github.com/RobAWilkinson
|
||||
|
@ -62,7 +62,7 @@ input[type="text"] {
|
||||
.jspsych-btn {
|
||||
display: inline-block;
|
||||
padding: 6px 12px;
|
||||
margin: 0px 8px;
|
||||
margin: 0px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
font-family: 'Open Sans', 'Arial', sans-serif;
|
||||
@ -98,7 +98,6 @@ input[type="text"] {
|
||||
#jspsych-progressbar-outer {
|
||||
background-color: #dedede;
|
||||
border-radius: 5px;
|
||||
padding: 1px;
|
||||
width: 800px;
|
||||
margin: auto;
|
||||
}
|
||||
|
@ -306,7 +306,6 @@ show_progress_bar | boolean | If true, then [a progress bar](../features/progres
|
||||
preload_audio | array | An array of audio files to preload before starting the experiment.
|
||||
preload_images | array | An array of image files to preload before starting the experiment.
|
||||
max_load_time | numeric | The maximum number of milliseconds to wait for content to preload. If the wait time is exceeded an error message is displayed and the experiment stops. The default value is 60 seconds.
|
||||
fullscreen | boolean | If true, the experiment will run in fullscreen mode. See the [feature page](../features/fullscreen.md) for more details.
|
||||
default_iti | numeric | The default inter-trial interval in ms. The default value if none is specified is 0ms.
|
||||
|
||||
Possible values for the exclusions parameter above.
|
||||
|
@ -15,8 +15,8 @@ In most cases, data collection will be automatic and hidden. Plugins save data o
|
||||
Often it is useful to add a piece of data to *all* of the trials in the experiment. For example, appending the subject ID to each trial. This can be done with the `jsPsych.data.addProperties()` function. Here is an example:
|
||||
|
||||
```javascript
|
||||
// generate a random subject ID
|
||||
var subject_id = Math.floor(Math.random()*100000);
|
||||
// generate a random subject ID with 15 characters
|
||||
var subject_id = jsPsych.randomization.randomID(15);
|
||||
|
||||
// pick a random condition for the subject at the start of the experiment
|
||||
var condition_assignment = jsPsych.randomization.sample(['conditionA', 'conditionB', 'conditionC'],1)[0];
|
||||
@ -143,28 +143,121 @@ The `file_put_contents($filename, $data)` method requires permission to write ne
|
||||
To use the PHP script, the JavaScript that runs jsPsych needs to send the `filename` and `filedata` information. This is done through an [AJAX](http://www.w3schools.com/xml/ajax_intro.asp) call.
|
||||
|
||||
```javascript
|
||||
function saveData(filename, filedata){
|
||||
$.ajax({
|
||||
type:'post',
|
||||
cache: false,
|
||||
url: 'save_data.php', // this is the path to the above PHP script
|
||||
data: {filename: filename, filedata: filedata}
|
||||
});
|
||||
function saveData(name, data){
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', 'write_data.php'); // 'write_data.php' is the path to the php file described above.
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.send(JSON.stringify({filename: filename, filedata: filedata}));
|
||||
}
|
||||
|
||||
// call the saveData function after the experiment is over
|
||||
jsPsych.init({
|
||||
|
||||
// code to define the experiment structure would go here...
|
||||
|
||||
on_finish: function(data){ saveData("filename.csv", jsPsych.data.getDataAsCSV()) }
|
||||
on_finish: function(){ saveData("experiment_data.csv", jsPsych.data.get().csv()); }
|
||||
});
|
||||
```
|
||||
|
||||
To use this in an actual experiment, it would be important to tie the filename to some unique identifier like a subject number.
|
||||
To use this in an actual experiment, it would be important to tie the filename to some unique identifier like a subject number. Otherwise the file may be overwritten by collecting new data.
|
||||
|
||||
## Storing data permanently in a MySQL database
|
||||
|
||||
The ideal solution for storing data generated by jsPsych is to write it to a database.
|
||||
|
||||
There are dozens of database options. MySQL is one of the most popular [relational databases](http://en.wikipedia.org/wiki/Relational_database), is free to use, and relatively easy [to install](https://www.google.com/search?q=how+to+install+mysql). This page will assume that you have a MySQL database installed on your server that is hosting the jsPsych experiment, and that your server is able to execute PHP code. If you are trying to run on a local machine, you'll need to install a local server environment like [XAMPP](https://www.apachefriends.org/index.html).
|
||||
There are dozens of database options. MySQL is one of the most popular [relational databases](http://en.wikipedia.org/wiki/Relational_database), is free to use, and relatively easy [to install](https://www.google.com/search?q=how+to+install+mysql). This code will assume that you have a MySQL database installed on your server that is hosting the jsPsych experiment, and that your server is able to execute PHP code. If you are trying to run on a local machine, you'll need to install a local server environment like [XAMPP](https://www.apachefriends.org/index.html).
|
||||
|
||||
You'll need two PHP scripts. The first is a configuration file for your database. Save it as `database_config.php` on your server. Within this file are configuration options for the database. You'll need to change these according to how you have configured your MySQL installation.
|
||||
|
||||
```php
|
||||
<?php
|
||||
$servername = "localhost";
|
||||
$port = 3306;
|
||||
$username = "username";
|
||||
$password = "password";
|
||||
$dbname = "database";
|
||||
$table = "tablename";
|
||||
?>
|
||||
```
|
||||
|
||||
The second PHP file will write data to the database. This script reads the database to discover what columns are in the table, and then only allows data to be entered in that matches those columns. This is a security feature. Save this file as `write_data.php` on your server.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
// this path should point to your configuration file.
|
||||
include('database_config.php');
|
||||
|
||||
$data_array = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
try {
|
||||
$conn = new PDO("mysql:host=$servername;port=$port;dbname=$dbname", $username, $password);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
// First stage is to get all column names from the table and store
|
||||
// them in $col_names array.
|
||||
$stmt = $conn->prepare("SHOW COLUMNS FROM `$table`");
|
||||
$stmt->execute();
|
||||
$col_names = array();
|
||||
while($row = $stmt->fetchColumn()) {
|
||||
$col_names[] = $row;
|
||||
}
|
||||
// Second stage is to create prepared SQL statement using the column
|
||||
// names as a guide to what values might be in the JSON.
|
||||
// If a value is missing from a particular trial, then NULL is inserted
|
||||
$sql = "INSERT INTO $table VALUES(";
|
||||
for($i = 0; $i < count($col_names); $i++){
|
||||
$name = $col_names[$i];
|
||||
$sql .= ":$name";
|
||||
if($i != count($col_names)-1){
|
||||
$sql .= ", ";
|
||||
}
|
||||
}
|
||||
$sql .= ");";
|
||||
$insertstmt = $conn->prepare($sql);
|
||||
for($i=0; $i < count($data_array); $i++){
|
||||
for($j = 0; $j < count($col_names); $j++){
|
||||
$colname = $col_names[$j];
|
||||
if(!isset($data_array[$i][$colname])){
|
||||
$insertstmt->bindValue(":$colname", null, PDO::PARAM_NULL);
|
||||
} else {
|
||||
$insertstmt->bindValue(":$colname", $data_array[$i][$colname]);
|
||||
}
|
||||
}
|
||||
$insertstmt->execute();
|
||||
}
|
||||
echo '{"success": true}';
|
||||
} catch(PDOException $e) {
|
||||
echo '{"success": false, "message": ' . $e->getMessage();
|
||||
}
|
||||
$conn = null;
|
||||
?>
|
||||
```
|
||||
|
||||
To send the data, we use an AJAX request in JavaScript.
|
||||
```JavaScript
|
||||
function saveData() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', 'write_data.php'); // change 'write_data.php' to point to php script.
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.onload = function() {
|
||||
if(xhr.status == 200){
|
||||
var response = JSON.parse(xhr.responseText);
|
||||
console.log(response.success);
|
||||
}
|
||||
};
|
||||
xhr.send(jsPsych.data.getData().json());
|
||||
}
|
||||
```
|
||||
|
||||
You can call the `saveData()` function using the `on_finish` handler for the experiment, or by using the `call-function` plugin.
|
||||
|
||||
```javascript
|
||||
// with on_finish handler
|
||||
jsPsych.init({
|
||||
on_finish: saveData
|
||||
});
|
||||
|
||||
// with call-function plugin
|
||||
timeline.push({
|
||||
type: 'call-function',
|
||||
func: saveData
|
||||
});
|
||||
```
|
||||
|
@ -1,14 +1,36 @@
|
||||
# Fullscreen Experiments
|
||||
|
||||
You can run your experiment in fullscreen mode by setting the `fullscreen` parameter in the `jsPsych.init` call that launches the experiment.
|
||||
You can run your experiment in fullscreen mode by using the jspsych-fullscreen plugin.
|
||||
|
||||
```javascript
|
||||
var timeline = [];
|
||||
|
||||
timeline.push({
|
||||
type: 'fullscreen',
|
||||
fullscreen_mode: true
|
||||
});
|
||||
|
||||
timeline.push({
|
||||
type: 'text',
|
||||
text: 'This trial will be in fullscreen mode.'
|
||||
});
|
||||
|
||||
// exit fullscreen mode
|
||||
timeline.push({
|
||||
type: 'fullscreen',
|
||||
fullscreen_mode: false
|
||||
});
|
||||
|
||||
timeline.push({
|
||||
type: 'text',
|
||||
text: 'This trial will NOT be in fullscreen mode.'
|
||||
});
|
||||
|
||||
jsPsych.init({
|
||||
timeline: timeline,
|
||||
fullscreen: true
|
||||
timeline: timeline
|
||||
});
|
||||
```
|
||||
|
||||
For security reasons, launching the browser into fullscreen mode requires that the user take an action. Therefore, if fullscreen mode is requested, a button will be displayed on the page to launch the experiment into fullscreen mode. The experiment will not begin until this button is pressed.
|
||||
For security reasons, web browsers require that users initiate an action to launch fullscreen mode. The fullscreen plugin displays a button that the user must click to change the display to fullscreen.
|
||||
|
||||
Safari does not support keyboard input when the browser is in fullscreen mode. Therefore, the function will not launch fullscreen mode on Safari.
|
||||
Safari does not support keyboard input when the browser is in fullscreen mode. Therefore, the function will not launch fullscreen mode on Safari. The experiment will ignore any trials using the fullscreen plugin in Safari.
|
||||
|
@ -47,8 +47,8 @@ var audio = ['audio/foo.mp3'];
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
audio_preload: audio,
|
||||
image_preload: images
|
||||
preload_audio: audio,
|
||||
preload_images: images
|
||||
});
|
||||
|
||||
```
|
||||
|
30
docs/markdown_docs/plugins/jspsych-resize.md
Normal file
30
docs/markdown_docs/plugins/jspsych-resize.md
Normal file
@ -0,0 +1,30 @@
|
||||
# jspsych-resize
|
||||
|
||||
This plugin displays a resizable div container that allows the user to drag until the container is the same size as the item being measured. Once the user measures the item as close as possible, clicking the button sets a scaling factor for the div containing jsPsych content. This causes the stimuli that follow to have a known size, independent of monitor resolution.
|
||||
|
||||
## Parameters
|
||||
|
||||
This table lists the parameters associated with this plugin. Parameters with a default value of *undefined* must be specified. Other parameters can be left unspecified if the default value is acceptable.
|
||||
|
||||
Parameter | Type | Default Value | Description
|
||||
----------|------|---------------|------------
|
||||
item_height | numeric | 1 | The height of the item to be measured. Any units can be used as long as you are consistent with using the same units for all parameters.
|
||||
item_width | numeric | 1 | The width of the item to be measured.
|
||||
pixels_per_unit | numeric | 100 | After the scaling factor is applied, this many pixels will equal one unit of measurement.
|
||||
prompt | string | `''` | HTML content to display below the resizable box, and above the button.
|
||||
button_text | string | `'Done'` | Label to display on the button to complete calibration.
|
||||
starting_size | numeric | 100 | The initial size of the box, in pixels, along the largest dimension. The aspect ratio will be set automatically to match the item width and height.
|
||||
|
||||
## Examples
|
||||
|
||||
#### Measuring a credit card and resizing the display to have 150 pixels equal an inch.
|
||||
|
||||
```javascript
|
||||
var inputs = {
|
||||
type: 'resize',
|
||||
item_width: 3 + 3/8,
|
||||
item_height: 2 + 1/8,
|
||||
prompt: "<p>Click and drag the lower right corner of the box until the box is the same size as a credit card held up to the screen.</p>",
|
||||
pixels_per_unit: 150
|
||||
};
|
||||
```
|
56
docs/markdown_docs/plugins/jspsych-survey-multi-picture.md
Normal file
56
docs/markdown_docs/plugins/jspsych-survey-multi-picture.md
Normal file
@ -0,0 +1,56 @@
|
||||
# jspsych-survey-multi-picture plugin
|
||||
|
||||
The survey-multi-picture plugin displays a set of questions with multiple picture response fields. The subject selects a single answer.
|
||||
|
||||
## Parameters
|
||||
|
||||
This table lists the parameters associated with this plugin. Parameters with a default value of *undefined* must be specified. Other parameters can be left unspecified if the default value is acceptable.
|
||||
|
||||
Parameter | Type | Default Value | Description
|
||||
----------|------|---------------|------------
|
||||
questions | array | *undefined* | An array of strings. The strings are the prompts/questions that will be associated with a group of options (images). All questions will get presented on the same page (trial).
|
||||
options | array | *undefined* | An array of arrays of objects containing url key and label key(label is optional). The innermost arrays contain a set of options to display for an individual question. The length of the outer array should be the same as the number of questions.
|
||||
horizontal | boolean | false | If true, then questions are centered and options are displayed horizontally.
|
||||
preamble | string | empty string | HTML formatted string to display at the top of the page above all the questions.
|
||||
|
||||
## Data Generated
|
||||
|
||||
In addition to the [default data collected by all plugins](overview#datacollectedbyplugins), this plugin collects the following data for each trial.
|
||||
|
||||
Name | Type | Value
|
||||
-----|------|------
|
||||
responses | JSON string | A string in JSON format containing the response for each question. The encoded object will have a separate variable for the response to each question, with the first question in the trial being recorded in `Q0`, the second in `Q1`, and so on. The responses are recorded as the image url for the picture clicked on.
|
||||
rt | numeric | The response time in milliseconds for the subject to make a response. The time is measured from when the questions first appear on the screen until the subject's response.
|
||||
|
||||
## Examples
|
||||
|
||||
#### Basic example with multiple questions on a page.
|
||||
|
||||
```javascript
|
||||
// defining groups of questions that will go together.
|
||||
var page_1_questions = ["I like vegetables.", "I like fruit."];
|
||||
|
||||
// definiting two different response scales that can be used.
|
||||
var page_1_options = [{url: "http://www.thetechconnectioninc.com/assets/img/Twitter.png", label: "twitter"}, {url: "http://www.freeiconspng.com/uploads/facebook-logo-png-20.png", label: "facebook"}];
|
||||
var page_2_options = [{url: "http://www.gameswithwords.org/WhichEnglish/images/1_1.jpg", label: "dog chase cat"}, {url: "http://www.gameswithwords.org/WhichEnglish/images/1_2.jpg", label: "cat chase dog"}];
|
||||
|
||||
var multi_picture_block = {
|
||||
type: 'survey-multi-picture',
|
||||
questions: page_1_questions,
|
||||
options: [page_1_options, page_2_options], // need one scale for every question on a page
|
||||
};
|
||||
|
||||
var multi_picture_block_horizontal = {
|
||||
type: 'survey-multi-picture',
|
||||
questions: page_1_questions,
|
||||
options: [page_1_options, page_2_options], // need one scale for every question on a page
|
||||
horizontal: true // centers questions and makes options display horizontally
|
||||
};
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [multi_picture_block, multi_picture_block_horizontal],
|
||||
on_finish: function() {
|
||||
jsPsych.data.displayData();
|
||||
}
|
||||
});
|
||||
```
|
58
docs/markdown_docs/plugins/jspsych-survey-multi-select.md
Normal file
58
docs/markdown_docs/plugins/jspsych-survey-multi-select.md
Normal file
@ -0,0 +1,58 @@
|
||||
# jspsych-survey-multi-select plugin
|
||||
|
||||
The survey-multi-select plugin displays a set of questions with multiple select response fields. The subject could select multiple answers.
|
||||
|
||||
## Parameters
|
||||
|
||||
This table lists the parameters associated with this plugin. Parameters with a default value of *undefined* must be specified. Other parameters can be left unspecified if the default value is acceptable.
|
||||
|
||||
Parameter | Type | Default Value | Description
|
||||
----------|------|---------------|------------
|
||||
questions | array | *undefined* | An array of strings. The strings are the prompts/questions that will be associated with a group of options (check boxes). All questions will get presented on the same page (trial).
|
||||
options | array | *undefined* | An array of arrays. The innermost arrays contain a set of options to display for an individual question. The length of the outer array should be the same as the number of questions.
|
||||
required | boolean | true | If true, then at least one option must be selected.
|
||||
required_msg | string | `*please select at least one option!` | Message to display if required check is not met.
|
||||
horizontal | boolean | false | If true, then questions are centered and options are displayed horizontally.
|
||||
preamble | string | empty string | HTML formatted string to display at the top of the page above all the questions.
|
||||
|
||||
## Data Generated
|
||||
|
||||
In addition to the [default data collected by all plugins](overview#datacollectedbyplugins), this plugin collects the following data for each trial.
|
||||
|
||||
Name | Type | Value
|
||||
-----|------|------
|
||||
responses | JSON string | An array containing all selected choices in JSON format for each question. The encoded object will have a separate variable for the response to each question, with the first question in the trial being recorded in `Q0`, the second in `Q1`, and so on. The responses are recorded as the name of the option label.
|
||||
rt | numeric | The response time in milliseconds for the subject to make a response. The time is measured from when the questions first appear on the screen until the subject's response.
|
||||
|
||||
## Examples
|
||||
|
||||
#### Basic example with multiple questions on a page.
|
||||
|
||||
```javascript
|
||||
// defining groups of questions that will go together.
|
||||
var page_1_questions = ["I like vegetables.", "I like fruit."];
|
||||
|
||||
// definiting two different response scales that can be used.
|
||||
var page_1_options = ["Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"];
|
||||
var page_2_options = ["Strongly Disagree", "Disagree", "Somewhat Disagree", "Neural", "Somewhat Agree", "Agree", "Strongly Agree"];
|
||||
|
||||
var multi_select_block = {
|
||||
type: 'survey-multi-select',
|
||||
questions: page_1_questions,
|
||||
options: [page_1_options, page_2_options], // need one scale for every question on a page
|
||||
};
|
||||
|
||||
var multi_select_block_horizontal = {
|
||||
type: 'survey-multi-select',
|
||||
questions: page_1_questions,
|
||||
options: [page_1_options, page_2_options], // need one scale for every question on a page
|
||||
horizontal: true // centers questions and makes options display horizontally
|
||||
};
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [multi_select_block, multi_select_block_horizontal],
|
||||
on_finish: function() {
|
||||
jsPsych.data.displayData();
|
||||
}
|
||||
});
|
||||
```
|
@ -14,6 +14,8 @@ height | numeric | heigh of the video file | The height of the video display in
|
||||
prompt | string | empty string | A message (any valid HTML) to display beneath the video element.
|
||||
autoplay | boolean | true | If true, the video will begin playing as soon as it has loaded.
|
||||
controls | boolean | false | If true, controls for the video player will be available to the subject. They will be able to pause the video or move the playback to any point in the video.
|
||||
start | numeric | 0 | If given a value, the video will start at this time point in seconds.
|
||||
stop| numeric | *undefined* | If given a value, the video will stop at this time point in seconds.
|
||||
|
||||
## Data Generated
|
||||
|
||||
|
@ -75,7 +75,8 @@ This table is a description of all plugins that are currently bundled with jsPsy
|
||||
[jspsych‑instructions](jspsych-instructions) | For displaying instructions to the subject.
|
||||
[jspsych‑multi‑stim‑multi‑response](jspsych-multi-stim-multi-response) | A more generalized version of the single-stim plugin. Can display multiple stimuli in a single trial, and collect multiple responses in a single trial.
|
||||
[jspsych‑palmer](jspsych-palmer) | Shows grid-like stimuli inspired by Stephen Palmer's work. The stimuli are editable: subjects can add and subtract parts interactively. Also contains a method for generating the HTML code to render the stimuli, allowing them to be used in other plugins.
|
||||
[jspsych‑reconstruction](jspsych-reconstruction) | The subject interacts with a stimulus by modifying a parameter of the stimulus and observing the change in the stimulus in real-time.
|
||||
[jspsych‑reconstruction](jspsych-reconstruction) | The subject interacts with a stimulus by modifying a parameter of the stimulus and observing the change in the stimulus in real-time.
|
||||
[jspsych‑resize](jspsych-resize) | Calibrate the display so that materials display with a known physical size.
|
||||
[jspsych‑same‑different](jspsych-same-different) | A same-different judgment task. A stimulus is shown, followed by a brief gap, and then another stimulus is shown. The subject indicates whether the stimuli are the same or different.
|
||||
[jspsych‑similarity](jspsych-similarity) | Two stimuli are shown sequentially, and the subject indicates how similar they are by dragging a slider object.
|
||||
[jspsych‑single‑audio](jspsych-single-audio) | A basic plugin for playing an audio stimulus and getting a keyboard response.
|
||||
|
@ -46,6 +46,7 @@ pages:
|
||||
- 'jspsych-multi-stim-multi-response': 'plugins/jspsych-multi-stim-multi-response.md'
|
||||
- 'jspsych-palmer': 'plugins/jspsych-palmer.md'
|
||||
- 'jspsych-reconstruction': 'plugins/jspsych-reconstruction.md'
|
||||
- 'jspsych-resize': 'plugins/jspsych-resize.md'
|
||||
- 'jspsych-same-different': 'plugins/jspsych-same-different.md'
|
||||
- 'jspsych-similarity': 'plugins/jspsych-similarity.md'
|
||||
- 'jspsych-single-audio': 'plugins/jspsych-single-audio.md'
|
||||
|
@ -14,7 +14,7 @@
|
||||
var timeline = [];
|
||||
|
||||
/* define welcome message trial */
|
||||
var welcome = {
|
||||
var welcome_block = {
|
||||
type: "text",
|
||||
text: "Welcome to the experiment. Press any key to begin."
|
||||
};
|
||||
|
30
examples/flex-layout-testing.html
Normal file
30
examples/flex-layout-testing.html
Normal file
@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="../jspsych.js"></script>
|
||||
<script src="../plugins/jspsych-survey-multi-choice.js"></script>
|
||||
<link rel="stylesheet" href="../css/jspsych.css"></link>
|
||||
</head>
|
||||
<body></body>
|
||||
<script>
|
||||
|
||||
var opts = ["Strongly Disagree", "Disagree", "Somewhat Disagree", "Neural", "Somewhat Agree", "Agree", "Strongly Agree"];
|
||||
|
||||
var trial_1 = {
|
||||
type: 'survey-multi-choice',
|
||||
questions: ["I like vegetables.", "I like fruit."],
|
||||
options: [opts, opts],
|
||||
}
|
||||
|
||||
var trial_2 = {
|
||||
type: 'survey-multi-choice',
|
||||
questions: ["I like vegetables."],
|
||||
options: [opts],
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial_1, trial_2],
|
||||
show_progress_bar: true
|
||||
});
|
||||
</script>
|
||||
</html>
|
44
examples/jspsych-fullscreen.html
Normal file
44
examples/jspsych-fullscreen.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
|
||||
<script src="../jspsych.js"></script>
|
||||
<script src="../plugins/jspsych-fullscreen.js"></script>
|
||||
<script src="../plugins/jspsych-single-stim.js"></script>
|
||||
<link rel="stylesheet" href="../css/jspsych.css"></link>
|
||||
<style>
|
||||
img {
|
||||
width: 300px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<script>
|
||||
var fullscreen_trial = {
|
||||
type: 'fullscreen',
|
||||
fullscreen_mode: true
|
||||
}
|
||||
|
||||
var trial_1 = {
|
||||
type: 'single-stim',
|
||||
stimulus: 'img/happy_face_1.jpg',
|
||||
choices: [89, 78], // Y or N
|
||||
prompt: '<p class="center-content">Have you seen this face before? Y or N.</p>'
|
||||
}
|
||||
|
||||
var fullscreen_trial_exit = {
|
||||
type: 'fullscreen',
|
||||
fullscreen_mode: false
|
||||
}
|
||||
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [ fullscreen_trial, trial_1, fullscreen_trial_exit ],
|
||||
on_finish: function() {
|
||||
jsPsych.data.displayData();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
34
examples/jspsych-resize.html
Normal file
34
examples/jspsych-resize.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="../jspsych.js"></script>
|
||||
<script src="../plugins/jspsych-resize.js"></script>
|
||||
<script src="../plugins/jspsych-single-stim.js"></script>
|
||||
<link rel="stylesheet" href="../css/jspsych.css"></link>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
<script>
|
||||
|
||||
var inputs = {
|
||||
type: 'resize',
|
||||
item_width: 3 + 3/8,
|
||||
item_height: 2 + 1/8,
|
||||
prompt: "<p>Click and drag the lower right corner of the box until the box is the same size as a credit card held up to the screen.</p>",
|
||||
pixels_per_unit: 642/4
|
||||
};
|
||||
|
||||
var image_display = {
|
||||
type: 'single-stim',
|
||||
stimulus: 'img/happy_face_4.jpg',
|
||||
prompt: '<p>If scaling worked, then the image above should be 4 inches wide.</p>',
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [inputs, image_display],
|
||||
on_finish: function() {
|
||||
jsPsych.data.displayData();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</html>
|
@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
|
||||
<script src="../jspsych.js"></script>
|
||||
<script src="../plugins/jspsych-survey-likert.js"></script>
|
||||
<link rel="stylesheet" href="../css/jspsych.css"></link>
|
||||
@ -18,6 +18,7 @@
|
||||
var likert_block = {
|
||||
type: 'survey-likert',
|
||||
questions: ["I like vegetables.", "I like fruit."],
|
||||
required: true,
|
||||
labels: [scale_1, scale_1], // need one scale for every question on a page
|
||||
};
|
||||
|
||||
|
31
examples/jspsych-survey-multi-picture.html
Normal file
31
examples/jspsych-survey-multi-picture.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<script src="../jspsych.js" ></script>
|
||||
<script src="../plugins/jspsych-survey-multi-picture.js" ></script>
|
||||
<link rel="stylesheet" href="../css/jspsych.css"></link>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
var questions=["Please choose the picture which describes the following sentence: The dog is chased by the cat."]
|
||||
var choices = [{url: "http://www.gameswithwords.org/WhichEnglish/images/1_2.jpg", label:""}, {url: "http://www.gameswithwords.org/WhichEnglish/images/1_1.jpg", label:""}]
|
||||
var multi_choice_block = {
|
||||
type: 'survey-multi-picture',
|
||||
questions: questions,
|
||||
options: [choices],
|
||||
horizontal: true,
|
||||
on_finish: function(data) {
|
||||
console.log("i'm data", data)
|
||||
}
|
||||
};
|
||||
jsPsych.init({
|
||||
timeline: [multi_choice_block],
|
||||
on_finish: function(data) {
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</html>
|
35
examples/jspsych-survey-multi-select.html
Normal file
35
examples/jspsych-survey-multi-select.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<script src="../jspsych.js" ></script>
|
||||
<script src="../plugins/jspsych-survey-multi-select.js" ></script>
|
||||
<link rel="stylesheet" href="../css/jspsych.css"></link>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
var questions=["Which of these colors do you like?", "Which of these foods do you like?"]
|
||||
var choices = [
|
||||
["Red", "Yellow", "Green", "Blue", "Black"],
|
||||
["Apples", "Bananas", "Carrots", "Donuts", "Eggplant"]
|
||||
]
|
||||
var multi_choice_block = {
|
||||
type: 'survey-multi-select',
|
||||
questions: questions,
|
||||
options: choices,
|
||||
horizontal: true,
|
||||
required: true,
|
||||
on_finish: function(data) {
|
||||
console.log("i'm data", data)
|
||||
}
|
||||
};
|
||||
jsPsych.init({
|
||||
timeline: [multi_choice_block],
|
||||
on_finish: function(data) {
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</html>
|
@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
|
||||
<script src="../jspsych.js"></script>
|
||||
<script src="../plugins/jspsych-survey-text.js"></script>
|
||||
<link rel="stylesheet" href="../css/jspsych.css"></link>
|
||||
@ -14,7 +14,9 @@
|
||||
|
||||
var survey_block = {
|
||||
type: 'survey-text',
|
||||
questions: page_1_questions
|
||||
questions: page_1_questions,
|
||||
rows: [1, 8],
|
||||
columns: [50, 20]
|
||||
};
|
||||
|
||||
jsPsych.init({
|
||||
|
194
jspsych.js
194
jspsych.js
@ -56,7 +56,7 @@ window.jsPsych = (function() {
|
||||
'on_finish': function(data) {
|
||||
return undefined;
|
||||
},
|
||||
'on_trial_start': function() {
|
||||
'on_trial_start': function(trial) {
|
||||
return undefined;
|
||||
},
|
||||
'on_trial_finish': function() {
|
||||
@ -74,7 +74,6 @@ window.jsPsych = (function() {
|
||||
'show_progress_bar': false,
|
||||
'auto_preload': true,
|
||||
'max_load_time': 60000,
|
||||
'fullscreen': false,
|
||||
'default_iti': 0
|
||||
};
|
||||
|
||||
@ -360,6 +359,12 @@ window.jsPsych = (function() {
|
||||
// set the order for going through the timeline variables array
|
||||
// TODO: this is where all the sampling options can be implemented
|
||||
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<timeline_parameters.timeline_variables.length; i++){
|
||||
order.push(i);
|
||||
@ -469,7 +474,7 @@ window.jsPsych = (function() {
|
||||
// if we're all done with the repetitions, check if there is a loop function.
|
||||
else if (typeof timeline_parameters.loop_function !== 'undefined') {
|
||||
if (timeline_parameters.loop_function(this.generatedData())) {
|
||||
this.reset(); // TODO: fix this probably...
|
||||
this.reset();
|
||||
return parent_node.advance();
|
||||
} else {
|
||||
progress.done = true;
|
||||
@ -559,6 +564,7 @@ window.jsPsych = (function() {
|
||||
progress.current_variable_set = 0;
|
||||
progress.current_iteration++;
|
||||
progress.done = false;
|
||||
this.setTimelineVariablesOrder();
|
||||
if (typeof timeline_parameters != 'undefined') {
|
||||
for (var i = 0; i < timeline_parameters.timeline.length; i++) {
|
||||
timeline_parameters.timeline[i].reset();
|
||||
@ -705,49 +711,18 @@ window.jsPsych = (function() {
|
||||
|
||||
loaded = true;
|
||||
|
||||
var fullscreen = opts.fullscreen;
|
||||
|
||||
// fullscreen setup
|
||||
if (fullscreen) {
|
||||
// check if keys are allowed in fullscreen mode
|
||||
var keyboardNotAllowed = typeof Element !== 'undefined' && 'ALLOW_KEYBOARD_INPUT' in Element;
|
||||
if (keyboardNotAllowed) {
|
||||
go();
|
||||
} else {
|
||||
DOM_target.innerHTML = '<div style=""><p>The experiment will launch in fullscreen mode when you click the button below.</p><button id="jspsych-fullscreen-btn" class="jspsych-btn">Launch Experiment</button></div>';
|
||||
var listener = DOM_target.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();
|
||||
}
|
||||
DOM_target.querySelector('#jspsych-fullscreen-btn').removeEventListener('click', listener);
|
||||
DOM_target.innerHTML = '';
|
||||
setTimeout(go, 1000);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
go();
|
||||
// show progress bar if requested
|
||||
if (opts.show_progress_bar === true) {
|
||||
drawProgressBar();
|
||||
}
|
||||
|
||||
function go() {
|
||||
// show progress bar if requested
|
||||
if (opts.show_progress_bar === true) {
|
||||
drawProgressBar();
|
||||
}
|
||||
// record the start time
|
||||
exp_start_time = new Date();
|
||||
|
||||
// record the start time
|
||||
exp_start_time = new Date();
|
||||
// begin!
|
||||
timeline.advance();
|
||||
doTrial(timeline.trial());
|
||||
|
||||
// begin!
|
||||
timeline.advance();
|
||||
doTrial(timeline.trial());
|
||||
}
|
||||
}
|
||||
|
||||
function finishExperiment() {
|
||||
@ -756,16 +731,6 @@ window.jsPsych = (function() {
|
||||
DOM_target.innerHTML = timeline.end_message;
|
||||
}
|
||||
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
}
|
||||
|
||||
opts.on_finish(jsPsych.data.get());
|
||||
|
||||
}
|
||||
@ -800,10 +765,10 @@ window.jsPsych = (function() {
|
||||
|
||||
function doTrial(trial) {
|
||||
|
||||
current_trial = trial;
|
||||
|
||||
// call experiment wide callback
|
||||
opts.on_trial_start();
|
||||
opts.on_trial_start(trial);
|
||||
|
||||
current_trial = trial;
|
||||
|
||||
// check if trial has it's own display element
|
||||
var display_element = DOM_target;
|
||||
@ -886,7 +851,7 @@ window.jsPsych = (function() {
|
||||
|
||||
//Leave a trace in the DOM that jspsych was loaded
|
||||
document.documentElement.setAttribute('jspsych', 'present');
|
||||
|
||||
|
||||
return core;
|
||||
})();
|
||||
|
||||
@ -973,7 +938,7 @@ jsPsych.data = (function() {
|
||||
}
|
||||
|
||||
data_collection.readOnly = function(){
|
||||
return DataCollection(deepExtend([], trials));
|
||||
return DataCollection(jsPsych.utils.deepExtend([], trials));
|
||||
}
|
||||
|
||||
data_collection.addToAll = function(properties){
|
||||
@ -998,9 +963,9 @@ jsPsych.data = (function() {
|
||||
// [{p1: v1, p2:v2}, {p1:v2}]
|
||||
// {p1: v1}
|
||||
if(!Array.isArray(filters)){
|
||||
var f = deepExtend([], [filters]);
|
||||
var f = jsPsych.utils.deepExtend([], [filters]);
|
||||
} else {
|
||||
var f = deepExtend([], filters);
|
||||
var f = jsPsych.utils.deepExtend([], filters);
|
||||
}
|
||||
|
||||
var filtered_data = [];
|
||||
@ -1054,7 +1019,7 @@ jsPsych.data = (function() {
|
||||
if(!Array.isArray(columns)){
|
||||
columns = [columns];
|
||||
}
|
||||
var o = deepExtend([], trials);
|
||||
var o = jsPsych.utils.deepExtend([], trials);
|
||||
for (var i = 0; i < o.length; i++) {
|
||||
for (var j in columns) {
|
||||
delete o[i][columns[j]];
|
||||
@ -1063,6 +1028,21 @@ jsPsych.data = (function() {
|
||||
return DataCollection(o);
|
||||
}
|
||||
|
||||
data_collection.uniqueNames = function(){
|
||||
var names = [];
|
||||
|
||||
for(var i=0; i<trials.length; i++){
|
||||
var keys = Object.keys(trials[i]);
|
||||
for(var j=0; j<keys.length; j++){
|
||||
if(!names.includes(keys[j])){
|
||||
names.push(keys[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
data_collection.csv = function(){
|
||||
return JSON2CSV(trials);
|
||||
}
|
||||
@ -2038,7 +2018,8 @@ jsPsych.pluginAPI = (function() {
|
||||
|
||||
module.preloadAudioFiles = function(files, callback_complete, callback_load) {
|
||||
|
||||
files = flatten(files);
|
||||
files = jsPsych.utils.flatten(files);
|
||||
files = jsPsych.utils.unique(files);
|
||||
|
||||
var n_loaded = 0;
|
||||
var loadfn = (typeof callback_load === 'undefined') ? function() {} : callback_load;
|
||||
@ -2104,7 +2085,8 @@ jsPsych.pluginAPI = (function() {
|
||||
module.preloadImages = function(images, callback_complete, callback_load) {
|
||||
|
||||
// flatten the images array
|
||||
images = flatten(images);
|
||||
images = jsPsych.utils.flatten(images);
|
||||
images = jsPsych.utils.unique(images);
|
||||
|
||||
var n_loaded = 0;
|
||||
var loadfn = (typeof callback_load === 'undefined') ? function() {} : callback_load;
|
||||
@ -2171,9 +2153,9 @@ jsPsych.pluginAPI = (function() {
|
||||
if (typeof trials[j][param] !== 'undefined' && typeof trials[j][param] !== 'function') {
|
||||
if ( typeof func == 'undefined' || func(trials[j]) ){
|
||||
if (media == 'image') {
|
||||
images = images.concat(flatten([trials[j][param]]));
|
||||
images = images.concat(jsPsych.utils.flatten([trials[j][param]]));
|
||||
} else if (media == 'audio') {
|
||||
audio = audio.concat(flatten([trials[j][param]]));
|
||||
audio = audio.concat(jsPsych.utils.flatten([trials[j][param]]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2203,57 +2185,73 @@ jsPsych.pluginAPI = (function() {
|
||||
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.
|
||||
};
|
||||
|
||||
|
||||
/** {boolean} Indicates whether this instance of jspsych has opened a hardware connection through our browser extension */
|
||||
module.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", function(evt){
|
||||
module.hardwareConnected = true;
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return module;
|
||||
})();
|
||||
|
||||
// methods used in multiple modules //
|
||||
jsPsych.utils = (function() {
|
||||
|
||||
function flatten(arr, out) {
|
||||
out = (typeof out === 'undefined') ? [] : out;
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (Array.isArray(arr[i])) {
|
||||
flatten(arr[i], out);
|
||||
} else {
|
||||
out.push(arr[i]);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
var module = {};
|
||||
|
||||
function deepExtend(out) {
|
||||
out = out || {};
|
||||
module.flatten = function(arr, out) {
|
||||
out = (typeof out === 'undefined') ? [] : out;
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (Array.isArray(arr[i])) {
|
||||
module.flatten(arr[i], out);
|
||||
} else {
|
||||
out.push(arr[i]);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var obj = arguments[i];
|
||||
module.unique = function(arr) {
|
||||
var out = [];
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr.indexOf(arr[i]) == i) {
|
||||
out.push(arr[i]);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
if (!obj)
|
||||
continue;
|
||||
module.deepExtend = function(out, obj) {
|
||||
out = out || {};
|
||||
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
if (typeof obj[key] === 'object')
|
||||
out[key] = deepExtend(out[key], obj[key]);
|
||||
else
|
||||
out[key] = obj[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var obj = arguments[i];
|
||||
|
||||
return out;
|
||||
};
|
||||
if (!obj)
|
||||
continue;
|
||||
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
if (typeof obj[key] === 'object')
|
||||
out[key] = module.deepExtend(out[key], obj[key]);
|
||||
else
|
||||
out[key] = obj[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
return module;
|
||||
})();
|
||||
|
||||
// polyfill for Object.assign to support IE
|
||||
if (typeof Object.assign != 'function') {
|
||||
|
@ -20,6 +20,6 @@
|
||||
},
|
||||
"homepage": "https://github.com/jodeleeuw/jsPsych#readme",
|
||||
"devDependencies": {
|
||||
"jest": "^17.0.3"
|
||||
"jest": "^20.0.4"
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,8 @@ jsPsych.plugins["button-response"] = (function() {
|
||||
trial.timing_response = trial.timing_response || -1; // if -1, then wait for response forever
|
||||
trial.is_html = (typeof trial.is_html === 'undefined') ? false : trial.is_html;
|
||||
trial.prompt = (typeof trial.prompt === 'undefined') ? "" : trial.prompt;
|
||||
trial.margin_vertical = trial.margin_vertical || "0px";
|
||||
trial.margin_horizontal = trial.margin_horizontal || "8px";
|
||||
|
||||
// if any trial variables are functions
|
||||
// this evaluates the function and replaces
|
||||
@ -88,9 +90,9 @@ jsPsych.plugins["button-response"] = (function() {
|
||||
|
||||
// display stimulus
|
||||
if (!trial.is_html) {
|
||||
display_element.innerHTML = '<img src="'+trial.stimulus+'" id="jspsych-button-response-stimulus" class="block-center"></img>';
|
||||
display_element.innerHTML = '<img src="'+trial.stimulus+'" id="jspsych-button-response-stimulus"></img>';
|
||||
} else {
|
||||
display_element.innerHTML = '<div id="jspsych-button-response-stimulus" class="block-center">'+trial.stimulus+'</div>';
|
||||
display_element.innerHTML = '<div id="jspsych-button-response-stimulus">'+trial.stimulus+'</div>';
|
||||
}
|
||||
|
||||
//display buttons
|
||||
@ -106,11 +108,11 @@ jsPsych.plugins["button-response"] = (function() {
|
||||
buttons.push(trial.button_html);
|
||||
}
|
||||
}
|
||||
display_element.innerHTML += '<div id="jspsych-button-response-btngroup" class="center-content block-center"></div>';
|
||||
display_element.innerHTML += '<div id="jspsych-button-response-btngroup"></div>';
|
||||
for (var i = 0; i < trial.choices.length; i++) {
|
||||
var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
|
||||
display_element.querySelector('#jspsych-button-response-btngroup').insertAdjacentHTML('beforeend',
|
||||
'<div class="jspsych-button-response-button" id="jspsych-button-response-button-' + i +'" data-choice="'+i+'">'+str+'</div>');
|
||||
'<div class="jspsych-button-response-button" style="display: inline-block; margin:'+trial.margin_vertical+' '+trial.margin_horizontal+'" id="jspsych-button-response-button-' + i +'" data-choice="'+i+'">'+str+'</div>');
|
||||
display_element.querySelector('#jspsych-button-response-button-' + i).addEventListener('click', function(e){
|
||||
var choice = e.currentTarget.dataset.choice;
|
||||
after_response(choice);
|
||||
|
@ -60,6 +60,12 @@ jsPsych.plugins['free-sort'] = (function() {
|
||||
default: 'above',
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
button_label: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: 'Done',
|
||||
no_function: false,
|
||||
description: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,6 +79,7 @@ jsPsych.plugins['free-sort'] = (function() {
|
||||
trial.prompt_location = trial.prompt_location || "above";
|
||||
trial.sort_area_width = trial.sort_area_width || 800;
|
||||
trial.sort_area_height = trial.sort_area_height || 800;
|
||||
trial.button_label = typeof trial.button_label === 'undefined' ? 'Done' : trial.button_label;
|
||||
|
||||
// if any trial variables are functions
|
||||
// this evaluates the function and replaces
|
||||
@ -118,7 +125,7 @@ jsPsych.plugins['free-sort'] = (function() {
|
||||
});
|
||||
}
|
||||
|
||||
display_element.innerHTML += '<button id="jspsych-free-sort-done-btn" class="jspsych-btn">Done</button>';
|
||||
display_element.innerHTML += '<button id="jspsych-free-sort-done-btn" class="jspsych-btn">'+trial.button_label+'</button>';
|
||||
|
||||
var maxz = 1;
|
||||
|
||||
|
114
plugins/jspsych-fullscreen.js
Normal file
114
plugins/jspsych-fullscreen.js
Normal file
@ -0,0 +1,114 @@
|
||||
/* 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],
|
||||
default: true,
|
||||
array: false,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
message: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: '<p>The experiment will switch to full screen mode when you press the button below</p>',
|
||||
array: false,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
button_label: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: "Go",
|
||||
array: false,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
delay_after: {
|
||||
type: [jsPsych.plugins.parameterType.INT],
|
||||
default: 1000,
|
||||
array: false,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
plugin.trial = function(display_element, trial) {
|
||||
|
||||
trial.fullscreen_mode = typeof trial.fullscreen_mode === 'undefined' ? true : trial.fullscreen_mode;
|
||||
trial.message = trial.message || '<p>The experiment will switch to full screen mode when you press the button below</p>';
|
||||
trial.button_label = trial.button_label || 'Go';
|
||||
trial.delay_after = trial.delay_after || 1000;
|
||||
|
||||
// if any trial variables are functions
|
||||
// this evaluates the function and replaces
|
||||
// it with the output of the function
|
||||
trial = jsPsych.pluginAPI.evaluateFunctionParameters(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 + '<button id="jspsych-fullscreen-btn" class="jspsych-btn">'+trial.button_label+'</button>';
|
||||
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.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;
|
||||
})();
|
@ -53,6 +53,18 @@ jsPsych.plugins.instructions = (function() {
|
||||
default: false,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
button_label_previous: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: 'Previous',
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
button_label_next: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: 'Next',
|
||||
no_function: false,
|
||||
description: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -64,6 +76,8 @@ jsPsych.plugins.instructions = (function() {
|
||||
trial.allow_backward = (typeof trial.allow_backward === 'undefined') ? true : trial.allow_backward;
|
||||
trial.allow_keys = (typeof trial.allow_keys === 'undefined') ? true : trial.allow_keys;
|
||||
trial.show_clickable_nav = (typeof trial.show_clickable_nav === 'undefined') ? false : trial.show_clickable_nav;
|
||||
trial.button_label_previous = (typeof trial.button_label_previous === 'undefined') ? 'Previous' : trial.button_label_previous;
|
||||
trial.button_label_next = (typeof trial.button_label_next === 'undefined') ? 'Next' : trial.button_label_next;
|
||||
|
||||
// if any trial variables are functions
|
||||
// this evaluates the function and replaces
|
||||
@ -78,6 +92,16 @@ jsPsych.plugins.instructions = (function() {
|
||||
|
||||
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() {
|
||||
display_element.innerHTML = trial.pages[current_page];
|
||||
|
||||
@ -85,50 +109,41 @@ jsPsych.plugins.instructions = (function() {
|
||||
|
||||
var nav_html = "<div class='jspsych-instructions-nav'>";
|
||||
if (current_page != 0 && trial.allow_backward) {
|
||||
nav_html += "<button id='jspsych-instructions-back' class='jspsych-btn'>< Previous</button>";
|
||||
nav_html += "<button id='jspsych-instructions-back' class='jspsych-btn'>< "+trial.button_label_previous+"</button>";
|
||||
}
|
||||
nav_html += "<button id='jspsych-instructions-next' class='jspsych-btn'>Next ></button></div>"
|
||||
nav_html += "<button id='jspsych-instructions-next' class='jspsych-btn'>"+trial.button_label_next+" ></button></div>"
|
||||
|
||||
display_element.innerHTML += nav_html;
|
||||
|
||||
if (current_page != 0 && trial.allow_backward) {
|
||||
display_element.querySelector('#jspsych-instructions-back').addEventListener('click', function() {
|
||||
clear_button_handlers();
|
||||
});
|
||||
display_element.querySelector('#jspsych-instructions-back').addEventListener('click', btnListener);
|
||||
}
|
||||
|
||||
display_element.querySelector('#jspsych-instructions-next').addEventListener('click', function() {
|
||||
clear_button_handlers();
|
||||
});
|
||||
|
||||
display_element.querySelector('#jspsych-instructions-next').addEventListener('click', btnListener);
|
||||
}
|
||||
}
|
||||
|
||||
function clear_button_handlers() {
|
||||
display_element.querySelector('#jspsych-instructions-next').removeEventListener('click');
|
||||
display_element.querySelector('#jspsych-instructions-back').removeEventListener('click');
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,12 @@ jsPsych.plugins.palmer = (function() {
|
||||
default: '',
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
button_label: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: 'Done',
|
||||
no_function: false,
|
||||
description: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -80,6 +86,7 @@ jsPsych.plugins.palmer = (function() {
|
||||
trial.timing_item = trial.timing_item || 1000;
|
||||
trial.timing_feedback = trial.timing_feedback || 1000;
|
||||
trial.prompt = (typeof trial.prompt === 'undefined') ? "" : trial.prompt;
|
||||
trial.button_label = typeof trial.button_label === 'undefined' ? 'Submit Answers' : trial.button_label;
|
||||
|
||||
// if any trial variables are functions
|
||||
// this evaluates the function and replaces
|
||||
@ -246,7 +253,7 @@ jsPsych.plugins.palmer = (function() {
|
||||
// start recording the time
|
||||
var startTime = (new Date()).getTime();
|
||||
|
||||
display_element.innerHTML += '<p><button id="jspsych-palmer-submitButton" class="jspsych-btn" type="button">Submit Answer</button></p>';
|
||||
display_element.innerHTML += '<p><button id="jspsych-palmer-submitButton" class="jspsych-btn" type="button">'+trial.button_label+'</button></p>';
|
||||
display_element.querySelector('#jspsych-palmer-submitButton').addEventListener('click', function() {
|
||||
save_data();
|
||||
});
|
||||
|
@ -47,6 +47,12 @@ jsPsych.plugins['reconstruction'] = (function() {
|
||||
default: 'g',
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
button_label: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: '',
|
||||
no_function: false,
|
||||
description: 'Submit Answers'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -58,6 +64,7 @@ jsPsych.plugins['reconstruction'] = (function() {
|
||||
trial.step_size = trial.step_size || 0.05;
|
||||
trial.key_increase = trial.key_increase || 'h';
|
||||
trial.key_decrease = trial.key_decrease || 'g';
|
||||
trial.button_label = typeof trial.button_label === 'undefined' ? 'Submit Answers' : trial.button_label;
|
||||
|
||||
// if any trial variables are functions
|
||||
// this evaluates the function and replaces
|
||||
@ -102,10 +109,10 @@ jsPsych.plugins['reconstruction'] = (function() {
|
||||
|
||||
//console.log(param);
|
||||
|
||||
display_element.innerHTML = '<div id="jspsych-reconstruction-stim-container">'+trial.stim_function(param)+'</div>');
|
||||
display_element.innerHTML = '<div id="jspsych-reconstruction-stim-container">'+trial.stim_function(param)+'</div>';
|
||||
|
||||
// add submit button
|
||||
display_element.innerHTML += '<button id="jspsych-reconstruction-next" class="jspsych-btn jspsych-reconstruction">Submit Answers</button>';
|
||||
display_element.innerHTML += '<button id="jspsych-reconstruction-next" class="jspsych-btn jspsych-reconstruction">'+trial.button_label+'</button>';
|
||||
|
||||
display_element.querySelector('#jspsych-reconstruction-next').addEventListener('click', endTrial);
|
||||
}
|
||||
|
135
plugins/jspsych-resize.js
Normal file
135
plugins/jspsych-resize.js
Normal file
@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 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.trial = function(display_element, trial) {
|
||||
|
||||
// if any trial variables are functions
|
||||
// this evaluates the function and replaces
|
||||
// it with the output of the function
|
||||
trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial);
|
||||
|
||||
// default trial paramters
|
||||
trial.item_height = trial.item_height || 1;
|
||||
trial.item_width = trial.item_width || 1;
|
||||
trial.prompt = trial.prompt || ' ';
|
||||
trial.pixels_per_unit = trial.pixels_per_unit || 100;
|
||||
trial.starting_size = trial.starting_size || 100;
|
||||
trial.button_label = trial.button_label || "Done";
|
||||
|
||||
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 ='<div id="jspsych-resize-div" style="border: 2px solid steelblue; height: '+start_div_height+'px; width:'+start_div_width+'px; margin: 7px auto; background-color: lightsteelblue; position: relative;">';
|
||||
html += '<div id="jspsych-resize-handle" style="cursor: nwse-resize; background-color: steelblue; width: 10px; height: 10px; border: 2px solid lightsteelblue; position: absolute; bottom: 0; right: 0;"></div>';
|
||||
html += '</div>';
|
||||
html += trial.prompt;
|
||||
html += '<a class="jspsych-btn" id="jspsych-resize-btn">'+trial.button_label+'</a>';
|
||||
|
||||
// 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();
|
||||
e.stopPropagation();
|
||||
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)*2;
|
||||
var dy = (e.pageY - origin_y)*2;
|
||||
|
||||
if(dx >= dy){
|
||||
scale_div.style.width = Math.max(10, cx+dx) + "px";
|
||||
scale_div.style.height = Math.round(Math.max(10, cx+dx) / aspect_ratio ) + "px";
|
||||
} else {
|
||||
scale_div.style.height = Math.max(10, cy+dy) + "px";
|
||||
scale_div.style.width = Math.round(aspect_ratio * Math.max(10, cy+dy)) + "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;
|
||||
})();
|
@ -82,6 +82,7 @@ jsPsych.plugins['same-different'] = (function() {
|
||||
// default parameters
|
||||
trial.same_key = trial.same_key || 81; // default is 'q'
|
||||
trial.different_key = trial.different_key || 80; // default is 'p'
|
||||
trial.advance_key = trial.advance_key || jsPsych.ALL_KEYS
|
||||
// timing parameters
|
||||
trial.timing_first_stim = trial.timing_first_stim || 1000; // if -1, the first stim is shown until any key is pressed
|
||||
trial.timing_second_stim = trial.timing_second_stim || 1000; // if -1, then second stim is shown until response.
|
||||
@ -112,11 +113,17 @@ jsPsych.plugins['same-different'] = (function() {
|
||||
first_stim_info = info;
|
||||
showBlankScreen();
|
||||
}
|
||||
jsPsych.pluginAPI.getKeyboardResponse(afterKeyboardResponse, [], 'date', false);
|
||||
jsPsych.pluginAPI.getKeyboardResponse({
|
||||
callback_function: afterKeyboardResponse,
|
||||
valid_responses: trial.advance_key,
|
||||
rt_method: 'date',
|
||||
persist: false,
|
||||
allow_held_key: false
|
||||
});
|
||||
}
|
||||
|
||||
function showBlankScreen() {
|
||||
display_element.querySelector('.jspsych-same-different-stimulus').outerHTML = '';
|
||||
display_element.innerHTML = '';
|
||||
|
||||
jsPsych.pluginAPI.setTimeout(function() {
|
||||
showSecondStim();
|
||||
@ -125,14 +132,14 @@ jsPsych.plugins['same-different'] = (function() {
|
||||
|
||||
function showSecondStim() {
|
||||
if (!trial.is_html) {
|
||||
display_element.innerHTML += '<img class="jspsych-same-different-stimulus" id="jspsych-same-different-second-stimulus" src="'+trial.stimuli[1]+'"></img>';
|
||||
display_element.innerHTML += '<img class="jspsych-same-different-stimulus" src="'+trial.stimuli[1]+'"></img>';
|
||||
} else {
|
||||
display_element.innerHTML += '<div class="jspsych-same-different-stimulus" id="jspsych-same-different-second-stimulus">'+trial.stimuli[1]+'</div>';
|
||||
display_element.innerHTML += '<div class="jspsych-same-different-stimulus">'+trial.stimuli[1]+'</div>';
|
||||
}
|
||||
|
||||
if (trial.timing_second_stim > 0) {
|
||||
jsPsych.pluginAPI.setTimeout(function() {
|
||||
display_element.querySelector('#jspsych-same-different-second-stimulus').style.visibility = 'hidden';
|
||||
display_element.querySelector('.jspsych-same-different-stimulus').style.visibility = 'hidden';
|
||||
}, trial.timing_second_stim);
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ jsPsych.plugins["serial-reaction-time"] = (function() {
|
||||
trial.grid = trial.grid || [[1,1,1,1]];
|
||||
trial.choices = trial.choices || [['3','5','7','9']];
|
||||
trial.grid_square_size = trial.grid_square_size || 100;
|
||||
trial.target_color = trial.target_color || "#999";
|
||||
trial.response_ends_trial = (typeof trial.response_ends_trial === 'undefined') ? true : trial.response_ends_trial;
|
||||
trial.timing_pre_target = (typeof trial.timing_pre_target === 'undefined') ? 0 : trial.timing_pre_target;
|
||||
trial.timing_max_duration = trial.timing_max_duration || -1; // if -1, then wait for response forever
|
||||
@ -28,7 +29,10 @@ jsPsych.plugins["serial-reaction-time"] = (function() {
|
||||
trial.prompt = (typeof trial.prompt === 'undefined') ? "" : trial.prompt;
|
||||
|
||||
// create a flattened version of the choices array
|
||||
var flat_choices = flatten(trial.choices);
|
||||
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);
|
||||
@ -51,15 +55,15 @@ jsPsych.plugins["serial-reaction-time"] = (function() {
|
||||
|
||||
function showTarget(){
|
||||
if(trial.fade_duration == -1){
|
||||
display_element.querySelector('#jspsych-serial-reaction-time-stimulus-cell-'+trial.target[0]+'-'+trial.target[1]).style.backgroundColor = '#999';
|
||||
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 = '#999';
|
||||
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: trial.flat_choices,
|
||||
valid_responses: flat_choices,
|
||||
allow_held_key: false
|
||||
});
|
||||
|
||||
@ -124,8 +128,8 @@ jsPsych.plugins["serial-reaction-time"] = (function() {
|
||||
|
||||
if (trial.show_response_feedback){
|
||||
var color = response.correct ? '#0f0' : '#f00';
|
||||
display_element.querySelector('#jspsych-serial-reaction-time-stimulus-cell-'+responseLoc[1]+'-'+responseLoc[0]).style.transition = "";
|
||||
display_element.querySelector('#jspsych-serial-reaction-time-stimulus-cell-'+responseLoc[1]+'-'+responseLoc[0]).style.backgroundColor = color;
|
||||
display_element.querySelector('#jspsych-serial-reaction-time-stimulus-cell-'+responseLoc[0]+'-'+responseLoc[1]).style.transition = "";
|
||||
display_element.querySelector('#jspsych-serial-reaction-time-stimulus-cell-'+responseLoc[0]+'-'+responseLoc[1]).style.backgroundColor = color;
|
||||
}
|
||||
|
||||
if (trial.response_ends_trial) {
|
||||
|
@ -81,6 +81,12 @@ jsPsych.plugins.similarity = (function() {
|
||||
default: '',
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
button_label: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: '',
|
||||
no_function: false,
|
||||
description: 'Submit Answers'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -100,6 +106,8 @@ jsPsych.plugins.similarity = (function() {
|
||||
|
||||
trial.is_html = (typeof trial.is_html === 'undefined') ? false : trial.is_html;
|
||||
trial.prompt = (typeof trial.prompt === 'undefined') ? '' : trial.prompt;
|
||||
|
||||
trial.button_label = typeof trial.button_label === 'undefined' ? 'Submit Answers' : trial.button_label;
|
||||
|
||||
// if any trial variables are functions
|
||||
// this evaluates the function and replaces
|
||||
@ -231,7 +239,7 @@ jsPsych.plugins.similarity = (function() {
|
||||
display_element.append($('<button>', {
|
||||
'id': 'next',
|
||||
'class': 'sim',
|
||||
'html': 'Submit Answer'
|
||||
'html': trial.button_label
|
||||
}));
|
||||
|
||||
// if prompt is set, show prompt
|
||||
|
@ -112,10 +112,13 @@ jsPsych.plugins["single-audio"] = (function() {
|
||||
jsPsych.pluginAPI.clearAllTimeouts();
|
||||
|
||||
// stop the audio file if it is playing
|
||||
// remove end event listeners if they exist
|
||||
if(context !== null){
|
||||
source.stop();
|
||||
source.onended = function() { }
|
||||
} else {
|
||||
audio.pause();
|
||||
audio.removeEventListener('end', end_trial);
|
||||
}
|
||||
|
||||
// kill keyboard listeners
|
||||
|
@ -154,14 +154,14 @@ jsPsych.plugins["single-stim"] = (function() {
|
||||
});
|
||||
}
|
||||
|
||||
// hide image if timing is set
|
||||
// hide stimulus if timing_stim is set
|
||||
if (trial.timing_stim > 0) {
|
||||
jsPsych.pluginAPI.setTimeout(function() {
|
||||
display_element.querySelector('#jspsych-single-stim-stimulus').style.visibility = 'hidden';
|
||||
}, trial.timing_stim);
|
||||
}
|
||||
|
||||
// end trial if time limit is set
|
||||
// end trial if timing_response is set
|
||||
if (trial.timing_response > 0) {
|
||||
jsPsych.pluginAPI.setTimeout(function() {
|
||||
end_trial();
|
||||
|
@ -29,6 +29,12 @@ jsPsych.plugins['survey-likert'] = (function() {
|
||||
default: undefined,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
button_label: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: 'Submit Answers',
|
||||
no_function: false,
|
||||
description: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -37,6 +43,8 @@ jsPsych.plugins['survey-likert'] = (function() {
|
||||
|
||||
// default parameters for the trial
|
||||
trial.preamble = typeof trial.preamble === 'undefined' ? "" : trial.preamble;
|
||||
trial.required = typeof trial.required === 'undefined' ? false : trial.required;
|
||||
trial.button_label = typeof trial.button_label === 'undefined' ? 'Submit Answers' : trial.button_label;
|
||||
|
||||
// if any trial variables are functions
|
||||
// this evaluates the function and replaces
|
||||
@ -45,9 +53,9 @@ jsPsych.plugins['survey-likert'] = (function() {
|
||||
|
||||
// inject CSS for trial
|
||||
var node = display_element.innerHTML += '<style id="jspsych-survey-likert-css"></style>';
|
||||
var cssstr = ".jspsych-survey-likert-statement { display:block; font-size: 18px; padding-top: 30px; margin-bottom:10px; }"+
|
||||
var cssstr = ".jspsych-survey-likert-statement { display:block; font-size: 16px; padding-top: 40px; margin-bottom:10px; }"+
|
||||
".jspsych-survey-likert-opts { list-style:none; width:100%; margin:0; padding:0 0 35px; display:block; font-size: 14px; line-height:1.1em; }"+
|
||||
".jspsych-survey-likert-opt-label { line-height: 1.1em; }"+
|
||||
".jspsych-survey-likert-opt-label { line-height: 1.1em; color: #444; }"+
|
||||
".jspsych-survey-likert-opts:before { content: ''; position:relative; top:11px; /*left:9.5%;*/ display:block; background-color:#efefef; height:4px; width:100%; }"+
|
||||
".jspsych-survey-likert-opts:last-of-type { border-bottom: 0; }"+
|
||||
".jspsych-survey-likert-opts li { display:inline-block; /*width:19%;*/ text-align:center; vertical-align: top; }"+
|
||||
@ -68,16 +76,21 @@ jsPsych.plugins['survey-likert'] = (function() {
|
||||
var width = 100 / trial.labels[i].length;
|
||||
options_string = '<ul class="jspsych-survey-likert-opts" data-radio-group="Q' + i + '">';
|
||||
for (var j = 0; j < trial.labels[i].length; j++) {
|
||||
options_string += '<li style="width:' + width + '%"><input type="radio" name="Q' + i + '" value="' + j + '"><label class="jspsych-survey-likert-opt-label">' + trial.labels[i][j] + '</label></li>';
|
||||
options_string += '<li style="width:' + width + '%"><input type="radio" name="Q' + i + '" value="' + j + '"';
|
||||
if(trial.required){
|
||||
options_string += ' required';
|
||||
}
|
||||
options_string += '><label class="jspsych-survey-likert-opt-label">' + trial.labels[i][j] + '</label></li>';
|
||||
}
|
||||
options_string += '</ul>';
|
||||
form_element.innerHTML += options_string;
|
||||
}
|
||||
|
||||
// add submit button
|
||||
display_element.innerHTML += '<button id="jspsych-survey-likert" class="jspsych-survey-likert jspsych-btn">Submit Answers</button>';
|
||||
form_element.innerHTML += '<input type="submit" id="jspsych-survey-likert-next" class="jspsych-survey-likert jspsych-btn" value="'+trial.button_label+'"></input>';
|
||||
|
||||
display_element.querySelector('#jspsych-survey-likert-next').addEventListener('click', function(){
|
||||
form_element.addEventListener('submit', function(e){
|
||||
e.preventDefault();
|
||||
// measure response time
|
||||
var endTime = (new Date()).getTime();
|
||||
var response_time = endTime - startTime;
|
||||
@ -86,10 +99,12 @@ jsPsych.plugins['survey-likert'] = (function() {
|
||||
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['radio-group'];
|
||||
var response = display_element.querySelector('input[name="' + id + '"]:checked').value;
|
||||
if (typeof response == 'undefined') {
|
||||
response = -1;
|
||||
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 = {};
|
||||
obje[id] = response;
|
||||
|
@ -48,6 +48,12 @@ jsPsych.plugins['survey-multi-choice'] = (function() {
|
||||
default: '',
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
button_label: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: '',
|
||||
no_function: false,
|
||||
description: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -63,6 +69,8 @@ jsPsych.plugins['survey-multi-choice'] = (function() {
|
||||
trial.preamble = typeof trial.preamble == 'undefined' ? "" : trial.preamble;
|
||||
trial.required = typeof trial.required == 'undefined' ? null : trial.required;
|
||||
trial.horizontal = typeof trial.required == 'undefined' ? false : trial.horizontal;
|
||||
//If button_label is empty, the browser's language will be used to determine the button label.
|
||||
trial.button_label = typeof trial.button_label === 'undefined' ? '' : trial.button_label;
|
||||
|
||||
// if any trial variables are functions
|
||||
// this evaluates the function and replaces
|
||||
@ -95,11 +103,11 @@ jsPsych.plugins['survey-multi-choice'] = (function() {
|
||||
if (trial.horizontal) {
|
||||
question_classes.push(_join(plugin_id_name, 'horizontal'));
|
||||
}
|
||||
|
||||
|
||||
trial_form.innerHTML += '<div id="'+_join(plugin_id_name, i)+'" class="'+question_classes.join(' ')+'"></div>';
|
||||
|
||||
|
||||
var question_selector = _join(plugin_id_selector, i);
|
||||
|
||||
|
||||
// add question text
|
||||
display_element.querySelector(question_selector).innerHTML += '<p class="' + plugin_id_name + '-text survey-multi-choice">' + trial.questions[i] + '</p>';
|
||||
|
||||
@ -107,38 +115,39 @@ jsPsych.plugins['survey-multi-choice'] = (function() {
|
||||
for (var j = 0; j < trial.options[i].length; j++) {
|
||||
var option_id_name = _join(plugin_id_name, "option", i, j),
|
||||
option_id_selector = '#' + option_id_name;
|
||||
|
||||
|
||||
// add radio button container
|
||||
display_element.querySelector(question_selector).innerHTML += '<div id="'+option_id_name+'" class="'+_join(plugin_id_name, 'option')+'"></div>';
|
||||
|
||||
|
||||
// add label and question text
|
||||
var form = document.getElementById(option_id_name)
|
||||
var input_id_name = _join(plugin_id_name, 'response', i);
|
||||
var input_name = _join(plugin_id_name, 'response', i);
|
||||
var input_id = _join(plugin_id_name, 'response', i, j);
|
||||
var label = document.createElement('label');
|
||||
label.setAttribute('class', plugin_id_name+'-text');
|
||||
label.innerHTML = trial.options[i][j];
|
||||
label.setAttribute('for', input_id_name)
|
||||
|
||||
label.setAttribute('for', input_id)
|
||||
|
||||
// create radio button
|
||||
var input = document.createElement('input');
|
||||
input.setAttribute('type', "radio");
|
||||
input.setAttribute('name', input_id_name);
|
||||
input.setAttribute('value', trial.options[i][j])
|
||||
label.prepend(input);
|
||||
form.appendChild(label)
|
||||
input.setAttribute('name', input_name);
|
||||
input.setAttribute('id', input_id);
|
||||
input.setAttribute('value', trial.options[i][j]);
|
||||
form.appendChild(label);
|
||||
form.insertBefore(input, label);
|
||||
}
|
||||
|
||||
if (trial.required && trial.required[i]) {
|
||||
// add "question required" asterisk
|
||||
display_element.querySelector(question_selector + " p").innerHMTL += "<span class='required'>*</span>";
|
||||
|
||||
|
||||
// add required property
|
||||
display_element.querySelector(question_selector + " input[type=radio]").required = true;
|
||||
}
|
||||
}
|
||||
// add submit button
|
||||
trial_form.innerHTML += '<input type="submit" id="'+plugin_id_name+'-next" class="'+plugin_id_name+' jspsych-btn"></input>';
|
||||
|
||||
trial_form.innerHTML += '<input type="submit" id="'+plugin_id_name+'-next" class="'+plugin_id_name+' jspsych-btn"' + (trial.button_label ? ' value="'+trial.button_label + '"': '') + '></input>';
|
||||
trial_form.addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
var matches = display_element.querySelectorAll("div." + plugin_id_name + "-question");
|
||||
@ -149,24 +158,29 @@ jsPsych.plugins['survey-multi-choice'] = (function() {
|
||||
// create object to hold responses
|
||||
var question_data = {};
|
||||
var matches = display_element.querySelectorAll("div." + plugin_id_name + "-question");
|
||||
matches.forEach(function(match, index) {
|
||||
var id = "Q" + index;
|
||||
var val = match.querySelector("input[type=radio]:checked").value;
|
||||
for(var i=0; i<matches.length; i++){
|
||||
match = matches[i];
|
||||
var id = "Q" + i;
|
||||
if(match.querySelector("input[type=radio]:checked") !== null){
|
||||
var val = match.querySelector("input[type=radio]:checked").value;
|
||||
} else {
|
||||
var val = "";
|
||||
}
|
||||
var obje = {};
|
||||
obje[id] = val;
|
||||
Object.assign(question_data, obje);
|
||||
})
|
||||
}
|
||||
// save data
|
||||
var trial_data = {
|
||||
"rt": response_time,
|
||||
"responses": JSON.stringify(question_data)
|
||||
};
|
||||
display_element.innerHTML = '';
|
||||
|
||||
|
||||
// next trial
|
||||
jsPsych.finishTrial(trial_data);
|
||||
});
|
||||
|
||||
|
||||
var startTime = (new Date()).getTime();
|
||||
};
|
||||
|
||||
|
146
plugins/jspsych-survey-multi-picture.js
Normal file
146
plugins/jspsych-survey-multi-picture.js
Normal file
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* jspsych-survey-multi-picture
|
||||
* a jspsych plugin for multiple choice survey questions
|
||||
*
|
||||
* documentation: docs.jspsych.org
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
jsPsych.plugins['survey-multi-picture'] = (function() {
|
||||
var plugin = {};
|
||||
|
||||
plugin.info = {
|
||||
name: 'survey-multi-picture',
|
||||
description: '',
|
||||
parameters: {
|
||||
questions: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
array: true,
|
||||
default: undefined,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
options: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
array: true,
|
||||
default: undefined,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
required: {
|
||||
type: [jsPsych.plugins.parameterType.BOOL],
|
||||
array: true,
|
||||
default: false,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
horitzontal: {
|
||||
type: [jsPsych.plugins.parameterType.BOOL],
|
||||
default: false,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
preamble: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: '',
|
||||
no_function: false,
|
||||
description: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
plugin.trial = function(display_element, trial) {
|
||||
var plugin_id_name = "jspsych-survey-multi-picture";
|
||||
var plugin_id_selector = '#' + plugin_id_name;
|
||||
var _join = function( /*args*/ ) {
|
||||
var arr = Array.prototype.slice.call(arguments, _join.length);
|
||||
return arr.join(separator = '-');
|
||||
}
|
||||
// trial defaults
|
||||
trial.preamble = typeof trial.preamble == 'undefined' ? "" : trial.preamble;
|
||||
trial.required = typeof trial.required == 'undefined' ? null : trial.required;
|
||||
trial.horizontal = typeof trial.required == 'undefined' ? false : trial.horizontal;
|
||||
|
||||
// if any trial variables are functions
|
||||
// this evaluates the function and replaces
|
||||
// it with the output of the function
|
||||
trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial);
|
||||
|
||||
// inject CSS for trial
|
||||
var node = display_element.innerHTML += '<style id="jspsych-survey-multi-picture-css">';
|
||||
var cssstr = ".jspsych-survey-multi-picture-question { margin-top: 2em; margin-bottom: 2em; text-align: center; }"+
|
||||
".jspsych-survey-multi-picture-text span.required {color: darkred;}"+
|
||||
".jspsych-survey-multi-picture-option { line-height: 2; margin-bottom: 10px; }"+
|
||||
".jspsych-survey-multi-picture-horizontal .jspsych-survey-multi-picture-option { display: inline-block; margin-left: 1em; margin-right: 1em; vertical-align: top;}"
|
||||
|
||||
display_element.querySelector('#jspsych-survey-multi-picture-css').innerHTML = cssstr;
|
||||
|
||||
// form element
|
||||
var trial_form_id = _join(plugin_id_name, "form");
|
||||
display_element.innerHTML += '<form id="'+trial_form_id+'"></form>';
|
||||
var trial_form = display_element.querySelector("#" + trial_form_id);
|
||||
// show preamble text
|
||||
var preamble_id_name = _join(plugin_id_name, 'preamble');
|
||||
trial_form.innerHTML += '<div id="'+preamble_id_name+'" class="'+preamble_id_name+'">'+trial.preamble+'</div>';
|
||||
|
||||
// add multiple-picture questions
|
||||
for (var i = 0; i < trial.questions.length; i++) {
|
||||
// create question container
|
||||
var question_classes = [_join(plugin_id_name, 'question')];
|
||||
if (trial.horizontal) {
|
||||
question_classes.push(_join(plugin_id_name, 'horizontal'));
|
||||
}
|
||||
|
||||
trial_form.innerHTML += '<div id="'+_join(plugin_id_name, i)+'" class="'+question_classes.join(' ')+'"></div>';
|
||||
|
||||
var question_selector = _join(plugin_id_selector, i);
|
||||
|
||||
// add question text
|
||||
display_element.querySelector(question_selector).innerHTML += '<p id="survey-question" class="' + plugin_id_name + '-text survey-multi-picture">' + trial.questions[i] + '</p>';
|
||||
|
||||
// create option clickble images
|
||||
for (var j = 0; j < trial.options[i].length; j++) {
|
||||
var option_id_name = _join(plugin_id_name, "option", i, j),
|
||||
option_id_selector = '#' + option_id_name;
|
||||
|
||||
// add image container
|
||||
display_element.querySelector(question_selector).innerHTML += '<div id="'+option_id_name+'" class="'+_join(plugin_id_name, 'option')+'"></div>';
|
||||
|
||||
// add label
|
||||
if(trial.options[i][j].label){
|
||||
var label = trial.options[i][j].label;
|
||||
var option_label = '<label class="' + plugin_id_name + '-text">' + label + '</label>';
|
||||
display_element.querySelector(option_id_selector).innerHTML += option_label;
|
||||
} else {
|
||||
var option_label = '<label class="' + plugin_id_name + '-text"></label>';
|
||||
display_element.querySelector(option_id_selector).innerHTML += option_label;
|
||||
}
|
||||
display_element.querySelector(option_id_selector + " label").innerHTML =
|
||||
'<img style="width: 250px; height: auto;" class="'+plugin_id_name+'-image" src="'+trial.options[i][j].url+'">' +
|
||||
display_element.querySelector(option_id_selector + " label").innerHTML;
|
||||
}
|
||||
}
|
||||
var matches = display_element.querySelectorAll(".jspsych-survey-multi-picture-option");
|
||||
for(var index=0; index<matches.length; index++){
|
||||
currentImageDiv = matches[index];
|
||||
currentImageDiv.addEventListener('click', function(event){
|
||||
var endTime = (new Date()).getTime();
|
||||
var response_time = endTime - startTime;
|
||||
var question_data = {};
|
||||
var id = 'Q' + index;
|
||||
var val = currentImageDiv.querySelector('.'+plugin_id_name+'-image').src;
|
||||
var obje = {};
|
||||
obje[id] = val;
|
||||
Object.assign(question_data, obje);
|
||||
var trial_data = {
|
||||
"rt": response_time,
|
||||
"responses": JSON.stringify(question_data)
|
||||
};
|
||||
display_element.innerHTML = '';
|
||||
jsPsych.finishTrial(trial_data);
|
||||
});
|
||||
}
|
||||
var startTime = (new Date()).getTime();
|
||||
};
|
||||
return plugin;
|
||||
})();
|
189
plugins/jspsych-survey-multi-select.js
Normal file
189
plugins/jspsych-survey-multi-select.js
Normal file
@ -0,0 +1,189 @@
|
||||
/**
|
||||
* jspsych-survey-multi-select
|
||||
* a jspsych plugin for multiple choice survey questions
|
||||
*
|
||||
* documentation: docs.jspsych.org
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
jsPsych.plugins['survey-multi-select'] = (function() {
|
||||
var plugin = {};
|
||||
|
||||
plugin.info = {
|
||||
name: 'survey-multi-select',
|
||||
description: '',
|
||||
parameters: {
|
||||
questions: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
array: true,
|
||||
default: undefined,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
options: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
array: true,
|
||||
default: undefined,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
required: {
|
||||
type: [jsPsych.plugins.parameterType.BOOL],
|
||||
array: true,
|
||||
default: false,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
horitzontal: {
|
||||
type: [jsPsych.plugins.parameterType.BOOL],
|
||||
default: false,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
preamble: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: '',
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
button_label: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: '',
|
||||
no_function: false,
|
||||
description: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
plugin.trial = function(display_element, trial) {
|
||||
var plugin_id_name = "jspsych-survey-multi-select";
|
||||
var plugin_id_selector = '#' + plugin_id_name;
|
||||
var _join = function( /*args*/ ) {
|
||||
var arr = Array.prototype.slice.call(arguments, _join.length);
|
||||
return arr.join(separator = '-');
|
||||
}
|
||||
|
||||
// trial defaults
|
||||
trial.preamble = typeof trial.preamble == 'undefined' ? "" : trial.preamble;
|
||||
trial.required = typeof trial.required == 'undefined' ? true : trial.required;
|
||||
trial.required_msg = trial.required_msg || '*please select at least one option!';
|
||||
trial.horizontal = typeof trial.horizontal == 'undefined' ? false : trial.horizontal;
|
||||
//If button_label is empty, the browser's language will be used to determine the button label.
|
||||
trial.button_label = typeof trial.button_label === 'undefined' ? '' : trial.button_label;
|
||||
|
||||
// if any trial variables are functions
|
||||
// this evaluates the function and replaces
|
||||
// it with the output of the function
|
||||
trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial);
|
||||
|
||||
// inject CSS for trial
|
||||
var node = display_element.innerHTML += '<style id="jspsych-survey-multi-select-css">';
|
||||
var cssstr = ".jspsych-survey-multi-select-question { margin-top: 2em; margin-bottom: 2em; text-align: left; }"+
|
||||
".jspsych-survey-multi-select-text span.required {color: darkred;}"+
|
||||
".jspsych-survey-multi-select-horizontal .jspsych-survey-multi-select-text { text-align: center;}"+
|
||||
".jspsych-survey-multi-select-option { line-height: 2; }"+
|
||||
".jspsych-survey-multi-select-horizontal .jspsych-survey-multi-select-option { display: inline-block; margin-left: 1em; margin-right: 1em; vertical-align: top;}"+
|
||||
"label.jspsych-survey-multi-select-text input[type='checkbox'] {margin-right: 1em;}"
|
||||
|
||||
display_element.querySelector('#jspsych-survey-multi-select-css').innerHTML = cssstr;
|
||||
|
||||
// form element
|
||||
var trial_form_id = _join(plugin_id_name, "form");
|
||||
display_element.innerHTML += '<form id="'+trial_form_id+'"></form>';
|
||||
var trial_form = display_element.querySelector("#" + trial_form_id);
|
||||
// show preamble text
|
||||
var preamble_id_name = _join(plugin_id_name, 'preamble');
|
||||
trial_form.innerHTML += '<div id="'+preamble_id_name+'" class="'+preamble_id_name+'">'+trial.preamble+'</div>';
|
||||
|
||||
// add multiple-select questions
|
||||
for (var i = 0; i < trial.questions.length; i++) {
|
||||
// create question container
|
||||
var question_classes = [_join(plugin_id_name, 'question')];
|
||||
if (trial.horizontal) {
|
||||
question_classes.push(_join(plugin_id_name, 'horizontal'));
|
||||
}
|
||||
|
||||
trial_form.innerHTML += '<div id="'+_join(plugin_id_name, i)+'" class="'+question_classes.join(' ')+'"></div>';
|
||||
|
||||
var question_selector = _join(plugin_id_selector, i);
|
||||
|
||||
// add question text
|
||||
display_element.querySelector(question_selector).innerHTML += '<p id="survey-question" class="' + plugin_id_name + '-text survey-multi-select">' + trial.questions[i] + '</p>';
|
||||
|
||||
// create option check boxes
|
||||
for (var j = 0; j < trial.options[i].length; j++) {
|
||||
var option_id_name = _join(plugin_id_name, "option", i, j),
|
||||
option_id_selector = '#' + option_id_name;
|
||||
|
||||
// add check box container
|
||||
display_element.querySelector(question_selector).innerHTML += '<div id="'+option_id_name+'" class="'+_join(plugin_id_name, 'option')+'"></div>';
|
||||
|
||||
// add label and question text
|
||||
var form = document.getElementById(option_id_name)
|
||||
var input_name = _join(plugin_id_name, 'response', i);
|
||||
var input_id = _join(plugin_id_name, 'response', i, j);
|
||||
var label = document.createElement('label');
|
||||
label.setAttribute('class', plugin_id_name+'-text');
|
||||
label.innerHTML = trial.options[i][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', trial.options[i][j])
|
||||
form.appendChild(label)
|
||||
form.insertBefore(input, label)
|
||||
}
|
||||
}
|
||||
// add submit button
|
||||
trial_form.innerHTML +='<div class="fail-message"></div>'
|
||||
trial_form.innerHTML += '<input type="submit" id="'+plugin_id_name+'-next" class="'+plugin_id_name+' jspsych-btn"' + (trial.button_label ? ' value="'+trial.button_label +'"': '') + '></input>';
|
||||
|
||||
trial_form.addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
// measure response time
|
||||
var endTime = (new Date()).getTime();
|
||||
var response_time = endTime - startTime;
|
||||
|
||||
// create object to hold responses
|
||||
var matches = display_element.querySelectorAll("div." + plugin_id_name + "-question");
|
||||
var question_data = {};
|
||||
var has_response = [];
|
||||
for(var index=0; index<matches.length; index++){
|
||||
match = matches[index];
|
||||
var val = [];
|
||||
var inputboxes = match.querySelectorAll("input[type=checkbox]:checked")
|
||||
for(var j=0; j<inputboxes.length; j++){
|
||||
currentChecked = inputboxes[j];
|
||||
val.push(currentChecked.value)
|
||||
}
|
||||
var id = 'Q' + index
|
||||
var obje = {};
|
||||
obje[id] = val;
|
||||
Object.assign(question_data, obje);
|
||||
if(val.length == 0){ has_response.push(false); } else { has_response.push(true); }
|
||||
}
|
||||
// adds validation to check if at least one option is selected
|
||||
if(trial.required && has_response.includes(false)) {
|
||||
var inputboxes = display_element.querySelectorAll("input[type=checkbox]")
|
||||
display_element.querySelector(".fail-message").innerHTML = '<span style="color: red;" class="required">'+trial.required_msg+'</span>';
|
||||
} else {
|
||||
// save data
|
||||
var trial_data = {
|
||||
"rt": response_time,
|
||||
"responses": JSON.stringify(question_data)
|
||||
};
|
||||
display_element.innerHTML = '';
|
||||
|
||||
// next trial
|
||||
jsPsych.finishTrial(trial_data);
|
||||
}
|
||||
});
|
||||
|
||||
var startTime = (new Date()).getTime();
|
||||
};
|
||||
|
||||
return plugin;
|
||||
})();
|
@ -24,7 +24,7 @@ jsPsych.plugins['survey-text'] = (function() {
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
premable: {
|
||||
preamble: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: '',
|
||||
no_function: false,
|
||||
@ -43,6 +43,19 @@ jsPsych.plugins['survey-text'] = (function() {
|
||||
default: 40,
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
values: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
array: true,
|
||||
default: '',
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
button_label: {
|
||||
type: [jsPsych.plugins.parameterType.STRING],
|
||||
default: '',
|
||||
no_function: false,
|
||||
description: 'Submit Answers'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -50,6 +63,8 @@ jsPsych.plugins['survey-text'] = (function() {
|
||||
plugin.trial = function(display_element, trial) {
|
||||
|
||||
trial.preamble = typeof trial.preamble == 'undefined' ? "" : trial.preamble;
|
||||
trial.button_label = typeof trial.button_label === 'undefined' ? 'Submit Answers' : trial.button_label;
|
||||
|
||||
if (typeof trial.rows == 'undefined') {
|
||||
trial.rows = [];
|
||||
for (var i = 0; i < trial.questions.length; i++) {
|
||||
@ -62,6 +77,12 @@ jsPsych.plugins['survey-text'] = (function() {
|
||||
trial.columns.push(40);
|
||||
}
|
||||
}
|
||||
if (typeof trial.values == 'undefined') {
|
||||
trial.values = [];
|
||||
for (var i = 0; i < trial.questions.length; i++) {
|
||||
trial.values.push("");
|
||||
}
|
||||
}
|
||||
|
||||
// if any trial variables are functions
|
||||
// this evaluates the function and replaces
|
||||
@ -69,18 +90,24 @@ jsPsych.plugins['survey-text'] = (function() {
|
||||
trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial);
|
||||
|
||||
// show preamble text
|
||||
display_element.innerHTML += '<div id="jspsych-survey-text-preamble" class="jspsych-survey-text-preamble">'+trial.preamble+'</div>';
|
||||
var html = '<div id="jspsych-survey-text-preamble" class="jspsych-survey-text-preamble">'+trial.preamble+'</div>';
|
||||
|
||||
// add questions
|
||||
for (var i = 0; i < trial.questions.length; i++) {
|
||||
display_element.innerHTML += '<div id="jspsych-survey-text-"'+i+'" class="jspsych-survey-text-question" style="margin: 2em 0em;">'+
|
||||
'<p class="jspsych-survey-text">' + trial.questions[i] + '</p>'+
|
||||
'<textarea name="#jspsych-survey-text-response-' + i + '" cols="' + trial.columns[i] + '" rows="' + trial.rows[i] + '"></textarea>'+
|
||||
'</div>';
|
||||
html += '<div id="jspsych-survey-text-"'+i+'" class="jspsych-survey-text-question" style="margin: 2em 0em;">';
|
||||
html += '<p class="jspsych-survey-text">' + trial.questions[i] + '</p>';
|
||||
if(trial.rows[i] == 1){
|
||||
html += '<input type="text" name="#jspsych-survey-text-response-' + i + '">'+trial.values[i]+'</input>';
|
||||
} else {
|
||||
html += '<textarea name="#jspsych-survey-text-response-' + i + '" cols="' + trial.columns[i] + '" rows="' + trial.rows[i] + '">'+trial.values[i]+'</textarea>';
|
||||
}
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
// add submit button
|
||||
display_element.innerHTML += '<button id="jspsych-survey-text-next" class="jspsych-btn jspsych-survey-text">Submit Answers</button>';
|
||||
html += '<button id="jspsych-survey-text-next" class="jspsych-btn jspsych-survey-text">'+trial.button_label+'</button>';
|
||||
|
||||
display_element.innerHTML = html;
|
||||
|
||||
display_element.querySelector('#jspsych-survey-text-next').addEventListener('click', function() {
|
||||
// measure response time
|
||||
@ -92,7 +119,7 @@ jsPsych.plugins['survey-text'] = (function() {
|
||||
var matches = display_element.querySelectorAll('div.jspsych-survey-text-question');
|
||||
for(var index=0; index<matches.length; index++){
|
||||
var id = "Q" + index;
|
||||
var val = matches[index].querySelector('textarea').value;
|
||||
var val = matches[index].querySelector('textarea, input').value;
|
||||
var obje = {};
|
||||
obje[id] = val;
|
||||
Object.assign(question_data, obje);
|
||||
|
@ -51,10 +51,23 @@ jsPsych.plugins.video = (function() {
|
||||
default: '',
|
||||
no_function: false,
|
||||
description: ''
|
||||
},
|
||||
start: {
|
||||
type: [jsPsych.plugins.parameterType.FLOAT],
|
||||
default: false,
|
||||
no_function: false,
|
||||
description: 'time to start the clip'
|
||||
},
|
||||
stop: {
|
||||
type: [jsPsych.plugins.parameterType.FLOAT],
|
||||
default: false,
|
||||
no_function: false,
|
||||
description: 'time to stop the clip'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
plugin.trial = function(display_element, trial) {
|
||||
|
||||
// set default values for the parameters
|
||||
@ -80,8 +93,22 @@ jsPsych.plugins.video = (function() {
|
||||
var s = trial.sources[i];
|
||||
var type = s.substr(s.lastIndexOf('.') + 1);
|
||||
type = type.toLowerCase();
|
||||
video_html+='<source src="'+s+'" type="video/'+type+'">';
|
||||
}
|
||||
|
||||
// adding start stop parameters if specified
|
||||
video_html+='<source src="'+s
|
||||
|
||||
if (trial.start) {
|
||||
video_html+= '#t=' + trial.start;
|
||||
} else {
|
||||
video_html+= '#t=0';
|
||||
}
|
||||
|
||||
if (trial.stop) {
|
||||
video_html+= ',' + trial.stop
|
||||
}
|
||||
|
||||
video_html+='" type="video/'+type+'">';
|
||||
}
|
||||
video_html +="</video>"
|
||||
|
||||
display_element.innerHTML += video_html;
|
||||
|
@ -90,7 +90,7 @@ jsPsych.plugins['vsl-grid-scene'] = (function() {
|
||||
for (var col = 0; col < ncols; col++) {
|
||||
display_element.querySelector('#jspsych-vsl-grid-scene-table-row-' + row).innerHTML += '<td id="jspsych-vsl-grid-scene-table-' + row + '-' + col +'" '+
|
||||
'style="padding: '+ (image_size[1] / 10) + 'px ' + (image_size[0] / 10) + 'px; border: 1px solid #555;">'+
|
||||
'<div id="jspsych-vsl-grid-scene-table-cell-' + row + '-' + col'" style="width: '+image_size[0]+'px; height: '+image_size[1]+'px;"></div>';
|
||||
'<div id="jspsych-vsl-grid-scene-table-cell-' + row + '-' + col + '" style="width: '+image_size[0]+'px; height: '+image_size[1]+'px;"></div>';
|
||||
}
|
||||
}
|
||||
|
||||
|
7
tests/README.md
Normal file
7
tests/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Testing jsPsych
|
||||
|
||||
Tests are written with [Jest](https://facebook.github.io/jest/).
|
||||
|
||||
To run the tests, install Node and npm. Run `npm install` in the root jsPsych directory. Then run `npm test`.
|
||||
|
||||
To add tests, follow examples contained in this folder.
|
297
tests/events.test.js
Normal file
297
tests/events.test.js
Normal file
@ -0,0 +1,297 @@
|
||||
describe('on_finish (trial)', function(){
|
||||
test('should get an object of data generated by the trial', function(){
|
||||
|
||||
require('../jspsych.js');
|
||||
require('../plugins/jspsych-text.js');
|
||||
|
||||
return (new Promise(function(resolve, reject){
|
||||
|
||||
var key_data = null;
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'hello',
|
||||
on_finish: function(data){
|
||||
key_data = data.key_press;
|
||||
}
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
on_finish: function() {
|
||||
resolve({key_data});
|
||||
}
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
})).then(function(data) { expect(data.key_data).toBe(32) });
|
||||
});
|
||||
|
||||
test('should be able to write to the data', function(){
|
||||
|
||||
require('../jspsych.js');
|
||||
require('../plugins/jspsych-text.js');
|
||||
|
||||
return (new Promise(function(resolve, reject){
|
||||
|
||||
var promise_data = {};
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'hello',
|
||||
on_finish: function(data){
|
||||
data.key_press = 1;
|
||||
}
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
on_finish: function() {
|
||||
promise_data.final_key_press = jsPsych.data.get().values()[0].key_press;
|
||||
resolve(promise_data);
|
||||
}
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
//resolve();
|
||||
})).then(function(pd) {
|
||||
expect(pd.final_key_press).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('on_trial_finish (experiment level)', function(){
|
||||
test('should get an object containing the trial data', function(){
|
||||
require('../jspsych.js');
|
||||
require('../plugins/jspsych-text.js');
|
||||
|
||||
return (new Promise(function(resolve, reject){
|
||||
|
||||
var promise_data = {};
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'hello'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
on_trial_finish: function(data){
|
||||
promise_data.key = data.key_press;
|
||||
},
|
||||
on_finish: function(){
|
||||
resolve(promise_data);
|
||||
}
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
//resolve();
|
||||
})).then(function(pd) {
|
||||
expect(pd.key).toBe(32);
|
||||
});
|
||||
});
|
||||
|
||||
test('should allow writing to the data object', function(){
|
||||
require('../jspsych.js');
|
||||
require('../plugins/jspsych-text.js');
|
||||
|
||||
return (new Promise(function(resolve, reject){
|
||||
|
||||
var promise_data = {};
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'hello'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
on_trial_finish: function(data){
|
||||
data.write = true;
|
||||
},
|
||||
on_finish: function(data){
|
||||
promise_data.write = data.values()[0].write;
|
||||
resolve(promise_data);
|
||||
}
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
//resolve();
|
||||
})).then(function(pd) {
|
||||
expect(pd.write).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('on_data_update', function(){
|
||||
test('should get an object containing the trial data', function(){
|
||||
require('../jspsych.js');
|
||||
require('../plugins/jspsych-text.js');
|
||||
|
||||
return (new Promise(function(resolve, reject){
|
||||
|
||||
var promise_data = {};
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'hello'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
on_data_update: function(data){
|
||||
promise_data.key = data.key_press;
|
||||
},
|
||||
on_finish: function(){
|
||||
resolve(promise_data);
|
||||
}
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
//resolve();
|
||||
})).then(function(pd) {
|
||||
expect(pd.key).toBe(32);
|
||||
});
|
||||
});
|
||||
|
||||
test('should contain data added with on_finish (trial level)', function(){
|
||||
require('../jspsych.js');
|
||||
require('../plugins/jspsych-text.js');
|
||||
|
||||
return (new Promise(function(resolve, reject){
|
||||
|
||||
var promise_data = {};
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'hello',
|
||||
on_finish: function(data){
|
||||
data.trial_level = true;
|
||||
}
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
on_data_update: function(data){
|
||||
promise_data.trial_level = data.trial_level;
|
||||
},
|
||||
on_finish: function(){
|
||||
resolve(promise_data);
|
||||
}
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
//resolve();
|
||||
})).then(function(pd) {
|
||||
expect(pd.trial_level).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('should contain data added with on_trial_finish (experiment level)', function(){
|
||||
require('../jspsych.js');
|
||||
require('../plugins/jspsych-text.js');
|
||||
|
||||
return (new Promise(function(resolve, reject){
|
||||
|
||||
var promise_data = {};
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'hello'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
on_trial_finish: function(data){
|
||||
data.experiment_level = true;
|
||||
},
|
||||
on_data_update: function(data){
|
||||
promise_data.experiment_level = data.experiment_level;
|
||||
},
|
||||
on_finish: function(){
|
||||
resolve(promise_data);
|
||||
}
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
//resolve();
|
||||
})).then(function(pd) {
|
||||
expect(pd.experiment_level).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('on_trial_start', function(){
|
||||
|
||||
test('should get an object containing the trial properties', function(){
|
||||
require('../jspsych.js');
|
||||
require('../plugins/jspsych-text.js');
|
||||
|
||||
return (new Promise(function(resolve, reject){
|
||||
|
||||
var promise_data = {};
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'hello'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
on_trial_start: function(trial){
|
||||
promise_data.text = trial.text;
|
||||
},
|
||||
on_finish: function(){
|
||||
resolve(promise_data);
|
||||
}
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
//resolve();
|
||||
})).then(function(pd) {
|
||||
expect(pd.text).toBe('hello');
|
||||
});
|
||||
});
|
||||
|
||||
test('should allow modification of the trial properties', function(){
|
||||
require('../jspsych.js');
|
||||
require('../plugins/jspsych-text.js');
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'hello'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
on_trial_start: function(trial){
|
||||
trial.text = 'goodbye';
|
||||
},
|
||||
on_finish: function(){
|
||||
resolve(promise_data);
|
||||
}
|
||||
});
|
||||
|
||||
var display_element = jsPsych.getDisplayElement();
|
||||
expect(display_element.innerHTML).toBe('goodbye');
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
});
|
||||
});
|
@ -79,4 +79,16 @@ describe('DataCollection', function(){
|
||||
expect(data.count()).toBe(3);
|
||||
expect(data.values()[2].rt).toBe(500);
|
||||
});
|
||||
test('#unqiueNames', function(){
|
||||
var data = [
|
||||
{rt: 100, filter: true},
|
||||
{rt: 200, filter: false},
|
||||
{rt: 300, filter: true, v1: false},
|
||||
{rt: 400, filter: false, v2: true},
|
||||
{rt: 500, filter: false, v1: false}
|
||||
];
|
||||
|
||||
jsPsych.data._customInsert(data);
|
||||
expect(jsPsych.data.get().uniqueNames().length).toBe(4);
|
||||
})
|
||||
});
|
||||
|
32
tests/jsPsych.utils/utils.test.js
Normal file
32
tests/jsPsych.utils/utils.test.js
Normal file
@ -0,0 +1,32 @@
|
||||
const root = '../../';
|
||||
|
||||
require(root + 'jspsych.js');
|
||||
|
||||
describe('unique', function(){
|
||||
test('generates unique array when there are duplicates', function(){
|
||||
var arr = [1,1,2,2,3,3];
|
||||
var out = jsPsych.utils.unique(arr);
|
||||
expect(out).toEqual([1,2,3]);
|
||||
expect(out).not.toEqual(arr);
|
||||
});
|
||||
|
||||
test('generates same array when there are no duplicates', function(){
|
||||
var arr = [1,2,3];
|
||||
var out = jsPsych.utils.unique(arr);
|
||||
expect(out).toEqual(arr);
|
||||
})
|
||||
});
|
||||
|
||||
describe('flatten', function(){
|
||||
test('generates flat array from flat input', function(){
|
||||
var arr = [1,1,2,2,3,3];
|
||||
var out = jsPsych.utils.flatten(arr);
|
||||
expect(out).toEqual(arr);
|
||||
});
|
||||
|
||||
test('generates flat array from nested input', function(){
|
||||
var arr = [1,[1,2,2],[3],3];
|
||||
var out = jsPsych.utils.flatten(arr);
|
||||
expect(out).toEqual([1,1,2,2,3,3]);
|
||||
});
|
||||
});
|
7
tests/loads.test.js
Normal file
7
tests/loads.test.js
Normal file
@ -0,0 +1,7 @@
|
||||
const root = '../';
|
||||
|
||||
require(root + 'jspsych.js');
|
||||
|
||||
test('jsPsych should be in the window object', function(){
|
||||
expect(typeof window.jsPsych).not.toBe('undefined');
|
||||
});
|
BIN
tests/media/blue.png
Normal file
BIN
tests/media/blue.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
BIN
tests/media/orange.png
Normal file
BIN
tests/media/orange.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
@ -1,132 +0,0 @@
|
||||
describe('The on_finish trial level event handler', function(){
|
||||
test('should get an object of data generated by the trial', function(){
|
||||
|
||||
require('../jspsych.js');
|
||||
require('../plugins/jspsych-text.js');
|
||||
|
||||
return (new Promise(function(resolve, reject){
|
||||
|
||||
var key_data = null;
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'hello',
|
||||
on_finish: function(data){
|
||||
key_data = data.key_press;
|
||||
}
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
on_finish: function() {
|
||||
resolve({key_data});
|
||||
}
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
//resolve();
|
||||
})).then(function(data) { expect(data.key_data).toBe(32) });
|
||||
});
|
||||
|
||||
test('data should be writeable', function(){
|
||||
|
||||
require('../jspsych.js');
|
||||
require('../plugins/jspsych-text.js');
|
||||
|
||||
return (new Promise(function(resolve, reject){
|
||||
|
||||
var promise_data = {};
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'hello',
|
||||
on_finish: function(data){
|
||||
data.key_press = 1;
|
||||
}
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
on_finish: function() {
|
||||
promise_data.final_key_press = jsPsych.data.get().values()[0].key_press;
|
||||
resolve(promise_data);
|
||||
}
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
//resolve();
|
||||
})).then(function(pd) {
|
||||
expect(pd.final_key_press).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('The on_trial_finish handler for trials set at the experiment level', function(){
|
||||
test('should get an object containing the trial data', function(){
|
||||
require('../jspsych.js');
|
||||
require('../plugins/jspsych-text.js');
|
||||
|
||||
return (new Promise(function(resolve, reject){
|
||||
|
||||
var promise_data = {};
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'hello'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
on_trial_finish: function(data){
|
||||
promise_data.key = data.key_press;
|
||||
},
|
||||
on_finish: function(){
|
||||
resolve(promise_data);
|
||||
}
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
//resolve();
|
||||
})).then(function(pd) {
|
||||
expect(pd.key).toBe(32);
|
||||
});
|
||||
});
|
||||
test('should allow writing to the data object', function(){
|
||||
require('../jspsych.js');
|
||||
require('../plugins/jspsych-text.js');
|
||||
|
||||
return (new Promise(function(resolve, reject){
|
||||
|
||||
var promise_data = {};
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'hello'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial],
|
||||
on_trial_finish: function(data){
|
||||
data.write = true;
|
||||
},
|
||||
on_finish: function(data){
|
||||
promise_data.write = data.values()[0].write;
|
||||
resolve(promise_data);
|
||||
}
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
//resolve();
|
||||
})).then(function(pd) {
|
||||
expect(pd.write).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
41
tests/plugins/plugin-fullscreen.test.js
Normal file
41
tests/plugins/plugin-fullscreen.test.js
Normal file
@ -0,0 +1,41 @@
|
||||
const root = '../../';
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
describe('fullscreen plugin', function(){
|
||||
|
||||
beforeEach(function(){
|
||||
require(root + 'jspsych.js');
|
||||
require(root + 'plugins/jspsych-fullscreen.js');
|
||||
require(root + 'plugins/jspsych-text.js');
|
||||
});
|
||||
|
||||
test('loads correctly', function(){
|
||||
expect(typeof window.jsPsych.plugins['fullscreen']).not.toBe('undefined');
|
||||
});
|
||||
|
||||
// can't test this right now because jsdom doesn't support fullscreen API.
|
||||
|
||||
/*test('launches fullscreen mode by default', function(){
|
||||
var trial = {
|
||||
type: 'fullscreen',
|
||||
delay_after: 0
|
||||
}
|
||||
|
||||
var text = {
|
||||
type: 'text',
|
||||
text: 'fullscreen'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial, text]
|
||||
});
|
||||
|
||||
expect(document.fullscreenElement).toBeUndefined();
|
||||
console.log(jsPsych.getDisplayElement().requestFullscreen);
|
||||
document.querySelector('#jspsych-fullscreen-btn').dispatchEvent(new MouseEvent('click', {}));
|
||||
|
||||
expect(document.fullscreenElement).not.toBeUndefined();
|
||||
});*/
|
||||
|
||||
});
|
70
tests/plugins/plugin-same-different.test.js
Normal file
70
tests/plugins/plugin-same-different.test.js
Normal file
@ -0,0 +1,70 @@
|
||||
const root = '../../';
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
describe('same-different plugin', function(){
|
||||
|
||||
beforeEach(function(){
|
||||
require(root + 'jspsych.js');
|
||||
require(root + 'plugins/jspsych-same-different.js');
|
||||
});
|
||||
|
||||
test('loads correctly', function(){
|
||||
expect(window.jsPsych.plugins['same-different']).not.toBeUndefined();
|
||||
});
|
||||
|
||||
test('works with default parameters', function(){
|
||||
var trial = {
|
||||
type: 'same-different',
|
||||
stimuli: ['../media/blue.png','../media/blue.png'],
|
||||
answer: 'same'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<img class="jspsych-same-different-stimulus" src="../media/blue.png">');
|
||||
jest.runTimersToTime(1000);
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('');
|
||||
jest.runTimersToTime(500);
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<img class="jspsych-same-different-stimulus" src="../media/blue.png">');
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 81}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 81}));
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('');
|
||||
|
||||
expect(jsPsych.data.get().values()[0].correct).toBe(true);
|
||||
|
||||
});
|
||||
|
||||
test('check when timing_first_stim is -1', function(){
|
||||
var trial = {
|
||||
type: 'same-different',
|
||||
stimuli: ['../media/blue.png','../media/blue.png'],
|
||||
answer: 'same',
|
||||
timing_first_stim: -1
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<img class="jspsych-same-different-stimulus" src="../media/blue.png">');
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 81}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 81}));
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('');
|
||||
jest.runTimersToTime(500);
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<img class="jspsych-same-different-stimulus" src="../media/blue.png">');
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 81}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 81}));
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('');
|
||||
|
||||
expect(jsPsych.data.get().values()[0].correct).toBe(true);
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -1,34 +1,223 @@
|
||||
const root = '../../';
|
||||
|
||||
require(root + 'jspsych.js');
|
||||
require(root + 'plugins/jspsych-single-stim.js');
|
||||
jest.useFakeTimers();
|
||||
|
||||
test('jsPsych should be in the window object', function(){
|
||||
expect(typeof window.jsPsych).not.toBe('undefined');
|
||||
describe('single-stim plugin', function(){
|
||||
|
||||
beforeEach(function(){
|
||||
require(root + 'jspsych.js');
|
||||
require(root + 'plugins/jspsych-single-stim.js');
|
||||
});
|
||||
|
||||
test('loads correctly', function(){
|
||||
expect(typeof window.jsPsych.plugins['single-stim']).not.toBe('undefined');
|
||||
});
|
||||
|
||||
test('displays image by default', function(){
|
||||
var trial = {
|
||||
type: 'single-stim',
|
||||
stimulus: '../media/blue.png'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<img src="../media/blue.png" id="jspsych-single-stim-stimulus">');
|
||||
});
|
||||
|
||||
test('displays html when is_html is true', function(){
|
||||
var trial = {
|
||||
type: 'single-stim',
|
||||
stimulus: '<p>hello</p>',
|
||||
is_html: true
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<div id="jspsych-single-stim-stimulus"><p>hello</p></div>');
|
||||
});
|
||||
|
||||
test('display should clear after key press', function(){
|
||||
var trial = {
|
||||
type: 'single-stim',
|
||||
stimulus: '<p>hello</p>',
|
||||
is_html: true
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('');
|
||||
});
|
||||
|
||||
test('display should not clear after key press when choices is jsPsych.NO_KEYS', function(){
|
||||
var trial = {
|
||||
type: 'single-stim',
|
||||
stimulus: '<p>hello</p>',
|
||||
is_html: true,
|
||||
choices: jsPsych.NO_KEYS
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<div id="jspsych-single-stim-stimulus"><p>hello</p></div>');
|
||||
});
|
||||
|
||||
test('display should only clear when key is in choices array', function(){
|
||||
var trial = {
|
||||
type: 'single-stim',
|
||||
stimulus: '<p>hello</p>',
|
||||
is_html: true,
|
||||
choices: ['f']
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<div id="jspsych-single-stim-stimulus"><p>hello</p></div>');
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 70}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 70}));
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('');
|
||||
});
|
||||
|
||||
test('prompt should append html below stimulus', function(){
|
||||
var trial = {
|
||||
type: 'single-stim',
|
||||
stimulus: '<p>hello</p>',
|
||||
is_html: true,
|
||||
prompt: '<div id="foo">this is the prompt</div>'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<div id="jspsych-single-stim-stimulus"><p>hello</p></div><div id="foo">this is the prompt</div>');
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 70}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 70}));
|
||||
|
||||
});
|
||||
|
||||
test('timing_stim should set visibility of content to hidden after time has elapsed', function(){
|
||||
|
||||
var trial = {
|
||||
type: 'single-stim',
|
||||
stimulus: '<p>hello</p>',
|
||||
is_html: true,
|
||||
timing_stim: 500
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<div id="jspsych-single-stim-stimulus"><p>hello</p></div>');
|
||||
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<div id="jspsych-single-stim-stimulus" style="visibility: hidden;"><p>hello</p></div>');
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 70}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 70}));
|
||||
|
||||
});
|
||||
|
||||
test('timing_response should end trial after time has elapsed', function(){
|
||||
|
||||
var trial = {
|
||||
type: 'single-stim',
|
||||
stimulus: '<p>hello</p>',
|
||||
is_html: true,
|
||||
timing_response: 500
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<div id="jspsych-single-stim-stimulus"><p>hello</p></div>');
|
||||
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('');
|
||||
|
||||
});
|
||||
|
||||
test('trial should not end when response_ends_trial is false and stimulus should get responded class', function(){
|
||||
var trial = {
|
||||
type: 'single-stim',
|
||||
stimulus: '<p>hello</p>',
|
||||
is_html: true,
|
||||
response_ends_trial: false,
|
||||
timing_response: 500
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 70}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 70}));
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<div id="jspsych-single-stim-stimulus" class=" responded"><p>hello</p></div>');
|
||||
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
test('should accept functions as parameters', function(){
|
||||
|
||||
var trial = {
|
||||
type: 'single-stim',
|
||||
stimulus: function(){ return '<p>hello</p>'; },
|
||||
is_html: function(){ return true; },
|
||||
choices: function(){ return ['j']; },
|
||||
prompt: function(){ return '<div>prompt</div>'; },
|
||||
timing_response: function(){ return 1000; },
|
||||
timing_stim: function(){ return 500; },
|
||||
response_ends_trial: function(){ return false; }
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<div id="jspsych-single-stim-stimulus"><p>hello</p></div><div>prompt</div>');
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<div id="jspsych-single-stim-stimulus"><p>hello</p></div><div>prompt</div>');
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 70}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 70}));
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<div id="jspsych-single-stim-stimulus"><p>hello</p></div><div>prompt</div>');
|
||||
|
||||
jest.runTimersToTime(500);
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('<div id="jspsych-single-stim-stimulus" style="visibility: hidden;"><p>hello</p></div><div>prompt</div>');
|
||||
|
||||
jest.runTimersToTime(1000);
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
test('the single-stim plugin should be loaded', function(){
|
||||
expect(typeof window.jsPsych.plugins['single-stim']).not.toBe('undefined');
|
||||
})
|
||||
|
||||
var trial = {
|
||||
type: 'single-stim',
|
||||
stimulus: '<p>Hello</p>',
|
||||
is_html: true
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
test('HMTL stimulus should display', function(){
|
||||
var display_element = jsPsych.getDisplayElement();
|
||||
expect(display_element.innerHTML).toBe('<div id="jspsych-single-stim-stimulus"><p>Hello</p></div>');
|
||||
});
|
||||
|
||||
test('Display should clear after keypress', function(){
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
var display_element = jsPsych.getDisplayElement();
|
||||
expect(display_element.innerHTML).toBe('');
|
||||
})
|
||||
|
69
tests/timeline-variables.test.js
Normal file
69
tests/timeline-variables.test.js
Normal file
@ -0,0 +1,69 @@
|
||||
const root = '../';
|
||||
|
||||
beforeEach(function(){
|
||||
require(root + 'jspsych.js');
|
||||
require(root + 'plugins/jspsych-text.js');
|
||||
});
|
||||
|
||||
describe('randomize order', function(){
|
||||
test('holder', function(){
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('repetitons', function(){
|
||||
test('holder', function(){
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sampling', function(){
|
||||
test('holder', function(){
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
test('sampling functions run when timeline loops', function(){
|
||||
|
||||
var count = 0;
|
||||
const reps = 100;
|
||||
|
||||
var trial = {
|
||||
timeline: [{
|
||||
type: 'text',
|
||||
text: jsPsych.timelineVariable('text')
|
||||
}],
|
||||
timeline_variables: [
|
||||
{text: '1'},
|
||||
{text: '2'},
|
||||
{text: '3'}
|
||||
],
|
||||
sample: {
|
||||
type: 'without-replacement',
|
||||
size: 1
|
||||
},
|
||||
loop_function: function(){
|
||||
count++;
|
||||
return(count < reps);
|
||||
}
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
var result_1 = [];
|
||||
var result_2 = [];
|
||||
for(var i=0; i<reps/2; i++){
|
||||
var html = jsPsych.getDisplayElement().innerHTML;
|
||||
result_1.push(html);
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
var html = jsPsych.getDisplayElement().innerHTML;
|
||||
result_2.push(html);
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
}
|
||||
|
||||
expect(result_1).not.toEqual(result_2);
|
||||
});
|
||||
});
|
266
tests/timelines.test.js
Normal file
266
tests/timelines.test.js
Normal file
@ -0,0 +1,266 @@
|
||||
const root = '../';
|
||||
|
||||
beforeEach(function(){
|
||||
require(root + 'jspsych.js');
|
||||
require(root + 'plugins/jspsych-text.js');
|
||||
});
|
||||
|
||||
describe('loop function', function(){
|
||||
|
||||
test('repeats a timeline when returns true', function(){
|
||||
|
||||
var count = 0;
|
||||
|
||||
var trial = {
|
||||
timeline: [{
|
||||
type: 'text',
|
||||
text: 'foo'
|
||||
}],
|
||||
loop_function: function(){
|
||||
if(count < 1){
|
||||
count++;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
// first trial
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
expect(jsPsych.data.get().count()).toBe(1);
|
||||
|
||||
// second trial
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
expect(jsPsych.data.get().count()).toBe(2);
|
||||
|
||||
});
|
||||
|
||||
test('does not repeat when returns false', function(){
|
||||
|
||||
var count = 0;
|
||||
|
||||
var trial = {
|
||||
timeline: [{
|
||||
type: 'text',
|
||||
text: 'foo'
|
||||
}],
|
||||
loop_function: function(){
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
// first trial
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
expect(jsPsych.data.get().count()).toBe(1);
|
||||
|
||||
// second trial
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
expect(jsPsych.data.get().count()).toBe(1);
|
||||
|
||||
});
|
||||
|
||||
test('gets the data from the most recent iteration', function(){
|
||||
|
||||
var data_count = [];
|
||||
var count = 0;
|
||||
|
||||
var trial = {
|
||||
timeline: [{
|
||||
type: 'text',
|
||||
text: 'foo'
|
||||
}],
|
||||
loop_function: function(data){
|
||||
data_count.push(data.count());
|
||||
if(count < 2){
|
||||
count++;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
// first trial
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
// second trial
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
// third trial
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
expect(data_count).toEqual([1,1,1]);
|
||||
expect(jsPsych.data.get().count()).toBe(3);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('conditional function', function(){
|
||||
|
||||
test('skips the timeline when returns false', function(){
|
||||
|
||||
var conditional = {
|
||||
timeline: [{
|
||||
type: 'text',
|
||||
text: 'foo'
|
||||
}],
|
||||
conditional_function: function(){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'bar'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [conditional, trial]
|
||||
});
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('bar');
|
||||
|
||||
// clear
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
});
|
||||
|
||||
test('completes the timeline when returns true', function(){
|
||||
var conditional = {
|
||||
timeline: [{
|
||||
type: 'text',
|
||||
text: 'foo'
|
||||
}],
|
||||
conditional_function: function(){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var trial = {
|
||||
type: 'text',
|
||||
text: 'bar'
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [conditional, trial]
|
||||
});
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('foo');
|
||||
|
||||
// next
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('bar');
|
||||
|
||||
// clear
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
});
|
||||
|
||||
test('executes on every loop of the timeline', function(){
|
||||
|
||||
var count = 0;
|
||||
var conditional_count = 0;
|
||||
|
||||
var trial = {
|
||||
timeline: [{
|
||||
type: 'text',
|
||||
text: 'foo'
|
||||
}],
|
||||
loop_function: function(){
|
||||
if(count < 1){
|
||||
count++;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
conditional_function: function(){
|
||||
conditional_count++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
expect(conditional_count).toBe(1);
|
||||
|
||||
// first trial
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
expect(conditional_count).toBe(2);
|
||||
|
||||
// second trial
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
expect(conditional_count).toBe(2);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('endCurrentTimeline', function(){
|
||||
|
||||
test('stops the current timeline, skipping to the end after the trial completes', function(){
|
||||
var t = {
|
||||
timeline: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'foo',
|
||||
on_finish: function(){
|
||||
jsPsych.endCurrentTimeline();
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'bar',
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [t, {type: 'text', text: 'woo'}]
|
||||
});
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('foo');
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
expect(jsPsych.getDisplayElement().innerHTML).toBe('woo');
|
||||
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32}));
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32}));
|
||||
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue
Block a user