Merge branch 'master' into respond-while-playing

This commit is contained in:
Becky Gilbert 2020-11-04 11:27:50 -08:00 committed by GitHub
commit dbb001c0c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 300 additions and 175 deletions

View File

@ -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:#000000;
}
.jspsych-btn:disabled {

View File

@ -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).

View File

@ -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.

View File

@ -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.

View File

@ -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,

View File

@ -52,7 +52,7 @@ window.jsPsych = (function() {
//
core.init = function(options) {
function init() {
if(typeof options.timeline === 'undefined'){
console.error('No timeline declared in jsPsych.init. Cannot start experiment.')
}
@ -205,6 +205,14 @@ window.jsPsych = (function() {
);
};
// execute init() when the document is ready
if (document.readyState === "complete") {
init();
} else {
window.addEventListener("load", init);
}
}
core.progress = function() {
var percent_complete = typeof timeline == 'undefined' ? 0 : timeline.percentComplete();
@ -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;

View File

@ -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');

View File

@ -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();
}
@ -169,21 +181,30 @@ 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,

View File

@ -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;

View 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("");
});
});