Merge pull request #1145 from becky-gilbert/respond-while-playing

Add response_allowed_while_playing parameter to audio/video plugins - fixes #1137
This commit is contained in:
Becky Gilbert 2020-11-04 11:29:46 -08:00 committed by GitHub
commit c2ff15435c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 322 additions and 96 deletions

View File

@ -23,6 +23,7 @@ margin_vertical | string | '0px' | Vertical margin of the button(s).
margin_horizontal | string | '8px' | Horizontal 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. 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. 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 ## Data Generated

View File

@ -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. 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. 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. 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 ## Data Generated

View File

@ -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). 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. 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_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 ## Data Generated

View File

@ -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_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. 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_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 ## Data Generated

View File

@ -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_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. 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_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 ## Data Generated

View File

@ -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_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. 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_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 ## Data Generated

View File

@ -13,9 +13,9 @@
timeline.push({ timeline.push({
type: 'html-button-response', type: 'html-button-response',
stimulus: 'Recent versions of Chrome require the user to interact with a page before it can play 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. Be aware of this when planning audio experiments if '+ '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.', 'you want the first trial to include audio.</p></div>',
choices: ['Continue'] choices: ['Continue']
}); });
@ -35,6 +35,14 @@
prompt: "<p>What word was said? (trial ends after 2s)</p>" 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({ jsPsych.init({
timeline: timeline, timeline: timeline,
use_webaudio: false, use_webaudio: false,

View File

@ -11,9 +11,9 @@
var pre_audio = { var pre_audio = {
type: 'html-button-response', type: 'html-button-response',
stimulus: 'Recent versions of Chrome require the user to interact with a page before it can play 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. Be aware of this when planning audio experiments if '+ '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.', 'you want the first trial to include audio.</p></div>',
choices: ['Continue'] choices: ['Continue']
} }
@ -40,8 +40,16 @@
prompt: '<p>No response allowed. 2s wait.</p>' 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({ 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, use_webaudio: false,
on_finish: function() { on_finish: function() {
jsPsych.data.displayData(); jsPsych.data.displayData();

View File

@ -11,9 +11,9 @@
var pre_audio = { var pre_audio = {
type: 'html-button-response', type: 'html-button-response',
stimulus: 'Recent versions of Chrome require the user to interact with a page before it can play 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. Be aware of this when planning audio experiments if '+ '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.', 'you want the first trial to include audio.</p></div>',
choices: ['Continue'] choices: ['Continue']
} }
@ -25,8 +25,26 @@
prompt: '<p>How funny is the joke?</p>' prompt: '<p>How funny is the joke?</p>'
} }
var trial_2 = {
type: 'audio-slider-response',
stimulus: 'sound/speech_red.mp3',
labels: ['Hate it', 'It&quot;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({ jsPsych.init({
timeline: [pre_audio, trial_1], timeline: [pre_audio, trial_1, trial_2, trial_3],
use_webaudio: false, use_webaudio: false,
on_finish: function() { on_finish: function() {
jsPsych.data.displayData(); jsPsych.data.displayData();

View File

@ -12,10 +12,10 @@
var pre_trial = { var pre_trial = {
type: 'html-button-response', 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>', 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', type: 'video-button-response',
sources: ['video/sample_video.mp4'], sources: ['video/sample_video.mp4'],
choices: ['y','n'], choices: ['y','n'],
@ -34,10 +34,23 @@
response_ends_trial: true 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({ jsPsych.init({
show_preload_progress_bar: true, show_preload_progress_bar: true,
timeline: [pre_trial, trial], timeline: [pre_trial, trial_1, trial_2],
on_finish: function() { jsPsych.data.displayData(); } on_finish: function() { jsPsych.data.displayData(); }
}); });

View File

@ -15,7 +15,7 @@
choices: ['continue'] choices: ['continue']
} }
var trial = { var trial_1 = {
type: 'video-keyboard-response', type: 'video-keyboard-response',
sources: ['video/sample_video.mp4'], sources: ['video/sample_video.mp4'],
choices: ['y','n'], choices: ['y','n'],
@ -32,12 +32,22 @@
response_ends_trial: 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, show_preload_progress_bar: true,
timeline: [pre_trial, trial], timeline: [pre_trial, trial_1, trial_2],
on_finish: function() { jsPsych.data.displayData(); } on_finish: function() { jsPsych.data.displayData(); }
}); });
</script> </script>
</html> </html>

View File

@ -12,10 +12,10 @@
var pre_trial = { var pre_trial = {
type: 'html-button-response', 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>', 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', type: 'video-slider-response',
sources: ['video/sample_video.mp4'], sources: ['video/sample_video.mp4'],
labels: ['1 Star', '2 Stars', '3 Stars', '4 Stars', '5 Stars'], labels: ['1 Star', '2 Stars', '3 Stars', '4 Stars', '5 Stars'],
@ -23,24 +23,32 @@
max: 5, max: 5,
step: 1, step: 1,
slider_start: 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, require_movement: true,
width: 600, width: 600,
//height: 600,
autoplay: true, autoplay: true,
//controls: true,
//start: 8,
//stop: 9,
rate: 1.5, rate: 1.5,
//trial_duration: 2000,
//trial_ends_after_video: true,
response_ends_trial: 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({ jsPsych.init({
show_preload_progress_bar: true, show_preload_progress_bar: true,
timeline: [pre_trial, trial], timeline: [pre_trial, trial_1, trial_2],
on_finish: function() { jsPsych.data.displayData(); } on_finish: function() { jsPsych.data.displayData(); }
}); });

View File

@ -73,6 +73,13 @@ jsPsych.plugins["audio-button-response"] = (function() {
default: false, default: false,
description: 'If true, then the trial will end as soon as the audio file finishes playing.' 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 // set up end event if trial needs it
if(trial.trial_ends_after_audio){ if(trial.trial_ends_after_audio){
if(context !== null){ if(context !== null){
source.onended = function() { source.addEventListener('ended', end_trial);
end_trial();
}
} else { } else {
audio.addEventListener('ended', end_trial); 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 //display buttons
var buttons = []; var buttons = [];
if (Array.isArray(trial.button_html)) { 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 var choice = e.currentTarget.getAttribute('data-choice'); // don't use dataset for jsdom compatibility
after_response(choice); after_response(choice);
}); });
if (!trial.response_allowed_while_playing) {
display_element.querySelector('#jspsych-audio-button-response-button-' + i).querySelector('button').disabled = true;
}
} }
// store response // store response
@ -160,24 +177,26 @@ jsPsych.plugins["audio-button-response"] = (function() {
if (trial.response_ends_trial) { if (trial.response_ends_trial) {
end_trial(); end_trial();
} }
}; }
// function to end trial when it is time // function to end trial when it is time
function end_trial() { function end_trial() {
// kill any remaining setTimeout handlers
jsPsych.pluginAPI.clearAllTimeouts();
// stop the audio file if it is playing // stop the audio file if it is playing
// remove end event listeners if they exist // remove end event listeners if they exist
if(context !== null){ if(context !== null){
source.stop(); source.stop();
source.onended = function() { } source.removeEventListener('ended', end_trial);
source.removeEventListener('ended', enable_buttons);
} else { } else {
audio.pause(); 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 // gather the data to store for the trial
var trial_data = { var trial_data = {
"rt": response.rt, "rt": response.rt,
@ -190,7 +209,15 @@ jsPsych.plugins["audio-button-response"] = (function() {
// move on to the next trial // move on to the next trial
jsPsych.finishTrial(trial_data); 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 // start time
var start_time = performance.now(); var start_time = performance.now();

View File

@ -55,6 +55,13 @@ jsPsych.plugins["audio-keyboard-response"] = (function() {
default: false, default: false,
description: 'If true, then the trial will end as soon as the audio file finishes playing.' 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 // set up end event if trial needs it
if(trial.trial_ends_after_audio){ if(trial.trial_ends_after_audio){
if(context !== null){ if(context !== null){
source.onended = function() { source.addEventListener('ended', end_trial);
end_trial();
}
} else { } else {
audio.addEventListener('ended', end_trial); audio.addEventListener('ended', end_trial);
} }
@ -104,10 +108,12 @@ jsPsych.plugins["audio-keyboard-response"] = (function() {
// remove end event listeners if they exist // remove end event listeners if they exist
if(context !== null){ if(context !== null){
source.stop(); source.stop();
source.onended = function() { } source.removeEventListener('ended', end_trial);
source.removeEventListener('ended', setup_keyboard_listener);
} else { } else {
audio.pause(); audio.pause();
audio.removeEventListener('ended', end_trial); audio.removeEventListener('ended', end_trial);
audio.removeEventListener('ended', setup_keyboard_listener);
} }
// kill keyboard listeners // kill keyboard listeners
@ -128,7 +134,7 @@ jsPsych.plugins["audio-keyboard-response"] = (function() {
// move on to the next trial // move on to the next trial
jsPsych.finishTrial(trial_data); jsPsych.finishTrial(trial_data);
}; }
// function to handle responses by the subject // function to handle responses by the subject
var after_response = function(info) { 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 // start audio
if(context !== null){ if(context !== null){
startTime = context.currentTime; startTime = context.currentTime;
@ -151,25 +180,15 @@ jsPsych.plugins["audio-keyboard-response"] = (function() {
audio.play(); audio.play();
} }
// start the response listener // start keyboard listener when trial starts or sound ends
if(context !== null) { if (trial.response_allowed_while_playing) {
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({ setup_keyboard_listener();
callback_function: after_response, } else if (!trial.trial_ends_after_audio) {
valid_responses: trial.choices, if(context !== null){
rt_method: 'audio', source.addEventListener('ended', setup_keyboard_listener);
persist: false, } else {
allow_held_key: false, audio.addEventListener('ended', setup_keyboard_listener);
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
});
} }
// end trial if time limit is set // end trial if time limit is set

View File

@ -81,6 +81,13 @@ jsPsych.plugins['audio-slider-response'] = (function() {
default: true, default: true,
description: 'If true, trial will end when user makes a response.' 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.'
}
} }
} }
@ -100,22 +107,32 @@ jsPsych.plugins['audio-slider-response'] = (function() {
// set up end event if trial needs it // set up end event if trial needs it
if(trial.trial_ends_after_audio){ if(trial.trial_ends_after_audio){
if(context !== null){ if(context !== null){
source.onended = function() { source.addEventListener('ended', end_trial);
end_trial();
}
} else { } else {
audio.addEventListener('ended', end_trial); 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;">'; 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; '; html += '<div class="jspsych-audio-slider-response-container" style="position:relative; margin: 0 auto 3em auto; ';
if(trial.slider_width !== null){ if(trial.slider_width !== null){
html += 'width:'+trial.slider_width+'px;'; html += 'width:'+trial.slider_width+'px;';
} }
html += '">'; html += '">';
html += '<input type="range" value="'+trial.slider_start+'" min="'+trial.min+'" max="'+trial.max+'" step="'+trial.step+'" style="width: 100%;" id="jspsych-audio-slider-response-response"></input>'; html += '<input type="range" value="'+trial.slider_start+'" min="'+trial.min+'" max="'+trial.max+'" step="'+trial.step+'" style="width: 100%;" id="jspsych-audio-slider-response-response"';
html += '<div>' if (!trial.response_allowed_while_playing) {
html += ' disabled';
}
html += '></input><div>'
for(var j=0; j < trial.labels.length; j++){ for(var j=0; j < trial.labels.length; j++){
var width = 100/(trial.labels.length-1); var width = 100/(trial.labels.length-1);
var left_offset = (j * (100 /(trial.labels.length - 1))) - (width/2); var left_offset = (j * (100 /(trial.labels.length - 1))) - (width/2);
@ -132,7 +149,11 @@ jsPsych.plugins['audio-slider-response'] = (function() {
} }
// add submit button // 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; display_element.innerHTML = html;
@ -141,6 +162,11 @@ jsPsych.plugins['audio-slider-response'] = (function() {
response: null response: null
}; };
if (!trial.response_allowed_while_playing) {
display_element.querySelector('#jspsych-audio-slider-response-response').disabled = true;
display_element.querySelector('#jspsych-audio-slider-response-next').disabled = true;
}
if(trial.require_movement){ if(trial.require_movement){
display_element.querySelector('#jspsych-audio-slider-response-response').addEventListener('click', function(){ display_element.querySelector('#jspsych-audio-slider-response-response').addEventListener('click', function(){
display_element.querySelector('#jspsych-audio-slider-response-next').disabled = false; display_element.querySelector('#jspsych-audio-slider-response-next').disabled = false;
@ -168,14 +194,19 @@ jsPsych.plugins['audio-slider-response'] = (function() {
function end_trial(){ function end_trial(){
// kill any remaining setTimeout handlers
jsPsych.pluginAPI.clearAllTimeouts(); jsPsych.pluginAPI.clearAllTimeouts();
// stop the audio file if it is playing
// remove end event listeners if they exist
if(context !== null){ if(context !== null){
source.stop(); source.stop();
source.onended = function() { } source.removeEventListener('ended', end_trial);
source.removeEventListener('ended', enable_slider);
} else { } else {
audio.pause(); audio.pause();
audio.removeEventListener('ended', end_trial); audio.removeEventListener('ended', end_trial);
audio.removeEventListener('ended', enable_slider);
} }
// save data // save data
@ -192,6 +223,14 @@ jsPsych.plugins['audio-slider-response'] = (function() {
jsPsych.finishTrial(trialdata); 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(); var startTime = performance.now();
// start audio // start audio
if(context !== null){ if(context !== null){

View File

@ -115,6 +115,13 @@ jsPsych.plugins["video-button-response"] = (function() {
pretty_name: 'Response ends trial', pretty_name: 'Response ends trial',
default: true, default: true,
description: 'If true, the trial will end when subject makes a response.' 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.'
} }
} }
} }
@ -199,6 +206,17 @@ jsPsych.plugins["video-button-response"] = (function() {
video_element.src = video_preload_blob; video_element.src = video_preload_blob;
} }
video_element.onended = function(){
if(trial.trial_ends_after_video){
end_trial();
} else if (!trial.response_allowed_while_playing) {
// enable 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; video_element.playbackRate = trial.rate;
// if video start time is specified, hide the video and set the starting time // if video start time is specified, hide the video and set the starting time
@ -223,18 +241,15 @@ jsPsych.plugins["video-button-response"] = (function() {
}) })
} }
video_element.onended = function(){
if(trial.trial_ends_after_video){
end_trial();
}
}
// add event listeners to buttons // add event listeners to buttons
for (var i = 0; i < trial.choices.length; i++) { for (var i = 0; i < trial.choices.length; i++) {
display_element.querySelector('#jspsych-video-button-response-button-' + i).addEventListener('click', function(e){ 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 var choice = e.currentTarget.getAttribute('data-choice'); // don't use dataset for jsdom compatibility
after_response(choice); after_response(choice);
}); });
if (!trial.response_allowed_while_playing) {
display_element.querySelector('#jspsych-video-button-response-button-' + i).querySelector('button').disabled = true;
}
} }
// store response // store response
@ -249,6 +264,11 @@ jsPsych.plugins["video-button-response"] = (function() {
// kill any remaining setTimeout handlers // kill any remaining setTimeout handlers
jsPsych.pluginAPI.clearAllTimeouts(); 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 // gather the data to store for the trial
var trial_data = { var trial_data = {
"rt": response.rt, "rt": response.rt,
@ -261,7 +281,7 @@ jsPsych.plugins["video-button-response"] = (function() {
// move on to the next trial // move on to the next trial
jsPsych.finishTrial(trial_data); jsPsych.finishTrial(trial_data);
}; }
// function to handle responses by the subject // function to handle responses by the subject
function after_response(choice) { function after_response(choice) {
@ -286,7 +306,7 @@ jsPsych.plugins["video-button-response"] = (function() {
if (trial.response_ends_trial) { if (trial.response_ends_trial) {
end_trial(); end_trial();
} }
}; }
// end trial if time limit is set // end trial if time limit is set
if (trial.trial_duration !== null) { if (trial.trial_duration !== null) {

View File

@ -96,6 +96,13 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
pretty_name: 'Response ends trial', pretty_name: 'Response ends trial',
default: true, default: true,
description: 'If true, the trial will end when subject makes a response.' 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.'
} }
} }
} }
@ -158,6 +165,22 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
video_element.src = video_preload_blob; video_element.src = video_preload_blob;
} }
video_element.onended = function(){
if(trial.trial_ends_after_video){
end_trial();
}
if ((trial.response_allowed_while_playing == false) & (!trial.trial_ends_after_video)) {
// start keyboard listener
var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response,
valid_responses: trial.choices,
rt_method: 'performance',
persist: false,
allow_held_key: false,
});
}
}
video_element.playbackRate = trial.rate; video_element.playbackRate = trial.rate;
// if video start time is specified, hide the video and set the starting time // if video start time is specified, hide the video and set the starting time
@ -182,12 +205,6 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
}) })
} }
video_element.onended = function(){
if(trial.trial_ends_after_video){
end_trial();
}
}
// store response // store response
var response = { var response = {
rt: null, rt: null,
@ -202,6 +219,11 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
// kill keyboard listeners // kill keyboard listeners
jsPsych.pluginAPI.cancelAllKeyboardResponses(); 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 // gather the data to store for the trial
var trial_data = { var trial_data = {
@ -215,7 +237,7 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
// move on to the next trial // move on to the next trial
jsPsych.finishTrial(trial_data); jsPsych.finishTrial(trial_data);
}; }
// function to handle responses by the subject // function to handle responses by the subject
var after_response = function(info) { var after_response = function(info) {
@ -235,7 +257,7 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
}; };
// start the response listener // 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({ var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response, callback_function: after_response,
valid_responses: trial.choices, valid_responses: trial.choices,

View File

@ -139,6 +139,13 @@ jsPsych.plugins["video-slider-response"] = (function() {
pretty_name: 'Response ends trial', pretty_name: 'Response ends trial',
default: true, default: true,
description: 'If true, the trial will end when subject makes a response.' 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.'
} }
} }
} }
@ -193,8 +200,11 @@ jsPsych.plugins["video-slider-response"] = (function() {
html += 'width:'+trial.slider_width+'px;'; html += 'width:'+trial.slider_width+'px;';
} }
html += '">'; html += '">';
html += '<input type="range" value="'+trial.slider_start+'" min="'+trial.min+'" max="'+trial.max+'" step="'+trial.step+'" style="width: 100%;" id="jspsych-video-slider-response-response"></input>'; html += '<input type="range" value="'+trial.slider_start+'" min="'+trial.min+'" max="'+trial.max+'" step="'+trial.step+'" style="width: 100%;" id="jspsych-video-slider-response-response"';
html += '<div>' if (!trial.response_allowed_while_playing) {
html += ' disabled';
}
html += '></input><div>'
for(var j=0; j < trial.labels.length; j++){ for(var j=0; j < trial.labels.length; j++){
var width = 100/(trial.labels.length-1); var width = 100/(trial.labels.length-1);
var left_offset = (j * (100 /(trial.labels.length - 1))) - (width/2); var left_offset = (j * (100 /(trial.labels.length - 1))) - (width/2);
@ -212,7 +222,11 @@ jsPsych.plugins["video-slider-response"] = (function() {
} }
// add submit button // 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; display_element.innerHTML = html;
@ -222,6 +236,14 @@ jsPsych.plugins["video-slider-response"] = (function() {
video_element.src = video_preload_blob; video_element.src = video_preload_blob;
} }
video_element.onended = function(){
if(trial.trial_ends_after_video){
end_trial();
} else if (!trial.response_allowed_while_playing) {
enable_slider();
}
}
video_element.playbackRate = trial.rate; video_element.playbackRate = trial.rate;
// if video start time is specified, hide the video and set the starting time // if video start time is specified, hide the video and set the starting time
@ -246,12 +268,6 @@ jsPsych.plugins["video-slider-response"] = (function() {
}) })
} }
video_element.onended = function(){
if(trial.trial_ends_after_video){
end_trial();
}
}
if(trial.require_movement){ if(trial.require_movement){
display_element.querySelector('#jspsych-video-slider-response-response').addEventListener('click', function(){ display_element.querySelector('#jspsych-video-slider-response-response').addEventListener('click', function(){
display_element.querySelector('#jspsych-video-slider-response-next').disabled = false; display_element.querySelector('#jspsych-video-slider-response-next').disabled = false;
@ -286,6 +302,11 @@ jsPsych.plugins["video-slider-response"] = (function() {
// kill any remaining setTimeout handlers // kill any remaining setTimeout handlers
jsPsych.pluginAPI.clearAllTimeouts(); 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 // gather the data to store for the trial
var trial_data = { var trial_data = {
"rt": response.rt, "rt": response.rt,
@ -302,6 +323,14 @@ jsPsych.plugins["video-slider-response"] = (function() {
jsPsych.finishTrial(trial_data); 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 // end trial if time limit is set
if (trial.trial_duration !== null) { if (trial.trial_duration !== null) {
jsPsych.pluginAPI.setTimeout(function() { jsPsych.pluginAPI.setTimeout(function() {