+
+
+
+
+
+
+
+
diff --git a/examples/jspsych-video-keyboard-response.html b/examples/jspsych-video-keyboard-response.html
new file mode 100644
index 00000000..cb2840b9
--- /dev/null
+++ b/examples/jspsych-video-keyboard-response.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/jspsych-video-slider-response.html b/examples/jspsych-video-slider-response.html
new file mode 100644
index 00000000..ef0521cb
--- /dev/null
+++ b/examples/jspsych-video-slider-response.html
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/jspsych-video-button-response.js b/plugins/jspsych-video-button-response.js
new file mode 100644
index 00000000..b34447cb
--- /dev/null
+++ b/plugins/jspsych-video-button-response.js
@@ -0,0 +1,279 @@
+/**
+ * jspsych-video-button-response
+ * Josh de Leeuw
+ *
+ * plugin for playing a video file and getting a button response
+ *
+ * documentation: docs.jspsych.org
+ *
+ **/
+
+jsPsych.plugins["video-button-response"] = (function() {
+
+ var plugin = {};
+
+ jsPsych.pluginAPI.registerPreload('video-button-response', 'stimulus', 'video');
+
+ plugin.info = {
+ name: 'video-button-response',
+ description: '',
+ parameters: {
+ sources: {
+ type: jsPsych.plugins.parameterType.VIDEO,
+ pretty_name: 'Video',
+ default: undefined,
+ description: 'The video file to play.'
+ },
+ choices: {
+ type: jsPsych.plugins.parameterType.STRING,
+ pretty_name: 'Choices',
+ default: undefined,
+ array: true,
+ description: 'The labels for the buttons.'
+ },
+ button_html: {
+ type: jsPsych.plugins.parameterType.STRING,
+ pretty_name: 'Button HTML',
+ default: '',
+ array: true,
+ description: 'The html of the button. Can create own style.'
+ },
+ prompt: {
+ type: jsPsych.plugins.parameterType.STRING,
+ pretty_name: 'Prompt',
+ default: null,
+ description: 'Any content here will be displayed below the buttons.'
+ },
+ width: {
+ type: jsPsych.plugins.parameterType.INT,
+ pretty_name: 'Width',
+ default: '',
+ description: 'The width of the video in pixels.'
+ },
+ height: {
+ type: jsPsych.plugins.parameterType.INT,
+ pretty_name: 'Height',
+ default: '',
+ description: 'The height of the video display in pixels.'
+ },
+ autoplay: {
+ type: jsPsych.plugins.parameterType.BOOL,
+ pretty_name: 'Autoplay',
+ default: true,
+ description: 'If true, the video will begin playing as soon as it has loaded.'
+ },
+ controls: {
+ type: jsPsych.plugins.parameterType.BOOL,
+ pretty_name: 'Controls',
+ default: false,
+ description: 'If true, the subject will be able to pause the video or move the playback to any point in the video.'
+ },
+ start: {
+ type: jsPsych.plugins.parameterType.FLOAT,
+ pretty_name: 'Start',
+ default: null,
+ description: 'Time to start the clip.'
+ },
+ stop: {
+ type: jsPsych.plugins.parameterType.FLOAT,
+ pretty_name: 'Stop',
+ default: null,
+ description: 'Time to stop the clip.'
+ },
+ rate: {
+ type: jsPsych.plugins.parameterType.FLOAT,
+ pretty_name: 'Rate',
+ default: 1,
+ description: 'The playback rate of the video. 1 is normal, <1 is slower, >1 is faster.'
+ },
+ trial_ends_after_video: {
+ type: jsPsych.plugins.parameterType.BOOL,
+ pretty_name: 'End trial after video finishes',
+ default: false,
+ description: 'If true, the trial will end immediately after the video finishes playing.'
+ },
+ trial_duration: {
+ type: jsPsych.plugins.parameterType.INT,
+ pretty_name: 'Trial duration',
+ default: null,
+ description: 'How long to show trial before it ends.'
+ },
+ margin_vertical: {
+ type: jsPsych.plugins.parameterType.STRING,
+ pretty_name: 'Margin vertical',
+ default: '0px',
+ description: 'The vertical margin of the button.'
+ },
+ margin_horizontal: {
+ type: jsPsych.plugins.parameterType.STRING,
+ pretty_name: 'Margin horizontal',
+ default: '8px',
+ description: 'The horizontal margin of the button.'
+ },
+ response_ends_trial: {
+ type: jsPsych.plugins.parameterType.BOOL,
+ pretty_name: 'Response ends trial',
+ default: true,
+ description: 'If true, the trial will end when subject makes a response.'
+ }
+ }
+ }
+
+ plugin.trial = function(display_element, trial) {
+
+ // setup stimulus
+ var video_html = '
'
+ video_html += '";
+ video_html += "
";
+
+ //display buttons
+ var buttons = [];
+ if (Array.isArray(trial.button_html)) {
+ if (trial.button_html.length == trial.choices.length) {
+ buttons = trial.button_html;
+ } else {
+ console.error('Error in video-button-response plugin. The length of the button_html array does not equal the length of the choices array');
+ }
+ } else {
+ for (var i = 0; i < trial.choices.length; i++) {
+ buttons.push(trial.button_html);
+ }
+ }
+ video_html += '
';
+ for (var i = 0; i < trial.choices.length; i++) {
+ var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
+ video_html += '
'+str+'
';
+ }
+ video_html += '
';
+
+ // add prompt if there is one
+ if (trial.prompt !== null) {
+ video_html += trial.prompt;
+ }
+
+ display_element.innerHTML = video_html;
+
+ var start_time = performance.now();
+
+ if(video_preload_blob){
+ display_element.querySelector('#jspsych-video-button-response-stimulus').src = video_preload_blob;
+ }
+
+ display_element.querySelector('#jspsych-video-button-response-stimulus').onended = function(){
+ if(trial.trial_ends_after_video){
+ end_trial();
+ }
+ }
+
+ if(trial.start !== null){
+ display_element.querySelector('#jspsych-video-button-response-stimulus').currentTime = trial.start;
+ }
+
+ 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;
+ if(currenttime >= trial.stop){
+ display_element.querySelector('#jspsych-video-button-response-stimulus').pause();
+ }
+ })
+ }
+
+ display_element.querySelector('#jspsych-video-button-response-stimulus').playbackRate = trial.rate;
+
+ // add event listeners to buttons
+ for (var i = 0; i < trial.choices.length; i++) {
+ display_element.querySelector('#jspsych-video-button-response-button-' + i).addEventListener('click', function(e){
+ var choice = e.currentTarget.getAttribute('data-choice'); // don't use dataset for jsdom compatibility
+ after_response(choice);
+ });
+ }
+
+ // store response
+ var response = {
+ rt: null,
+ button: null
+ };
+
+ // function to end trial when it is time
+ function end_trial() {
+
+ // kill any remaining setTimeout handlers
+ jsPsych.pluginAPI.clearAllTimeouts();
+
+ // gather the data to store for the trial
+ var trial_data = {
+ "rt": response.rt,
+ "stimulus": trial.stimulus,
+ "button_pressed": response.button
+ };
+
+ // clear the display
+ display_element.innerHTML = '';
+
+ // move on to the next trial
+ jsPsych.finishTrial(trial_data);
+ };
+
+ // function to handle responses by the subject
+ function after_response(choice) {
+
+ // measure rt
+ var end_time = performance.now();
+ var rt = end_time - start_time;
+ response.button = choice;
+ response.rt = rt;
+
+ // after a valid response, the stimulus will have the CSS class 'responded'
+ // which can be used to provide visual feedback that a response was recorded
+ display_element.querySelector('#jspsych-video-button-response-stimulus').className += ' responded';
+
+ // disable all the buttons after a response
+ var btns = document.querySelectorAll('.jspsych-video-button-response-button button');
+ for(var i=0; i1 is faster.'
+ },
+ trial_ends_after_video: {
+ type: jsPsych.plugins.parameterType.BOOL,
+ pretty_name: 'End trial after video finishes',
+ default: false,
+ description: 'If true, the trial will end immediately after the video finishes playing.'
+ },
trial_duration: {
type: jsPsych.plugins.parameterType.INT,
pretty_name: 'Trial duration',
@@ -79,7 +103,8 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
plugin.trial = function(display_element, trial) {
// setup stimulus
- var video_html = '";
+ video_html += "";
// add prompt if there is one
if (trial.prompt !== null) {
@@ -118,6 +146,27 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
display_element.querySelector('#jspsych-video-keyboard-response-stimulus').src = video_preload_blob;
}
+ display_element.querySelector('#jspsych-video-keyboard-response-stimulus').onended = function(){
+ if(trial.trial_ends_after_video){
+ end_trial();
+ }
+ }
+
+ if(trial.start !== null){
+ display_element.querySelector('#jspsych-video-keyboard-response-stimulus').currentTime = trial.start;
+ }
+
+ 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;
+ if(currenttime >= trial.stop){
+ display_element.querySelector('#jspsych-video-keyboard-response-stimulus').pause();
+ }
+ })
+ }
+
+ display_element.querySelector('#jspsych-video-keyboard-response-stimulus').playbackRate = trial.rate;
+
// store response
var response = {
rt: null,
@@ -134,9 +183,6 @@ jsPsych.plugins["video-keyboard-response"] = (function() {
jsPsych.pluginAPI.cancelAllKeyboardResponses();
// gather the data to store for the trial
- if(response.rt !== null){
- response.rt = Math.round(response.rt * 1000);
- }
var trial_data = {
"rt": response.rt,
"stimulus": trial.stimulus,
diff --git a/plugins/jspsych-video-slider-response.js b/plugins/jspsych-video-slider-response.js
new file mode 100644
index 00000000..067251d5
--- /dev/null
+++ b/plugins/jspsych-video-slider-response.js
@@ -0,0 +1,291 @@
+/**
+ * jspsych-video-slider-response
+ * Josh de Leeuw
+ *
+ * plugin for playing a video file and getting a slider response
+ *
+ * documentation: docs.jspsych.org
+ *
+ **/
+
+jsPsych.plugins["video-slider-response"] = (function() {
+
+ var plugin = {};
+
+ jsPsych.pluginAPI.registerPreload('video-slider-response', 'stimulus', 'video');
+
+ plugin.info = {
+ name: 'video-slider-response',
+ description: '',
+ parameters: {
+ sources: {
+ type: jsPsych.plugins.parameterType.VIDEO,
+ pretty_name: 'Video',
+ default: undefined,
+ description: 'The video file to play.'
+ },
+ prompt: {
+ type: jsPsych.plugins.parameterType.STRING,
+ pretty_name: 'Prompt',
+ default: null,
+ description: 'Any content here will be displayed below the stimulus.'
+ },
+ width: {
+ type: jsPsych.plugins.parameterType.INT,
+ pretty_name: 'Width',
+ default: '',
+ description: 'The width of the video in pixels.'
+ },
+ height: {
+ type: jsPsych.plugins.parameterType.INT,
+ pretty_name: 'Height',
+ default: '',
+ description: 'The height of the video display in pixels.'
+ },
+ autoplay: {
+ type: jsPsych.plugins.parameterType.BOOL,
+ pretty_name: 'Autoplay',
+ default: true,
+ description: 'If true, the video will begin playing as soon as it has loaded.'
+ },
+ controls: {
+ type: jsPsych.plugins.parameterType.BOOL,
+ pretty_name: 'Controls',
+ default: false,
+ description: 'If true, the subject will be able to pause the video or move the playback to any point in the video.'
+ },
+ start: {
+ type: jsPsych.plugins.parameterType.FLOAT,
+ pretty_name: 'Start',
+ default: null,
+ description: 'Time to start the clip.'
+ },
+ stop: {
+ type: jsPsych.plugins.parameterType.FLOAT,
+ pretty_name: 'Stop',
+ default: null,
+ description: 'Time to stop the clip.'
+ },
+ rate: {
+ type: jsPsych.plugins.parameterType.FLOAT,
+ pretty_name: 'Rate',
+ default: 1,
+ description: 'The playback rate of the video. 1 is normal, <1 is slower, >1 is faster.'
+ },
+ min: {
+ type: jsPsych.plugins.parameterType.INT,
+ pretty_name: 'Min slider',
+ default: 0,
+ description: 'Sets the minimum value of the slider.'
+ },
+ max: {
+ type: jsPsych.plugins.parameterType.INT,
+ pretty_name: 'Max slider',
+ default: 100,
+ description: 'Sets the maximum value of the slider',
+ },
+ slider_start: {
+ type: jsPsych.plugins.parameterType.INT,
+ pretty_name: 'Slider starting value',
+ default: 50,
+ description: 'Sets the starting value of the slider',
+ },
+ step: {
+ type: jsPsych.plugins.parameterType.INT,
+ pretty_name: 'Step',
+ default: 1,
+ description: 'Sets the step of the slider'
+ },
+ labels: {
+ type: jsPsych.plugins.parameterType.HTML_STRING,
+ pretty_name:'Labels',
+ default: [],
+ array: true,
+ description: 'Labels of the slider.',
+ },
+ slider_width: {
+ type: jsPsych.plugins.parameterType.INT,
+ pretty_name:'Slider width',
+ default: null,
+ description: 'Width of the slider in pixels.'
+ },
+ button_label: {
+ type: jsPsych.plugins.parameterType.STRING,
+ pretty_name: 'Button label',
+ default: 'Continue',
+ array: false,
+ description: 'Label of the button to advance.'
+ },
+ require_movement: {
+ type: jsPsych.plugins.parameterType.BOOL,
+ pretty_name: 'Require movement',
+ default: false,
+ description: 'If true, the participant will have to move the slider before continuing.'
+ },
+ trial_ends_after_video: {
+ type: jsPsych.plugins.parameterType.BOOL,
+ pretty_name: 'End trial after video finishes',
+ default: false,
+ description: 'If true, the trial will end immediately after the video finishes playing.'
+ },
+ trial_duration: {
+ type: jsPsych.plugins.parameterType.INT,
+ pretty_name: 'Trial duration',
+ default: null,
+ description: 'How long to show trial before it ends.'
+ },
+ response_ends_trial: {
+ type: jsPsych.plugins.parameterType.BOOL,
+ pretty_name: 'Response ends trial',
+ default: true,
+ description: 'If true, the trial will end when subject makes a response.'
+ }
+ }
+ }
+
+ plugin.trial = function(display_element, trial) {
+
+ // setup stimulus
+ var video_html = '";
+
+ var html = '
';
+ html += '
' + video_html + '
';
+ html += '
';
+ html += '';
+ html += '
'
+ for(var j=0; j < trial.labels.length; j++){
+ var width = 100/(trial.labels.length-1);
+ var left_offset = (j * (100 /(trial.labels.length - 1))) - (width/2);
+ html += '
';
+ html += ''+trial.labels[j]+'';
+ html += '
'
+ }
+ html += '
';
+ html += '
';
+ html += '
';
+
+ // add prompt if there is one
+ if (trial.prompt !== null) {
+ html += '
'+trial.prompt+'
';
+ }
+
+ // add submit button
+ html += '';
+
+ display_element.innerHTML = html;
+
+ if(video_preload_blob){
+ display_element.querySelector('#jspsych-video-slider-response-stimulus').src = video_preload_blob;
+ }
+
+ display_element.querySelector('#jspsych-video-slider-response-stimulus').onended = function(){
+ if(trial.trial_ends_after_video){
+ end_trial();
+ }
+ }
+
+ if(trial.start !== null){
+ display_element.querySelector('#jspsych-video-slider-response-stimulus').currentTime = trial.start;
+ }
+
+ if(trial.stop !== null){
+ display_element.querySelector('#jspsych-video-slider-response-stimulus').addEventListener('timeupdate', function(e){
+ var currenttime = display_element.querySelector('#jspsych-video-slider-response-stimulus').currentTime;
+ if(currenttime >= trial.stop){
+ display_element.querySelector('#jspsych-video-slider-response-stimulus').pause();
+ }
+ })
+ }
+
+ display_element.querySelector('#jspsych-video-slider-response-stimulus').playbackRate = trial.rate;
+
+ if(trial.require_movement){
+ display_element.querySelector('#jspsych-video-slider-response-response').addEventListener('change', function(){
+ display_element.querySelector('#jspsych-video-slider-response-next').disabled = false;
+ })
+ }
+
+ var startTime = performance.now();
+
+ // store response
+ var response = {
+ rt: null,
+ response: null
+ };
+
+ display_element.querySelector('#jspsych-video-slider-response-next').addEventListener('click', function() {
+ // measure response time
+ var endTime = performance.now();
+ response.rt = endTime - startTime;
+ response.response = display_element.querySelector('#jspsych-video-slider-response-response').value;
+
+ if(trial.response_ends_trial){
+ end_trial();
+ } else {
+ display_element.querySelector('#jspsych-video-slider-response-next').disabled = true;
+ }
+
+ });
+
+ // function to end trial when it is time
+ function end_trial() {
+
+ // kill any remaining setTimeout handlers
+ jsPsych.pluginAPI.clearAllTimeouts();
+
+ // gather the data to store for the trial
+ var trial_data = {
+ "rt": response.rt,
+ "stimulus": trial.stimulus,
+ "response": response.response
+ };
+
+ // clear the display
+ display_element.innerHTML = '';
+
+ // move on to the next trial
+ jsPsych.finishTrial(trial_data);
+ };
+
+ // end trial if time limit is set
+ if (trial.trial_duration !== null) {
+ jsPsych.pluginAPI.setTimeout(function() {
+ end_trial();
+ }, trial.trial_duration);
+ }
+ };
+
+ return plugin;
+})();
diff --git a/plugins/jspsych-video.js b/plugins/jspsych-video.js
deleted file mode 100644
index 39ace459..00000000
--- a/plugins/jspsych-video.js
+++ /dev/null
@@ -1,164 +0,0 @@
-/* jspsych-video.js
- * Josh de Leeuw
- *
- * This plugin displays a video. The trial ends when the video finishes.
- *
- * documentation: docs.jspsych.org
- *
- */
-
-jsPsych.plugins.video = (function() {
-
- var plugin = {};
-
- jsPsych.pluginAPI.registerPreload('video', 'sources', 'video');
-
- plugin.info = {
- name: 'video',
- description: '',
- parameters: {
- sources: {
- type: jsPsych.plugins.parameterType.VIDEO,
- pretty_name: 'Sources',
- array: true,
- default: undefined,
- description: 'The video file to play.'
- },
- width: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Width',
- default: '',
- description: 'The width of the video in pixels.'
- },
- height: {
- type: jsPsych.plugins.parameterType.INT,
- pretty_name: 'Height',
- default: '',
- description: 'The height of the video display in pixels.'
- },
- autoplay: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Autoplay',
- default: true,
- description: 'If true, the video will begin playing as soon as it has loaded.'
- },
- controls: {
- type: jsPsych.plugins.parameterType.BOOL,
- pretty_name: 'Controls',
- default: false,
- description: 'If true, the subject will be able to pause the video or move the playback to any point in the video.'
- },
- prompt: {
- type: jsPsych.plugins.parameterType.STRING,
- pretty_name: 'Prompt',
- default: null,
- description: 'Any content here will be displayed below the video content.'
- },
- start: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: 'Start',
- default: null,
- description: 'Time to start the clip.'
- },
- stop: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: 'Stop',
- default: null,
- description: 'Time to stop the clip.'
- },
- rate: {
- type: jsPsych.plugins.parameterType.FLOAT,
- pretty_name: 'Rate',
- default: null,
- description: 'The playback rate of the video. 1 is normal, <1 is slower, >1 is faster.'
- }
- }
- }
-
-
- plugin.trial = function(display_element, trial) {
-
- // display stimulus
-
-
- var video_html = '";
-
- //show prompt if there is one
- if (trial.prompt !== null) {
- video_html += trial.prompt;
- }
-
- display_element.innerHTML = video_html;
- if(video_preload_blob){
- display_element.querySelector('#jspsych-video-player').src = video_preload_blob;
- }
-
- display_element.querySelector('#jspsych-video-player').onended = function(){
- end_trial();
- }
-
- // event handler to set timeout to end trial if video is stopped
- display_element.querySelector('#jspsych-video-player').onplay = function(){
- if(trial.stop !== null){
- if(trial.start == null){
- trial.start = 0;
- }
- jsPsych.pluginAPI.setTimeout(end_trial, (trial.stop-trial.start)*1000);
- }
- }
-
- if(trial.start !== null){
- display_element.querySelector('#jspsych-video-player').currentTime = trial.start;
- }
-
- if(trial.rate !== null){
- display_element.querySelector('#jspsych-video-player').playbackRate = trial.rate;
- }
-
- // function to end trial when it is time
- var end_trial = function() {
-
- // gather the data to store for the trial
- var trial_data = {
- stimulus: JSON.stringify(trial.sources)
- };
-
- // clear the display
- display_element.innerHTML = '';
-
- // move on to the next trial
- jsPsych.finishTrial(trial_data);
- };
-
- };
-
- return plugin;
-})();