mirror of
https://github.com/jspsych/jsPsych.git
synced 2025-05-10 19:20:55 +00:00
Merge branch 'master' into respond-while-playing
This commit is contained in:
commit
dbb001c0c7
@ -76,9 +76,17 @@
|
||||
border-color: #ccc;
|
||||
}
|
||||
|
||||
.jspsych-btn:hover {
|
||||
/* only apply the hover style on devices with a mouse/pointer that can hover - issue #977 */
|
||||
@media (hover: hover) {
|
||||
.jspsych-btn:hover {
|
||||
background-color: #ddd;
|
||||
border-color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
.jspsych-btn:active {
|
||||
background-color: #ddd;
|
||||
border-color: #aaa;
|
||||
border-color:#000000;
|
||||
}
|
||||
|
||||
.jspsych-btn:disabled {
|
||||
|
@ -8,7 +8,7 @@ Parameters with a default value of *undefined* must be specified. Other paramete
|
||||
|
||||
Parameter | Type | Default Value | Description
|
||||
----------|------|---------------|------------
|
||||
sources | array | *undefined* | An array of file paths to the video. You can specify multiple formats of the same video (e.g., .mp4, .ogg, .webm) to maximize the [cross-browser compatibility](https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats). Usually .mp4 is a safe cross-browser option. The player will use the first source file in the array that is compatible with the browser, so specify the files in order of preference.
|
||||
sources | array | *undefined* | An array of file paths to the video. You can specify multiple formats of the same video (e.g., .mp4, .ogg, .webm) to maximize the [cross-browser compatibility](https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats). Usually .mp4 is a safe cross-browser option. The plugin does not reliably support .mov files. The player will use the first source file in the array that is compatible with the browser, so specify the files in order of preference.
|
||||
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.
|
||||
margin_vertical | string | '0px' | Vertical margin of the button(s).
|
||||
|
@ -8,7 +8,7 @@ Parameters with a default value of *undefined* must be specified. Other paramete
|
||||
|
||||
Parameter | Type | Default Value | Description
|
||||
----------|------|---------------|------------
|
||||
sources | array | *undefined* | An array of file paths to the video. You can specify multiple formats of the same video (e.g., .mp4, .ogg, .webm) to maximize the [cross-browser compatibility](https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats). Usually .mp4 is a safe cross-browser option. The player will use the first source file in the array that is compatible with the browser, so specify the files in order of preference.
|
||||
sources | array | *undefined* | An array of file paths to the video. You can specify multiple formats of the same video (e.g., .mp4, .ogg, .webm) to maximize the [cross-browser compatibility](https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats). Usually .mp4 is a safe cross-browser option. The plugin does not reliably support .mov files. The player will use the first source file in the array that is compatible with the browser, so specify the files in order of preference.
|
||||
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).
|
||||
width | numeric | width of the video file | The width of the video display in pixels.
|
||||
height | numeric | heigh of the video file | The height of the video display in pixels.
|
||||
|
@ -8,7 +8,7 @@ Parameters with a default value of *undefined* must be specified. Other paramete
|
||||
|
||||
Parameter | Type | Default Value | Description
|
||||
----------|------|---------------|------------
|
||||
sources | array | *undefined* | An array of file paths to the video. You can specify multiple formats of the same video (e.g., .mp4, .ogg, .webm) to maximize the [cross-browser compatibility](https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats). Usually .mp4 is a safe cross-browser option. The player will use the first source file in the array that is compatible with the browser, so specify the files in order of preference.
|
||||
sources | array | *undefined* | An array of file paths to the video. You can specify multiple formats of the same video (e.g., .mp4, .ogg, .webm) to maximize the [cross-browser compatibility](https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats). Usually .mp4 is a safe cross-browser option. The plugin does not reliably support .mov files. The player will use the first source file in the array that is compatible with the browser, so specify the files in order of preference.
|
||||
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).
|
||||
width | numeric | width of the video file | The width of the video display in pixels.
|
||||
height | numeric | heigh of the video file | The height of the video display in pixels.
|
||||
|
@ -24,8 +24,8 @@
|
||||
//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,
|
||||
|
300
jspsych.js
300
jspsych.js
@ -52,158 +52,166 @@ window.jsPsych = (function() {
|
||||
//
|
||||
|
||||
core.init = function(options) {
|
||||
|
||||
if(typeof options.timeline === 'undefined'){
|
||||
console.error('No timeline declared in jsPsych.init. Cannot start experiment.')
|
||||
}
|
||||
|
||||
if(options.timeline.length == 0){
|
||||
console.error('No trials have been added to the timeline (the timeline is an empty array). Cannot start experiment.')
|
||||
}
|
||||
|
||||
// reset variables
|
||||
timeline = null;
|
||||
global_trial_index = 0;
|
||||
current_trial = {};
|
||||
current_trial_finished = false;
|
||||
paused = false;
|
||||
waiting = false;
|
||||
loaded = false;
|
||||
loadfail = false;
|
||||
jsPsych.data.reset();
|
||||
|
||||
var defaults = {
|
||||
'display_element': undefined,
|
||||
'on_finish': function(data) {
|
||||
return undefined;
|
||||
},
|
||||
'on_trial_start': function(trial) {
|
||||
return undefined;
|
||||
},
|
||||
'on_trial_finish': function() {
|
||||
return undefined;
|
||||
},
|
||||
'on_data_update': function(data) {
|
||||
return undefined;
|
||||
},
|
||||
'on_interaction_data_update': function(data){
|
||||
return undefined;
|
||||
},
|
||||
'on_close': function(){
|
||||
return undefined;
|
||||
},
|
||||
'preload_images': [],
|
||||
'preload_audio': [],
|
||||
'preload_video': [],
|
||||
'use_webaudio': true,
|
||||
'exclusions': {},
|
||||
'show_progress_bar': false,
|
||||
'message_progress_bar': 'Completion Progress',
|
||||
'auto_update_progress_bar': true,
|
||||
'auto_preload': true,
|
||||
'show_preload_progress_bar': true,
|
||||
'max_load_time': 60000,
|
||||
'max_preload_attempts': 10,
|
||||
'default_iti': 0,
|
||||
'minimum_valid_rt': 0,
|
||||
'experiment_width': null
|
||||
};
|
||||
|
||||
// override default options if user specifies an option
|
||||
opts = Object.assign({}, defaults, options);
|
||||
|
||||
// set DOM element where jsPsych will render content
|
||||
// if undefined, then jsPsych will use the <body> tag and the entire page
|
||||
if(typeof opts.display_element == 'undefined'){
|
||||
// check if there is a body element on the page
|
||||
var body = document.querySelector('body');
|
||||
if (body === null) {
|
||||
document.documentElement.appendChild(document.createElement('body'));
|
||||
function init() {
|
||||
if(typeof options.timeline === 'undefined'){
|
||||
console.error('No timeline declared in jsPsych.init. Cannot start experiment.')
|
||||
}
|
||||
// using the full page, so we need the HTML element to
|
||||
// have 100% height, and body to be full width and height with
|
||||
// no margin
|
||||
document.querySelector('html').style.height = '100%';
|
||||
document.querySelector('body').style.margin = '0px';
|
||||
document.querySelector('body').style.height = '100%';
|
||||
document.querySelector('body').style.width = '100%';
|
||||
opts.display_element = document.querySelector('body');
|
||||
} else {
|
||||
// make sure that the display element exists on the page
|
||||
var display;
|
||||
if (opts.display_element instanceof Element) {
|
||||
var display = opts.display_element;
|
||||
|
||||
if(options.timeline.length == 0){
|
||||
console.error('No trials have been added to the timeline (the timeline is an empty array). Cannot start experiment.')
|
||||
}
|
||||
|
||||
// reset variables
|
||||
timeline = null;
|
||||
global_trial_index = 0;
|
||||
current_trial = {};
|
||||
current_trial_finished = false;
|
||||
paused = false;
|
||||
waiting = false;
|
||||
loaded = false;
|
||||
loadfail = false;
|
||||
jsPsych.data.reset();
|
||||
|
||||
var defaults = {
|
||||
'display_element': undefined,
|
||||
'on_finish': function(data) {
|
||||
return undefined;
|
||||
},
|
||||
'on_trial_start': function(trial) {
|
||||
return undefined;
|
||||
},
|
||||
'on_trial_finish': function() {
|
||||
return undefined;
|
||||
},
|
||||
'on_data_update': function(data) {
|
||||
return undefined;
|
||||
},
|
||||
'on_interaction_data_update': function(data){
|
||||
return undefined;
|
||||
},
|
||||
'on_close': function(){
|
||||
return undefined;
|
||||
},
|
||||
'preload_images': [],
|
||||
'preload_audio': [],
|
||||
'preload_video': [],
|
||||
'use_webaudio': true,
|
||||
'exclusions': {},
|
||||
'show_progress_bar': false,
|
||||
'message_progress_bar': 'Completion Progress',
|
||||
'auto_update_progress_bar': true,
|
||||
'auto_preload': true,
|
||||
'show_preload_progress_bar': true,
|
||||
'max_load_time': 60000,
|
||||
'max_preload_attempts': 10,
|
||||
'default_iti': 0,
|
||||
'minimum_valid_rt': 0,
|
||||
'experiment_width': null
|
||||
};
|
||||
|
||||
// override default options if user specifies an option
|
||||
opts = Object.assign({}, defaults, options);
|
||||
|
||||
// set DOM element where jsPsych will render content
|
||||
// if undefined, then jsPsych will use the <body> tag and the entire page
|
||||
if(typeof opts.display_element == 'undefined'){
|
||||
// check if there is a body element on the page
|
||||
var body = document.querySelector('body');
|
||||
if (body === null) {
|
||||
document.documentElement.appendChild(document.createElement('body'));
|
||||
}
|
||||
// using the full page, so we need the HTML element to
|
||||
// have 100% height, and body to be full width and height with
|
||||
// no margin
|
||||
document.querySelector('html').style.height = '100%';
|
||||
document.querySelector('body').style.margin = '0px';
|
||||
document.querySelector('body').style.height = '100%';
|
||||
document.querySelector('body').style.width = '100%';
|
||||
opts.display_element = document.querySelector('body');
|
||||
} else {
|
||||
var display = document.querySelector('#' + opts.display_element);
|
||||
// make sure that the display element exists on the page
|
||||
var display;
|
||||
if (opts.display_element instanceof Element) {
|
||||
var display = opts.display_element;
|
||||
} else {
|
||||
var display = document.querySelector('#' + opts.display_element);
|
||||
}
|
||||
if(display === null) {
|
||||
console.error('The display_element specified in jsPsych.init() does not exist in the DOM.');
|
||||
} else {
|
||||
opts.display_element = display;
|
||||
}
|
||||
}
|
||||
if(display === null) {
|
||||
console.error('The display_element specified in jsPsych.init() does not exist in the DOM.');
|
||||
} else {
|
||||
opts.display_element = display;
|
||||
}
|
||||
}
|
||||
opts.display_element.innerHTML = '<div class="jspsych-content-wrapper"><div id="jspsych-content"></div></div>';
|
||||
DOM_container = opts.display_element;
|
||||
DOM_target = document.querySelector('#jspsych-content');
|
||||
opts.display_element.innerHTML = '<div class="jspsych-content-wrapper"><div id="jspsych-content"></div></div>';
|
||||
DOM_container = opts.display_element;
|
||||
DOM_target = document.querySelector('#jspsych-content');
|
||||
|
||||
|
||||
// add tabIndex attribute to scope event listeners
|
||||
opts.display_element.tabIndex = 0;
|
||||
|
||||
// add CSS class to DOM_target
|
||||
if(opts.display_element.className.indexOf('jspsych-display-element') == -1){
|
||||
opts.display_element.className += ' jspsych-display-element';
|
||||
}
|
||||
DOM_target.className += 'jspsych-content';
|
||||
|
||||
// set experiment_width if not null
|
||||
if(opts.experiment_width !== null){
|
||||
DOM_target.style.width = opts.experiment_width + "px";
|
||||
}
|
||||
|
||||
// create experiment timeline
|
||||
timeline = new TimelineNode({
|
||||
timeline: opts.timeline
|
||||
});
|
||||
|
||||
// initialize audio context based on options and browser capabilities
|
||||
jsPsych.pluginAPI.initAudio();
|
||||
|
||||
// below code resets event listeners that may have lingered from
|
||||
// a previous incomplete experiment loaded in same DOM.
|
||||
jsPsych.pluginAPI.reset(opts.display_element);
|
||||
// create keyboard event listeners
|
||||
jsPsych.pluginAPI.createKeyboardEventListeners(opts.display_element);
|
||||
// create listeners for user browser interaction
|
||||
jsPsych.data.createInteractionListeners();
|
||||
|
||||
// add event for closing window
|
||||
window.addEventListener('beforeunload', opts.on_close);
|
||||
|
||||
// check exclusions before continuing
|
||||
checkExclusions(opts.exclusions,
|
||||
function(){
|
||||
// success! user can continue...
|
||||
// start experiment, with or without preloading
|
||||
if(opts.auto_preload){
|
||||
jsPsych.pluginAPI.autoPreload(timeline, startExperiment, opts.preload_images, opts.preload_audio, opts.preload_video, opts.show_preload_progress_bar);
|
||||
if(opts.max_load_time > 0){
|
||||
setTimeout(function(){
|
||||
if(!loaded && !loadfail){
|
||||
core.loadFail();
|
||||
}
|
||||
}, opts.max_load_time);
|
||||
}
|
||||
} else {
|
||||
startExperiment();
|
||||
}
|
||||
},
|
||||
function(){
|
||||
// fail. incompatible user.
|
||||
// add tabIndex attribute to scope event listeners
|
||||
opts.display_element.tabIndex = 0;
|
||||
|
||||
// add CSS class to DOM_target
|
||||
if(opts.display_element.className.indexOf('jspsych-display-element') == -1){
|
||||
opts.display_element.className += ' jspsych-display-element';
|
||||
}
|
||||
);
|
||||
};
|
||||
DOM_target.className += 'jspsych-content';
|
||||
|
||||
// set experiment_width if not null
|
||||
if(opts.experiment_width !== null){
|
||||
DOM_target.style.width = opts.experiment_width + "px";
|
||||
}
|
||||
|
||||
// create experiment timeline
|
||||
timeline = new TimelineNode({
|
||||
timeline: opts.timeline
|
||||
});
|
||||
|
||||
// initialize audio context based on options and browser capabilities
|
||||
jsPsych.pluginAPI.initAudio();
|
||||
|
||||
// below code resets event listeners that may have lingered from
|
||||
// a previous incomplete experiment loaded in same DOM.
|
||||
jsPsych.pluginAPI.reset(opts.display_element);
|
||||
// create keyboard event listeners
|
||||
jsPsych.pluginAPI.createKeyboardEventListeners(opts.display_element);
|
||||
// create listeners for user browser interaction
|
||||
jsPsych.data.createInteractionListeners();
|
||||
|
||||
// add event for closing window
|
||||
window.addEventListener('beforeunload', opts.on_close);
|
||||
|
||||
// check exclusions before continuing
|
||||
checkExclusions(opts.exclusions,
|
||||
function(){
|
||||
// success! user can continue...
|
||||
// start experiment, with or without preloading
|
||||
if(opts.auto_preload){
|
||||
jsPsych.pluginAPI.autoPreload(timeline, startExperiment, opts.preload_images, opts.preload_audio, opts.preload_video, opts.show_preload_progress_bar);
|
||||
if(opts.max_load_time > 0){
|
||||
setTimeout(function(){
|
||||
if(!loaded && !loadfail){
|
||||
core.loadFail();
|
||||
}
|
||||
}, opts.max_load_time);
|
||||
}
|
||||
} else {
|
||||
startExperiment();
|
||||
}
|
||||
},
|
||||
function(){
|
||||
// fail. incompatible user.
|
||||
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// execute init() when the document is ready
|
||||
if (document.readyState === "complete") {
|
||||
init();
|
||||
} else {
|
||||
window.addEventListener("load", init);
|
||||
}
|
||||
}
|
||||
|
||||
core.progress = function() {
|
||||
|
||||
@ -2600,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;
|
||||
|
@ -138,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]);
|
||||
@ -155,6 +162,9 @@ jsPsych.plugins["video-button-response"] = (function() {
|
||||
}
|
||||
var type = file_name.substr(file_name.lastIndexOf('.') + 1);
|
||||
type = type.toLowerCase();
|
||||
if (type == "mov") {
|
||||
console.warn('Warning: video-button-response plugin does not reliably support .mov files.')
|
||||
}
|
||||
video_html+='<source src="' + file_name + '" type="video/'+type+'">';
|
||||
}
|
||||
}
|
||||
@ -190,11 +200,13 @@ 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) {
|
||||
@ -205,21 +217,30 @@ jsPsych.plugins["video-button-response"] = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
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){
|
||||
@ -273,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');
|
||||
|
@ -119,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]);
|
||||
@ -136,6 +143,9 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
|
||||
}
|
||||
var type = file_name.substr(file_name.lastIndexOf('.') + 1);
|
||||
type = type.toLowerCase();
|
||||
if (type == "mov") {
|
||||
console.warn('Warning: video-keyboard-response plugin does not reliably support .mov files.')
|
||||
}
|
||||
video_html+='<source src="' + file_name + '" type="video/'+type+'">';
|
||||
}
|
||||
}
|
||||
@ -149,11 +159,13 @@ 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();
|
||||
}
|
||||
@ -168,22 +180,31 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -161,12 +161,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]);
|
||||
@ -178,6 +185,9 @@ jsPsych.plugins["video-slider-response"] = (function() {
|
||||
}
|
||||
var type = file_name.substr(file_name.lastIndexOf('.') + 1);
|
||||
type = type.toLowerCase();
|
||||
if (type == "mov") {
|
||||
console.warn('Warning: video-slider-response plugin does not reliably support .mov files.')
|
||||
}
|
||||
video_html+='<source src="' + file_name + '" type="video/'+type+'">';
|
||||
}
|
||||
}
|
||||
@ -234,8 +244,19 @@ jsPsych.plugins["video-slider-response"] = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
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){
|
||||
@ -247,8 +268,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;
|
||||
|
48
tests/jsPsych/init.test.js
Normal file
48
tests/jsPsych/init.test.js
Normal file
@ -0,0 +1,48 @@
|
||||
require("../../jspsych");
|
||||
require("../../plugins/jspsych-html-keyboard-response");
|
||||
|
||||
describe("jsPsych init", () => {
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
function setReadyState(targetState) {
|
||||
jest
|
||||
.spyOn(document, "readyState", "get")
|
||||
.mockImplementation(() => targetState);
|
||||
}
|
||||
|
||||
function getBodyHTML() {
|
||||
return document.body.innerHTML;
|
||||
}
|
||||
|
||||
function init() {
|
||||
jsPsych.init({
|
||||
timeline: [
|
||||
{
|
||||
type: "html-keyboard-response",
|
||||
stimulus: "foo",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
it("should delay execution until the document is ready", () => {
|
||||
expect(getBodyHTML()).toBe("");
|
||||
|
||||
setReadyState("loading");
|
||||
init();
|
||||
expect(getBodyHTML()).toBe("");
|
||||
|
||||
// Simulate the document getting ready
|
||||
setReadyState("complete");
|
||||
window.dispatchEvent(new Event("load"));
|
||||
expect(getBodyHTML()).not.toBe("");
|
||||
});
|
||||
|
||||
it("should execute immediately when the document is ready", () => {
|
||||
// The document is ready by default in jsdom
|
||||
init();
|
||||
expect(getBodyHTML()).not.toBe("");
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user