mirror of
https://github.com/jspsych/jsPsych.git
synced 2025-05-11 16:18:11 +00:00
Merge branch 'master' into slider-positions
This commit is contained in:
commit
8c81c9f420
@ -5,7 +5,9 @@ The following people have contributed to the development of jsPsych by writing c
|
||||
* Steve Chao - https://github.com/stchao
|
||||
* Krisitn Diep - https://github.com/kristiyip
|
||||
* Becky Gilbert - https://github.com/becky-gilbert
|
||||
* Angus Hughes - https://github.com/awhug
|
||||
* Gustavo Juantorena - https://github.com/GEJ1
|
||||
* Chris Jungerius - https://github.com/cjungerius
|
||||
* Jana Klaus - https://github.com/janakl4us
|
||||
* Jonas Lambers
|
||||
* Shane Martin - https://github.com/shamrt
|
||||
|
@ -23,6 +23,7 @@ margin_vertical | string | '0px' | Vertical margin of the button(s).
|
||||
margin_horizontal | string | '8px' | Horizontal margin of the button(s).
|
||||
response_ends_trial | boolean | true | If true, then the trial will end whenever the subject makes a response (assuming they make their response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force the subject to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete.
|
||||
trial_ends_after_audio | boolean | false | If true, then the trial will end as soon as the audio file finishes playing.
|
||||
response_allowed_while_playing | boolean | true | If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before the button choices are enabled and a response is accepted. Once the audio has played all the way through, the buttons are enabled and a response is allowed (including while the audio is being re-played via on-screen playback controls).
|
||||
|
||||
## Data Generated
|
||||
|
||||
|
@ -20,6 +20,7 @@ prompt | string | null | This string can contain HTML markup. Any content here w
|
||||
trial_duration | numeric | null | How long to wait for the subject to make a response before ending the trial in milliseconds. If the subject fails to make a response before this timer is reached, the subject's response will be recorded as null for the trial and the trial will end. If the value of this parameter is null, then the trial will wait for a response indefinitely.
|
||||
response_ends_trial | boolean | true | If true, then the trial will end whenever the subject makes a response (assuming they make their response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will continue until the value for `trial_duration` is reached. You can use set this parameter to `false` to force the subject to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete.
|
||||
trial_ends_after_audio | boolean | false | If true, then the trial will end as soon as the audio file finishes playing.
|
||||
response_allowed_while_playing | boolean | true | If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a keyboard response is accepted. Once the audio has played all the way through, a valid keyboard response is allowed (including while the audio is being re-played via on-screen playback controls).
|
||||
|
||||
## Data Generated
|
||||
|
||||
|
@ -26,6 +26,7 @@ require_movement | boolean | false | If true, the subject must move the slider b
|
||||
prompt | string | null | This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that it can be used to provide a reminder about the action the subject is supposed to take (e.g., which key to press).
|
||||
trial_duration | numeric | null | How long to wait for the subject to make a response before ending the trial in milliseconds. If the subject fails to make a response before this timer is reached, the subject's response will be recorded as null for the trial and the trial will end. If the value of this parameter is null, then the trial will wait for a response indefinitely.
|
||||
response_ends_trial | boolean | true | If true, then the trial will end whenever the subject makes a response (assuming they make their response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force the subject to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete.
|
||||
response_allowed_while_playing | boolean | true | If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before the slider is enabled and the trial can end via the next button click. Once the audio has played all the way through, the slider is enabled and a response is allowed (including while the audio is being re-played via on-screen playback controls).
|
||||
|
||||
## Data Generated
|
||||
|
||||
|
66
docs/plugins/jspsych-canvas-button-response.md
Normal file
66
docs/plugins/jspsych-canvas-button-response.md
Normal file
@ -0,0 +1,66 @@
|
||||
# jspsych-canvas-button-response
|
||||
|
||||
This plugin can be used to draw a stimulus on a [HTML canvas element](https://www.w3schools.com/html/html5_canvas.asp), and record a button click response and response time. The canvas stimulus can be useful for displaying dynamic, parametrically-defined graphics, and for controlling the positioning of multiple graphical elements (shapes, text, images). The stimulus can be displayed until a response is given, or for a pre-determined amount of time. The trial can be ended automatically if the subject has failed to respond within a fixed length of time. One or more button choices will be displayed under the canvas, and the button style can be customized using HTML formatting.
|
||||
|
||||
## Parameters
|
||||
|
||||
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
|
||||
----------|------|---------------|------------
|
||||
stimulus | function | *undefined* | The function to draw on the canvas. This function automatically takes a canvas element as its only argument, e.g. `function(c) {...}` or `function drawStim(c) {...}`, where `c` refers to the canvas element. Note that the stimulus function will still generally need to set the correct context itself, using a line like `let ctx = c.getContext("2d")`.
|
||||
canvas_size | array | [500, 500] | Array that defines the size of the canvas element in pixels. First value is height, second value is width.
|
||||
choices | array of strings | [] | Labels for the buttons. Each different string in the array will generate a different button.
|
||||
button_html | HTML string | `'<button class="jspsych-btn">%choice%</button>'` | A template of HTML for generating the button elements. You can override this to create customized buttons of various kinds. The string `%choice%` will be changed to the corresponding element of the `choices` array. You may also specify an array of strings, if you need different HTML to render for each button. If you do specify an array, the `choices` array and this array must have the same length. The HTML from position 0 in the `button_html` array will be used to create the button for element 0 in the `choices` array, and so on.
|
||||
prompt | string | null | This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that it can be used to provide a reminder about the action the subject is supposed to take (e.g., what question to answer).
|
||||
trial_duration | numeric | null | How long to wait for the subject to make a response before ending the trial in milliseconds. If the subject fails to make a response before this timer is reached, the subject's response will be recorded as null for the trial and the trial will end. If the value of this parameter is null, the trial will wait for a response indefinitely.
|
||||
stimulus_duration | numeric | null | How long to display the stimulus in milliseconds. The visibility CSS property of the stimulus will be set to `hidden` after this time has elapsed. If this is null, then the stimulus will remain visible until the trial ends.
|
||||
margin_vertical | string | '0px' | Vertical margin of the button(s).
|
||||
margin_horizontal | string | '8px' | Horizontal margin of the button(s).
|
||||
response_ends_trial | boolean | true | If true, then the trial will end whenever the subject makes a response (assuming they make their response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will continue until the value for `trial_duration` is reached. You can use this parameter to force the subject to view a stimulus for a fixed amount of time, even if they respond before the time is complete.
|
||||
|
||||
## Data Generated
|
||||
|
||||
In addition to the [default data collected by all plugins](overview#data-collected-by-plugins), this plugin collects the following data for each trial.
|
||||
|
||||
Name | Type | Value
|
||||
-----|------|------
|
||||
rt | numeric | The response time in milliseconds for the subject to make a response. The time is measured from when the stimulus first appears on the screen until the subject's response.
|
||||
button_pressed | numeric | Indicates which button the subject pressed. The first button in the `choices` array is 0, the second is 1, and so on.
|
||||
|
||||
Note: the canvas stimulus is *not* included in the trial data because it is a function. Any stimulus information that should be saved in the trial data can be added via the `data` parameter.
|
||||
|
||||
## Examples
|
||||
|
||||
### Displaying question until subject gives a response
|
||||
|
||||
```javascript
|
||||
function filledCirc(canvas, radius, color){
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.beginPath();
|
||||
ctx.arc(250, 250, radius, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill()
|
||||
}
|
||||
|
||||
var circle_1 = {
|
||||
type: 'canvas-button-response',
|
||||
stimulus: function (c) {
|
||||
filledCirc(c, 100, 'blue');
|
||||
},
|
||||
choices: ['Red', 'Green', 'Blue'],
|
||||
prompt: '<p>What color is the circle?</p>'
|
||||
data: {color: 'blue', radius: 100}
|
||||
};
|
||||
|
||||
var circle_2 = {
|
||||
type: 'canvas-button-response',
|
||||
stimulus: function (c) {
|
||||
filledCirc(c, 150, 'green');
|
||||
},
|
||||
choices: ['Larger', 'Smaller'],
|
||||
prompt: '<p>Is this circle larger or smaller than the last one?</p>',
|
||||
data: {color: 'green', radius: 150}
|
||||
};
|
||||
|
||||
```
|
68
docs/plugins/jspsych-canvas-keyboard-response.md
Normal file
68
docs/plugins/jspsych-canvas-keyboard-response.md
Normal file
@ -0,0 +1,68 @@
|
||||
# jspsych-canvas-keyboard-response
|
||||
|
||||
This plugin can be used to draw a stimulus on a [HTML canvas element](https://www.w3schools.com/html/html5_canvas.asp) and record a keyboard response. The canvas stimulus can be useful for displaying dynamic, parametrically-defined graphics, and for controlling the positioning of multiple graphical elements (shapes, text, images). The stimulus can be displayed until a response is given, or for a pre-determined amount of time. The trial can be ended automatically if the subject has failed to respond within a fixed length of time.
|
||||
|
||||
## Parameters
|
||||
|
||||
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
|
||||
----------|------|---------------|------------
|
||||
stimulus | function | *undefined* | The function to draw on the canvas. This function automatically takes a canvas element as its only argument, e.g. `function(c) {...}` or `function drawStim(c) {...}`, where `c` refers to the canvas element. Note that the stimulus function will still generally need to set the correct context itself, using a line like `let ctx = c.getContext("2d")`.
|
||||
canvas_size | array | [500, 500] | Array that defines the size of the canvas element in pixels. First value is height, second value is width.
|
||||
choices | array of keycodes | `jsPsych.ALL_KEYS` | This array contains the keys that the subject is allowed to press in order to respond to the stimulus. Keys can be specified as their [numeric key code](http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes) or as characters (e.g., `'a'`, `'q'`). The default value of `jsPsych.ALL_KEYS` means that all keys will be accepted as valid responses. Specifying `jsPsych.NO_KEYS` will mean that no responses are allowed.
|
||||
prompt | string | null | This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that it can be used to provide a reminder about the action the subject is supposed to take (e.g., which key to press).
|
||||
stimulus_duration | numeric | null | How long to display the stimulus in milliseconds. The visibility CSS property of the stimulus will be set to `hidden` after this time has elapsed. If this is null, then the stimulus will remain visible until the trial ends.
|
||||
trial_duration | numeric | null | How long to wait for the subject to make a response before ending the trial in milliseconds. If the subject fails to make a response before this timer is reached, the subject's response will be recorded as null for the trial and the trial will end. If the value of this parameter is null, then the trial will wait for a response indefinitely.
|
||||
response_ends_trial | boolean | true | If true, then the trial will end whenever the subject makes a response (assuming they make their response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will continue until the value for `trial_duration` is reached. You can use this parameter to force the subject to view a stimulus for a fixed amount of time, even if they respond before the time is complete.
|
||||
|
||||
## Data Generated
|
||||
|
||||
In addition to the [default data collected by all plugins](overview#data-collected-by-plugins), this plugin collects the following data for each trial.
|
||||
|
||||
Name | Type | Value
|
||||
-----|------|------
|
||||
key_press | numeric | Indicates which key the subject pressed. The value is the [numeric key code](http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes) corresponding to the subject's response.
|
||||
rt | numeric | The response time in milliseconds for the subject to make a response. The time is measured from when the stimulus first appears on the screen until the subject's response.
|
||||
|
||||
Note: the canvas stimulus is *not* included in the trial data because it is a function. Any stimulus information that should be saved in the trial data can be added via the `data` parameter.
|
||||
|
||||
## Examples
|
||||
|
||||
### Displaying a shape until subject gives a response
|
||||
|
||||
```javascript
|
||||
function drawRect(c){
|
||||
var ctx = c.getContext('2d');
|
||||
ctx.beginPath();
|
||||
ctx.rect(30, 30, 200, 50);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
var trial = {
|
||||
type: 'canvas-keyboard-response',
|
||||
stimulus: drawRect,
|
||||
choices: ['e','i'],
|
||||
prompt: '<p>Is this a circle or a rectangle? Press "e" for circle and "i" for rectangle.</p>',
|
||||
data: {shape: 'rectangle'}
|
||||
}
|
||||
```
|
||||
|
||||
### Displaying a circle for 1 second, no response allowed
|
||||
|
||||
```javascript
|
||||
function drawCirc(c){
|
||||
var ctx = c.getContext('2d');
|
||||
ctx.beginPath();
|
||||
ctx.arc(100, 75, 50, 0, 2 * Math.PI);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
var trial = {
|
||||
type: 'canvas-keyboard-response',
|
||||
stimulus: drawCirc,
|
||||
choices: jsPsych.NO_KEYS,
|
||||
trial_duration: 1000,
|
||||
data: {shape: 'circle', radius: 50}
|
||||
}
|
||||
```
|
89
docs/plugins/jspsych-canvas-slider-response.md
Normal file
89
docs/plugins/jspsych-canvas-slider-response.md
Normal file
@ -0,0 +1,89 @@
|
||||
# jspsych-canvas-slider-response
|
||||
|
||||
This plugin can be used to draw a stimulus on a [HTML canvas element](https://www.w3schools.com/html/html5_canvas.asp) and collect a response within a range of values, which is made by dragging a slider. The canvas stimulus can be useful for displaying dynamic, parametrically-defined graphics, and for controlling the positioning of multiple graphical elements (shapes, text, images). The stimulus can be displayed until a response is given, or for a pre-determined amount of time. The trial can be ended automatically if the subject has failed to respond within a fixed length of time.
|
||||
|
||||
## Parameters
|
||||
|
||||
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
|
||||
----------|------|---------------|------------
|
||||
stimulus | function | *undefined* | The function to draw on the canvas. This function automatically takes a canvas element as its only argument, e.g. `function(c) {...}` or `function drawStim(c) {...}`, where `c` refers to the canvas element. Note that the stimulus function will still generally need to set the correct context itself, using a line like `let ctx = c.getContext("2d")`.
|
||||
canvas_size | array | [500, 500] | Array that defines the size of the canvas element in pixels. First value is height, second value is width.
|
||||
labels | array of strings | [] | Labels displayed at equidistant locations on the slider. For example, two labels will be placed at the ends of the slider. Three labels would place two at the ends and one in the middle. Four will place two at the ends, and the other two will be at 33% and 67% of the slider width.
|
||||
button_label | string | 'Continue' | Label of the button to end the trial.
|
||||
min | integer | 0 | Sets the minimum value of the slider.
|
||||
max | integer | 100 | Sets the maximum value of the slider.
|
||||
slider_start | integer | 50 | Sets the starting value of the slider.
|
||||
step | integer | 1 | Sets the step of the slider. This is the smallest amount by which the slider can change.
|
||||
slider_width | integer | null | Set the width of the slider in pixels. If left null, then the width will be equal to the widest element in the display.
|
||||
require_movement | boolean | false | If true, the subject must click the slider before clicking the continue button.
|
||||
prompt | string | null | This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that it can be used to provide a reminder about the action the subject is supposed to take (e.g., what question to answer).
|
||||
stimulus_duration | numeric | null | How long to display the stimulus in milliseconds. The visibility CSS property of the stimulus will be set to `hidden` after this time has elapsed. If this is null, then the stimulus will remain visible until the trial ends.
|
||||
trial_duration | numeric | null | How long to wait for the subject to make a response before ending the trial in milliseconds. If the subject fails to make a response before this timer is reached, the subject's response will be recorded as null for the trial and the trial will end. If the value of this parameter is null, then the trial will wait for a response indefinitely.
|
||||
response_ends_trial | boolean | true | If true, then the trial will end whenever the subject makes a response (assuming they make their response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will continue until the value for `trial_duration` is reached. You can use this parameter to force the subject to view a stimulus for a fixed amount of time, even if they respond before the time is complete.
|
||||
|
||||
## Data Generated
|
||||
|
||||
In addition to the [default data collected by all plugins](overview#data-collected-by-plugins), this plugin collects the following data for each trial.
|
||||
|
||||
Name | Type | Value
|
||||
-----|------|------
|
||||
response | numeric | The numeric value of the slider.
|
||||
rt | numeric | The time in milliseconds for the subject to make a response. The time is measured from when the stimulus first appears on the screen until the subject's response.
|
||||
|
||||
Note: the canvas stimulus is *not* included in the trial data because it is a function. Any stimulus information that should be saved in the trial data can be added via the `data` parameter.
|
||||
|
||||
## Examples
|
||||
|
||||
### stimulus function that only takes canvas argument
|
||||
|
||||
```javascript
|
||||
var colors = [#'FF3333', '#FF6A33'];
|
||||
|
||||
function twoSquares(c) {
|
||||
var ctx = c.getContext('2d');
|
||||
ctx.fillStyle = colors[0];
|
||||
ctx.fillRect(200, 70, 40, 40);
|
||||
ctx.fillStyle = colors[1];
|
||||
ctx.fillRect(260, 70, 40, 40);
|
||||
}
|
||||
|
||||
var trial = {
|
||||
type: 'canvas-slider-response',
|
||||
stimulus: twoSquares,
|
||||
labels: ['0','10'],
|
||||
canvas_size: [200, 500],
|
||||
prompt: '<p>How different would you say the colors of these two squares are on a scale from 0 (the same) to 10 (completely different)</p>',
|
||||
data: {color1: colors[0], color2: colors[1]}
|
||||
}
|
||||
```
|
||||
|
||||
### stimulus function that takes additional parameters
|
||||
|
||||
```javascript
|
||||
var colors;
|
||||
|
||||
function twoSquares(c, colors) {
|
||||
var ctx = c.getContext('2d');
|
||||
ctx.fillStyle = colors[0];
|
||||
ctx.fillRect(200, 70, 40, 40);
|
||||
ctx.fillStyle = colors[1];
|
||||
ctx.fillRect(260, 70, 40, 40);
|
||||
}
|
||||
|
||||
var trial = {
|
||||
type: 'canvas-slider-response',
|
||||
stimulus: function(c) {
|
||||
colors = ['darkred', 'cyan'];
|
||||
twoSquares(c, colors);
|
||||
},
|
||||
labels: ['Exactly<br>the same','Totally<br>different'],
|
||||
canvas_size: [200, 500],
|
||||
prompt: '<p>How different would you say the colors of these two squares are?</p>',
|
||||
on_finish: function(data) {
|
||||
data.color1 = colors[0];
|
||||
data.color2 = colors[1];
|
||||
}
|
||||
};
|
||||
```
|
42
docs/plugins/jspsych-maxdiff.md
Normal file
42
docs/plugins/jspsych-maxdiff.md
Normal file
@ -0,0 +1,42 @@
|
||||
# jspsych-maxdiff plugin
|
||||
|
||||
The maxdiff plugin displays a table with rows of alternatives to be endorsed, typically as 'most' or 'least' on a particular criteria (e.g. importance, preference, similarity). The participant responds by selecting one radio button corresponding to an alternative in both the left and right response columns. The same alternative cannot be endorsed on both the left and right response columns (e.g. 'most' and 'least') simultaneously.
|
||||
|
||||
## Parameters
|
||||
|
||||
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
|
||||
----------|------|---------------|------------
|
||||
alternatives | array | *undefined* | An array of one or more alternatives of string type to fill the rows of the maxdiff table. If `required` is true, then the array must contain two or more alternatives, so that at least one can be selected for both the left and right columns.
|
||||
labels | array | *undefined* | An array with exactly two labels of string type to display as column headings (to the left and right of the alternatives) for responses on the criteria of interest.
|
||||
randomize_alternative_order | boolean | `false` | If true, the display order of `alternatives` is randomly determined at the start of the trial.
|
||||
preamble | string | empty string | HTML formatted string to display at the top of the page above the maxdiff table.
|
||||
required | boolean | `false` | If true, prevents the user from submitting the response and proceeding until a radio button in both the left and right response columns has been selected.
|
||||
button_label | string | 'Continue' | Label of the button.
|
||||
|
||||
|
||||
## Data Generated
|
||||
|
||||
In addition to the [default data collected by all plugins](overview#data-collected-by-plugins), this plugin collects the following data for each trial.
|
||||
|
||||
Name | Type | Value
|
||||
-----|------|------
|
||||
rt | numeric | The response time in milliseconds for the subject to make a response. The time is measured from when the maxdiff table first appears on the screen until the subject's response.
|
||||
labels | JSON string | A string in JSON format containing the labels corresponding to the left and right response columns.
|
||||
left | string | The alternative endorsed on the left column.
|
||||
right | string | The alternative endorsed on the right column.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
#### Basic example
|
||||
|
||||
```javascript
|
||||
var maxdiff_page = {
|
||||
type: 'maxdiff',
|
||||
alternatives: ['apple', 'orange', 'pear', 'banana'],
|
||||
labels: ['Most Preferred', 'Least Preferred'],
|
||||
preamble: '<p> Please select your <b>most preferred</b> and <b>least preferred</b> fruits. </p>'
|
||||
};
|
||||
```
|
@ -24,6 +24,7 @@ rate | numeric | null | The playback rate of the video. 1 is normal, <1 is slowe
|
||||
trial_ends_after_video | bool | false | If true, then the trial will end as soon as the video file finishes playing.
|
||||
trial_duration | numeric | null | How long to wait for the subject to make a response before ending the trial in milliseconds. If the subject fails to make a response before this timer is reached, the subject's response will be recorded as null for the trial and the trial will end. If the value of this parameter is null, then the trial will wait for a response indefinitely.
|
||||
response_ends_trial | boolean | true | If true, then the trial will end whenever the subject makes a response (assuming they make their response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force the subject to view a stimulus for a fixed amount of time, even if they respond before the time is complete.
|
||||
response_allowed_while_playing | boolean | true | If true, then responses are allowed while the video is playing. If false, then the video must finish playing before the button choices are enabled and a response is accepted. Once the video has played all the way through, the buttons are enabled and a response is allowed (including while the video is being re-played via on-screen playback controls).
|
||||
|
||||
|
||||
## Data Generated
|
||||
|
@ -21,7 +21,7 @@ choices | array of keycodes | `jsPsych.ALL_KEYS` | This array contains the keys
|
||||
trial_ends_after_video | bool | false | If true, then the trial will end as soon as the video file finishes playing.
|
||||
trial_duration | numeric | null | How long to wait for the subject to make a response before ending the trial in milliseconds. If the subject fails to make a response before this timer is reached, the subject's response will be recorded as null for the trial and the trial will end. If the value of this parameter is null, then the trial will wait for a response indefinitely.
|
||||
response_ends_trial | boolean | true | If true, then the trial will end whenever the subject makes a response (assuming they make their response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force the subject to view a stimulus for a fixed amount of time, even if they respond before the time is complete.
|
||||
|
||||
response_allowed_while_playing | boolean | true | If true, then responses are allowed while the video is playing. If false, then the video must finish playing before a keyboard response is accepted. Once the video has played all the way through, a valid keyboard response is allowed (including while the video is being re-played via on-screen playback controls).
|
||||
|
||||
## Data Generated
|
||||
|
||||
|
@ -27,6 +27,7 @@ button_label | string | 'Continue' | Label of the button to end the trial.
|
||||
trial_ends_after_video | bool | false | If true, then the trial will end as soon as the video file finishes playing.
|
||||
trial_duration | numeric | null | How long to wait for the subject to make a response before ending the trial in milliseconds. If the subject fails to make a response before this timer is reached, the subject's response will be recorded as null for the trial and the trial will end. If the value of this parameter is null, then the trial will wait for a response indefinitely.
|
||||
response_ends_trial | boolean | true | If true, then the trial will end whenever the subject makes a response (assuming they make their response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force the subject to view a stimulus for a fixed amount of time, even if they respond before the time is complete.
|
||||
response_allowed_while_playing | boolean | true | If true, then responses are allowed while the video is playing. If false, then the video must finish playing before the slider is enabled and the trial can end via the next button click. Once the video has played all the way through, the slider is enabled and a response is allowed (including while the video is being re-played via on-screen playback controls).
|
||||
|
||||
|
||||
## Data Generated
|
||||
|
@ -13,9 +13,9 @@
|
||||
|
||||
timeline.push({
|
||||
type: 'html-button-response',
|
||||
stimulus: 'Recent versions of Chrome require the user to interact with a page before it can play audio. '+
|
||||
'Clicking the button below counts as an interaction. Be aware of this when planning audio experiments if '+
|
||||
'you want the first trial to include audio.',
|
||||
stimulus: '<div style="max-width:600px;"><p>Some browsers now require the user to interact with a page before it can play audio. '+
|
||||
'Clicking the button below counts as an interaction.</p><p>Be aware of this when planning audio experiments if '+
|
||||
'you want the first trial to include audio.</p></div>',
|
||||
choices: ['Continue']
|
||||
});
|
||||
|
||||
@ -35,6 +35,14 @@
|
||||
prompt: "<p>What word was said? (trial ends after 2s)</p>"
|
||||
});
|
||||
|
||||
timeline.push({
|
||||
type: 'audio-button-response',
|
||||
stimulus: 'sound/speech_joke.mp3',
|
||||
choices: ['Not funny', 'Funny'],
|
||||
prompt: '<p>How funny was the joke?</p><p>When the audio stops, click a button to end the trial.</p><p>Response buttons are disabled while the audio is playing.</p>',
|
||||
response_allowed_while_playing: false
|
||||
})
|
||||
|
||||
jsPsych.init({
|
||||
timeline: timeline,
|
||||
use_webaudio: false,
|
||||
|
@ -11,9 +11,9 @@
|
||||
|
||||
var pre_audio = {
|
||||
type: 'html-button-response',
|
||||
stimulus: 'Recent versions of Chrome require the user to interact with a page before it can play audio. '+
|
||||
'Clicking the button below counts as an interaction. Be aware of this when planning audio experiments if '+
|
||||
'you want the first trial to include audio.',
|
||||
stimulus: '<div style="max-width:600px;"><p>Some browsers now require the user to interact with a page before it can play audio. '+
|
||||
'Clicking the button below counts as an interaction.</p><p>Be aware of this when planning audio experiments if '+
|
||||
'you want the first trial to include audio.</p></div>',
|
||||
choices: ['Continue']
|
||||
}
|
||||
|
||||
@ -40,8 +40,16 @@
|
||||
prompt: '<p>No response allowed. 2s wait.</p>'
|
||||
}
|
||||
|
||||
trial_4 = {
|
||||
type: 'audio-keyboard-response',
|
||||
stimulus: 'sound/speech_joke.mp3',
|
||||
choices: jsPsych.ALL_KEYS,
|
||||
prompt: '<p>When the audio stops, press any key to end the trial.</p><p>Responses made while the audio is still playing will be ignored.</p>',
|
||||
response_allowed_while_playing: false
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [pre_audio, trial_1, trial_2, trial_3],
|
||||
timeline: [pre_audio, trial_1, trial_2, trial_3, trial_4],
|
||||
use_webaudio: false,
|
||||
on_finish: function() {
|
||||
jsPsych.data.displayData();
|
||||
|
@ -11,9 +11,9 @@
|
||||
|
||||
var pre_audio = {
|
||||
type: 'html-button-response',
|
||||
stimulus: 'Recent versions of Chrome require the user to interact with a page before it can play audio. '+
|
||||
'Clicking the button below counts as an interaction. Be aware of this when planning audio experiments if '+
|
||||
'you want the first trial to include audio.',
|
||||
stimulus: '<div style="max-width:600px;"><p>Some browsers now require the user to interact with a page before it can play audio. '+
|
||||
'Clicking the button below counts as an interaction.</p><p>Be aware of this when planning audio experiments if '+
|
||||
'you want the first trial to include audio.</p></div>',
|
||||
choices: ['Continue']
|
||||
}
|
||||
|
||||
@ -25,8 +25,26 @@
|
||||
prompt: '<p>How funny is the joke?</p>'
|
||||
}
|
||||
|
||||
var trial_2 = {
|
||||
type: 'audio-slider-response',
|
||||
stimulus: 'sound/speech_red.mp3',
|
||||
labels: ['Hate it', 'It"s OK', 'Love it'],
|
||||
slider_width: 500,
|
||||
prompt: '<p>How much do you like this color?</p><p>Slider movement is required, so you must interact with (click) the slider before you can continue.</p>',
|
||||
require_movement: true
|
||||
}
|
||||
|
||||
var trial_3 = {
|
||||
type: 'audio-slider-response',
|
||||
stimulus: 'sound/hammer.mp3',
|
||||
labels: ['Unpleasant', 'OK', 'Very pleasant'],
|
||||
slider_width: 500,
|
||||
prompt: '<p>How pleasant was this sound?</p><p>The slider will be enabled after the audio ends.</p>',
|
||||
response_allowed_while_playing: false
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [pre_audio, trial_1],
|
||||
timeline: [pre_audio, trial_1, trial_2, trial_3],
|
||||
use_webaudio: false,
|
||||
on_finish: function() {
|
||||
jsPsych.data.displayData();
|
||||
|
95
examples/jspsych-canvas-button-response.html
Normal file
95
examples/jspsych-canvas-button-response.html
Normal file
@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="../jspsych.js"></script>
|
||||
<script src="../plugins/jspsych-canvas-button-response.js"></script>
|
||||
<link rel="stylesheet" href="../css/jspsych.css">
|
||||
</head>
|
||||
<body></body>
|
||||
<script>
|
||||
|
||||
// stimulus function that takes the canvas and additional parameters (radius, color)
|
||||
// this can be called inside of an anonymous stimulus function, which takes the canvas (c) as its only argument
|
||||
function filledCirc(canvas, radius, color) {
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.beginPath();
|
||||
ctx.arc(250, 250, radius, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
var circle_1 = {
|
||||
type: 'canvas-button-response',
|
||||
stimulus: function(c) {
|
||||
filledCirc(c, 100, 'blue');
|
||||
},
|
||||
choices: ['Red', 'Green', 'Blue'],
|
||||
prompt: '<p>What color is the circle?</p>',
|
||||
data: {color: 'blue', radius: 100}
|
||||
};
|
||||
|
||||
var circle_2 = {
|
||||
type: 'canvas-button-response',
|
||||
stimulus: function(c) {
|
||||
filledCirc(c, 150, 'green');
|
||||
},
|
||||
choices: ['Larger', 'Smaller'],
|
||||
stimulus_duration: 1000,
|
||||
prompt: '<p>Is this circle larger or smaller than the last one?</p>'+
|
||||
'<p>Stimulus will be hidden after 1 second.</p>',
|
||||
data: {color: 'green', radius: 150}
|
||||
};
|
||||
|
||||
// write the canvas stimulus drawing function without using a named function
|
||||
// the anonymous function must take the canvas as an argument
|
||||
var lines = {
|
||||
type: 'canvas-button-response',
|
||||
stimulus: function(c) {
|
||||
var ctx = c.getContext("2d");
|
||||
// first line
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(300, 10);
|
||||
ctx.lineTo(300, 300);
|
||||
ctx.lineWidth = 10;
|
||||
ctx.strokeStyle = 'MediumBlue';
|
||||
ctx.stroke();
|
||||
// second line
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(20, 200);
|
||||
ctx.lineTo(100, 350);
|
||||
ctx.lineWidth = 10;
|
||||
ctx.strokeStyle = 'MediumPurple';
|
||||
ctx.stroke();
|
||||
},
|
||||
choices: ['Blue line', 'Purple line'],
|
||||
prompt: '<p>Which line is longer?</p>',
|
||||
data: {line1_color: 'blue', line1_length: 290, line2_color: "purple", line2_length: 170}
|
||||
};
|
||||
|
||||
// to use the canvas stimulus function with timeline variables,
|
||||
// use the jsPsych.timelineVariable() function inside your stimulus function with the second 'true' argument
|
||||
var circle_procedure = {
|
||||
timeline: [{
|
||||
type: 'canvas-button-response',
|
||||
stimulus: function(c) {
|
||||
filledCirc(c, jsPsych.timelineVariable('radius', true), jsPsych.timelineVariable('color', true));
|
||||
},
|
||||
choices: ['Red', 'Green', 'Blue'],
|
||||
prompt: '<p>What color is the circle?</p>',
|
||||
data: {radius: jsPsych.timelineVariable('radius'), color: jsPsych.timelineVariable('color')}
|
||||
}],
|
||||
timeline_variables: [
|
||||
{radius: 100, color: 'red'},
|
||||
{radius: 200, color: 'green'},
|
||||
{radius: 50, color: 'blue'}
|
||||
]
|
||||
};
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [circle_1, circle_2, lines, circle_procedure],
|
||||
on_finish: function () {
|
||||
jsPsych.data.displayData();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</html>
|
78
examples/jspsych-canvas-keyboard-response.html
Normal file
78
examples/jspsych-canvas-keyboard-response.html
Normal file
@ -0,0 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="../jspsych.js"></script>
|
||||
<script src="../plugins/jspsych-canvas-keyboard-response.js"></script>
|
||||
<link rel="stylesheet" href="../css/jspsych.css">
|
||||
</head>
|
||||
<body></body>
|
||||
<script>
|
||||
|
||||
// stimulus functions that take the canvas as its only argument
|
||||
// these function names can be used as the value for the stimulus parameter
|
||||
function drawRect(c){
|
||||
var ctx = c.getContext('2d');
|
||||
ctx.beginPath();
|
||||
ctx.rect(150, 225, 200, 50);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function drawCirc(c) {
|
||||
var ctx = c.getContext('2d');
|
||||
ctx.beginPath();
|
||||
ctx.arc(250, 250, 200, 0, 2 * Math.PI);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
var trial_1 = {
|
||||
type: 'canvas-keyboard-response',
|
||||
stimulus: drawRect,
|
||||
choices: ['e','i'],
|
||||
prompt: '<p>Is this a circle or a rectangle? Press "e" for circle and "i" for rectangle.</p>',
|
||||
data: {shape: 'rectangle'}
|
||||
};
|
||||
|
||||
var trial_2 = {
|
||||
type: 'canvas-keyboard-response',
|
||||
stimulus: drawCirc,
|
||||
choices: ['e','i'],
|
||||
prompt: '<p>Is this a circle or a rectangle? Press "e" for circle and "i" for rectangle.</p>',
|
||||
data: {shape: 'circle'}
|
||||
}
|
||||
|
||||
// to use the canvas stimulus function with timeline variables,
|
||||
// use the jsPsych.timelineVariable() function inside your stimulus function with the second 'true' argument
|
||||
var trial_procedure = {
|
||||
timeline: [{
|
||||
type: 'canvas-keyboard-response',
|
||||
stimulus: function(c) {
|
||||
var ctx = c.getContext('2d');
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = jsPsych.timelineVariable('color', true);
|
||||
ctx.fillRect(
|
||||
jsPsych.timelineVariable('upper_left_x', true),
|
||||
jsPsych.timelineVariable('upper_left_y', true),
|
||||
jsPsych.timelineVariable('width', true),
|
||||
jsPsych.timelineVariable('height', true)
|
||||
);
|
||||
ctx.stroke();
|
||||
},
|
||||
choices: ['r','b'],
|
||||
prompt: '<p>What color is the rectangle? Press "r" for red and "b" for blue.</p>',
|
||||
data: {color: jsPsych.timelineVariable('color')}
|
||||
}],
|
||||
timeline_variables: [
|
||||
{upper_left_x: 150, upper_left_y: 100, height: 100, width: 150, color: 'red'},
|
||||
{upper_left_x: 270, upper_left_y: 200, height: 300, width: 200, color: 'blue'},
|
||||
{upper_left_x: 150, upper_left_y: 130, height: 200, width: 50, color: 'blue'}
|
||||
]
|
||||
};
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial_1, trial_2, trial_procedure],
|
||||
on_finish: function () {
|
||||
jsPsych.data.displayData();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</html>
|
67
examples/jspsych-canvas-slider-response.html
Normal file
67
examples/jspsych-canvas-slider-response.html
Normal file
@ -0,0 +1,67 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="../jspsych.js"></script>
|
||||
<script src="../plugins/jspsych-canvas-slider-response.js"></script>
|
||||
<link rel="stylesheet" href="../css/jspsych.css">
|
||||
</head>
|
||||
<body></body>
|
||||
<script>
|
||||
|
||||
var colors;
|
||||
|
||||
// stimulus function that takes the canvas as its only argument
|
||||
// this function name can be used as the value for the stimulus parameter
|
||||
function twoSquares(c) {
|
||||
var ctx = c.getContext('2d');
|
||||
ctx.fillStyle = '#FF3333';
|
||||
ctx.fillRect(200, 70, 40, 40);
|
||||
ctx.fillStyle = '#FF6A33';
|
||||
ctx.fillRect(260, 70, 40, 40);
|
||||
}
|
||||
|
||||
var trial_1 = {
|
||||
type: 'canvas-slider-response',
|
||||
stimulus: twoSquares,
|
||||
labels: ['0','10'],
|
||||
canvas_size: [200, 500],
|
||||
prompt: '<p>How different would you say the colors of these two squares are on a scale from 0 (the same) to 10 (completely different)?</p>',
|
||||
data: {color1: '#FF3333', color2: '#FF6A33'}
|
||||
}
|
||||
|
||||
// stimulus function that takes the canvas and additional parameters
|
||||
// this can be called inside of an anonymous stimulus function, which takes the canvas as its only argument
|
||||
function twoSquaresColors(c, colors) {
|
||||
var ctx = c.getContext('2d');
|
||||
ctx.fillStyle = colors[0];
|
||||
ctx.fillRect(200, 70, 40, 40);
|
||||
ctx.fillStyle = colors[1];
|
||||
ctx.fillRect(260, 70, 40, 40);
|
||||
}
|
||||
|
||||
var trial_2 = {
|
||||
type: 'canvas-slider-response',
|
||||
stimulus: function(c) {
|
||||
colors = ['darkred', 'cyan'];
|
||||
twoSquaresColors(c, colors);
|
||||
},
|
||||
labels: ['Exactly<br>the same','Totally<br>different'],
|
||||
canvas_size: [200, 500],
|
||||
require_movement: true,
|
||||
stimulus_duration: 1000,
|
||||
prompt: '<p>How different would you say the colors of these two squares are on a scale from 0 (the same) to 10 (completely different)?</p>'+
|
||||
'<p>Interaction with the slider is required to continue. Stimulus will be hidden after 1 second.</p>',
|
||||
on_finish: function(data) {
|
||||
data.color1 = colors[0];
|
||||
data.color2 = colors[1];
|
||||
}
|
||||
};
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial_1, trial_2],
|
||||
on_finish: function () {
|
||||
jsPsych.data.displayData();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</html>
|
33
examples/jspsych-maxdiff.html
Normal file
33
examples/jspsych-maxdiff.html
Normal file
@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="../jspsych.js"></script>
|
||||
<script src="../plugins/jspsych-maxdiff.js"></script>
|
||||
<link rel="stylesheet" href="../css/jspsych.css">
|
||||
</head>
|
||||
<body></body>
|
||||
<script>
|
||||
|
||||
var maxdiff_trial = {
|
||||
type: 'maxdiff',
|
||||
alternatives: ['apple', 'orange', 'pear', 'banana'],
|
||||
labels: ['Most Preferred', 'Least Preferred'],
|
||||
preamble: '<p> Please select your <b>most preferred</b> and <b>least preferred</b> fruits. </p>'
|
||||
};
|
||||
|
||||
var maxdiff_trial_random_required = {
|
||||
type: 'maxdiff',
|
||||
alternatives: ['strawberry', 'watermelon', 'kiwi', 'grapefruit'],
|
||||
labels: ['Most Preferred', 'Least Preferred'],
|
||||
preamble: '<p> Please select your <b>most preferred</b> and <b>least preferred</b> fruits. </p>',
|
||||
randomize_alternative_order: true,
|
||||
required: true
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [maxdiff_trial, maxdiff_trial_random_required],
|
||||
on_finish: function() { jsPsych.data.displayData(); }
|
||||
});
|
||||
|
||||
</script>
|
||||
</html>
|
@ -12,10 +12,10 @@
|
||||
var pre_trial = {
|
||||
type: 'html-button-response',
|
||||
stimulus: '<div style="max-width:600px;"><p>Some browsers now require that a user interacts with a web page before video or audio content will autoplay. Clicking the button below satisfies that requirement.</p><p>Without this trial, the video will load but not play.</p></div>',
|
||||
choices: ['continue']
|
||||
choices: ['Continue']
|
||||
}
|
||||
|
||||
var trial = {
|
||||
var trial_1 = {
|
||||
type: 'video-button-response',
|
||||
sources: ['video/sample_video.mp4'],
|
||||
choices: ['y','n'],
|
||||
@ -31,14 +31,26 @@
|
||||
//stop: 9,
|
||||
rate: 1.5,
|
||||
//trial_duration: 2000,
|
||||
|
||||
response_ends_trial: true
|
||||
}
|
||||
|
||||
var trial_2 = {
|
||||
type: 'video-button-response',
|
||||
sources: ['video/sample_video.mp4'],
|
||||
choices: ['Great','Not great'],
|
||||
margin_vertical: '10px',
|
||||
margin_horizontal: '8px',
|
||||
prompt: '<p>How great was the video?</p><p>When the video stops, click a button to end the trial.</p><p>Response buttons are disabled while the video is playing.</p>',
|
||||
width: 600,
|
||||
autoplay: true,
|
||||
response_ends_trial: true,
|
||||
response_allowed_while_playing: false
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
|
||||
show_preload_progress_bar: true,
|
||||
timeline: [pre_trial, trial],
|
||||
timeline: [pre_trial, trial_1, trial_2],
|
||||
on_finish: function() { jsPsych.data.displayData(); }
|
||||
});
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
choices: ['continue']
|
||||
}
|
||||
|
||||
var trial = {
|
||||
var trial_1 = {
|
||||
type: 'video-keyboard-response',
|
||||
sources: ['video/sample_video.mp4'],
|
||||
choices: ['y','n'],
|
||||
@ -24,20 +24,30 @@
|
||||
//height: 600,
|
||||
autoplay: true,
|
||||
//controls: true,
|
||||
start: 8.75,
|
||||
stop: 9,
|
||||
//start: 8,
|
||||
//stop: 9,
|
||||
rate: 1.5,
|
||||
//trial_duration: 2000,
|
||||
//trial_ends_after_video: true,
|
||||
response_ends_trial: true
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
var trial_2 = {
|
||||
type: 'video-keyboard-response',
|
||||
sources: ['video/sample_video.mp4'],
|
||||
choices: jsPsych.ALL_KEYS,
|
||||
prompt: '<p>When the video stops, press any key to end the trial.</p><p>Responses that are made before the video ends will be ignored.</p>',
|
||||
width: 600,
|
||||
autoplay: true,
|
||||
response_ends_trial: true,
|
||||
response_allowed_while_playing: false
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
show_preload_progress_bar: true,
|
||||
timeline: [pre_trial, trial],
|
||||
timeline: [pre_trial, trial_1, trial_2],
|
||||
on_finish: function() { jsPsych.data.displayData(); }
|
||||
});
|
||||
|
||||
</script>
|
||||
</html>
|
||||
</html>
|
@ -12,10 +12,10 @@
|
||||
var pre_trial = {
|
||||
type: 'html-button-response',
|
||||
stimulus: '<div style="max-width:600px;"><p>Some browsers now require that a user interacts with a web page before video or audio content will autoplay. Clicking the button below satisfies that requirement.</p><p>Without this trial, the video will load but not play.</p></div>',
|
||||
choices: ['continue']
|
||||
choices: ['Continue']
|
||||
}
|
||||
|
||||
var trial = {
|
||||
var trial_1 = {
|
||||
type: 'video-slider-response',
|
||||
sources: ['video/sample_video.mp4'],
|
||||
labels: ['1 Star', '2 Stars', '3 Stars', '4 Stars', '5 Stars'],
|
||||
@ -23,24 +23,32 @@
|
||||
max: 5,
|
||||
step: 1,
|
||||
slider_start: 1,
|
||||
prompt: 'Rate this video clip',
|
||||
prompt: '<p>Rate this video clip.</p><p>Slider movement is required, so you must interact with (click) the slider cursor to continue.</p>',
|
||||
require_movement: true,
|
||||
width: 600,
|
||||
//height: 600,
|
||||
autoplay: true,
|
||||
//controls: true,
|
||||
//start: 8,
|
||||
//stop: 9,
|
||||
rate: 1.5,
|
||||
//trial_duration: 2000,
|
||||
//trial_ends_after_video: true,
|
||||
response_ends_trial: true
|
||||
}
|
||||
|
||||
var trial_2 = {
|
||||
type: 'video-slider-response',
|
||||
sources: ['video/sample_video.mp4'],
|
||||
labels: ['1 Star', '2 Stars', '3 Stars', '4 Stars', '5 Stars'],
|
||||
min: 1,
|
||||
max: 5,
|
||||
step: 1,
|
||||
slider_start: 3,
|
||||
prompt: '<p>Rate this video clip after the video finishes playing.</p><p>The slider will be enabled after the video ends.</p>',
|
||||
width: 600,
|
||||
autoplay: true,
|
||||
response_allowed_while_playing: false
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
|
||||
show_preload_progress_bar: true,
|
||||
timeline: [pre_trial, trial],
|
||||
timeline: [pre_trial, trial_1, trial_2],
|
||||
on_finish: function() { jsPsych.data.displayData(); }
|
||||
});
|
||||
|
||||
|
@ -2608,7 +2608,7 @@ jsPsych.pluginAPI = (function() {
|
||||
var loaded = 0;
|
||||
|
||||
if(progress_bar){
|
||||
var pb_html = "<div id='jspsych-loading-progress-bar-container' style='height: 10px; width: 300px; background-color: #ddd;'>";
|
||||
var pb_html = "<div id='jspsych-loading-progress-bar-container' style='height: 10px; width: 300px; background-color: #ddd; margin: auto;'>";
|
||||
pb_html += "<div id='jspsych-loading-progress-bar' style='height: 10px; width: 0%; background-color: #777;'></div>";
|
||||
pb_html += "</div>";
|
||||
jsPsych.getDisplayElement().innerHTML = pb_html;
|
||||
|
@ -73,6 +73,13 @@ jsPsych.plugins["audio-button-response"] = (function() {
|
||||
default: false,
|
||||
description: 'If true, then the trial will end as soon as the audio file finishes playing.'
|
||||
},
|
||||
response_allowed_while_playing: {
|
||||
type: jsPsych.plugins.parameterType.BOOL,
|
||||
pretty_name: 'Response allowed while playing',
|
||||
default: true,
|
||||
description: 'If true, then responses are allowed while the audio is playing. '+
|
||||
'If false, then the audio must finish playing before a response is accepted.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,14 +99,21 @@ jsPsych.plugins["audio-button-response"] = (function() {
|
||||
// set up end event if trial needs it
|
||||
if(trial.trial_ends_after_audio){
|
||||
if(context !== null){
|
||||
source.onended = function() {
|
||||
end_trial();
|
||||
}
|
||||
source.addEventListener('ended', end_trial);
|
||||
} else {
|
||||
audio.addEventListener('ended', end_trial);
|
||||
}
|
||||
}
|
||||
|
||||
// enable buttons after audio ends if necessary
|
||||
if ((!trial.response_allowed_while_playing) & (!trial.trial_ends_after_audio)) {
|
||||
if (context !== null) {
|
||||
source.addEventListener('ended', enable_buttons);
|
||||
} else {
|
||||
audio.addEventListener('ended', enable_buttons);
|
||||
}
|
||||
}
|
||||
|
||||
//display buttons
|
||||
var buttons = [];
|
||||
if (Array.isArray(trial.button_html)) {
|
||||
@ -133,6 +147,9 @@ jsPsych.plugins["audio-button-response"] = (function() {
|
||||
var choice = e.currentTarget.getAttribute('data-choice'); // don't use dataset for jsdom compatibility
|
||||
after_response(choice);
|
||||
});
|
||||
if (!trial.response_allowed_while_playing) {
|
||||
display_element.querySelector('#jspsych-audio-button-response-button-' + i).querySelector('button').disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// store response
|
||||
@ -160,24 +177,26 @@ jsPsych.plugins["audio-button-response"] = (function() {
|
||||
if (trial.response_ends_trial) {
|
||||
end_trial();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// function to end trial when it is time
|
||||
function end_trial() {
|
||||
|
||||
// kill any remaining setTimeout handlers
|
||||
jsPsych.pluginAPI.clearAllTimeouts();
|
||||
|
||||
// stop the audio file if it is playing
|
||||
// remove end event listeners if they exist
|
||||
if(context !== null){
|
||||
source.stop();
|
||||
source.onended = function() { }
|
||||
source.removeEventListener('ended', end_trial);
|
||||
source.removeEventListener('ended', enable_buttons);
|
||||
} else {
|
||||
audio.pause();
|
||||
audio.removeEventListener('ended', end_trial);
|
||||
audio.removeEventListener('ended', end_trial);
|
||||
audio.removeEventListener('ended', enable_buttons);
|
||||
}
|
||||
|
||||
// kill any remaining setTimeout handlers
|
||||
jsPsych.pluginAPI.clearAllTimeouts();
|
||||
|
||||
// gather the data to store for the trial
|
||||
var trial_data = {
|
||||
"rt": response.rt,
|
||||
@ -190,7 +209,15 @@ jsPsych.plugins["audio-button-response"] = (function() {
|
||||
|
||||
// move on to the next trial
|
||||
jsPsych.finishTrial(trial_data);
|
||||
};
|
||||
}
|
||||
|
||||
// function to enable buttons after audio ends
|
||||
function enable_buttons() {
|
||||
var btns = document.querySelectorAll('.jspsych-audio-button-response-button button');
|
||||
for (var i=0; i<btns.length; i++) {
|
||||
btns[i].disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// start time
|
||||
var start_time = performance.now();
|
||||
|
@ -55,6 +55,13 @@ jsPsych.plugins["audio-keyboard-response"] = (function() {
|
||||
default: false,
|
||||
description: 'If true, then the trial will end as soon as the audio file finishes playing.'
|
||||
},
|
||||
response_allowed_while_playing: {
|
||||
type: jsPsych.plugins.parameterType.BOOL,
|
||||
pretty_name: 'Response allowed while playing',
|
||||
default: true,
|
||||
description: 'If true, then responses are allowed while the audio is playing. '+
|
||||
'If false, then the audio must finish playing before a response is accepted.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,12 +79,9 @@ jsPsych.plugins["audio-keyboard-response"] = (function() {
|
||||
}
|
||||
|
||||
// set up end event if trial needs it
|
||||
|
||||
if(trial.trial_ends_after_audio){
|
||||
if(context !== null){
|
||||
source.onended = function() {
|
||||
end_trial();
|
||||
}
|
||||
source.addEventListener('ended', end_trial);
|
||||
} else {
|
||||
audio.addEventListener('ended', end_trial);
|
||||
}
|
||||
@ -104,10 +108,12 @@ jsPsych.plugins["audio-keyboard-response"] = (function() {
|
||||
// remove end event listeners if they exist
|
||||
if(context !== null){
|
||||
source.stop();
|
||||
source.onended = function() { }
|
||||
source.removeEventListener('ended', end_trial);
|
||||
source.removeEventListener('ended', setup_keyboard_listener);
|
||||
} else {
|
||||
audio.pause();
|
||||
audio.removeEventListener('ended', end_trial);
|
||||
audio.removeEventListener('ended', setup_keyboard_listener);
|
||||
}
|
||||
|
||||
// kill keyboard listeners
|
||||
@ -128,7 +134,7 @@ jsPsych.plugins["audio-keyboard-response"] = (function() {
|
||||
|
||||
// move on to the next trial
|
||||
jsPsych.finishTrial(trial_data);
|
||||
};
|
||||
}
|
||||
|
||||
// function to handle responses by the subject
|
||||
var after_response = function(info) {
|
||||
@ -143,6 +149,29 @@ jsPsych.plugins["audio-keyboard-response"] = (function() {
|
||||
}
|
||||
};
|
||||
|
||||
function setup_keyboard_listener() {
|
||||
// start the response listener
|
||||
if(context !== null) {
|
||||
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
|
||||
callback_function: after_response,
|
||||
valid_responses: trial.choices,
|
||||
rt_method: 'audio',
|
||||
persist: false,
|
||||
allow_held_key: false,
|
||||
audio_context: context,
|
||||
audio_context_start_time: startTime
|
||||
});
|
||||
} else {
|
||||
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
|
||||
callback_function: after_response,
|
||||
valid_responses: trial.choices,
|
||||
rt_method: 'performance',
|
||||
persist: false,
|
||||
allow_held_key: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// start audio
|
||||
if(context !== null){
|
||||
startTime = context.currentTime;
|
||||
@ -151,25 +180,15 @@ jsPsych.plugins["audio-keyboard-response"] = (function() {
|
||||
audio.play();
|
||||
}
|
||||
|
||||
// start the response listener
|
||||
if(context !== null) {
|
||||
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
|
||||
callback_function: after_response,
|
||||
valid_responses: trial.choices,
|
||||
rt_method: 'audio',
|
||||
persist: false,
|
||||
allow_held_key: false,
|
||||
audio_context: context,
|
||||
audio_context_start_time: startTime
|
||||
});
|
||||
} else {
|
||||
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
|
||||
callback_function: after_response,
|
||||
valid_responses: trial.choices,
|
||||
rt_method: 'performance',
|
||||
persist: false,
|
||||
allow_held_key: false
|
||||
});
|
||||
// start keyboard listener when trial starts or sound ends
|
||||
if (trial.response_allowed_while_playing) {
|
||||
setup_keyboard_listener();
|
||||
} else if (!trial.trial_ends_after_audio) {
|
||||
if(context !== null){
|
||||
source.addEventListener('ended', setup_keyboard_listener);
|
||||
} else {
|
||||
audio.addEventListener('ended', setup_keyboard_listener);
|
||||
}
|
||||
}
|
||||
|
||||
// end trial if time limit is set
|
||||
|
@ -81,6 +81,13 @@ jsPsych.plugins['audio-slider-response'] = (function() {
|
||||
default: true,
|
||||
description: 'If true, trial will end when user makes a response.'
|
||||
},
|
||||
response_allowed_while_playing: {
|
||||
type: jsPsych.plugins.parameterType.BOOL,
|
||||
pretty_name: 'Response allowed while playing',
|
||||
default: true,
|
||||
description: 'If true, then responses are allowed while the audio is playing. '+
|
||||
'If false, then the audio must finish playing before a response is accepted.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,14 +110,21 @@ jsPsych.plugins['audio-slider-response'] = (function() {
|
||||
// set up end event if trial needs it
|
||||
if(trial.trial_ends_after_audio){
|
||||
if(context !== null){
|
||||
source.onended = function() {
|
||||
end_trial();
|
||||
}
|
||||
source.addEventListener('ended', end_trial);
|
||||
} else {
|
||||
audio.addEventListener('ended', end_trial);
|
||||
}
|
||||
}
|
||||
|
||||
// enable slider after audio ends if necessary
|
||||
if ((!trial.response_allowed_while_playing) & (!trial.trial_ends_after_audio)) {
|
||||
if (context !== null) {
|
||||
source.addEventListener('ended', enable_slider);
|
||||
} else {
|
||||
audio.addEventListener('ended', enable_slider);
|
||||
}
|
||||
}
|
||||
|
||||
var html = '<div id="jspsych-audio-slider-response-wrapper" style="margin: 100px 0px;">';
|
||||
html += '<div class="jspsych-audio-slider-response-container" style="position:relative; margin: 0 auto 3em auto; width:';
|
||||
if(trial.slider_width !== null){
|
||||
@ -119,8 +133,11 @@ jsPsych.plugins['audio-slider-response'] = (function() {
|
||||
html += 'auto;';
|
||||
}
|
||||
html += '">';
|
||||
html += '<input type="range" class="jspsych-slider" value="'+trial.slider_start+'" min="'+trial.min+'" max="'+trial.max+'" step="'+trial.step+'" id="jspsych-audio-slider-response-response"></input>';
|
||||
html += '<div>'
|
||||
html += '<input type="range" class="jspsych-slider" value="'+trial.slider_start+'" min="'+trial.min+'" max="'+trial.max+'" step="'+trial.step+'" id="jspsych-audio-slider-response-response"';
|
||||
if (!trial.response_allowed_while_playing) {
|
||||
html += ' disabled';
|
||||
}
|
||||
html += '></input><div>'
|
||||
for(var j=0; j < trial.labels.length; j++){
|
||||
var label_width_perc = 100/(trial.labels.length-1);
|
||||
var percent_of_range = j * (100/(trial.labels.length - 1));
|
||||
@ -140,7 +157,11 @@ jsPsych.plugins['audio-slider-response'] = (function() {
|
||||
}
|
||||
|
||||
// add submit button
|
||||
html += '<button id="jspsych-audio-slider-response-next" class="jspsych-btn" '+ (trial.require_movement ? "disabled" : "") + '>'+trial.button_label+'</button>';
|
||||
var next_disabled_attribute = "";
|
||||
if (trial.require_movement | !trial.response_allowed_while_playing) {
|
||||
next_disabled_attribute = "disabled";
|
||||
}
|
||||
html += '<button id="jspsych-audio-slider-response-next" class="jspsych-btn" '+ next_disabled_attribute + '>'+trial.button_label+'</button>';
|
||||
|
||||
display_element.innerHTML = html;
|
||||
|
||||
@ -149,6 +170,11 @@ jsPsych.plugins['audio-slider-response'] = (function() {
|
||||
response: null
|
||||
};
|
||||
|
||||
if (!trial.response_allowed_while_playing) {
|
||||
display_element.querySelector('#jspsych-audio-slider-response-response').disabled = true;
|
||||
display_element.querySelector('#jspsych-audio-slider-response-next').disabled = true;
|
||||
}
|
||||
|
||||
if(trial.require_movement){
|
||||
display_element.querySelector('#jspsych-audio-slider-response-response').addEventListener('click', function(){
|
||||
display_element.querySelector('#jspsych-audio-slider-response-next').disabled = false;
|
||||
@ -176,14 +202,19 @@ jsPsych.plugins['audio-slider-response'] = (function() {
|
||||
|
||||
function end_trial(){
|
||||
|
||||
// kill any remaining setTimeout handlers
|
||||
jsPsych.pluginAPI.clearAllTimeouts();
|
||||
|
||||
// stop the audio file if it is playing
|
||||
// remove end event listeners if they exist
|
||||
if(context !== null){
|
||||
source.stop();
|
||||
source.onended = function() { }
|
||||
source.removeEventListener('ended', end_trial);
|
||||
source.removeEventListener('ended', enable_slider);
|
||||
} else {
|
||||
audio.pause();
|
||||
audio.removeEventListener('ended', end_trial);
|
||||
audio.removeEventListener('ended', enable_slider);
|
||||
}
|
||||
|
||||
// save data
|
||||
@ -200,6 +231,14 @@ jsPsych.plugins['audio-slider-response'] = (function() {
|
||||
jsPsych.finishTrial(trialdata);
|
||||
}
|
||||
|
||||
// function to enable slider after audio ends
|
||||
function enable_slider() {
|
||||
document.querySelector('#jspsych-audio-slider-response-response').disabled = false;
|
||||
if (!trial.require_movement) {
|
||||
document.querySelector('#jspsych-audio-slider-response-next').disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
var startTime = performance.now();
|
||||
// start audio
|
||||
if(context !== null){
|
||||
|
199
plugins/jspsych-canvas-button-response.js
Normal file
199
plugins/jspsych-canvas-button-response.js
Normal file
@ -0,0 +1,199 @@
|
||||
/**
|
||||
* jspsych-canvas-button-response
|
||||
* Chris Jungerius (modified from Josh de Leeuw)
|
||||
*
|
||||
* a jsPsych plugin for displaying a canvas stimulus and getting a button response
|
||||
*
|
||||
* documentation: docs.jspsych.org
|
||||
*
|
||||
**/
|
||||
|
||||
jsPsych.plugins["canvas-button-response"] = (function () {
|
||||
|
||||
var plugin = {};
|
||||
|
||||
plugin.info = {
|
||||
name: 'canvas-button-response',
|
||||
description: '',
|
||||
parameters: {
|
||||
stimulus: {
|
||||
type: jsPsych.plugins.parameterType.FUNCTION,
|
||||
pretty_name: 'Stimulus',
|
||||
default: undefined,
|
||||
description: 'The drawing function to apply to the canvas. Should take the canvas object as argument.'
|
||||
},
|
||||
choices: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
pretty_name: 'Choices',
|
||||
default: undefined,
|
||||
array: true,
|
||||
description: 'The labels for the buttons.'
|
||||
},
|
||||
button_html: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
pretty_name: 'Button HTML',
|
||||
default: '<button class="jspsych-btn">%choice%</button>',
|
||||
array: true,
|
||||
description: 'The html of the button. Can create own style.'
|
||||
},
|
||||
prompt: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
pretty_name: 'Prompt',
|
||||
default: null,
|
||||
description: 'Any content here will be displayed under the button.'
|
||||
},
|
||||
stimulus_duration: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
pretty_name: 'Stimulus duration',
|
||||
default: null,
|
||||
description: 'How long to hide the stimulus.'
|
||||
},
|
||||
trial_duration: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
pretty_name: 'Trial duration',
|
||||
default: null,
|
||||
description: 'How long to show the trial.'
|
||||
},
|
||||
margin_vertical: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
pretty_name: 'Margin vertical',
|
||||
default: '0px',
|
||||
description: 'The vertical margin of the button.'
|
||||
},
|
||||
margin_horizontal: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
pretty_name: 'Margin horizontal',
|
||||
default: '8px',
|
||||
description: 'The horizontal margin of the button.'
|
||||
},
|
||||
response_ends_trial: {
|
||||
type: jsPsych.plugins.parameterType.BOOL,
|
||||
pretty_name: 'Response ends trial',
|
||||
default: true,
|
||||
description: 'If true, then trial will end when user responds.'
|
||||
},
|
||||
canvas_size: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
array: true,
|
||||
pretty_name: 'Canvas size',
|
||||
default: [500, 500],
|
||||
description: 'Array containing the height (first value) and width (second value) of the canvas element.'
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
plugin.trial = function (display_element, trial) {
|
||||
|
||||
// create canvas
|
||||
var html = '<div id="jspsych-canvas-button-response-stimulus">' + '<canvas id="jspsych-canvas-stimulus" height="' + trial.canvas_size[0] + '" width="' + trial.canvas_size[1] + '"></canvas>' + '</div>';
|
||||
|
||||
//display buttons
|
||||
var buttons = [];
|
||||
if (Array.isArray(trial.button_html)) {
|
||||
if (trial.button_html.length == trial.choices.length) {
|
||||
buttons = trial.button_html;
|
||||
} else {
|
||||
console.error('Error in canvas-button-response plugin. The length of the button_html array does not equal the length of the choices array');
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < trial.choices.length; i++) {
|
||||
buttons.push(trial.button_html);
|
||||
}
|
||||
}
|
||||
html += '<div id="jspsych-canvas-button-response-btngroup">';
|
||||
for (var i = 0; i < trial.choices.length; i++) {
|
||||
var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
|
||||
html += '<div class="jspsych-canvas-button-response-button" style="display: inline-block; margin:' + trial.margin_vertical + ' ' + trial.margin_horizontal + '" id="jspsych-canvas-button-response-button-' + i + '" data-choice="' + i + '">' + str + '</div>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
//show prompt if there is one
|
||||
if (trial.prompt !== null) {
|
||||
html += trial.prompt;
|
||||
}
|
||||
display_element.innerHTML = html;
|
||||
|
||||
//draw
|
||||
let c = document.getElementById("jspsych-canvas-stimulus")
|
||||
trial.stimulus(c)
|
||||
|
||||
// start time
|
||||
var start_time = performance.now();
|
||||
|
||||
// add event listeners to buttons
|
||||
for (var i = 0; i < trial.choices.length; i++) {
|
||||
display_element.querySelector('#jspsych-canvas-button-response-button-' + i).addEventListener('click', function (e) {
|
||||
var choice = e.currentTarget.getAttribute('data-choice'); // don't use dataset for jsdom compatibility
|
||||
after_response(choice);
|
||||
});
|
||||
}
|
||||
|
||||
// store response
|
||||
var response = {
|
||||
rt: null,
|
||||
button: null
|
||||
};
|
||||
|
||||
// function to handle responses by the subject
|
||||
function after_response(choice) {
|
||||
|
||||
// measure rt
|
||||
var end_time = performance.now();
|
||||
var rt = end_time - start_time;
|
||||
response.button = parseInt(choice);
|
||||
response.rt = rt;
|
||||
|
||||
// after a valid response, the stimulus will have the CSS class 'responded'
|
||||
// which can be used to provide visual feedback that a response was recorded
|
||||
display_element.querySelector('#jspsych-canvas-button-response-stimulus').className += ' responded';
|
||||
|
||||
// disable all the buttons after a response
|
||||
var btns = document.querySelectorAll('.jspsych-canvas-button-response-button button');
|
||||
for (var i = 0; i < btns.length; i++) {
|
||||
//btns[i].removeEventListener('click');
|
||||
btns[i].setAttribute('disabled', 'disabled');
|
||||
}
|
||||
|
||||
if (trial.response_ends_trial) {
|
||||
end_trial();
|
||||
}
|
||||
};
|
||||
|
||||
// function to end trial when it is time
|
||||
function end_trial() {
|
||||
|
||||
// kill any remaining setTimeout handlers
|
||||
jsPsych.pluginAPI.clearAllTimeouts();
|
||||
|
||||
// gather the data to store for the trial
|
||||
var trial_data = {
|
||||
"rt": response.rt,
|
||||
"button_pressed": response.button
|
||||
};
|
||||
|
||||
// clear the display
|
||||
display_element.innerHTML = '';
|
||||
|
||||
// move on to the next trial
|
||||
jsPsych.finishTrial(trial_data);
|
||||
};
|
||||
|
||||
// hide image if timing is set
|
||||
if (trial.stimulus_duration !== null) {
|
||||
jsPsych.pluginAPI.setTimeout(function () {
|
||||
display_element.querySelector('#jspsych-canvas-button-response-stimulus').style.visibility = 'hidden';
|
||||
}, trial.stimulus_duration);
|
||||
}
|
||||
|
||||
// end trial if time limit is set
|
||||
if (trial.trial_duration !== null) {
|
||||
jsPsych.pluginAPI.setTimeout(function () {
|
||||
end_trial();
|
||||
}, trial.trial_duration);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return plugin;
|
||||
})();
|
155
plugins/jspsych-canvas-keyboard-response.js
Normal file
155
plugins/jspsych-canvas-keyboard-response.js
Normal file
@ -0,0 +1,155 @@
|
||||
/**
|
||||
* jspsych-canvas-keyboard-response
|
||||
* Chris Jungerius (modified from Josh de Leeuw)
|
||||
*
|
||||
* a jsPsych plugin for displaying a canvas stimulus and getting a keyboard response
|
||||
*
|
||||
* documentation: docs.jspsych.org
|
||||
*
|
||||
**/
|
||||
|
||||
|
||||
jsPsych.plugins["canvas-keyboard-response"] = (function () {
|
||||
|
||||
var plugin = {};
|
||||
|
||||
plugin.info = {
|
||||
name: 'canvas-keyboard-response',
|
||||
description: '',
|
||||
parameters: {
|
||||
stimulus: {
|
||||
type: jsPsych.plugins.parameterType.FUNCTION,
|
||||
pretty_name: 'Stimulus',
|
||||
default: undefined,
|
||||
description: 'The drawing function to apply to the canvas. Should take the canvas object as argument.'
|
||||
},
|
||||
choices: {
|
||||
type: jsPsych.plugins.parameterType.KEYCODE,
|
||||
array: true,
|
||||
pretty_name: 'Choices',
|
||||
default: jsPsych.ALL_KEYS,
|
||||
description: 'The keys the subject is allowed to press to respond to the stimulus.'
|
||||
},
|
||||
prompt: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
pretty_name: 'Prompt',
|
||||
default: null,
|
||||
description: 'Any content here will be displayed below the stimulus.'
|
||||
},
|
||||
stimulus_duration: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
pretty_name: 'Stimulus duration',
|
||||
default: null,
|
||||
description: 'How long to hide the stimulus.'
|
||||
},
|
||||
trial_duration: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
pretty_name: 'Trial duration',
|
||||
default: null,
|
||||
description: 'How long to show trial before it ends.'
|
||||
},
|
||||
response_ends_trial: {
|
||||
type: jsPsych.plugins.parameterType.BOOL,
|
||||
pretty_name: 'Response ends trial',
|
||||
default: true,
|
||||
description: 'If true, trial will end when subject makes a response.'
|
||||
},
|
||||
canvas_size: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
array: true,
|
||||
pretty_name: 'Canvas size',
|
||||
default: [500, 500],
|
||||
description: 'Array containing the height (first value) and width (second value) of the canvas element.'
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
plugin.trial = function (display_element, trial) {
|
||||
|
||||
var new_html = '<div id="jspsych-canvas-keyboard-response-stimulus">' + '<canvas id="jspsych-canvas-stimulus" height="' + trial.canvas_size[0] + '" width="' + trial.canvas_size[1] + '"></canvas>' + '</div>';
|
||||
// add prompt
|
||||
if (trial.prompt !== null) {
|
||||
new_html += trial.prompt;
|
||||
}
|
||||
|
||||
// draw
|
||||
display_element.innerHTML = new_html;
|
||||
let c = document.getElementById("jspsych-canvas-stimulus")
|
||||
trial.stimulus(c)
|
||||
// store response
|
||||
var response = {
|
||||
rt: null,
|
||||
key: null
|
||||
};
|
||||
|
||||
// function to end trial when it is time
|
||||
var end_trial = function () {
|
||||
|
||||
// kill any remaining setTimeout handlers
|
||||
jsPsych.pluginAPI.clearAllTimeouts();
|
||||
|
||||
// kill keyboard listeners
|
||||
if (typeof keyboardListener !== 'undefined') {
|
||||
jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener);
|
||||
}
|
||||
|
||||
// gather the data to store for the trial
|
||||
var trial_data = {
|
||||
"rt": response.rt,
|
||||
"key_press": response.key
|
||||
};
|
||||
|
||||
// clear the display
|
||||
display_element.innerHTML = '';
|
||||
|
||||
// move on to the next trial
|
||||
jsPsych.finishTrial(trial_data);
|
||||
};
|
||||
|
||||
// function to handle responses by the subject
|
||||
var after_response = function (info) {
|
||||
|
||||
// after a valid response, the stimulus will have the CSS class 'responded'
|
||||
// which can be used to provide visual feedback that a response was recorded
|
||||
display_element.querySelector('#jspsych-canvas-keyboard-response-stimulus').className += ' responded';
|
||||
|
||||
// only record the first response
|
||||
if (response.key == null) {
|
||||
response = info;
|
||||
}
|
||||
|
||||
if (trial.response_ends_trial) {
|
||||
end_trial();
|
||||
}
|
||||
};
|
||||
|
||||
// start the response listener
|
||||
if (trial.choices != jsPsych.NO_KEYS) {
|
||||
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
|
||||
callback_function: after_response,
|
||||
valid_responses: trial.choices,
|
||||
rt_method: 'performance',
|
||||
persist: false,
|
||||
allow_held_key: false
|
||||
});
|
||||
}
|
||||
|
||||
// hide stimulus if stimulus_duration is set
|
||||
if (trial.stimulus_duration !== null) {
|
||||
jsPsych.pluginAPI.setTimeout(function () {
|
||||
display_element.querySelector('#jspsych-canvas-keyboard-response-stimulus').style.visibility = 'hidden';
|
||||
}, trial.stimulus_duration);
|
||||
}
|
||||
|
||||
// end trial if trial_duration is set
|
||||
if (trial.trial_duration !== null) {
|
||||
jsPsych.pluginAPI.setTimeout(function () {
|
||||
end_trial();
|
||||
}, trial.trial_duration);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return plugin;
|
||||
})();
|
207
plugins/jspsych-canvas-slider-response.js
Normal file
207
plugins/jspsych-canvas-slider-response.js
Normal file
@ -0,0 +1,207 @@
|
||||
/**
|
||||
* jspsych-canvas-slider-response
|
||||
* Chris Jungerius (modified from Josh de Leeuw)
|
||||
*
|
||||
* a jsPsych plugin for displaying a canvas stimulus and getting a slider response
|
||||
*
|
||||
* documentation: docs.jspsych.org
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
jsPsych.plugins['canvas-slider-response'] = (function () {
|
||||
|
||||
var plugin = {};
|
||||
|
||||
plugin.info = {
|
||||
name: 'canvas-slider-response',
|
||||
description: '',
|
||||
parameters: {
|
||||
stimulus: {
|
||||
type: jsPsych.plugins.parameterType.FUNCTION,
|
||||
pretty_name: 'Stimulus',
|
||||
default: undefined,
|
||||
description: 'The drawing function to apply to the canvas. Should take the canvas object as argument.'
|
||||
},
|
||||
min: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
pretty_name: 'Min slider',
|
||||
default: 0,
|
||||
description: 'Sets the minimum value of the slider.'
|
||||
},
|
||||
max: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
pretty_name: 'Max slider',
|
||||
default: 100,
|
||||
description: 'Sets the maximum value of the slider',
|
||||
},
|
||||
slider_start: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
pretty_name: 'Slider starting value',
|
||||
default: 50,
|
||||
description: 'Sets the starting value of the slider',
|
||||
},
|
||||
step: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
pretty_name: 'Step',
|
||||
default: 1,
|
||||
description: 'Sets the step of the slider'
|
||||
},
|
||||
labels: {
|
||||
type: jsPsych.plugins.parameterType.HTML_STRING,
|
||||
pretty_name: 'Labels',
|
||||
default: [],
|
||||
array: true,
|
||||
description: 'Labels of the slider.',
|
||||
},
|
||||
slider_width: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
pretty_name: 'Slider width',
|
||||
default: null,
|
||||
description: 'Width of the slider in pixels.'
|
||||
},
|
||||
button_label: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
pretty_name: 'Button label',
|
||||
default: 'Continue',
|
||||
array: false,
|
||||
description: 'Label of the button to advance.'
|
||||
},
|
||||
require_movement: {
|
||||
type: jsPsych.plugins.parameterType.BOOL,
|
||||
pretty_name: 'Require movement',
|
||||
default: false,
|
||||
description: 'If true, the participant will have to move the slider before continuing.'
|
||||
},
|
||||
prompt: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
pretty_name: 'Prompt',
|
||||
default: null,
|
||||
description: 'Any content here will be displayed below the slider.'
|
||||
},
|
||||
stimulus_duration: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
pretty_name: 'Stimulus duration',
|
||||
default: null,
|
||||
description: 'How long to hide the stimulus.'
|
||||
},
|
||||
trial_duration: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
pretty_name: 'Trial duration',
|
||||
default: null,
|
||||
description: 'How long to show the trial.'
|
||||
},
|
||||
response_ends_trial: {
|
||||
type: jsPsych.plugins.parameterType.BOOL,
|
||||
pretty_name: 'Response ends trial',
|
||||
default: true,
|
||||
description: 'If true, trial will end when user makes a response.'
|
||||
},
|
||||
canvas_size: {
|
||||
type: jsPsych.plugins.parameterType.INT,
|
||||
array: true,
|
||||
pretty_name: 'Canvas size',
|
||||
default: [500, 500],
|
||||
description: 'Array containing the height (first value) and width (second value) of the canvas element.'
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
plugin.trial = function (display_element, trial) {
|
||||
|
||||
var html = '<div id="jspsych-canvas-slider-response-wrapper" style="margin: 100px 0px;">';
|
||||
html += '<div id="jspsych-canvas-slider-response-stimulus">' + '<canvas id="jspsych-canvas-stimulus" height="' + trial.canvas_size[0] + '" width="' + trial.canvas_size[1] + '"></canvas>' + '</div>';
|
||||
html += '<div class="jspsych-canvas-slider-response-container" style="position:relative; margin: 0 auto 3em auto; width:';
|
||||
if (trial.slider_width !== null) {
|
||||
html += trial.slider_width + 'px;';
|
||||
} else {
|
||||
html += trial.canvas_size[1] + 'px;';
|
||||
}
|
||||
html += '">';
|
||||
html += '<input type="range" value="' + trial.slider_start + '" min="' + trial.min + '" max="' + trial.max + '" step="' + trial.step + '" style="width: 100%;" id="jspsych-canvas-slider-response-response"></input>';
|
||||
html += '<div>'
|
||||
for (var j = 0; j < trial.labels.length; j++) {
|
||||
var width = 100 / (trial.labels.length - 1);
|
||||
var left_offset = (j * (100 / (trial.labels.length - 1))) - (width / 2);
|
||||
html += '<div style="display: inline-block; position: absolute; left:' + left_offset + '%; text-align: center; width: ' + width + '%;">';
|
||||
html += '<span style="text-align: center; font-size: 80%;">' + trial.labels[j] + '</span>';
|
||||
html += '</div>'
|
||||
}
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
|
||||
if (trial.prompt !== null) {
|
||||
html += trial.prompt;
|
||||
}
|
||||
|
||||
// add submit button
|
||||
html += '<button id="jspsych-canvas-slider-response-next" class="jspsych-btn" ' + (trial.require_movement ? "disabled" : "") + '>' + trial.button_label + '</button>';
|
||||
|
||||
display_element.innerHTML = html;
|
||||
|
||||
// draw
|
||||
let c = document.getElementById("jspsych-canvas-stimulus")
|
||||
trial.stimulus(c)
|
||||
|
||||
var response = {
|
||||
rt: null,
|
||||
response: null
|
||||
};
|
||||
|
||||
if (trial.require_movement) {
|
||||
display_element.querySelector('#jspsych-canvas-slider-response-response').addEventListener('click', function () {
|
||||
display_element.querySelector('#jspsych-canvas-slider-response-next').disabled = false;
|
||||
})
|
||||
}
|
||||
|
||||
display_element.querySelector('#jspsych-canvas-slider-response-next').addEventListener('click', function () {
|
||||
// measure response time
|
||||
var endTime = performance.now();
|
||||
response.rt = endTime - startTime;
|
||||
response.response = display_element.querySelector('#jspsych-canvas-slider-response-response').valueAsNumber;
|
||||
|
||||
if (trial.response_ends_trial) {
|
||||
end_trial();
|
||||
} else {
|
||||
display_element.querySelector('#jspsych-canvas-slider-response-next').disabled = true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function end_trial() {
|
||||
|
||||
jsPsych.pluginAPI.clearAllTimeouts();
|
||||
|
||||
// save data
|
||||
var trialdata = {
|
||||
"rt": response.rt,
|
||||
"response": response.response,
|
||||
"slider_start": trial.slider_start
|
||||
};
|
||||
|
||||
display_element.innerHTML = '';
|
||||
|
||||
// next trial
|
||||
jsPsych.finishTrial(trialdata);
|
||||
}
|
||||
|
||||
if (trial.stimulus_duration !== null) {
|
||||
jsPsych.pluginAPI.setTimeout(function () {
|
||||
display_element.querySelector('#jspsych-canvas-slider-response-stimulus').style.visibility = 'hidden';
|
||||
}, trial.stimulus_duration);
|
||||
}
|
||||
|
||||
// end trial if trial_duration is set
|
||||
if (trial.trial_duration !== null) {
|
||||
jsPsych.pluginAPI.setTimeout(function () {
|
||||
end_trial();
|
||||
}, trial.trial_duration);
|
||||
}
|
||||
|
||||
var startTime = performance.now();
|
||||
};
|
||||
|
||||
return plugin;
|
||||
})();
|
174
plugins/jspsych-maxdiff.js
Normal file
174
plugins/jspsych-maxdiff.js
Normal file
@ -0,0 +1,174 @@
|
||||
/**
|
||||
* jspsych-maxdiff
|
||||
* Angus Hughes
|
||||
*
|
||||
* a jspsych plugin for maxdiff/conjoint analysis designs
|
||||
*
|
||||
*/
|
||||
|
||||
jsPsych.plugins['maxdiff'] = (function () {
|
||||
|
||||
var plugin = {};
|
||||
|
||||
plugin.info = {
|
||||
name: 'maxdiff',
|
||||
description: '',
|
||||
parameters: {
|
||||
alternatives: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
pretty_name: 'Alternatives',
|
||||
array: true,
|
||||
default: undefined,
|
||||
description: 'Alternatives presented in the maxdiff table.'
|
||||
},
|
||||
labels: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
array: true,
|
||||
pretty_name: 'Labels',
|
||||
default: undefined,
|
||||
description: 'Labels to display for left and right response columns.'
|
||||
},
|
||||
randomize_alternative_order: {
|
||||
type: jsPsych.plugins.parameterType.BOOL,
|
||||
pretty_name: 'Randomize Alternative Order',
|
||||
default: false,
|
||||
description: 'If true, the order of the alternatives will be randomized'
|
||||
},
|
||||
preamble: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
pretty_name: 'Preamble',
|
||||
default: '',
|
||||
description: 'String to display at top of the page.'
|
||||
},
|
||||
button_label: {
|
||||
type: jsPsych.plugins.parameterType.STRING,
|
||||
pretty_name: 'Button Label',
|
||||
default: 'Continue',
|
||||
description: 'Label of the button.'
|
||||
},
|
||||
required: {
|
||||
type: jsPsych.plugins.parameterType.BOOL,
|
||||
pretty_name: 'Required',
|
||||
default: false,
|
||||
description: 'Makes answering the alternative required.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugin.trial = function (display_element, trial) {
|
||||
|
||||
var html = "";
|
||||
// inject CSS for trial
|
||||
html += '<style id="jspsych-maxdiff-css">';
|
||||
html += ".jspsych-maxdiff-statement {display:block; font-size: 16px; padding-top: 40px; margin-bottom:10px;}" +
|
||||
'table.jspsych-maxdiff-table {border-collapse: collapse; padding: 15px; margin-left: auto; margin-right: auto;}' +
|
||||
'table.jspsych-maxdiff-table td, th {border-bottom: 1px solid #dddddd; text-align: center; padding: 8px;}' +
|
||||
'table.jspsych-maxdiff-table tr:nth-child(even) {background-color: #dddddd;}';
|
||||
html += '</style>';
|
||||
|
||||
// show preamble text
|
||||
if (trial.preamble !== null) {
|
||||
html += '<div id="jspsych-maxdiff-preamble" class="jspsych-maxdiff-preamble">' + trial.preamble + '</div>';
|
||||
}
|
||||
html += '<form id="jspsych-maxdiff-form">';
|
||||
|
||||
// add maxdiff options ///
|
||||
// first generate alternative order, randomized here as opposed to randomizing the order of alternatives
|
||||
// so that the data are always associated with the same alternative regardless of order.
|
||||
var alternative_order = [];
|
||||
for (var i = 0; i < trial.alternatives.length; i++) {
|
||||
alternative_order.push(i);
|
||||
}
|
||||
if (trial.randomize_alternative_order) {
|
||||
alternative_order = jsPsych.randomization.shuffle(alternative_order);
|
||||
}
|
||||
|
||||
// Start with column headings
|
||||
var maxdiff_table = '<table class="jspsych-maxdiff-table"><tr><th id="jspsych-maxdiff-left-label">' + trial.labels[0] + '</th><th></th><th id="jspsych-maxdiff-right-label">' + trial.labels[1] + '</th></tr>';
|
||||
|
||||
// construct each row of the maxdiff table
|
||||
for (var i = 0; i < trial.alternatives.length; i++) {
|
||||
var alternative = trial.alternatives[alternative_order[i]];
|
||||
// add alternative
|
||||
maxdiff_table += '<tr><td><input class= "jspsych-maxdiff-alt-' + i.toString() + '" type="radio" name="left" data-name = ' + alternative_order[i].toString() + ' /><br></td>';
|
||||
maxdiff_table += '<td id="jspsych-maxdiff-alternative-' + i.toString() + '">' + alternative + '</td>';
|
||||
maxdiff_table += '<td><input class= "jspsych-maxdiff-alt-' + i.toString() + '" type="radio" name="right" data-name = ' + alternative_order[i].toString() + ' /><br></td></tr>';
|
||||
}
|
||||
maxdiff_table += '</table><br><br>';
|
||||
html += maxdiff_table;
|
||||
|
||||
// add submit button
|
||||
var enable_submit = trial.required == true ? 'disabled = "disabled"' : '';
|
||||
html += '<input type="submit" id="jspsych-maxdiff-next" class="jspsych-maxdiff jspsych-btn" ' + enable_submit + ' value="' + trial.button_label + '"></input>';
|
||||
html += '</form>';
|
||||
|
||||
display_element.innerHTML = html;
|
||||
|
||||
// function to control responses
|
||||
// first checks that the same alternative cannot be endorsed in the left and right columns simultaneously.
|
||||
// then enables the submit button if the trial is required.
|
||||
const left_right = ["left", "right"]
|
||||
left_right.forEach(function(p) {
|
||||
// Get all elements either 'left' or 'right'
|
||||
document.getElementsByName(p).forEach(function(alt) {
|
||||
alt.addEventListener('click', function() {
|
||||
// Find the opposite (if left, then right & vice versa) identified by the class (jspsych-maxdiff-alt-1, 2, etc)
|
||||
var op = alt.name == 'left' ? 'right' : 'left';
|
||||
var n = document.getElementsByClassName(alt.className).namedItem(op);
|
||||
// If it's checked, uncheck it.
|
||||
if (n.checked) {
|
||||
n.checked = false;
|
||||
}
|
||||
|
||||
// check response
|
||||
if (trial.required){
|
||||
// Now check if one of both left and right have been enabled to allow submission
|
||||
var left_checked = [...document.getElementsByName('left')].some(c => c.checked);
|
||||
var right_checked = [...document.getElementsByName('right')].some(c => c.checked);
|
||||
if (left_checked && right_checked) {
|
||||
document.getElementById("jspsych-maxdiff-next").disabled = false;
|
||||
} else {
|
||||
document.getElementById("jspsych-maxdiff-next").disabled = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Get the data once the submit button is clicked
|
||||
// Get the data once the submit button is clicked
|
||||
display_element.querySelector('#jspsych-maxdiff-form').addEventListener('submit', function(e){
|
||||
e.preventDefault();
|
||||
|
||||
// measure response time
|
||||
var endTime = performance.now();
|
||||
var response_time = endTime - startTime;
|
||||
|
||||
// get the alternative by the data-name attribute, allowing a null response if unchecked
|
||||
get_response = function(side){
|
||||
var col = display_element.querySelectorAll('[name=\"' + side + '\"]:checked')[0];
|
||||
if (col === undefined){
|
||||
return null;
|
||||
} else {
|
||||
var i = parseInt(col.getAttribute('data-name'));
|
||||
return trial.alternatives[i];
|
||||
}
|
||||
}
|
||||
|
||||
// data saving
|
||||
var trial_data = {
|
||||
"rt": response_time,
|
||||
"labels": JSON.stringify({"left": trial.labels[0], "right": trial.labels[1]}),
|
||||
"left": get_response('left'),
|
||||
"right": get_response('right')
|
||||
};
|
||||
|
||||
// next trial
|
||||
jsPsych.finishTrial(trial_data);
|
||||
});
|
||||
|
||||
var startTime = performance.now();
|
||||
};
|
||||
|
||||
return plugin;
|
||||
})();
|
@ -115,6 +115,13 @@ jsPsych.plugins["video-button-response"] = (function() {
|
||||
pretty_name: 'Response ends trial',
|
||||
default: true,
|
||||
description: 'If true, the trial will end when subject makes a response.'
|
||||
},
|
||||
response_allowed_while_playing: {
|
||||
type: jsPsych.plugins.parameterType.BOOL,
|
||||
pretty_name: 'Response allowed while playing',
|
||||
default: true,
|
||||
description: 'If true, then responses are allowed while the video is playing. '+
|
||||
'If false, then the video must finish playing before a response is accepted.'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -131,12 +138,19 @@ jsPsych.plugins["video-button-response"] = (function() {
|
||||
if(trial.height) {
|
||||
video_html += ' height="'+trial.height+'"';
|
||||
}
|
||||
if(trial.autoplay){
|
||||
if(trial.autoplay & (trial.start == null)){
|
||||
// if autoplay is true and the start time is specified, then the video will start automatically
|
||||
// via the play() method, rather than the autoplay attribute, to prevent showing the first frame
|
||||
video_html += " autoplay ";
|
||||
}
|
||||
if(trial.controls){
|
||||
video_html +=" controls ";
|
||||
}
|
||||
if (trial.start !== null) {
|
||||
// hide video element when page loads if the start time is specified,
|
||||
// to prevent the video element from showing the first frame
|
||||
video_html += ' style="visibility: hidden;"';
|
||||
}
|
||||
video_html +=">";
|
||||
|
||||
var video_preload_blob = jsPsych.pluginAPI.getVideoBuffer(trial.sources[0]);
|
||||
@ -186,37 +200,56 @@ jsPsych.plugins["video-button-response"] = (function() {
|
||||
|
||||
var start_time = performance.now();
|
||||
|
||||
var video_element = display_element.querySelector('#jspsych-video-button-response-stimulus');
|
||||
|
||||
if(video_preload_blob){
|
||||
display_element.querySelector('#jspsych-video-button-response-stimulus').src = video_preload_blob;
|
||||
video_element.src = video_preload_blob;
|
||||
}
|
||||
|
||||
display_element.querySelector('#jspsych-video-button-response-stimulus').onended = function(){
|
||||
video_element.onended = function(){
|
||||
if(trial.trial_ends_after_video){
|
||||
end_trial();
|
||||
} else if (!trial.response_allowed_while_playing) {
|
||||
// enable response buttons
|
||||
for (var i=0; i<trial.choices.length; i++) {
|
||||
display_element.querySelector('#jspsych-video-button-response-button-' + i).querySelector('button').disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
video_element.playbackRate = trial.rate;
|
||||
|
||||
// if video start time is specified, hide the video and set the starting time
|
||||
// before showing and playing, so that the video doesn't automatically show the first frame
|
||||
if(trial.start !== null){
|
||||
display_element.querySelector('#jspsych-video-button-response-stimulus').currentTime = trial.start;
|
||||
video_element.pause();
|
||||
video_element.currentTime = trial.start;
|
||||
video_element.onseeked = function() {
|
||||
video_element.style.visibility = "visible";
|
||||
if (trial.autoplay) {
|
||||
video_element.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(trial.stop !== null){
|
||||
display_element.querySelector('#jspsych-video-button-response-stimulus').addEventListener('timeupdate', function(e){
|
||||
var currenttime = display_element.querySelector('#jspsych-video-button-response-stimulus').currentTime;
|
||||
video_element.addEventListener('timeupdate', function(e){
|
||||
var currenttime = video_element.currentTime;
|
||||
if(currenttime >= trial.stop){
|
||||
display_element.querySelector('#jspsych-video-button-response-stimulus').pause();
|
||||
video_element.pause();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
display_element.querySelector('#jspsych-video-button-response-stimulus').playbackRate = trial.rate;
|
||||
|
||||
// add event listeners to buttons
|
||||
for (var i = 0; i < trial.choices.length; i++) {
|
||||
display_element.querySelector('#jspsych-video-button-response-button-' + i).addEventListener('click', function(e){
|
||||
var choice = e.currentTarget.getAttribute('data-choice'); // don't use dataset for jsdom compatibility
|
||||
after_response(choice);
|
||||
});
|
||||
if (!trial.response_allowed_while_playing) {
|
||||
display_element.querySelector('#jspsych-video-button-response-button-' + i).querySelector('button').disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// store response
|
||||
@ -231,6 +264,11 @@ jsPsych.plugins["video-button-response"] = (function() {
|
||||
// kill any remaining setTimeout handlers
|
||||
jsPsych.pluginAPI.clearAllTimeouts();
|
||||
|
||||
// stop the video file if it is playing
|
||||
// remove any remaining end event handlers
|
||||
display_element.querySelector('#jspsych-video-button-response-stimulus').pause();
|
||||
display_element.querySelector('#jspsych-video-button-response-stimulus').onended = function() {};
|
||||
|
||||
// gather the data to store for the trial
|
||||
var trial_data = {
|
||||
"rt": response.rt,
|
||||
@ -243,7 +281,7 @@ jsPsych.plugins["video-button-response"] = (function() {
|
||||
|
||||
// move on to the next trial
|
||||
jsPsych.finishTrial(trial_data);
|
||||
};
|
||||
}
|
||||
|
||||
// function to handle responses by the subject
|
||||
function after_response(choice) {
|
||||
@ -256,7 +294,7 @@ jsPsych.plugins["video-button-response"] = (function() {
|
||||
|
||||
// after a valid response, the stimulus will have the CSS class 'responded'
|
||||
// which can be used to provide visual feedback that a response was recorded
|
||||
display_element.querySelector('#jspsych-video-button-response-stimulus').className += ' responded';
|
||||
video_element.className += ' responded';
|
||||
|
||||
// disable all the buttons after a response
|
||||
var btns = document.querySelectorAll('.jspsych-video-button-response-button button');
|
||||
@ -268,7 +306,7 @@ jsPsych.plugins["video-button-response"] = (function() {
|
||||
if (trial.response_ends_trial) {
|
||||
end_trial();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// end trial if time limit is set
|
||||
if (trial.trial_duration !== null) {
|
||||
|
@ -96,6 +96,13 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
|
||||
pretty_name: 'Response ends trial',
|
||||
default: true,
|
||||
description: 'If true, the trial will end when subject makes a response.'
|
||||
},
|
||||
response_allowed_while_playing: {
|
||||
type: jsPsych.plugins.parameterType.BOOL,
|
||||
pretty_name: 'Response allowed while playing',
|
||||
default: true,
|
||||
description: 'If true, then responses are allowed while the video is playing. '+
|
||||
'If false, then the video must finish playing before a response is accepted.'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -112,12 +119,19 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
|
||||
if(trial.height) {
|
||||
video_html += ' height="'+trial.height+'"';
|
||||
}
|
||||
if(trial.autoplay){
|
||||
if(trial.autoplay & (trial.start == null)){
|
||||
// if autoplay is true and the start time is specified, then the video will start automatically
|
||||
// via the play() method, rather than the autoplay attribute, to prevent showing the first frame
|
||||
video_html += " autoplay ";
|
||||
}
|
||||
if(trial.controls){
|
||||
video_html +=" controls ";
|
||||
}
|
||||
if (trial.start !== null) {
|
||||
// hide video element when page loads if the start time is specified,
|
||||
// to prevent the video element from showing the first frame
|
||||
video_html += ' style="visibility: hidden;"';
|
||||
}
|
||||
video_html +=">";
|
||||
|
||||
var video_preload_blob = jsPsych.pluginAPI.getVideoBuffer(trial.sources[0]);
|
||||
@ -145,31 +159,52 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
|
||||
|
||||
display_element.innerHTML = video_html;
|
||||
|
||||
var video_element = display_element.querySelector('#jspsych-video-keyboard-response-stimulus');
|
||||
|
||||
if(video_preload_blob){
|
||||
display_element.querySelector('#jspsych-video-keyboard-response-stimulus').src = video_preload_blob;
|
||||
video_element.src = video_preload_blob;
|
||||
}
|
||||
|
||||
display_element.querySelector('#jspsych-video-keyboard-response-stimulus').onended = function(){
|
||||
video_element.onended = function(){
|
||||
if(trial.trial_ends_after_video){
|
||||
end_trial();
|
||||
}
|
||||
if ((trial.response_allowed_while_playing == false) & (!trial.trial_ends_after_video)) {
|
||||
// start keyboard listener
|
||||
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
|
||||
callback_function: after_response,
|
||||
valid_responses: trial.choices,
|
||||
rt_method: 'performance',
|
||||
persist: false,
|
||||
allow_held_key: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
video_element.playbackRate = trial.rate;
|
||||
|
||||
// if video start time is specified, hide the video and set the starting time
|
||||
// before showing and playing, so that the video doesn't automatically show the first frame
|
||||
if(trial.start !== null){
|
||||
display_element.querySelector('#jspsych-video-keyboard-response-stimulus').currentTime = trial.start;
|
||||
video_element.pause();
|
||||
video_element.currentTime = trial.start;
|
||||
video_element.onseeked = function() {
|
||||
video_element.style.visibility = "visible";
|
||||
if (trial.autoplay) {
|
||||
video_element.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(trial.stop !== null){
|
||||
display_element.querySelector('#jspsych-video-keyboard-response-stimulus').addEventListener('timeupdate', function(e){
|
||||
var currenttime = display_element.querySelector('#jspsych-video-keyboard-response-stimulus').currentTime;
|
||||
video_element.addEventListener('timeupdate', function(e){
|
||||
var currenttime = video_element.currentTime;
|
||||
if(currenttime >= trial.stop){
|
||||
display_element.querySelector('#jspsych-video-keyboard-response-stimulus').pause();
|
||||
video_element.pause();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
display_element.querySelector('#jspsych-video-keyboard-response-stimulus').playbackRate = trial.rate;
|
||||
|
||||
// store response
|
||||
var response = {
|
||||
rt: null,
|
||||
@ -184,6 +219,11 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
|
||||
|
||||
// kill keyboard listeners
|
||||
jsPsych.pluginAPI.cancelAllKeyboardResponses();
|
||||
|
||||
// stop the video file if it is playing
|
||||
// remove end event listeners if they exist
|
||||
display_element.querySelector('#jspsych-video-keyboard-response-stimulus').pause();
|
||||
display_element.querySelector('#jspsych-video-keyboard-response-stimulus').onended = function(){ };
|
||||
|
||||
// gather the data to store for the trial
|
||||
var trial_data = {
|
||||
@ -197,7 +237,7 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
|
||||
|
||||
// move on to the next trial
|
||||
jsPsych.finishTrial(trial_data);
|
||||
};
|
||||
}
|
||||
|
||||
// function to handle responses by the subject
|
||||
var after_response = function(info) {
|
||||
@ -217,7 +257,7 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
|
||||
};
|
||||
|
||||
// start the response listener
|
||||
if (trial.choices != jsPsych.NO_KEYS) {
|
||||
if ((trial.choices != jsPsych.NO_KEYS) & (trial.response_allowed_while_playing)) {
|
||||
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
|
||||
callback_function: after_response,
|
||||
valid_responses: trial.choices,
|
||||
|
@ -139,6 +139,13 @@ jsPsych.plugins["video-slider-response"] = (function() {
|
||||
pretty_name: 'Response ends trial',
|
||||
default: true,
|
||||
description: 'If true, the trial will end when subject makes a response.'
|
||||
},
|
||||
response_allowed_while_playing: {
|
||||
type: jsPsych.plugins.parameterType.BOOL,
|
||||
pretty_name: 'Response allowed while playing',
|
||||
default: true,
|
||||
description: 'If true, then responses are allowed while the video is playing. '+
|
||||
'If false, then the video must finish playing before a response is accepted.'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -157,12 +164,19 @@ jsPsych.plugins["video-slider-response"] = (function() {
|
||||
if(trial.height) {
|
||||
video_html += ' height="'+trial.height+'"';
|
||||
}
|
||||
if(trial.autoplay){
|
||||
if(trial.autoplay & (trial.start == null)){
|
||||
// if autoplay is true and the start time is specified, then the video will start automatically
|
||||
// via the play() method, rather than the autoplay attribute, to prevent showing the first frame
|
||||
video_html += " autoplay ";
|
||||
}
|
||||
if(trial.controls){
|
||||
video_html +=" controls ";
|
||||
}
|
||||
if (trial.start !== null) {
|
||||
// hide video element when page loads if the start time is specified,
|
||||
// to prevent the video element from showing the first frame
|
||||
video_html += ' style="visibility: hidden;"';
|
||||
}
|
||||
video_html +=">";
|
||||
|
||||
var video_preload_blob = jsPsych.pluginAPI.getVideoBuffer(trial.sources[0]);
|
||||
@ -191,8 +205,11 @@ jsPsych.plugins["video-slider-response"] = (function() {
|
||||
html += 'auto;'
|
||||
}
|
||||
html += '">';
|
||||
html += '<input type="range" class="jspsych-slider" value="'+trial.slider_start+'" min="'+trial.min+'" max="'+trial.max+'" step="'+trial.step+'" id="jspsych-video-slider-response-response"></input>';
|
||||
html += '<div>'
|
||||
html += '<input type="range" class="jspsych-slider" value="'+trial.slider_start+'" min="'+trial.min+'" max="'+trial.max+'" step="'+trial.step+'" id="jspsych-video-slider-response-response"';
|
||||
if (!trial.response_allowed_while_playing) {
|
||||
html += ' disabled';
|
||||
}
|
||||
html += '></input><div>'
|
||||
for(var j=0; j < trial.labels.length; j++){
|
||||
var label_width_perc = 100/(trial.labels.length-1);
|
||||
var percent_of_range = j * (100/(trial.labels.length - 1));
|
||||
@ -213,7 +230,11 @@ jsPsych.plugins["video-slider-response"] = (function() {
|
||||
}
|
||||
|
||||
// add submit button
|
||||
html += '<button id="jspsych-video-slider-response-next" class="jspsych-btn" '+ (trial.require_movement ? "disabled" : "") + '>'+trial.button_label+'</button>';
|
||||
var next_disabled_attribute = "";
|
||||
if (trial.require_movement | !trial.response_allowed_while_playing) {
|
||||
next_disabled_attribute = "disabled";
|
||||
}
|
||||
html += '<button id="jspsych-video-slider-response-next" class="jspsych-btn" '+ next_disabled_attribute + '>'+trial.button_label+'</button>';
|
||||
|
||||
display_element.innerHTML = html;
|
||||
|
||||
@ -226,11 +247,24 @@ jsPsych.plugins["video-slider-response"] = (function() {
|
||||
video_element.onended = function(){
|
||||
if(trial.trial_ends_after_video){
|
||||
end_trial();
|
||||
} else if (!trial.response_allowed_while_playing) {
|
||||
enable_slider();
|
||||
}
|
||||
}
|
||||
|
||||
video_element.playbackRate = trial.rate;
|
||||
|
||||
// if video start time is specified, hide the video and set the starting time
|
||||
// before showing and playing, so that the video doesn't automatically show the first frame
|
||||
if(trial.start !== null){
|
||||
video_element.pause();
|
||||
video_element.currentTime = trial.start;
|
||||
video_element.onseeked = function() {
|
||||
video_element.style.visibility = "visible";
|
||||
if (trial.autoplay) {
|
||||
video_element.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(trial.stop !== null){
|
||||
@ -242,8 +276,6 @@ jsPsych.plugins["video-slider-response"] = (function() {
|
||||
})
|
||||
}
|
||||
|
||||
video_element.playbackRate = trial.rate;
|
||||
|
||||
if(trial.require_movement){
|
||||
display_element.querySelector('#jspsych-video-slider-response-response').addEventListener('click', function(){
|
||||
display_element.querySelector('#jspsych-video-slider-response-next').disabled = false;
|
||||
@ -278,6 +310,11 @@ jsPsych.plugins["video-slider-response"] = (function() {
|
||||
// kill any remaining setTimeout handlers
|
||||
jsPsych.pluginAPI.clearAllTimeouts();
|
||||
|
||||
// stop the video file if it is playing
|
||||
// remove any remaining end event handlers
|
||||
display_element.querySelector('#jspsych-video-slider-response-stimulus-video').pause();
|
||||
display_element.querySelector('#jspsych-video-slider-response-stimulus-video').onended = function() {};
|
||||
|
||||
// gather the data to store for the trial
|
||||
var trial_data = {
|
||||
"rt": response.rt,
|
||||
@ -294,6 +331,14 @@ jsPsych.plugins["video-slider-response"] = (function() {
|
||||
jsPsych.finishTrial(trial_data);
|
||||
};
|
||||
|
||||
// function to enable slider after video ends
|
||||
function enable_slider() {
|
||||
document.querySelector('#jspsych-video-slider-response-response').disabled = false;
|
||||
if (!trial.require_movement) {
|
||||
document.querySelector('#jspsych-video-slider-response-next').disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// end trial if time limit is set
|
||||
if (trial.trial_duration !== null) {
|
||||
jsPsych.pluginAPI.setTimeout(function() {
|
||||
|
39
tests/plugins/plugin-maxdiff.test.js
Normal file
39
tests/plugins/plugin-maxdiff.test.js
Normal file
@ -0,0 +1,39 @@
|
||||
const root = '../../';
|
||||
const utils = require('../testing-utils.js');
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
describe('maxdiff plugin', function(){
|
||||
|
||||
beforeEach(function(){
|
||||
require(root + 'jspsych.js');
|
||||
require(root + 'plugins/jspsych-maxdiff.js');
|
||||
});
|
||||
|
||||
test('loads correctly', function(){
|
||||
expect(typeof window.jsPsych.plugins['maxdiff']).not.toBe('undefined');
|
||||
});
|
||||
|
||||
test('returns appropriate response with randomization', function(){
|
||||
var trial = {
|
||||
type: 'maxdiff',
|
||||
alternatives: ['a', 'b', 'c', 'd'],
|
||||
labels: ['Most', 'Least'],
|
||||
randomize_alternative_order: true
|
||||
}
|
||||
|
||||
jsPsych.init({
|
||||
timeline: [trial]
|
||||
});
|
||||
|
||||
document.querySelector('input[data-name="0"][name="left"]').checked = true;
|
||||
document.querySelector('input[data-name="1"][name="right"]').checked = true;
|
||||
|
||||
utils.clickTarget(document.querySelector('#jspsych-maxdiff-next'));
|
||||
|
||||
var maxdiff_data = jsPsych.data.get().values()[0];
|
||||
expect(maxdiff_data.left).toBe("a");
|
||||
expect(maxdiff_data.right).toBe("b");
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue
Block a user