@jspsych/plugin-video-keyboard-response 2.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.browser.js +34 -52
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.min.js +2 -5
- package/dist/index.browser.min.js.map +1 -1
- package/dist/index.cjs +33 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +16 -6
- package/dist/index.js +33 -51
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.spec.ts +1 -1
- package/src/index.ts +9 -11
package/dist/index.browser.js
CHANGED
|
@@ -1,120 +1,96 @@
|
|
|
1
1
|
var jsPsychVideoKeyboardResponse = (function (jspsych) {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
name: "@jspsych/plugin-video-keyboard-response",
|
|
6
|
-
version: "2.0.0",
|
|
7
|
-
description: "jsPsych plugin for playing a video file and getting a keyboard response",
|
|
8
|
-
type: "module",
|
|
9
|
-
main: "dist/index.cjs",
|
|
10
|
-
exports: {
|
|
11
|
-
import: "./dist/index.js",
|
|
12
|
-
require: "./dist/index.cjs"
|
|
13
|
-
},
|
|
14
|
-
typings: "dist/index.d.ts",
|
|
15
|
-
unpkg: "dist/index.browser.min.js",
|
|
16
|
-
files: [
|
|
17
|
-
"src",
|
|
18
|
-
"dist"
|
|
19
|
-
],
|
|
20
|
-
source: "src/index.ts",
|
|
21
|
-
scripts: {
|
|
22
|
-
test: "jest --passWithNoTests",
|
|
23
|
-
"test:watch": "npm test -- --watch",
|
|
24
|
-
tsc: "tsc",
|
|
25
|
-
build: "rollup --config",
|
|
26
|
-
"build:watch": "npm run build -- --watch"
|
|
27
|
-
},
|
|
28
|
-
repository: {
|
|
29
|
-
type: "git",
|
|
30
|
-
url: "git+https://github.com/jspsych/jsPsych.git",
|
|
31
|
-
directory: "packages/plugin-video-keyboard-response"
|
|
32
|
-
},
|
|
33
|
-
author: "Josh de Leeuw",
|
|
34
|
-
license: "MIT",
|
|
35
|
-
bugs: {
|
|
36
|
-
url: "https://github.com/jspsych/jsPsych/issues"
|
|
37
|
-
},
|
|
38
|
-
homepage: "https://www.jspsych.org/latest/plugins/video-keyboard-response",
|
|
39
|
-
peerDependencies: {
|
|
40
|
-
jspsych: ">=7.1.0"
|
|
41
|
-
},
|
|
42
|
-
devDependencies: {
|
|
43
|
-
"@jspsych/config": "^3.0.0",
|
|
44
|
-
"@jspsych/test-utils": "^1.2.0"
|
|
45
|
-
}
|
|
46
|
-
};
|
|
4
|
+
var version = "2.1.1";
|
|
47
5
|
|
|
48
6
|
const info = {
|
|
49
7
|
name: "video-keyboard-response",
|
|
50
|
-
version
|
|
8
|
+
version,
|
|
51
9
|
parameters: {
|
|
10
|
+
/** Array of the video file(s) to play. Video can be provided in multiple file formats for better cross-browser support. */
|
|
52
11
|
stimulus: {
|
|
53
12
|
type: jspsych.ParameterType.VIDEO,
|
|
54
13
|
pretty_name: "Video",
|
|
55
14
|
default: void 0,
|
|
56
15
|
array: true
|
|
57
16
|
},
|
|
17
|
+
/** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
|
|
58
18
|
choices: {
|
|
59
19
|
type: jspsych.ParameterType.KEYS,
|
|
60
20
|
pretty_name: "Choices",
|
|
61
21
|
default: "ALL_KEYS"
|
|
62
22
|
},
|
|
23
|
+
/** Any content here will be displayed below the stimulus. */
|
|
63
24
|
prompt: {
|
|
64
25
|
type: jspsych.ParameterType.HTML_STRING,
|
|
65
26
|
pretty_name: "Prompt",
|
|
66
27
|
default: null
|
|
67
28
|
},
|
|
29
|
+
/** The width of the video display in pixels. If `null`, the video will take the original video's dimensions,
|
|
30
|
+
* or properly scaled with the aspect ratio if the height is also specified.
|
|
31
|
+
*/
|
|
68
32
|
width: {
|
|
69
33
|
type: jspsych.ParameterType.INT,
|
|
70
34
|
pretty_name: "Width",
|
|
71
35
|
default: ""
|
|
72
36
|
},
|
|
37
|
+
/** The height of the video display in pixels. If `null`, the video will take the original video's dimensions,
|
|
38
|
+
* or properly scaled with the aspect ratio if the width is also specified.
|
|
39
|
+
*/
|
|
73
40
|
height: {
|
|
74
41
|
type: jspsych.ParameterType.INT,
|
|
75
42
|
pretty_name: "Height",
|
|
76
43
|
default: ""
|
|
77
44
|
},
|
|
45
|
+
/** If true, the video will begin playing as soon as it has loaded. */
|
|
78
46
|
autoplay: {
|
|
79
47
|
type: jspsych.ParameterType.BOOL,
|
|
80
48
|
pretty_name: "Autoplay",
|
|
81
49
|
default: true
|
|
82
50
|
},
|
|
51
|
+
/** If true, the subject will be able to pause the video or move the playback to any point in the video. */
|
|
83
52
|
controls: {
|
|
84
53
|
type: jspsych.ParameterType.BOOL,
|
|
85
54
|
pretty_name: "Controls",
|
|
86
55
|
default: false
|
|
87
56
|
},
|
|
57
|
+
/** Time to start the clip. If null (default), video will start at the beginning of the file. */
|
|
88
58
|
start: {
|
|
89
59
|
type: jspsych.ParameterType.FLOAT,
|
|
90
60
|
pretty_name: "Start",
|
|
91
61
|
default: null
|
|
92
62
|
},
|
|
63
|
+
/** Time to stop the clip. If null (default), video will stop at the end of the file. */
|
|
93
64
|
stop: {
|
|
94
65
|
type: jspsych.ParameterType.FLOAT,
|
|
95
66
|
pretty_name: "Stop",
|
|
96
67
|
default: null
|
|
97
68
|
},
|
|
69
|
+
/** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */
|
|
98
70
|
rate: {
|
|
99
71
|
type: jspsych.ParameterType.FLOAT,
|
|
100
72
|
pretty_name: "Rate",
|
|
101
73
|
default: 1
|
|
102
74
|
},
|
|
75
|
+
/** If true, the trial will end immediately after the video finishes playing. */
|
|
103
76
|
trial_ends_after_video: {
|
|
104
77
|
type: jspsych.ParameterType.BOOL,
|
|
105
78
|
pretty_name: "End trial after video finishes",
|
|
106
79
|
default: false
|
|
107
80
|
},
|
|
81
|
+
/** How long to show trial before it ends. */
|
|
108
82
|
trial_duration: {
|
|
109
83
|
type: jspsych.ParameterType.INT,
|
|
110
84
|
pretty_name: "Trial duration",
|
|
111
85
|
default: null
|
|
112
86
|
},
|
|
87
|
+
/** If true, the trial will end when subject makes a response. */
|
|
113
88
|
response_ends_trial: {
|
|
114
89
|
type: jspsych.ParameterType.BOOL,
|
|
115
90
|
pretty_name: "Response ends trial",
|
|
116
91
|
default: true
|
|
117
92
|
},
|
|
93
|
+
/** If true, then responses are allowed while the video is playing. If false, then the video must finish playing before a response is accepted. */
|
|
118
94
|
response_allowed_while_playing: {
|
|
119
95
|
type: jspsych.ParameterType.BOOL,
|
|
120
96
|
pretty_name: "Response allowed while playing",
|
|
@@ -122,30 +98,36 @@ var jsPsychVideoKeyboardResponse = (function (jspsych) {
|
|
|
122
98
|
}
|
|
123
99
|
},
|
|
124
100
|
data: {
|
|
101
|
+
/** Indicates which key the participant pressed. */
|
|
125
102
|
response: {
|
|
126
103
|
type: jspsych.ParameterType.STRING
|
|
127
104
|
},
|
|
105
|
+
/** The response time in milliseconds for the participant to make a response. The time is measured from when the
|
|
106
|
+
* stimulus first appears on the screen until the participant's response.
|
|
107
|
+
*/
|
|
128
108
|
rt: {
|
|
129
109
|
type: jspsych.ParameterType.INT
|
|
130
110
|
},
|
|
111
|
+
/** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */
|
|
131
112
|
stimulus: {
|
|
132
113
|
type: jspsych.ParameterType.STRING,
|
|
133
114
|
array: true
|
|
134
115
|
}
|
|
116
|
+
},
|
|
117
|
+
// prettier-ignore
|
|
118
|
+
citations: {
|
|
119
|
+
"apa": "de Leeuw, J. R., Gilbert, R. A., & Luchterhandt, B. (2023). jsPsych: Enabling an Open-Source Collaborative Ecosystem of Behavioral Experiments. Journal of Open Source Software, 8(85), 5351. https://doi.org/10.21105/joss.05351 ",
|
|
120
|
+
"bibtex": '@article{Leeuw2023jsPsych, author = {de Leeuw, Joshua R. and Gilbert, Rebecca A. and Luchterhandt, Bj{\\" o}rn}, journal = {Journal of Open Source Software}, doi = {10.21105/joss.05351}, issn = {2475-9066}, number = {85}, year = {2023}, month = {may 11}, pages = {5351}, publisher = {Open Journals}, title = {jsPsych: Enabling an {Open}-{Source} {Collaborative} {Ecosystem} of {Behavioral} {Experiments}}, url = {https://joss.theoj.org/papers/10.21105/joss.05351}, volume = {8}, } '
|
|
135
121
|
}
|
|
136
122
|
};
|
|
137
123
|
class VideoKeyboardResponsePlugin {
|
|
138
124
|
constructor(jsPsych) {
|
|
139
125
|
this.jsPsych = jsPsych;
|
|
140
126
|
}
|
|
141
|
-
static
|
|
127
|
+
static {
|
|
128
|
+
this.info = info;
|
|
129
|
+
}
|
|
142
130
|
trial(display_element, trial) {
|
|
143
|
-
if (!Array.isArray(trial.stimulus)) {
|
|
144
|
-
throw new Error(`
|
|
145
|
-
The stimulus property for the video-keyboard-response plugin must be an array
|
|
146
|
-
of files. See https://www.jspsych.org/latest/plugins/video-keyboard-response/#parameters
|
|
147
|
-
`);
|
|
148
|
-
}
|
|
149
131
|
var video_html = "<div>";
|
|
150
132
|
video_html += '<video id="jspsych-video-keyboard-response-stimulus"';
|
|
151
133
|
if (trial.width) {
|
|
@@ -338,4 +320,4 @@ var jsPsychVideoKeyboardResponse = (function (jspsych) {
|
|
|
338
320
|
return VideoKeyboardResponsePlugin;
|
|
339
321
|
|
|
340
322
|
})(jsPsychModule);
|
|
341
|
-
//# sourceMappingURL=https://unpkg.com/@jspsych/plugin-video-keyboard-response@2.
|
|
323
|
+
//# sourceMappingURL=https://unpkg.com/@jspsych/plugin-video-keyboard-response@2.1.1/dist/index.browser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.browser.js","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"video-keyboard-response\",\n version: version,\n parameters: {\n /** Array of the video file(s) to play. Video can be provided in multiple file formats for better cross-browser support. */\n stimulus: {\n type: ParameterType.VIDEO,\n pretty_name: \"Video\",\n default: undefined,\n array: true,\n },\n /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */\n choices: {\n type: ParameterType.KEYS,\n pretty_name: \"Choices\",\n default: \"ALL_KEYS\",\n },\n /** Any content here will be displayed below the stimulus. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** The width of the video in pixels. */\n width: {\n type: ParameterType.INT,\n pretty_name: \"Width\",\n default: \"\",\n },\n /** The height of the video display in pixels. */\n height: {\n type: ParameterType.INT,\n pretty_name: \"Height\",\n default: \"\",\n },\n /** If true, the video will begin playing as soon as it has loaded. */\n autoplay: {\n type: ParameterType.BOOL,\n pretty_name: \"Autoplay\",\n default: true,\n },\n /** If true, the subject will be able to pause the video or move the playback to any point in the video. */\n controls: {\n type: ParameterType.BOOL,\n pretty_name: \"Controls\",\n default: false,\n },\n /** Time to start the clip. If null (default), video will start at the beginning of the file. */\n start: {\n type: ParameterType.FLOAT,\n pretty_name: \"Start\",\n default: null,\n },\n /** Time to stop the clip. If null (default), video will stop at the end of the file. */\n stop: {\n type: ParameterType.FLOAT,\n pretty_name: \"Stop\",\n default: null,\n },\n /** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */\n rate: {\n type: ParameterType.FLOAT,\n pretty_name: \"Rate\",\n default: 1,\n },\n /** If true, the trial will end immediately after the video finishes playing. */\n trial_ends_after_video: {\n type: ParameterType.BOOL,\n pretty_name: \"End trial after video finishes\",\n default: false,\n },\n /** How long to show trial before it ends. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, the trial will end when subject makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then responses are allowed while the video is playing. If false, then the video must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the\n * stimulus first appears on the screen until the participant's response.\n * */\n rt: {\n type: ParameterType.INT,\n },\n /** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */\n stimulus: {\n type: ParameterType.STRING,\n array: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays a video file and records a keyboard response. The stimulus can be displayed until a response is\n * given, or for a pre-determined amount of time. The trial can be ended automatically when the participant responds,\n * when the video file has finished playing, or if the participant has failed to respond within a fixed length of time.\n * You can also prevent a keyboard response from being recorded before the video has finished playing.\n *\n * Video files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are\n * using timeline variables or another dynamic method to specify the video stimulus, you will need to\n * [manually preload](../overview/media-preloading.md#manual-preloading) the videos. Also note that video preloading\n * is disabled when the experiment is running as a file (i.e. opened directly in the browser, rather than through a\n * server), in order to prevent CORS errors - see the section on [Running Experiments](../overview/running-experiments.md)\n * for more information.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/video-keyboard-response/ video-keyboard-response plugin documentation on jspsych.org}\n */\nclass VideoKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>) {\n // catch mistake where stimuli are not an array\n if (!Array.isArray(trial.stimulus)) {\n throw new Error(`\n The stimulus property for the video-keyboard-response plugin must be an array\n of files. See https://www.jspsych.org/latest/plugins/video-keyboard-response/#parameters\n `);\n }\n\n // setup stimulus\n var video_html = \"<div>\";\n video_html += '<video id=\"jspsych-video-keyboard-response-stimulus\"';\n\n if (trial.width) {\n video_html += ' width=\"' + trial.width + '\"';\n }\n if (trial.height) {\n video_html += ' height=\"' + trial.height + '\"';\n }\n if (trial.autoplay && trial.start == null) {\n // if autoplay is true and the start time is specified, then the video will start automatically\n // via the play() method, rather than the autoplay attribute, to prevent showing the first frame\n video_html += \" autoplay \";\n }\n if (trial.controls) {\n video_html += \" controls \";\n }\n if (trial.start !== null) {\n // hide video element when page loads if the start time is specified,\n // to prevent the video element from showing the first frame\n video_html += ' style=\"visibility: hidden;\"';\n }\n video_html += \">\";\n\n var video_preload_blob = this.jsPsych.pluginAPI.getVideoBuffer(trial.stimulus[0]);\n if (!video_preload_blob) {\n for (var i = 0; i < trial.stimulus.length; i++) {\n var file_name = trial.stimulus[i];\n if (file_name.indexOf(\"?\") > -1) {\n file_name = file_name.substring(0, file_name.indexOf(\"?\"));\n }\n var type = file_name.substr(file_name.lastIndexOf(\".\") + 1);\n type = type.toLowerCase();\n if (type == \"mov\") {\n console.warn(\n \"Warning: video-keyboard-response plugin does not reliably support .mov files.\"\n );\n }\n video_html += '<source src=\"' + file_name + '\" type=\"video/' + type + '\">';\n }\n }\n video_html += \"</video>\";\n video_html += \"</div>\";\n\n // add prompt if there is one\n if (trial.prompt !== null) {\n video_html += trial.prompt;\n }\n\n display_element.innerHTML = video_html;\n\n var video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n );\n\n if (video_preload_blob) {\n video_element.src = video_preload_blob;\n }\n\n video_element.onended = () => {\n if (trial.trial_ends_after_video) {\n end_trial();\n }\n if (trial.response_allowed_while_playing == false && !trial.trial_ends_after_video) {\n // start keyboard listener\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n };\n\n video_element.playbackRate = trial.rate;\n\n // if video start time is specified, hide the video and set the starting time\n // before showing and playing, so that the video doesn't automatically show the first frame\n if (trial.start !== null) {\n video_element.pause();\n video_element.onseeked = () => {\n video_element.style.visibility = \"visible\";\n video_element.muted = false;\n if (trial.autoplay) {\n video_element.play();\n } else {\n video_element.pause();\n }\n video_element.onseeked = () => {};\n };\n video_element.onplaying = () => {\n video_element.currentTime = trial.start;\n video_element.onplaying = () => {};\n };\n // fix for iOS/MacOS browsers: videos aren't seekable until they start playing, so need to hide/mute, play,\n // change current time, then show/unmute\n video_element.muted = true;\n video_element.play();\n }\n\n let stopped = false;\n if (trial.stop !== null) {\n video_element.addEventListener(\"timeupdate\", (e) => {\n var currenttime = video_element.currentTime;\n if (currenttime >= trial.stop) {\n if (!trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n video_element.pause();\n if (trial.trial_ends_after_video && !stopped) {\n // this is to prevent end_trial from being called twice, because the timeupdate event\n // can fire in quick succession\n stopped = true;\n end_trial();\n }\n }\n });\n }\n\n // store response\n var response = {\n rt: null,\n key: null,\n };\n\n // function to end trial when it is time\n const end_trial = () => {\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // stop the video file if it is playing\n // remove end event listeners if they exist\n display_element\n .querySelector<HTMLVideoElement>(\"#jspsych-video-keyboard-response-stimulus\")\n .pause();\n display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n ).onended = () => {};\n\n // gather the data to store for the trial\n var trial_data = {\n rt: response.rt,\n stimulus: trial.stimulus,\n response: response.key,\n };\n\n // move on to the next trial\n this.jsPsych.finishTrial(trial_data);\n };\n\n // function to handle responses by the subject\n var after_response = (info) => {\n // after a valid response, the stimulus will have the CSS class 'responded'\n // which can be used to provide visual feedback that a response was recorded\n display_element.querySelector(\"#jspsych-video-keyboard-response-stimulus\").className +=\n \" responded\";\n\n // only record the first response\n if (response.key == null) {\n response = info;\n }\n\n if (trial.response_ends_trial) {\n end_trial();\n }\n };\n\n // start the response listener\n if (trial.choices != \"NO_KEYS\" && trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(end_trial, trial.trial_duration);\n }\n }\n\n simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n this.jsPsych.finishTrial(data);\n }\n\n private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n this.trial(display_element, trial);\n load_callback();\n\n const video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-button-response-stimulus\"\n );\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n if (!trial.response_allowed_while_playing) {\n video_element.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default VideoKeyboardResponsePlugin;\n"],"names":["version","ParameterType","info"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.browser.js","sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@jspsych/plugin-video-keyboard-response\",\n \"version\": \"2.1.1\",\n \"description\": \"jsPsych plugin for playing a video file and getting a keyboard response\",\n \"type\": \"module\",\n \"main\": \"dist/index.cjs\",\n \"exports\": {\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n },\n \"typings\": \"dist/index.d.ts\",\n \"unpkg\": \"dist/index.browser.min.js\",\n \"files\": [\n \"src\",\n \"dist\"\n ],\n \"source\": \"src/index.ts\",\n \"scripts\": {\n \"test\": \"jest --passWithNoTests\",\n \"test:watch\": \"npm test -- --watch\",\n \"tsc\": \"tsc\",\n \"build\": \"rollup --config\",\n \"build:watch\": \"npm run build -- --watch\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/jspsych/jsPsych.git\",\n \"directory\": \"packages/plugin-video-keyboard-response\"\n },\n \"author\": \"Josh de Leeuw\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/jspsych/jsPsych/issues\"\n },\n \"homepage\": \"https://www.jspsych.org/latest/plugins/video-keyboard-response\",\n \"peerDependencies\": {\n \"jspsych\": \">=7.1.0\"\n },\n \"devDependencies\": {\n \"@jspsych/config\": \"^3.3.1\",\n \"@jspsych/test-utils\": \"^1.2.0\"\n }\n}\n","import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"video-keyboard-response\",\n version: version,\n parameters: {\n /** Array of the video file(s) to play. Video can be provided in multiple file formats for better cross-browser support. */\n stimulus: {\n type: ParameterType.VIDEO,\n pretty_name: \"Video\",\n default: undefined,\n array: true,\n },\n /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */\n choices: {\n type: ParameterType.KEYS,\n pretty_name: \"Choices\",\n default: \"ALL_KEYS\",\n },\n /** Any content here will be displayed below the stimulus. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** The width of the video display in pixels. If `null`, the video will take the original video's dimensions, \n * or properly scaled with the aspect ratio if the height is also specified.\n */\n width: {\n type: ParameterType.INT,\n pretty_name: \"Width\",\n default: \"\",\n },\n /** The height of the video display in pixels. If `null`, the video will take the original video's dimensions,\n * or properly scaled with the aspect ratio if the width is also specified.\n */\n height: {\n type: ParameterType.INT,\n pretty_name: \"Height\",\n default: \"\",\n },\n /** If true, the video will begin playing as soon as it has loaded. */\n autoplay: {\n type: ParameterType.BOOL,\n pretty_name: \"Autoplay\",\n default: true,\n },\n /** If true, the subject will be able to pause the video or move the playback to any point in the video. */\n controls: {\n type: ParameterType.BOOL,\n pretty_name: \"Controls\",\n default: false,\n },\n /** Time to start the clip. If null (default), video will start at the beginning of the file. */\n start: {\n type: ParameterType.FLOAT,\n pretty_name: \"Start\",\n default: null,\n },\n /** Time to stop the clip. If null (default), video will stop at the end of the file. */\n stop: {\n type: ParameterType.FLOAT,\n pretty_name: \"Stop\",\n default: null,\n },\n /** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */\n rate: {\n type: ParameterType.FLOAT,\n pretty_name: \"Rate\",\n default: 1,\n },\n /** If true, the trial will end immediately after the video finishes playing. */\n trial_ends_after_video: {\n type: ParameterType.BOOL,\n pretty_name: \"End trial after video finishes\",\n default: false,\n },\n /** How long to show trial before it ends. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, the trial will end when subject makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then responses are allowed while the video is playing. If false, then the video must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the\n * stimulus first appears on the screen until the participant's response.\n */\n rt: {\n type: ParameterType.INT,\n },\n /** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */\n stimulus: {\n type: ParameterType.STRING,\n array: true,\n },\n },\n // prettier-ignore\n citations: '__CITATIONS__',\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays a video file and records a keyboard response. The stimulus can be displayed until a response is\n * given, or for a pre-determined amount of time. The trial can be ended automatically when the participant responds,\n * when the video file has finished playing, or if the participant has failed to respond within a fixed length of time.\n * You can also prevent a keyboard response from being recorded before the video has finished playing.\n *\n * Video files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are\n * using timeline variables or another dynamic method to specify the video stimulus, you will need to\n * [manually preload](../overview/media-preloading.md#manual-preloading) the videos. Also note that video preloading\n * is disabled when the experiment is running as a file (i.e. opened directly in the browser, rather than through a\n * server), in order to prevent CORS errors - see the section on [Running Experiments](../overview/running-experiments.md)\n * for more information.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/video-keyboard-response/ video-keyboard-response plugin documentation on jspsych.org}\n */\nclass VideoKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>) {\n // setup stimulus\n var video_html = \"<div>\";\n video_html += '<video id=\"jspsych-video-keyboard-response-stimulus\"';\n\n if (trial.width) {\n video_html += ' width=\"' + trial.width + '\"';\n }\n if (trial.height) {\n video_html += ' height=\"' + trial.height + '\"';\n }\n if (trial.autoplay && trial.start == null) {\n // if autoplay is true and the start time is specified, then the video will start automatically\n // via the play() method, rather than the autoplay attribute, to prevent showing the first frame\n video_html += \" autoplay \";\n }\n if (trial.controls) {\n video_html += \" controls \";\n }\n if (trial.start !== null) {\n // hide video element when page loads if the start time is specified,\n // to prevent the video element from showing the first frame\n video_html += ' style=\"visibility: hidden;\"';\n }\n video_html += \">\";\n\n var video_preload_blob = this.jsPsych.pluginAPI.getVideoBuffer(trial.stimulus[0]);\n if (!video_preload_blob) {\n for (var i = 0; i < trial.stimulus.length; i++) {\n var file_name = trial.stimulus[i];\n if (file_name.indexOf(\"?\") > -1) {\n file_name = file_name.substring(0, file_name.indexOf(\"?\"));\n }\n var type = file_name.substr(file_name.lastIndexOf(\".\") + 1);\n type = type.toLowerCase();\n if (type == \"mov\") {\n console.warn(\n \"Warning: video-keyboard-response plugin does not reliably support .mov files.\"\n );\n }\n video_html += '<source src=\"' + file_name + '\" type=\"video/' + type + '\">';\n }\n }\n video_html += \"</video>\";\n video_html += \"</div>\";\n\n // add prompt if there is one\n if (trial.prompt !== null) {\n video_html += trial.prompt;\n }\n\n display_element.innerHTML = video_html;\n\n var video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n );\n\n if (video_preload_blob) {\n video_element.src = video_preload_blob;\n }\n\n video_element.onended = () => {\n if (trial.trial_ends_after_video) {\n end_trial();\n }\n if (trial.response_allowed_while_playing == false && !trial.trial_ends_after_video) {\n // start keyboard listener\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n };\n\n video_element.playbackRate = trial.rate;\n\n // if video start time is specified, hide the video and set the starting time\n // before showing and playing, so that the video doesn't automatically show the first frame\n if (trial.start !== null) {\n video_element.pause();\n video_element.onseeked = () => {\n video_element.style.visibility = \"visible\";\n video_element.muted = false;\n if (trial.autoplay) {\n video_element.play();\n } else {\n video_element.pause();\n }\n video_element.onseeked = () => {};\n };\n video_element.onplaying = () => {\n video_element.currentTime = trial.start;\n video_element.onplaying = () => {};\n };\n // fix for iOS/MacOS browsers: videos aren't seekable until they start playing, so need to hide/mute, play,\n // change current time, then show/unmute\n video_element.muted = true;\n video_element.play();\n }\n\n let stopped = false;\n if (trial.stop !== null) {\n video_element.addEventListener(\"timeupdate\", (e) => {\n var currenttime = video_element.currentTime;\n if (currenttime >= trial.stop) {\n if (!trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n video_element.pause();\n if (trial.trial_ends_after_video && !stopped) {\n // this is to prevent end_trial from being called twice, because the timeupdate event\n // can fire in quick succession\n stopped = true;\n end_trial();\n }\n }\n });\n }\n\n // store response\n var response = {\n rt: null,\n key: null,\n };\n\n // function to end trial when it is time\n const end_trial = () => {\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // stop the video file if it is playing\n // remove end event listeners if they exist\n display_element\n .querySelector<HTMLVideoElement>(\"#jspsych-video-keyboard-response-stimulus\")\n .pause();\n display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n ).onended = () => {};\n\n // gather the data to store for the trial\n var trial_data = {\n rt: response.rt,\n stimulus: trial.stimulus,\n response: response.key,\n };\n\n // move on to the next trial\n this.jsPsych.finishTrial(trial_data);\n };\n\n // function to handle responses by the subject\n var after_response = (info) => {\n // after a valid response, the stimulus will have the CSS class 'responded'\n // which can be used to provide visual feedback that a response was recorded\n display_element.querySelector(\"#jspsych-video-keyboard-response-stimulus\").className +=\n \" responded\";\n\n // only record the first response\n if (response.key == null) {\n response = info;\n }\n\n if (trial.response_ends_trial) {\n end_trial();\n }\n };\n\n // start the response listener\n if (trial.choices != \"NO_KEYS\" && trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(end_trial, trial.trial_duration);\n }\n }\n\n simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n this.jsPsych.finishTrial(data);\n }\n\n private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n this.trial(display_element, trial);\n load_callback();\n\n const video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-button-response-stimulus\"\n );\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n if (!trial.response_allowed_while_playing) {\n video_element.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default VideoKeyboardResponsePlugin;\n"],"names":[],"mappings":";;;EAEE,IAAW,OAAA,GAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ICkHA,SAAA,EAAA;EAAA;;KAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
var jsPsychVideoKeyboardResponse=function(r){"use strict";var
|
|
2
|
-
|
|
3
|
-
of files. See https://www.jspsych.org/latest/plugins/video-keyboard-response/#parameters
|
|
4
|
-
`);var t="<div>";t+='<video id="jspsych-video-keyboard-response-stimulus"',e.width&&(t+=' width="'+e.width+'"'),e.height&&(t+=' height="'+e.height+'"'),e.autoplay&&e.start==null&&(t+=" autoplay "),e.controls&&(t+=" controls "),e.start!==null&&(t+=' style="visibility: hidden;"'),t+=">";var i=this.jsPsych.pluginAPI.getVideoBuffer(e.stimulus[0]);if(!i)for(var n=0;n<e.stimulus.length;n++){var l=e.stimulus[n];l.indexOf("?")>-1&&(l=l.substring(0,l.indexOf("?")));var o=l.substr(l.lastIndexOf(".")+1);o=o.toLowerCase(),o=="mov"&&console.warn("Warning: video-keyboard-response plugin does not reliably support .mov files."),t+='<source src="'+l+'" type="video/'+o+'">'}t+="</video>",t+="</div>",e.prompt!==null&&(t+=e.prompt),s.innerHTML=t;var a=s.querySelector("#jspsych-video-keyboard-response-stimulus");i&&(a.src=i),a.onended=()=>{if(e.trial_ends_after_video&&d(),e.response_allowed_while_playing==!1&&!e.trial_ends_after_video)var p=this.jsPsych.pluginAPI.getKeyboardResponse({callback_function:y,valid_responses:e.choices,rt_method:"performance",persist:!1,allow_held_key:!1})},a.playbackRate=e.rate,e.start!==null&&(a.pause(),a.onseeked=()=>{a.style.visibility="visible",a.muted=!1,e.autoplay?a.play():a.pause(),a.onseeked=()=>{}},a.onplaying=()=>{a.currentTime=e.start,a.onplaying=()=>{}},a.muted=!0,a.play());let m=!1;e.stop!==null&&a.addEventListener("timeupdate",p=>{var g=a.currentTime;if(g>=e.stop){if(!e.response_allowed_while_playing)var P=this.jsPsych.pluginAPI.getKeyboardResponse({callback_function:y,valid_responses:e.choices,rt_method:"performance",persist:!1,allow_held_key:!1});a.pause(),e.trial_ends_after_video&&!m&&(m=!0,d())}});var u={rt:null,key:null};const d=()=>{this.jsPsych.pluginAPI.cancelAllKeyboardResponses(),s.querySelector("#jspsych-video-keyboard-response-stimulus").pause(),s.querySelector("#jspsych-video-keyboard-response-stimulus").onended=()=>{};var p={rt:u.rt,stimulus:e.stimulus,response:u.key};this.jsPsych.finishTrial(p)};var y=p=>{s.querySelector("#jspsych-video-keyboard-response-stimulus").className+=" responded",u.key==null&&(u=p),e.response_ends_trial&&d()};if(e.choices!="NO_KEYS"&&e.response_allowed_while_playing)var f=this.jsPsych.pluginAPI.getKeyboardResponse({callback_function:y,valid_responses:e.choices,rt_method:"performance",persist:!1,allow_held_key:!1});e.trial_duration!==null&&this.jsPsych.pluginAPI.setTimeout(d,e.trial_duration)}simulate(s,e,t,i){e=="data-only"&&(i(),this.simulate_data_only(s,t)),e=="visual"&&this.simulate_visual(s,t,i)}simulate_data_only(s,e){const t=this.create_simulation_data(s,e);this.jsPsych.finishTrial(t)}simulate_visual(s,e,t){const i=this.create_simulation_data(s,e),n=this.jsPsych.getDisplayElement();this.trial(n,s),t();const l=n.querySelector("#jspsych-video-button-response-stimulus"),o=()=>{i.rt!==null&&this.jsPsych.pluginAPI.pressKey(i.response,i.rt)};s.response_allowed_while_playing?o():l.addEventListener("ended",o)}create_simulation_data(s,e){const t={stimulus:s.stimulus,rt:this.jsPsych.randomization.sampleExGaussian(500,50,.006666666666666667,!0),response:this.jsPsych.pluginAPI.getValidKey(s.choices)},i=this.jsPsych.pluginAPI.mergeSimulationData(t,e);return this.jsPsych.pluginAPI.ensureSimulationDataConsistency(s,i),i}}return c.info=_,c}(jsPsychModule);
|
|
5
|
-
//# sourceMappingURL=https://unpkg.com/@jspsych/plugin-video-keyboard-response@2.0.0/dist/index.browser.min.js.map
|
|
1
|
+
var jsPsychVideoKeyboardResponse=function(r){"use strict";var c="2.1.1";const _={name:"video-keyboard-response",version:c,parameters:{stimulus:{type:r.ParameterType.VIDEO,pretty_name:"Video",default:void 0,array:!0},choices:{type:r.ParameterType.KEYS,pretty_name:"Choices",default:"ALL_KEYS"},prompt:{type:r.ParameterType.HTML_STRING,pretty_name:"Prompt",default:null},width:{type:r.ParameterType.INT,pretty_name:"Width",default:""},height:{type:r.ParameterType.INT,pretty_name:"Height",default:""},autoplay:{type:r.ParameterType.BOOL,pretty_name:"Autoplay",default:!0},controls:{type:r.ParameterType.BOOL,pretty_name:"Controls",default:!1},start:{type:r.ParameterType.FLOAT,pretty_name:"Start",default:null},stop:{type:r.ParameterType.FLOAT,pretty_name:"Stop",default:null},rate:{type:r.ParameterType.FLOAT,pretty_name:"Rate",default:1},trial_ends_after_video:{type:r.ParameterType.BOOL,pretty_name:"End trial after video finishes",default:!1},trial_duration:{type:r.ParameterType.INT,pretty_name:"Trial duration",default:null},response_ends_trial:{type:r.ParameterType.BOOL,pretty_name:"Response ends trial",default:!0},response_allowed_while_playing:{type:r.ParameterType.BOOL,pretty_name:"Response allowed while playing",default:!0}},data:{response:{type:r.ParameterType.STRING},rt:{type:r.ParameterType.INT},stimulus:{type:r.ParameterType.STRING,array:!0}},citations:{apa:"de Leeuw, J. R., Gilbert, R. A., & Luchterhandt, B. (2023). jsPsych: Enabling an Open-Source Collaborative Ecosystem of Behavioral Experiments. Journal of Open Source Software, 8(85), 5351. https://doi.org/10.21105/joss.05351 ",bibtex:'@article{Leeuw2023jsPsych, author = {de Leeuw, Joshua R. and Gilbert, Rebecca A. and Luchterhandt, Bj{\\" o}rn}, journal = {Journal of Open Source Software}, doi = {10.21105/joss.05351}, issn = {2475-9066}, number = {85}, year = {2023}, month = {may 11}, pages = {5351}, publisher = {Open Journals}, title = {jsPsych: Enabling an {Open}-{Source} {Collaborative} {Ecosystem} of {Behavioral} {Experiments}}, url = {https://joss.theoj.org/papers/10.21105/joss.05351}, volume = {8}, } '}};class m{constructor(a){this.jsPsych=a}trial(a,e){var t="<div>";t+='<video id="jspsych-video-keyboard-response-stimulus"',e.width&&(t+=' width="'+e.width+'"'),e.height&&(t+=' height="'+e.height+'"'),e.autoplay&&e.start==null&&(t+=" autoplay "),e.controls&&(t+=" controls "),e.start!==null&&(t+=' style="visibility: hidden;"'),t+=">";var i=this.jsPsych.pluginAPI.getVideoBuffer(e.stimulus[0]);if(!i)for(var n=0;n<e.stimulus.length;n++){var l=e.stimulus[n];l.indexOf("?")>-1&&(l=l.substring(0,l.indexOf("?")));var o=l.substr(l.lastIndexOf(".")+1);o=o.toLowerCase(),o=="mov"&&console.warn("Warning: video-keyboard-response plugin does not reliably support .mov files."),t+='<source src="'+l+'" type="video/'+o+'">'}t+="</video>",t+="</div>",e.prompt!==null&&(t+=e.prompt),a.innerHTML=t;var s=a.querySelector("#jspsych-video-keyboard-response-stimulus");i&&(s.src=i),s.onended=()=>{if(e.trial_ends_after_video&&d(),e.response_allowed_while_playing==!1&&!e.trial_ends_after_video)var u=this.jsPsych.pluginAPI.getKeyboardResponse({callback_function:y,valid_responses:e.choices,rt_method:"performance",persist:!1,allow_held_key:!1})},s.playbackRate=e.rate,e.start!==null&&(s.pause(),s.onseeked=()=>{s.style.visibility="visible",s.muted=!1,e.autoplay?s.play():s.pause(),s.onseeked=()=>{}},s.onplaying=()=>{s.currentTime=e.start,s.onplaying=()=>{}},s.muted=!0,s.play());let h=!1;e.stop!==null&&s.addEventListener("timeupdate",u=>{var v=s.currentTime;if(v>=e.stop){if(!e.response_allowed_while_playing)var g=this.jsPsych.pluginAPI.getKeyboardResponse({callback_function:y,valid_responses:e.choices,rt_method:"performance",persist:!1,allow_held_key:!1});s.pause(),e.trial_ends_after_video&&!h&&(h=!0,d())}});var p={rt:null,key:null};const d=()=>{this.jsPsych.pluginAPI.cancelAllKeyboardResponses(),a.querySelector("#jspsych-video-keyboard-response-stimulus").pause(),a.querySelector("#jspsych-video-keyboard-response-stimulus").onended=()=>{};var u={rt:p.rt,stimulus:e.stimulus,response:p.key};this.jsPsych.finishTrial(u)};var y=u=>{a.querySelector("#jspsych-video-keyboard-response-stimulus").className+=" responded",p.key==null&&(p=u),e.response_ends_trial&&d()};if(e.choices!="NO_KEYS"&&e.response_allowed_while_playing)var P=this.jsPsych.pluginAPI.getKeyboardResponse({callback_function:y,valid_responses:e.choices,rt_method:"performance",persist:!1,allow_held_key:!1});e.trial_duration!==null&&this.jsPsych.pluginAPI.setTimeout(d,e.trial_duration)}simulate(a,e,t,i){e=="data-only"&&(i(),this.simulate_data_only(a,t)),e=="visual"&&this.simulate_visual(a,t,i)}simulate_data_only(a,e){const t=this.create_simulation_data(a,e);this.jsPsych.finishTrial(t)}simulate_visual(a,e,t){const i=this.create_simulation_data(a,e),n=this.jsPsych.getDisplayElement();this.trial(n,a),t();const l=n.querySelector("#jspsych-video-button-response-stimulus"),o=()=>{i.rt!==null&&this.jsPsych.pluginAPI.pressKey(i.response,i.rt)};a.response_allowed_while_playing?o():l.addEventListener("ended",o)}create_simulation_data(a,e){const t={stimulus:a.stimulus,rt:this.jsPsych.randomization.sampleExGaussian(500,50,.006666666666666667,!0),response:this.jsPsych.pluginAPI.getValidKey(a.choices)},i=this.jsPsych.pluginAPI.mergeSimulationData(t,e);return this.jsPsych.pluginAPI.ensureSimulationDataConsistency(a,i),i}}return m.info=_,m}(jsPsychModule);
|
|
2
|
+
//# sourceMappingURL=https://unpkg.com/@jspsych/plugin-video-keyboard-response@2.1.1/dist/index.browser.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.browser.min.js","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"video-keyboard-response\",\n version: version,\n parameters: {\n /** Array of the video file(s) to play. Video can be provided in multiple file formats for better cross-browser support. */\n stimulus: {\n type: ParameterType.VIDEO,\n pretty_name: \"Video\",\n default: undefined,\n array: true,\n },\n /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */\n choices: {\n type: ParameterType.KEYS,\n pretty_name: \"Choices\",\n default: \"ALL_KEYS\",\n },\n /** Any content here will be displayed below the stimulus. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** The width of the video in pixels. */\n width: {\n type: ParameterType.INT,\n pretty_name: \"Width\",\n default: \"\",\n },\n /** The height of the video display in pixels. */\n height: {\n type: ParameterType.INT,\n pretty_name: \"Height\",\n default: \"\",\n },\n /** If true, the video will begin playing as soon as it has loaded. */\n autoplay: {\n type: ParameterType.BOOL,\n pretty_name: \"Autoplay\",\n default: true,\n },\n /** If true, the subject will be able to pause the video or move the playback to any point in the video. */\n controls: {\n type: ParameterType.BOOL,\n pretty_name: \"Controls\",\n default: false,\n },\n /** Time to start the clip. If null (default), video will start at the beginning of the file. */\n start: {\n type: ParameterType.FLOAT,\n pretty_name: \"Start\",\n default: null,\n },\n /** Time to stop the clip. If null (default), video will stop at the end of the file. */\n stop: {\n type: ParameterType.FLOAT,\n pretty_name: \"Stop\",\n default: null,\n },\n /** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */\n rate: {\n type: ParameterType.FLOAT,\n pretty_name: \"Rate\",\n default: 1,\n },\n /** If true, the trial will end immediately after the video finishes playing. */\n trial_ends_after_video: {\n type: ParameterType.BOOL,\n pretty_name: \"End trial after video finishes\",\n default: false,\n },\n /** How long to show trial before it ends. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, the trial will end when subject makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then responses are allowed while the video is playing. If false, then the video must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the\n * stimulus first appears on the screen until the participant's response.\n * */\n rt: {\n type: ParameterType.INT,\n },\n /** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */\n stimulus: {\n type: ParameterType.STRING,\n array: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays a video file and records a keyboard response. The stimulus can be displayed until a response is\n * given, or for a pre-determined amount of time. The trial can be ended automatically when the participant responds,\n * when the video file has finished playing, or if the participant has failed to respond within a fixed length of time.\n * You can also prevent a keyboard response from being recorded before the video has finished playing.\n *\n * Video files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are\n * using timeline variables or another dynamic method to specify the video stimulus, you will need to\n * [manually preload](../overview/media-preloading.md#manual-preloading) the videos. Also note that video preloading\n * is disabled when the experiment is running as a file (i.e. opened directly in the browser, rather than through a\n * server), in order to prevent CORS errors - see the section on [Running Experiments](../overview/running-experiments.md)\n * for more information.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/video-keyboard-response/ video-keyboard-response plugin documentation on jspsych.org}\n */\nclass VideoKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>) {\n // catch mistake where stimuli are not an array\n if (!Array.isArray(trial.stimulus)) {\n throw new Error(`\n The stimulus property for the video-keyboard-response plugin must be an array\n of files. See https://www.jspsych.org/latest/plugins/video-keyboard-response/#parameters\n `);\n }\n\n // setup stimulus\n var video_html = \"<div>\";\n video_html += '<video id=\"jspsych-video-keyboard-response-stimulus\"';\n\n if (trial.width) {\n video_html += ' width=\"' + trial.width + '\"';\n }\n if (trial.height) {\n video_html += ' height=\"' + trial.height + '\"';\n }\n if (trial.autoplay && trial.start == null) {\n // if autoplay is true and the start time is specified, then the video will start automatically\n // via the play() method, rather than the autoplay attribute, to prevent showing the first frame\n video_html += \" autoplay \";\n }\n if (trial.controls) {\n video_html += \" controls \";\n }\n if (trial.start !== null) {\n // hide video element when page loads if the start time is specified,\n // to prevent the video element from showing the first frame\n video_html += ' style=\"visibility: hidden;\"';\n }\n video_html += \">\";\n\n var video_preload_blob = this.jsPsych.pluginAPI.getVideoBuffer(trial.stimulus[0]);\n if (!video_preload_blob) {\n for (var i = 0; i < trial.stimulus.length; i++) {\n var file_name = trial.stimulus[i];\n if (file_name.indexOf(\"?\") > -1) {\n file_name = file_name.substring(0, file_name.indexOf(\"?\"));\n }\n var type = file_name.substr(file_name.lastIndexOf(\".\") + 1);\n type = type.toLowerCase();\n if (type == \"mov\") {\n console.warn(\n \"Warning: video-keyboard-response plugin does not reliably support .mov files.\"\n );\n }\n video_html += '<source src=\"' + file_name + '\" type=\"video/' + type + '\">';\n }\n }\n video_html += \"</video>\";\n video_html += \"</div>\";\n\n // add prompt if there is one\n if (trial.prompt !== null) {\n video_html += trial.prompt;\n }\n\n display_element.innerHTML = video_html;\n\n var video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n );\n\n if (video_preload_blob) {\n video_element.src = video_preload_blob;\n }\n\n video_element.onended = () => {\n if (trial.trial_ends_after_video) {\n end_trial();\n }\n if (trial.response_allowed_while_playing == false && !trial.trial_ends_after_video) {\n // start keyboard listener\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n };\n\n video_element.playbackRate = trial.rate;\n\n // if video start time is specified, hide the video and set the starting time\n // before showing and playing, so that the video doesn't automatically show the first frame\n if (trial.start !== null) {\n video_element.pause();\n video_element.onseeked = () => {\n video_element.style.visibility = \"visible\";\n video_element.muted = false;\n if (trial.autoplay) {\n video_element.play();\n } else {\n video_element.pause();\n }\n video_element.onseeked = () => {};\n };\n video_element.onplaying = () => {\n video_element.currentTime = trial.start;\n video_element.onplaying = () => {};\n };\n // fix for iOS/MacOS browsers: videos aren't seekable until they start playing, so need to hide/mute, play,\n // change current time, then show/unmute\n video_element.muted = true;\n video_element.play();\n }\n\n let stopped = false;\n if (trial.stop !== null) {\n video_element.addEventListener(\"timeupdate\", (e) => {\n var currenttime = video_element.currentTime;\n if (currenttime >= trial.stop) {\n if (!trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n video_element.pause();\n if (trial.trial_ends_after_video && !stopped) {\n // this is to prevent end_trial from being called twice, because the timeupdate event\n // can fire in quick succession\n stopped = true;\n end_trial();\n }\n }\n });\n }\n\n // store response\n var response = {\n rt: null,\n key: null,\n };\n\n // function to end trial when it is time\n const end_trial = () => {\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // stop the video file if it is playing\n // remove end event listeners if they exist\n display_element\n .querySelector<HTMLVideoElement>(\"#jspsych-video-keyboard-response-stimulus\")\n .pause();\n display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n ).onended = () => {};\n\n // gather the data to store for the trial\n var trial_data = {\n rt: response.rt,\n stimulus: trial.stimulus,\n response: response.key,\n };\n\n // move on to the next trial\n this.jsPsych.finishTrial(trial_data);\n };\n\n // function to handle responses by the subject\n var after_response = (info) => {\n // after a valid response, the stimulus will have the CSS class 'responded'\n // which can be used to provide visual feedback that a response was recorded\n display_element.querySelector(\"#jspsych-video-keyboard-response-stimulus\").className +=\n \" responded\";\n\n // only record the first response\n if (response.key == null) {\n response = info;\n }\n\n if (trial.response_ends_trial) {\n end_trial();\n }\n };\n\n // start the response listener\n if (trial.choices != \"NO_KEYS\" && trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(end_trial, trial.trial_duration);\n }\n }\n\n simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n this.jsPsych.finishTrial(data);\n }\n\n private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n this.trial(display_element, trial);\n load_callback();\n\n const video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-button-response-stimulus\"\n );\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n if (!trial.response_allowed_while_playing) {\n video_element.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default VideoKeyboardResponsePlugin;\n"],"names":["info","version","ParameterType","VideoKeyboardResponsePlugin","jsPsych","display_element","trial","video_html","video_preload_blob","i","file_name","type","video_element","end_trial","keyboardListener","after_response","stopped","e","currenttime","response","trial_data","simulation_mode","simulation_options","load_callback","data","respond","default_data"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.browser.min.js","sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@jspsych/plugin-video-keyboard-response\",\n \"version\": \"2.1.1\",\n \"description\": \"jsPsych plugin for playing a video file and getting a keyboard response\",\n \"type\": \"module\",\n \"main\": \"dist/index.cjs\",\n \"exports\": {\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n },\n \"typings\": \"dist/index.d.ts\",\n \"unpkg\": \"dist/index.browser.min.js\",\n \"files\": [\n \"src\",\n \"dist\"\n ],\n \"source\": \"src/index.ts\",\n \"scripts\": {\n \"test\": \"jest --passWithNoTests\",\n \"test:watch\": \"npm test -- --watch\",\n \"tsc\": \"tsc\",\n \"build\": \"rollup --config\",\n \"build:watch\": \"npm run build -- --watch\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/jspsych/jsPsych.git\",\n \"directory\": \"packages/plugin-video-keyboard-response\"\n },\n \"author\": \"Josh de Leeuw\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/jspsych/jsPsych/issues\"\n },\n \"homepage\": \"https://www.jspsych.org/latest/plugins/video-keyboard-response\",\n \"peerDependencies\": {\n \"jspsych\": \">=7.1.0\"\n },\n \"devDependencies\": {\n \"@jspsych/config\": \"^3.3.1\",\n \"@jspsych/test-utils\": \"^1.2.0\"\n }\n}\n","import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"video-keyboard-response\",\n version: version,\n parameters: {\n /** Array of the video file(s) to play. Video can be provided in multiple file formats for better cross-browser support. */\n stimulus: {\n type: ParameterType.VIDEO,\n pretty_name: \"Video\",\n default: undefined,\n array: true,\n },\n /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */\n choices: {\n type: ParameterType.KEYS,\n pretty_name: \"Choices\",\n default: \"ALL_KEYS\",\n },\n /** Any content here will be displayed below the stimulus. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** The width of the video display in pixels. If `null`, the video will take the original video's dimensions, \n * or properly scaled with the aspect ratio if the height is also specified.\n */\n width: {\n type: ParameterType.INT,\n pretty_name: \"Width\",\n default: \"\",\n },\n /** The height of the video display in pixels. If `null`, the video will take the original video's dimensions,\n * or properly scaled with the aspect ratio if the width is also specified.\n */\n height: {\n type: ParameterType.INT,\n pretty_name: \"Height\",\n default: \"\",\n },\n /** If true, the video will begin playing as soon as it has loaded. */\n autoplay: {\n type: ParameterType.BOOL,\n pretty_name: \"Autoplay\",\n default: true,\n },\n /** If true, the subject will be able to pause the video or move the playback to any point in the video. */\n controls: {\n type: ParameterType.BOOL,\n pretty_name: \"Controls\",\n default: false,\n },\n /** Time to start the clip. If null (default), video will start at the beginning of the file. */\n start: {\n type: ParameterType.FLOAT,\n pretty_name: \"Start\",\n default: null,\n },\n /** Time to stop the clip. If null (default), video will stop at the end of the file. */\n stop: {\n type: ParameterType.FLOAT,\n pretty_name: \"Stop\",\n default: null,\n },\n /** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */\n rate: {\n type: ParameterType.FLOAT,\n pretty_name: \"Rate\",\n default: 1,\n },\n /** If true, the trial will end immediately after the video finishes playing. */\n trial_ends_after_video: {\n type: ParameterType.BOOL,\n pretty_name: \"End trial after video finishes\",\n default: false,\n },\n /** How long to show trial before it ends. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, the trial will end when subject makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then responses are allowed while the video is playing. If false, then the video must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the\n * stimulus first appears on the screen until the participant's response.\n */\n rt: {\n type: ParameterType.INT,\n },\n /** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */\n stimulus: {\n type: ParameterType.STRING,\n array: true,\n },\n },\n // prettier-ignore\n citations: '__CITATIONS__',\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays a video file and records a keyboard response. The stimulus can be displayed until a response is\n * given, or for a pre-determined amount of time. The trial can be ended automatically when the participant responds,\n * when the video file has finished playing, or if the participant has failed to respond within a fixed length of time.\n * You can also prevent a keyboard response from being recorded before the video has finished playing.\n *\n * Video files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are\n * using timeline variables or another dynamic method to specify the video stimulus, you will need to\n * [manually preload](../overview/media-preloading.md#manual-preloading) the videos. Also note that video preloading\n * is disabled when the experiment is running as a file (i.e. opened directly in the browser, rather than through a\n * server), in order to prevent CORS errors - see the section on [Running Experiments](../overview/running-experiments.md)\n * for more information.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/video-keyboard-response/ video-keyboard-response plugin documentation on jspsych.org}\n */\nclass VideoKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>) {\n // setup stimulus\n var video_html = \"<div>\";\n video_html += '<video id=\"jspsych-video-keyboard-response-stimulus\"';\n\n if (trial.width) {\n video_html += ' width=\"' + trial.width + '\"';\n }\n if (trial.height) {\n video_html += ' height=\"' + trial.height + '\"';\n }\n if (trial.autoplay && trial.start == null) {\n // if autoplay is true and the start time is specified, then the video will start automatically\n // via the play() method, rather than the autoplay attribute, to prevent showing the first frame\n video_html += \" autoplay \";\n }\n if (trial.controls) {\n video_html += \" controls \";\n }\n if (trial.start !== null) {\n // hide video element when page loads if the start time is specified,\n // to prevent the video element from showing the first frame\n video_html += ' style=\"visibility: hidden;\"';\n }\n video_html += \">\";\n\n var video_preload_blob = this.jsPsych.pluginAPI.getVideoBuffer(trial.stimulus[0]);\n if (!video_preload_blob) {\n for (var i = 0; i < trial.stimulus.length; i++) {\n var file_name = trial.stimulus[i];\n if (file_name.indexOf(\"?\") > -1) {\n file_name = file_name.substring(0, file_name.indexOf(\"?\"));\n }\n var type = file_name.substr(file_name.lastIndexOf(\".\") + 1);\n type = type.toLowerCase();\n if (type == \"mov\") {\n console.warn(\n \"Warning: video-keyboard-response plugin does not reliably support .mov files.\"\n );\n }\n video_html += '<source src=\"' + file_name + '\" type=\"video/' + type + '\">';\n }\n }\n video_html += \"</video>\";\n video_html += \"</div>\";\n\n // add prompt if there is one\n if (trial.prompt !== null) {\n video_html += trial.prompt;\n }\n\n display_element.innerHTML = video_html;\n\n var video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n );\n\n if (video_preload_blob) {\n video_element.src = video_preload_blob;\n }\n\n video_element.onended = () => {\n if (trial.trial_ends_after_video) {\n end_trial();\n }\n if (trial.response_allowed_while_playing == false && !trial.trial_ends_after_video) {\n // start keyboard listener\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n };\n\n video_element.playbackRate = trial.rate;\n\n // if video start time is specified, hide the video and set the starting time\n // before showing and playing, so that the video doesn't automatically show the first frame\n if (trial.start !== null) {\n video_element.pause();\n video_element.onseeked = () => {\n video_element.style.visibility = \"visible\";\n video_element.muted = false;\n if (trial.autoplay) {\n video_element.play();\n } else {\n video_element.pause();\n }\n video_element.onseeked = () => {};\n };\n video_element.onplaying = () => {\n video_element.currentTime = trial.start;\n video_element.onplaying = () => {};\n };\n // fix for iOS/MacOS browsers: videos aren't seekable until they start playing, so need to hide/mute, play,\n // change current time, then show/unmute\n video_element.muted = true;\n video_element.play();\n }\n\n let stopped = false;\n if (trial.stop !== null) {\n video_element.addEventListener(\"timeupdate\", (e) => {\n var currenttime = video_element.currentTime;\n if (currenttime >= trial.stop) {\n if (!trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n video_element.pause();\n if (trial.trial_ends_after_video && !stopped) {\n // this is to prevent end_trial from being called twice, because the timeupdate event\n // can fire in quick succession\n stopped = true;\n end_trial();\n }\n }\n });\n }\n\n // store response\n var response = {\n rt: null,\n key: null,\n };\n\n // function to end trial when it is time\n const end_trial = () => {\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // stop the video file if it is playing\n // remove end event listeners if they exist\n display_element\n .querySelector<HTMLVideoElement>(\"#jspsych-video-keyboard-response-stimulus\")\n .pause();\n display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n ).onended = () => {};\n\n // gather the data to store for the trial\n var trial_data = {\n rt: response.rt,\n stimulus: trial.stimulus,\n response: response.key,\n };\n\n // move on to the next trial\n this.jsPsych.finishTrial(trial_data);\n };\n\n // function to handle responses by the subject\n var after_response = (info) => {\n // after a valid response, the stimulus will have the CSS class 'responded'\n // which can be used to provide visual feedback that a response was recorded\n display_element.querySelector(\"#jspsych-video-keyboard-response-stimulus\").className +=\n \" responded\";\n\n // only record the first response\n if (response.key == null) {\n response = info;\n }\n\n if (trial.response_ends_trial) {\n end_trial();\n }\n };\n\n // start the response listener\n if (trial.choices != \"NO_KEYS\" && trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(end_trial, trial.trial_duration);\n }\n }\n\n simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n this.jsPsych.finishTrial(data);\n }\n\n private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n this.trial(display_element, trial);\n load_callback();\n\n const video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-button-response-stimulus\"\n );\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n if (!trial.response_allowed_while_playing) {\n video_element.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default VideoKeyboardResponsePlugin;\n"],"names":["version"],"mappings":"0DAEEA,IAAAA,EAAW,kxCCkHA,UAAA,iuBAAe"}
|
package/dist/index.cjs
CHANGED
|
@@ -2,120 +2,96 @@
|
|
|
2
2
|
|
|
3
3
|
var jspsych = require('jspsych');
|
|
4
4
|
|
|
5
|
-
var
|
|
6
|
-
name: "@jspsych/plugin-video-keyboard-response",
|
|
7
|
-
version: "2.0.0",
|
|
8
|
-
description: "jsPsych plugin for playing a video file and getting a keyboard response",
|
|
9
|
-
type: "module",
|
|
10
|
-
main: "dist/index.cjs",
|
|
11
|
-
exports: {
|
|
12
|
-
import: "./dist/index.js",
|
|
13
|
-
require: "./dist/index.cjs"
|
|
14
|
-
},
|
|
15
|
-
typings: "dist/index.d.ts",
|
|
16
|
-
unpkg: "dist/index.browser.min.js",
|
|
17
|
-
files: [
|
|
18
|
-
"src",
|
|
19
|
-
"dist"
|
|
20
|
-
],
|
|
21
|
-
source: "src/index.ts",
|
|
22
|
-
scripts: {
|
|
23
|
-
test: "jest --passWithNoTests",
|
|
24
|
-
"test:watch": "npm test -- --watch",
|
|
25
|
-
tsc: "tsc",
|
|
26
|
-
build: "rollup --config",
|
|
27
|
-
"build:watch": "npm run build -- --watch"
|
|
28
|
-
},
|
|
29
|
-
repository: {
|
|
30
|
-
type: "git",
|
|
31
|
-
url: "git+https://github.com/jspsych/jsPsych.git",
|
|
32
|
-
directory: "packages/plugin-video-keyboard-response"
|
|
33
|
-
},
|
|
34
|
-
author: "Josh de Leeuw",
|
|
35
|
-
license: "MIT",
|
|
36
|
-
bugs: {
|
|
37
|
-
url: "https://github.com/jspsych/jsPsych/issues"
|
|
38
|
-
},
|
|
39
|
-
homepage: "https://www.jspsych.org/latest/plugins/video-keyboard-response",
|
|
40
|
-
peerDependencies: {
|
|
41
|
-
jspsych: ">=7.1.0"
|
|
42
|
-
},
|
|
43
|
-
devDependencies: {
|
|
44
|
-
"@jspsych/config": "^3.0.0",
|
|
45
|
-
"@jspsych/test-utils": "^1.2.0"
|
|
46
|
-
}
|
|
47
|
-
};
|
|
5
|
+
var version = "2.1.1";
|
|
48
6
|
|
|
49
7
|
const info = {
|
|
50
8
|
name: "video-keyboard-response",
|
|
51
|
-
version
|
|
9
|
+
version,
|
|
52
10
|
parameters: {
|
|
11
|
+
/** Array of the video file(s) to play. Video can be provided in multiple file formats for better cross-browser support. */
|
|
53
12
|
stimulus: {
|
|
54
13
|
type: jspsych.ParameterType.VIDEO,
|
|
55
14
|
pretty_name: "Video",
|
|
56
15
|
default: void 0,
|
|
57
16
|
array: true
|
|
58
17
|
},
|
|
18
|
+
/** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
|
|
59
19
|
choices: {
|
|
60
20
|
type: jspsych.ParameterType.KEYS,
|
|
61
21
|
pretty_name: "Choices",
|
|
62
22
|
default: "ALL_KEYS"
|
|
63
23
|
},
|
|
24
|
+
/** Any content here will be displayed below the stimulus. */
|
|
64
25
|
prompt: {
|
|
65
26
|
type: jspsych.ParameterType.HTML_STRING,
|
|
66
27
|
pretty_name: "Prompt",
|
|
67
28
|
default: null
|
|
68
29
|
},
|
|
30
|
+
/** The width of the video display in pixels. If `null`, the video will take the original video's dimensions,
|
|
31
|
+
* or properly scaled with the aspect ratio if the height is also specified.
|
|
32
|
+
*/
|
|
69
33
|
width: {
|
|
70
34
|
type: jspsych.ParameterType.INT,
|
|
71
35
|
pretty_name: "Width",
|
|
72
36
|
default: ""
|
|
73
37
|
},
|
|
38
|
+
/** The height of the video display in pixels. If `null`, the video will take the original video's dimensions,
|
|
39
|
+
* or properly scaled with the aspect ratio if the width is also specified.
|
|
40
|
+
*/
|
|
74
41
|
height: {
|
|
75
42
|
type: jspsych.ParameterType.INT,
|
|
76
43
|
pretty_name: "Height",
|
|
77
44
|
default: ""
|
|
78
45
|
},
|
|
46
|
+
/** If true, the video will begin playing as soon as it has loaded. */
|
|
79
47
|
autoplay: {
|
|
80
48
|
type: jspsych.ParameterType.BOOL,
|
|
81
49
|
pretty_name: "Autoplay",
|
|
82
50
|
default: true
|
|
83
51
|
},
|
|
52
|
+
/** If true, the subject will be able to pause the video or move the playback to any point in the video. */
|
|
84
53
|
controls: {
|
|
85
54
|
type: jspsych.ParameterType.BOOL,
|
|
86
55
|
pretty_name: "Controls",
|
|
87
56
|
default: false
|
|
88
57
|
},
|
|
58
|
+
/** Time to start the clip. If null (default), video will start at the beginning of the file. */
|
|
89
59
|
start: {
|
|
90
60
|
type: jspsych.ParameterType.FLOAT,
|
|
91
61
|
pretty_name: "Start",
|
|
92
62
|
default: null
|
|
93
63
|
},
|
|
64
|
+
/** Time to stop the clip. If null (default), video will stop at the end of the file. */
|
|
94
65
|
stop: {
|
|
95
66
|
type: jspsych.ParameterType.FLOAT,
|
|
96
67
|
pretty_name: "Stop",
|
|
97
68
|
default: null
|
|
98
69
|
},
|
|
70
|
+
/** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */
|
|
99
71
|
rate: {
|
|
100
72
|
type: jspsych.ParameterType.FLOAT,
|
|
101
73
|
pretty_name: "Rate",
|
|
102
74
|
default: 1
|
|
103
75
|
},
|
|
76
|
+
/** If true, the trial will end immediately after the video finishes playing. */
|
|
104
77
|
trial_ends_after_video: {
|
|
105
78
|
type: jspsych.ParameterType.BOOL,
|
|
106
79
|
pretty_name: "End trial after video finishes",
|
|
107
80
|
default: false
|
|
108
81
|
},
|
|
82
|
+
/** How long to show trial before it ends. */
|
|
109
83
|
trial_duration: {
|
|
110
84
|
type: jspsych.ParameterType.INT,
|
|
111
85
|
pretty_name: "Trial duration",
|
|
112
86
|
default: null
|
|
113
87
|
},
|
|
88
|
+
/** If true, the trial will end when subject makes a response. */
|
|
114
89
|
response_ends_trial: {
|
|
115
90
|
type: jspsych.ParameterType.BOOL,
|
|
116
91
|
pretty_name: "Response ends trial",
|
|
117
92
|
default: true
|
|
118
93
|
},
|
|
94
|
+
/** If true, then responses are allowed while the video is playing. If false, then the video must finish playing before a response is accepted. */
|
|
119
95
|
response_allowed_while_playing: {
|
|
120
96
|
type: jspsych.ParameterType.BOOL,
|
|
121
97
|
pretty_name: "Response allowed while playing",
|
|
@@ -123,29 +99,36 @@ const info = {
|
|
|
123
99
|
}
|
|
124
100
|
},
|
|
125
101
|
data: {
|
|
102
|
+
/** Indicates which key the participant pressed. */
|
|
126
103
|
response: {
|
|
127
104
|
type: jspsych.ParameterType.STRING
|
|
128
105
|
},
|
|
106
|
+
/** The response time in milliseconds for the participant to make a response. The time is measured from when the
|
|
107
|
+
* stimulus first appears on the screen until the participant's response.
|
|
108
|
+
*/
|
|
129
109
|
rt: {
|
|
130
110
|
type: jspsych.ParameterType.INT
|
|
131
111
|
},
|
|
112
|
+
/** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */
|
|
132
113
|
stimulus: {
|
|
133
114
|
type: jspsych.ParameterType.STRING,
|
|
134
115
|
array: true
|
|
135
116
|
}
|
|
117
|
+
},
|
|
118
|
+
// prettier-ignore
|
|
119
|
+
citations: {
|
|
120
|
+
"apa": "de Leeuw, J. R., Gilbert, R. A., & Luchterhandt, B. (2023). jsPsych: Enabling an Open-Source Collaborative Ecosystem of Behavioral Experiments. Journal of Open Source Software, 8(85), 5351. https://doi.org/10.21105/joss.05351 ",
|
|
121
|
+
"bibtex": '@article{Leeuw2023jsPsych, author = {de Leeuw, Joshua R. and Gilbert, Rebecca A. and Luchterhandt, Bj{\\" o}rn}, journal = {Journal of Open Source Software}, doi = {10.21105/joss.05351}, issn = {2475-9066}, number = {85}, year = {2023}, month = {may 11}, pages = {5351}, publisher = {Open Journals}, title = {jsPsych: Enabling an {Open}-{Source} {Collaborative} {Ecosystem} of {Behavioral} {Experiments}}, url = {https://joss.theoj.org/papers/10.21105/joss.05351}, volume = {8}, } '
|
|
136
122
|
}
|
|
137
123
|
};
|
|
138
124
|
class VideoKeyboardResponsePlugin {
|
|
139
125
|
constructor(jsPsych) {
|
|
140
126
|
this.jsPsych = jsPsych;
|
|
141
127
|
}
|
|
128
|
+
static {
|
|
129
|
+
this.info = info;
|
|
130
|
+
}
|
|
142
131
|
trial(display_element, trial) {
|
|
143
|
-
if (!Array.isArray(trial.stimulus)) {
|
|
144
|
-
throw new Error(`
|
|
145
|
-
The stimulus property for the video-keyboard-response plugin must be an array
|
|
146
|
-
of files. See https://www.jspsych.org/latest/plugins/video-keyboard-response/#parameters
|
|
147
|
-
`);
|
|
148
|
-
}
|
|
149
132
|
var video_html = "<div>";
|
|
150
133
|
video_html += '<video id="jspsych-video-keyboard-response-stimulus"';
|
|
151
134
|
if (trial.width) {
|
|
@@ -334,7 +317,6 @@ class VideoKeyboardResponsePlugin {
|
|
|
334
317
|
return data;
|
|
335
318
|
}
|
|
336
319
|
}
|
|
337
|
-
VideoKeyboardResponsePlugin.info = info;
|
|
338
320
|
|
|
339
321
|
module.exports = VideoKeyboardResponsePlugin;
|
|
340
322
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"video-keyboard-response\",\n version: version,\n parameters: {\n /** Array of the video file(s) to play. Video can be provided in multiple file formats for better cross-browser support. */\n stimulus: {\n type: ParameterType.VIDEO,\n pretty_name: \"Video\",\n default: undefined,\n array: true,\n },\n /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */\n choices: {\n type: ParameterType.KEYS,\n pretty_name: \"Choices\",\n default: \"ALL_KEYS\",\n },\n /** Any content here will be displayed below the stimulus. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** The width of the video in pixels. */\n width: {\n type: ParameterType.INT,\n pretty_name: \"Width\",\n default: \"\",\n },\n /** The height of the video display in pixels. */\n height: {\n type: ParameterType.INT,\n pretty_name: \"Height\",\n default: \"\",\n },\n /** If true, the video will begin playing as soon as it has loaded. */\n autoplay: {\n type: ParameterType.BOOL,\n pretty_name: \"Autoplay\",\n default: true,\n },\n /** If true, the subject will be able to pause the video or move the playback to any point in the video. */\n controls: {\n type: ParameterType.BOOL,\n pretty_name: \"Controls\",\n default: false,\n },\n /** Time to start the clip. If null (default), video will start at the beginning of the file. */\n start: {\n type: ParameterType.FLOAT,\n pretty_name: \"Start\",\n default: null,\n },\n /** Time to stop the clip. If null (default), video will stop at the end of the file. */\n stop: {\n type: ParameterType.FLOAT,\n pretty_name: \"Stop\",\n default: null,\n },\n /** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */\n rate: {\n type: ParameterType.FLOAT,\n pretty_name: \"Rate\",\n default: 1,\n },\n /** If true, the trial will end immediately after the video finishes playing. */\n trial_ends_after_video: {\n type: ParameterType.BOOL,\n pretty_name: \"End trial after video finishes\",\n default: false,\n },\n /** How long to show trial before it ends. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, the trial will end when subject makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then responses are allowed while the video is playing. If false, then the video must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the\n * stimulus first appears on the screen until the participant's response.\n * */\n rt: {\n type: ParameterType.INT,\n },\n /** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */\n stimulus: {\n type: ParameterType.STRING,\n array: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays a video file and records a keyboard response. The stimulus can be displayed until a response is\n * given, or for a pre-determined amount of time. The trial can be ended automatically when the participant responds,\n * when the video file has finished playing, or if the participant has failed to respond within a fixed length of time.\n * You can also prevent a keyboard response from being recorded before the video has finished playing.\n *\n * Video files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are\n * using timeline variables or another dynamic method to specify the video stimulus, you will need to\n * [manually preload](../overview/media-preloading.md#manual-preloading) the videos. Also note that video preloading\n * is disabled when the experiment is running as a file (i.e. opened directly in the browser, rather than through a\n * server), in order to prevent CORS errors - see the section on [Running Experiments](../overview/running-experiments.md)\n * for more information.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/video-keyboard-response/ video-keyboard-response plugin documentation on jspsych.org}\n */\nclass VideoKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>) {\n // catch mistake where stimuli are not an array\n if (!Array.isArray(trial.stimulus)) {\n throw new Error(`\n The stimulus property for the video-keyboard-response plugin must be an array\n of files. See https://www.jspsych.org/latest/plugins/video-keyboard-response/#parameters\n `);\n }\n\n // setup stimulus\n var video_html = \"<div>\";\n video_html += '<video id=\"jspsych-video-keyboard-response-stimulus\"';\n\n if (trial.width) {\n video_html += ' width=\"' + trial.width + '\"';\n }\n if (trial.height) {\n video_html += ' height=\"' + trial.height + '\"';\n }\n if (trial.autoplay && trial.start == null) {\n // if autoplay is true and the start time is specified, then the video will start automatically\n // via the play() method, rather than the autoplay attribute, to prevent showing the first frame\n video_html += \" autoplay \";\n }\n if (trial.controls) {\n video_html += \" controls \";\n }\n if (trial.start !== null) {\n // hide video element when page loads if the start time is specified,\n // to prevent the video element from showing the first frame\n video_html += ' style=\"visibility: hidden;\"';\n }\n video_html += \">\";\n\n var video_preload_blob = this.jsPsych.pluginAPI.getVideoBuffer(trial.stimulus[0]);\n if (!video_preload_blob) {\n for (var i = 0; i < trial.stimulus.length; i++) {\n var file_name = trial.stimulus[i];\n if (file_name.indexOf(\"?\") > -1) {\n file_name = file_name.substring(0, file_name.indexOf(\"?\"));\n }\n var type = file_name.substr(file_name.lastIndexOf(\".\") + 1);\n type = type.toLowerCase();\n if (type == \"mov\") {\n console.warn(\n \"Warning: video-keyboard-response plugin does not reliably support .mov files.\"\n );\n }\n video_html += '<source src=\"' + file_name + '\" type=\"video/' + type + '\">';\n }\n }\n video_html += \"</video>\";\n video_html += \"</div>\";\n\n // add prompt if there is one\n if (trial.prompt !== null) {\n video_html += trial.prompt;\n }\n\n display_element.innerHTML = video_html;\n\n var video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n );\n\n if (video_preload_blob) {\n video_element.src = video_preload_blob;\n }\n\n video_element.onended = () => {\n if (trial.trial_ends_after_video) {\n end_trial();\n }\n if (trial.response_allowed_while_playing == false && !trial.trial_ends_after_video) {\n // start keyboard listener\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n };\n\n video_element.playbackRate = trial.rate;\n\n // if video start time is specified, hide the video and set the starting time\n // before showing and playing, so that the video doesn't automatically show the first frame\n if (trial.start !== null) {\n video_element.pause();\n video_element.onseeked = () => {\n video_element.style.visibility = \"visible\";\n video_element.muted = false;\n if (trial.autoplay) {\n video_element.play();\n } else {\n video_element.pause();\n }\n video_element.onseeked = () => {};\n };\n video_element.onplaying = () => {\n video_element.currentTime = trial.start;\n video_element.onplaying = () => {};\n };\n // fix for iOS/MacOS browsers: videos aren't seekable until they start playing, so need to hide/mute, play,\n // change current time, then show/unmute\n video_element.muted = true;\n video_element.play();\n }\n\n let stopped = false;\n if (trial.stop !== null) {\n video_element.addEventListener(\"timeupdate\", (e) => {\n var currenttime = video_element.currentTime;\n if (currenttime >= trial.stop) {\n if (!trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n video_element.pause();\n if (trial.trial_ends_after_video && !stopped) {\n // this is to prevent end_trial from being called twice, because the timeupdate event\n // can fire in quick succession\n stopped = true;\n end_trial();\n }\n }\n });\n }\n\n // store response\n var response = {\n rt: null,\n key: null,\n };\n\n // function to end trial when it is time\n const end_trial = () => {\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // stop the video file if it is playing\n // remove end event listeners if they exist\n display_element\n .querySelector<HTMLVideoElement>(\"#jspsych-video-keyboard-response-stimulus\")\n .pause();\n display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n ).onended = () => {};\n\n // gather the data to store for the trial\n var trial_data = {\n rt: response.rt,\n stimulus: trial.stimulus,\n response: response.key,\n };\n\n // move on to the next trial\n this.jsPsych.finishTrial(trial_data);\n };\n\n // function to handle responses by the subject\n var after_response = (info) => {\n // after a valid response, the stimulus will have the CSS class 'responded'\n // which can be used to provide visual feedback that a response was recorded\n display_element.querySelector(\"#jspsych-video-keyboard-response-stimulus\").className +=\n \" responded\";\n\n // only record the first response\n if (response.key == null) {\n response = info;\n }\n\n if (trial.response_ends_trial) {\n end_trial();\n }\n };\n\n // start the response listener\n if (trial.choices != \"NO_KEYS\" && trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(end_trial, trial.trial_duration);\n }\n }\n\n simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n this.jsPsych.finishTrial(data);\n }\n\n private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n this.trial(display_element, trial);\n load_callback();\n\n const video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-button-response-stimulus\"\n );\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n if (!trial.response_allowed_while_playing) {\n video_element.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default VideoKeyboardResponsePlugin;\n"],"names":["version","ParameterType","info"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@jspsych/plugin-video-keyboard-response\",\n \"version\": \"2.1.1\",\n \"description\": \"jsPsych plugin for playing a video file and getting a keyboard response\",\n \"type\": \"module\",\n \"main\": \"dist/index.cjs\",\n \"exports\": {\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n },\n \"typings\": \"dist/index.d.ts\",\n \"unpkg\": \"dist/index.browser.min.js\",\n \"files\": [\n \"src\",\n \"dist\"\n ],\n \"source\": \"src/index.ts\",\n \"scripts\": {\n \"test\": \"jest --passWithNoTests\",\n \"test:watch\": \"npm test -- --watch\",\n \"tsc\": \"tsc\",\n \"build\": \"rollup --config\",\n \"build:watch\": \"npm run build -- --watch\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/jspsych/jsPsych.git\",\n \"directory\": \"packages/plugin-video-keyboard-response\"\n },\n \"author\": \"Josh de Leeuw\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/jspsych/jsPsych/issues\"\n },\n \"homepage\": \"https://www.jspsych.org/latest/plugins/video-keyboard-response\",\n \"peerDependencies\": {\n \"jspsych\": \">=7.1.0\"\n },\n \"devDependencies\": {\n \"@jspsych/config\": \"^3.3.1\",\n \"@jspsych/test-utils\": \"^1.2.0\"\n }\n}\n","import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"video-keyboard-response\",\n version: version,\n parameters: {\n /** Array of the video file(s) to play. Video can be provided in multiple file formats for better cross-browser support. */\n stimulus: {\n type: ParameterType.VIDEO,\n pretty_name: \"Video\",\n default: undefined,\n array: true,\n },\n /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */\n choices: {\n type: ParameterType.KEYS,\n pretty_name: \"Choices\",\n default: \"ALL_KEYS\",\n },\n /** Any content here will be displayed below the stimulus. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** The width of the video display in pixels. If `null`, the video will take the original video's dimensions, \n * or properly scaled with the aspect ratio if the height is also specified.\n */\n width: {\n type: ParameterType.INT,\n pretty_name: \"Width\",\n default: \"\",\n },\n /** The height of the video display in pixels. If `null`, the video will take the original video's dimensions,\n * or properly scaled with the aspect ratio if the width is also specified.\n */\n height: {\n type: ParameterType.INT,\n pretty_name: \"Height\",\n default: \"\",\n },\n /** If true, the video will begin playing as soon as it has loaded. */\n autoplay: {\n type: ParameterType.BOOL,\n pretty_name: \"Autoplay\",\n default: true,\n },\n /** If true, the subject will be able to pause the video or move the playback to any point in the video. */\n controls: {\n type: ParameterType.BOOL,\n pretty_name: \"Controls\",\n default: false,\n },\n /** Time to start the clip. If null (default), video will start at the beginning of the file. */\n start: {\n type: ParameterType.FLOAT,\n pretty_name: \"Start\",\n default: null,\n },\n /** Time to stop the clip. If null (default), video will stop at the end of the file. */\n stop: {\n type: ParameterType.FLOAT,\n pretty_name: \"Stop\",\n default: null,\n },\n /** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */\n rate: {\n type: ParameterType.FLOAT,\n pretty_name: \"Rate\",\n default: 1,\n },\n /** If true, the trial will end immediately after the video finishes playing. */\n trial_ends_after_video: {\n type: ParameterType.BOOL,\n pretty_name: \"End trial after video finishes\",\n default: false,\n },\n /** How long to show trial before it ends. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, the trial will end when subject makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then responses are allowed while the video is playing. If false, then the video must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the\n * stimulus first appears on the screen until the participant's response.\n */\n rt: {\n type: ParameterType.INT,\n },\n /** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */\n stimulus: {\n type: ParameterType.STRING,\n array: true,\n },\n },\n // prettier-ignore\n citations: '__CITATIONS__',\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays a video file and records a keyboard response. The stimulus can be displayed until a response is\n * given, or for a pre-determined amount of time. The trial can be ended automatically when the participant responds,\n * when the video file has finished playing, or if the participant has failed to respond within a fixed length of time.\n * You can also prevent a keyboard response from being recorded before the video has finished playing.\n *\n * Video files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are\n * using timeline variables or another dynamic method to specify the video stimulus, you will need to\n * [manually preload](../overview/media-preloading.md#manual-preloading) the videos. Also note that video preloading\n * is disabled when the experiment is running as a file (i.e. opened directly in the browser, rather than through a\n * server), in order to prevent CORS errors - see the section on [Running Experiments](../overview/running-experiments.md)\n * for more information.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/video-keyboard-response/ video-keyboard-response plugin documentation on jspsych.org}\n */\nclass VideoKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>) {\n // setup stimulus\n var video_html = \"<div>\";\n video_html += '<video id=\"jspsych-video-keyboard-response-stimulus\"';\n\n if (trial.width) {\n video_html += ' width=\"' + trial.width + '\"';\n }\n if (trial.height) {\n video_html += ' height=\"' + trial.height + '\"';\n }\n if (trial.autoplay && trial.start == null) {\n // if autoplay is true and the start time is specified, then the video will start automatically\n // via the play() method, rather than the autoplay attribute, to prevent showing the first frame\n video_html += \" autoplay \";\n }\n if (trial.controls) {\n video_html += \" controls \";\n }\n if (trial.start !== null) {\n // hide video element when page loads if the start time is specified,\n // to prevent the video element from showing the first frame\n video_html += ' style=\"visibility: hidden;\"';\n }\n video_html += \">\";\n\n var video_preload_blob = this.jsPsych.pluginAPI.getVideoBuffer(trial.stimulus[0]);\n if (!video_preload_blob) {\n for (var i = 0; i < trial.stimulus.length; i++) {\n var file_name = trial.stimulus[i];\n if (file_name.indexOf(\"?\") > -1) {\n file_name = file_name.substring(0, file_name.indexOf(\"?\"));\n }\n var type = file_name.substr(file_name.lastIndexOf(\".\") + 1);\n type = type.toLowerCase();\n if (type == \"mov\") {\n console.warn(\n \"Warning: video-keyboard-response plugin does not reliably support .mov files.\"\n );\n }\n video_html += '<source src=\"' + file_name + '\" type=\"video/' + type + '\">';\n }\n }\n video_html += \"</video>\";\n video_html += \"</div>\";\n\n // add prompt if there is one\n if (trial.prompt !== null) {\n video_html += trial.prompt;\n }\n\n display_element.innerHTML = video_html;\n\n var video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n );\n\n if (video_preload_blob) {\n video_element.src = video_preload_blob;\n }\n\n video_element.onended = () => {\n if (trial.trial_ends_after_video) {\n end_trial();\n }\n if (trial.response_allowed_while_playing == false && !trial.trial_ends_after_video) {\n // start keyboard listener\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n };\n\n video_element.playbackRate = trial.rate;\n\n // if video start time is specified, hide the video and set the starting time\n // before showing and playing, so that the video doesn't automatically show the first frame\n if (trial.start !== null) {\n video_element.pause();\n video_element.onseeked = () => {\n video_element.style.visibility = \"visible\";\n video_element.muted = false;\n if (trial.autoplay) {\n video_element.play();\n } else {\n video_element.pause();\n }\n video_element.onseeked = () => {};\n };\n video_element.onplaying = () => {\n video_element.currentTime = trial.start;\n video_element.onplaying = () => {};\n };\n // fix for iOS/MacOS browsers: videos aren't seekable until they start playing, so need to hide/mute, play,\n // change current time, then show/unmute\n video_element.muted = true;\n video_element.play();\n }\n\n let stopped = false;\n if (trial.stop !== null) {\n video_element.addEventListener(\"timeupdate\", (e) => {\n var currenttime = video_element.currentTime;\n if (currenttime >= trial.stop) {\n if (!trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n video_element.pause();\n if (trial.trial_ends_after_video && !stopped) {\n // this is to prevent end_trial from being called twice, because the timeupdate event\n // can fire in quick succession\n stopped = true;\n end_trial();\n }\n }\n });\n }\n\n // store response\n var response = {\n rt: null,\n key: null,\n };\n\n // function to end trial when it is time\n const end_trial = () => {\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // stop the video file if it is playing\n // remove end event listeners if they exist\n display_element\n .querySelector<HTMLVideoElement>(\"#jspsych-video-keyboard-response-stimulus\")\n .pause();\n display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n ).onended = () => {};\n\n // gather the data to store for the trial\n var trial_data = {\n rt: response.rt,\n stimulus: trial.stimulus,\n response: response.key,\n };\n\n // move on to the next trial\n this.jsPsych.finishTrial(trial_data);\n };\n\n // function to handle responses by the subject\n var after_response = (info) => {\n // after a valid response, the stimulus will have the CSS class 'responded'\n // which can be used to provide visual feedback that a response was recorded\n display_element.querySelector(\"#jspsych-video-keyboard-response-stimulus\").className +=\n \" responded\";\n\n // only record the first response\n if (response.key == null) {\n response = info;\n }\n\n if (trial.response_ends_trial) {\n end_trial();\n }\n };\n\n // start the response listener\n if (trial.choices != \"NO_KEYS\" && trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(end_trial, trial.trial_duration);\n }\n }\n\n simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n this.jsPsych.finishTrial(data);\n }\n\n private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n this.trial(display_element, trial);\n load_callback();\n\n const video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-button-response-stimulus\"\n );\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n if (!trial.response_allowed_while_playing) {\n video_element.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default VideoKeyboardResponsePlugin;\n"],"names":[],"mappings":";;;;AAEE,IAAW,OAAA,GAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECkHA,SAAA,EAAA;AAAA;;GAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -23,13 +23,17 @@ declare const info: {
|
|
|
23
23
|
readonly pretty_name: "Prompt";
|
|
24
24
|
readonly default: any;
|
|
25
25
|
};
|
|
26
|
-
/** The width of the video in pixels.
|
|
26
|
+
/** The width of the video display in pixels. If `null`, the video will take the original video's dimensions,
|
|
27
|
+
* or properly scaled with the aspect ratio if the height is also specified.
|
|
28
|
+
*/
|
|
27
29
|
readonly width: {
|
|
28
30
|
readonly type: ParameterType.INT;
|
|
29
31
|
readonly pretty_name: "Width";
|
|
30
32
|
readonly default: "";
|
|
31
33
|
};
|
|
32
|
-
/** The height of the video display in pixels.
|
|
34
|
+
/** The height of the video display in pixels. If `null`, the video will take the original video's dimensions,
|
|
35
|
+
* or properly scaled with the aspect ratio if the width is also specified.
|
|
36
|
+
*/
|
|
33
37
|
readonly height: {
|
|
34
38
|
readonly type: ParameterType.INT;
|
|
35
39
|
readonly pretty_name: "Height";
|
|
@@ -97,7 +101,7 @@ declare const info: {
|
|
|
97
101
|
};
|
|
98
102
|
/** The response time in milliseconds for the participant to make a response. The time is measured from when the
|
|
99
103
|
* stimulus first appears on the screen until the participant's response.
|
|
100
|
-
|
|
104
|
+
*/
|
|
101
105
|
readonly rt: {
|
|
102
106
|
readonly type: ParameterType.INT;
|
|
103
107
|
};
|
|
@@ -107,6 +111,7 @@ declare const info: {
|
|
|
107
111
|
readonly array: true;
|
|
108
112
|
};
|
|
109
113
|
};
|
|
114
|
+
readonly citations: "__CITATIONS__";
|
|
110
115
|
};
|
|
111
116
|
type Info = typeof info;
|
|
112
117
|
/**
|
|
@@ -150,13 +155,17 @@ declare class VideoKeyboardResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
150
155
|
readonly pretty_name: "Prompt";
|
|
151
156
|
readonly default: any;
|
|
152
157
|
};
|
|
153
|
-
/** The width of the video in pixels.
|
|
158
|
+
/** The width of the video display in pixels. If `null`, the video will take the original video's dimensions,
|
|
159
|
+
* or properly scaled with the aspect ratio if the height is also specified.
|
|
160
|
+
*/
|
|
154
161
|
readonly width: {
|
|
155
162
|
readonly type: ParameterType.INT;
|
|
156
163
|
readonly pretty_name: "Width";
|
|
157
164
|
readonly default: "";
|
|
158
165
|
};
|
|
159
|
-
/** The height of the video display in pixels.
|
|
166
|
+
/** The height of the video display in pixels. If `null`, the video will take the original video's dimensions,
|
|
167
|
+
* or properly scaled with the aspect ratio if the width is also specified.
|
|
168
|
+
*/
|
|
160
169
|
readonly height: {
|
|
161
170
|
readonly type: ParameterType.INT;
|
|
162
171
|
readonly pretty_name: "Height";
|
|
@@ -224,7 +233,7 @@ declare class VideoKeyboardResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
224
233
|
};
|
|
225
234
|
/** The response time in milliseconds for the participant to make a response. The time is measured from when the
|
|
226
235
|
* stimulus first appears on the screen until the participant's response.
|
|
227
|
-
|
|
236
|
+
*/
|
|
228
237
|
readonly rt: {
|
|
229
238
|
readonly type: ParameterType.INT;
|
|
230
239
|
};
|
|
@@ -234,6 +243,7 @@ declare class VideoKeyboardResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
234
243
|
readonly array: true;
|
|
235
244
|
};
|
|
236
245
|
};
|
|
246
|
+
readonly citations: "__CITATIONS__";
|
|
237
247
|
};
|
|
238
248
|
constructor(jsPsych: JsPsych);
|
|
239
249
|
trial(display_element: HTMLElement, trial: TrialType<Info>): void;
|
package/dist/index.js
CHANGED
|
@@ -1,119 +1,95 @@
|
|
|
1
1
|
import { ParameterType } from 'jspsych';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
name: "@jspsych/plugin-video-keyboard-response",
|
|
5
|
-
version: "2.0.0",
|
|
6
|
-
description: "jsPsych plugin for playing a video file and getting a keyboard response",
|
|
7
|
-
type: "module",
|
|
8
|
-
main: "dist/index.cjs",
|
|
9
|
-
exports: {
|
|
10
|
-
import: "./dist/index.js",
|
|
11
|
-
require: "./dist/index.cjs"
|
|
12
|
-
},
|
|
13
|
-
typings: "dist/index.d.ts",
|
|
14
|
-
unpkg: "dist/index.browser.min.js",
|
|
15
|
-
files: [
|
|
16
|
-
"src",
|
|
17
|
-
"dist"
|
|
18
|
-
],
|
|
19
|
-
source: "src/index.ts",
|
|
20
|
-
scripts: {
|
|
21
|
-
test: "jest --passWithNoTests",
|
|
22
|
-
"test:watch": "npm test -- --watch",
|
|
23
|
-
tsc: "tsc",
|
|
24
|
-
build: "rollup --config",
|
|
25
|
-
"build:watch": "npm run build -- --watch"
|
|
26
|
-
},
|
|
27
|
-
repository: {
|
|
28
|
-
type: "git",
|
|
29
|
-
url: "git+https://github.com/jspsych/jsPsych.git",
|
|
30
|
-
directory: "packages/plugin-video-keyboard-response"
|
|
31
|
-
},
|
|
32
|
-
author: "Josh de Leeuw",
|
|
33
|
-
license: "MIT",
|
|
34
|
-
bugs: {
|
|
35
|
-
url: "https://github.com/jspsych/jsPsych/issues"
|
|
36
|
-
},
|
|
37
|
-
homepage: "https://www.jspsych.org/latest/plugins/video-keyboard-response",
|
|
38
|
-
peerDependencies: {
|
|
39
|
-
jspsych: ">=7.1.0"
|
|
40
|
-
},
|
|
41
|
-
devDependencies: {
|
|
42
|
-
"@jspsych/config": "^3.0.0",
|
|
43
|
-
"@jspsych/test-utils": "^1.2.0"
|
|
44
|
-
}
|
|
45
|
-
};
|
|
3
|
+
var version = "2.1.1";
|
|
46
4
|
|
|
47
5
|
const info = {
|
|
48
6
|
name: "video-keyboard-response",
|
|
49
|
-
version
|
|
7
|
+
version,
|
|
50
8
|
parameters: {
|
|
9
|
+
/** Array of the video file(s) to play. Video can be provided in multiple file formats for better cross-browser support. */
|
|
51
10
|
stimulus: {
|
|
52
11
|
type: ParameterType.VIDEO,
|
|
53
12
|
pretty_name: "Video",
|
|
54
13
|
default: void 0,
|
|
55
14
|
array: true
|
|
56
15
|
},
|
|
16
|
+
/** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
|
|
57
17
|
choices: {
|
|
58
18
|
type: ParameterType.KEYS,
|
|
59
19
|
pretty_name: "Choices",
|
|
60
20
|
default: "ALL_KEYS"
|
|
61
21
|
},
|
|
22
|
+
/** Any content here will be displayed below the stimulus. */
|
|
62
23
|
prompt: {
|
|
63
24
|
type: ParameterType.HTML_STRING,
|
|
64
25
|
pretty_name: "Prompt",
|
|
65
26
|
default: null
|
|
66
27
|
},
|
|
28
|
+
/** The width of the video display in pixels. If `null`, the video will take the original video's dimensions,
|
|
29
|
+
* or properly scaled with the aspect ratio if the height is also specified.
|
|
30
|
+
*/
|
|
67
31
|
width: {
|
|
68
32
|
type: ParameterType.INT,
|
|
69
33
|
pretty_name: "Width",
|
|
70
34
|
default: ""
|
|
71
35
|
},
|
|
36
|
+
/** The height of the video display in pixels. If `null`, the video will take the original video's dimensions,
|
|
37
|
+
* or properly scaled with the aspect ratio if the width is also specified.
|
|
38
|
+
*/
|
|
72
39
|
height: {
|
|
73
40
|
type: ParameterType.INT,
|
|
74
41
|
pretty_name: "Height",
|
|
75
42
|
default: ""
|
|
76
43
|
},
|
|
44
|
+
/** If true, the video will begin playing as soon as it has loaded. */
|
|
77
45
|
autoplay: {
|
|
78
46
|
type: ParameterType.BOOL,
|
|
79
47
|
pretty_name: "Autoplay",
|
|
80
48
|
default: true
|
|
81
49
|
},
|
|
50
|
+
/** If true, the subject will be able to pause the video or move the playback to any point in the video. */
|
|
82
51
|
controls: {
|
|
83
52
|
type: ParameterType.BOOL,
|
|
84
53
|
pretty_name: "Controls",
|
|
85
54
|
default: false
|
|
86
55
|
},
|
|
56
|
+
/** Time to start the clip. If null (default), video will start at the beginning of the file. */
|
|
87
57
|
start: {
|
|
88
58
|
type: ParameterType.FLOAT,
|
|
89
59
|
pretty_name: "Start",
|
|
90
60
|
default: null
|
|
91
61
|
},
|
|
62
|
+
/** Time to stop the clip. If null (default), video will stop at the end of the file. */
|
|
92
63
|
stop: {
|
|
93
64
|
type: ParameterType.FLOAT,
|
|
94
65
|
pretty_name: "Stop",
|
|
95
66
|
default: null
|
|
96
67
|
},
|
|
68
|
+
/** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */
|
|
97
69
|
rate: {
|
|
98
70
|
type: ParameterType.FLOAT,
|
|
99
71
|
pretty_name: "Rate",
|
|
100
72
|
default: 1
|
|
101
73
|
},
|
|
74
|
+
/** If true, the trial will end immediately after the video finishes playing. */
|
|
102
75
|
trial_ends_after_video: {
|
|
103
76
|
type: ParameterType.BOOL,
|
|
104
77
|
pretty_name: "End trial after video finishes",
|
|
105
78
|
default: false
|
|
106
79
|
},
|
|
80
|
+
/** How long to show trial before it ends. */
|
|
107
81
|
trial_duration: {
|
|
108
82
|
type: ParameterType.INT,
|
|
109
83
|
pretty_name: "Trial duration",
|
|
110
84
|
default: null
|
|
111
85
|
},
|
|
86
|
+
/** If true, the trial will end when subject makes a response. */
|
|
112
87
|
response_ends_trial: {
|
|
113
88
|
type: ParameterType.BOOL,
|
|
114
89
|
pretty_name: "Response ends trial",
|
|
115
90
|
default: true
|
|
116
91
|
},
|
|
92
|
+
/** If true, then responses are allowed while the video is playing. If false, then the video must finish playing before a response is accepted. */
|
|
117
93
|
response_allowed_while_playing: {
|
|
118
94
|
type: ParameterType.BOOL,
|
|
119
95
|
pretty_name: "Response allowed while playing",
|
|
@@ -121,29 +97,36 @@ const info = {
|
|
|
121
97
|
}
|
|
122
98
|
},
|
|
123
99
|
data: {
|
|
100
|
+
/** Indicates which key the participant pressed. */
|
|
124
101
|
response: {
|
|
125
102
|
type: ParameterType.STRING
|
|
126
103
|
},
|
|
104
|
+
/** The response time in milliseconds for the participant to make a response. The time is measured from when the
|
|
105
|
+
* stimulus first appears on the screen until the participant's response.
|
|
106
|
+
*/
|
|
127
107
|
rt: {
|
|
128
108
|
type: ParameterType.INT
|
|
129
109
|
},
|
|
110
|
+
/** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */
|
|
130
111
|
stimulus: {
|
|
131
112
|
type: ParameterType.STRING,
|
|
132
113
|
array: true
|
|
133
114
|
}
|
|
115
|
+
},
|
|
116
|
+
// prettier-ignore
|
|
117
|
+
citations: {
|
|
118
|
+
"apa": "de Leeuw, J. R., Gilbert, R. A., & Luchterhandt, B. (2023). jsPsych: Enabling an Open-Source Collaborative Ecosystem of Behavioral Experiments. Journal of Open Source Software, 8(85), 5351. https://doi.org/10.21105/joss.05351 ",
|
|
119
|
+
"bibtex": '@article{Leeuw2023jsPsych, author = {de Leeuw, Joshua R. and Gilbert, Rebecca A. and Luchterhandt, Bj{\\" o}rn}, journal = {Journal of Open Source Software}, doi = {10.21105/joss.05351}, issn = {2475-9066}, number = {85}, year = {2023}, month = {may 11}, pages = {5351}, publisher = {Open Journals}, title = {jsPsych: Enabling an {Open}-{Source} {Collaborative} {Ecosystem} of {Behavioral} {Experiments}}, url = {https://joss.theoj.org/papers/10.21105/joss.05351}, volume = {8}, } '
|
|
134
120
|
}
|
|
135
121
|
};
|
|
136
122
|
class VideoKeyboardResponsePlugin {
|
|
137
123
|
constructor(jsPsych) {
|
|
138
124
|
this.jsPsych = jsPsych;
|
|
139
125
|
}
|
|
126
|
+
static {
|
|
127
|
+
this.info = info;
|
|
128
|
+
}
|
|
140
129
|
trial(display_element, trial) {
|
|
141
|
-
if (!Array.isArray(trial.stimulus)) {
|
|
142
|
-
throw new Error(`
|
|
143
|
-
The stimulus property for the video-keyboard-response plugin must be an array
|
|
144
|
-
of files. See https://www.jspsych.org/latest/plugins/video-keyboard-response/#parameters
|
|
145
|
-
`);
|
|
146
|
-
}
|
|
147
130
|
var video_html = "<div>";
|
|
148
131
|
video_html += '<video id="jspsych-video-keyboard-response-stimulus"';
|
|
149
132
|
if (trial.width) {
|
|
@@ -332,7 +315,6 @@ class VideoKeyboardResponsePlugin {
|
|
|
332
315
|
return data;
|
|
333
316
|
}
|
|
334
317
|
}
|
|
335
|
-
VideoKeyboardResponsePlugin.info = info;
|
|
336
318
|
|
|
337
319
|
export { VideoKeyboardResponsePlugin as default };
|
|
338
320
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"video-keyboard-response\",\n version: version,\n parameters: {\n /** Array of the video file(s) to play. Video can be provided in multiple file formats for better cross-browser support. */\n stimulus: {\n type: ParameterType.VIDEO,\n pretty_name: \"Video\",\n default: undefined,\n array: true,\n },\n /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */\n choices: {\n type: ParameterType.KEYS,\n pretty_name: \"Choices\",\n default: \"ALL_KEYS\",\n },\n /** Any content here will be displayed below the stimulus. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** The width of the video in pixels. */\n width: {\n type: ParameterType.INT,\n pretty_name: \"Width\",\n default: \"\",\n },\n /** The height of the video display in pixels. */\n height: {\n type: ParameterType.INT,\n pretty_name: \"Height\",\n default: \"\",\n },\n /** If true, the video will begin playing as soon as it has loaded. */\n autoplay: {\n type: ParameterType.BOOL,\n pretty_name: \"Autoplay\",\n default: true,\n },\n /** If true, the subject will be able to pause the video or move the playback to any point in the video. */\n controls: {\n type: ParameterType.BOOL,\n pretty_name: \"Controls\",\n default: false,\n },\n /** Time to start the clip. If null (default), video will start at the beginning of the file. */\n start: {\n type: ParameterType.FLOAT,\n pretty_name: \"Start\",\n default: null,\n },\n /** Time to stop the clip. If null (default), video will stop at the end of the file. */\n stop: {\n type: ParameterType.FLOAT,\n pretty_name: \"Stop\",\n default: null,\n },\n /** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */\n rate: {\n type: ParameterType.FLOAT,\n pretty_name: \"Rate\",\n default: 1,\n },\n /** If true, the trial will end immediately after the video finishes playing. */\n trial_ends_after_video: {\n type: ParameterType.BOOL,\n pretty_name: \"End trial after video finishes\",\n default: false,\n },\n /** How long to show trial before it ends. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, the trial will end when subject makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then responses are allowed while the video is playing. If false, then the video must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the\n * stimulus first appears on the screen until the participant's response.\n * */\n rt: {\n type: ParameterType.INT,\n },\n /** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */\n stimulus: {\n type: ParameterType.STRING,\n array: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays a video file and records a keyboard response. The stimulus can be displayed until a response is\n * given, or for a pre-determined amount of time. The trial can be ended automatically when the participant responds,\n * when the video file has finished playing, or if the participant has failed to respond within a fixed length of time.\n * You can also prevent a keyboard response from being recorded before the video has finished playing.\n *\n * Video files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are\n * using timeline variables or another dynamic method to specify the video stimulus, you will need to\n * [manually preload](../overview/media-preloading.md#manual-preloading) the videos. Also note that video preloading\n * is disabled when the experiment is running as a file (i.e. opened directly in the browser, rather than through a\n * server), in order to prevent CORS errors - see the section on [Running Experiments](../overview/running-experiments.md)\n * for more information.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/video-keyboard-response/ video-keyboard-response plugin documentation on jspsych.org}\n */\nclass VideoKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>) {\n // catch mistake where stimuli are not an array\n if (!Array.isArray(trial.stimulus)) {\n throw new Error(`\n The stimulus property for the video-keyboard-response plugin must be an array\n of files. See https://www.jspsych.org/latest/plugins/video-keyboard-response/#parameters\n `);\n }\n\n // setup stimulus\n var video_html = \"<div>\";\n video_html += '<video id=\"jspsych-video-keyboard-response-stimulus\"';\n\n if (trial.width) {\n video_html += ' width=\"' + trial.width + '\"';\n }\n if (trial.height) {\n video_html += ' height=\"' + trial.height + '\"';\n }\n if (trial.autoplay && trial.start == null) {\n // if autoplay is true and the start time is specified, then the video will start automatically\n // via the play() method, rather than the autoplay attribute, to prevent showing the first frame\n video_html += \" autoplay \";\n }\n if (trial.controls) {\n video_html += \" controls \";\n }\n if (trial.start !== null) {\n // hide video element when page loads if the start time is specified,\n // to prevent the video element from showing the first frame\n video_html += ' style=\"visibility: hidden;\"';\n }\n video_html += \">\";\n\n var video_preload_blob = this.jsPsych.pluginAPI.getVideoBuffer(trial.stimulus[0]);\n if (!video_preload_blob) {\n for (var i = 0; i < trial.stimulus.length; i++) {\n var file_name = trial.stimulus[i];\n if (file_name.indexOf(\"?\") > -1) {\n file_name = file_name.substring(0, file_name.indexOf(\"?\"));\n }\n var type = file_name.substr(file_name.lastIndexOf(\".\") + 1);\n type = type.toLowerCase();\n if (type == \"mov\") {\n console.warn(\n \"Warning: video-keyboard-response plugin does not reliably support .mov files.\"\n );\n }\n video_html += '<source src=\"' + file_name + '\" type=\"video/' + type + '\">';\n }\n }\n video_html += \"</video>\";\n video_html += \"</div>\";\n\n // add prompt if there is one\n if (trial.prompt !== null) {\n video_html += trial.prompt;\n }\n\n display_element.innerHTML = video_html;\n\n var video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n );\n\n if (video_preload_blob) {\n video_element.src = video_preload_blob;\n }\n\n video_element.onended = () => {\n if (trial.trial_ends_after_video) {\n end_trial();\n }\n if (trial.response_allowed_while_playing == false && !trial.trial_ends_after_video) {\n // start keyboard listener\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n };\n\n video_element.playbackRate = trial.rate;\n\n // if video start time is specified, hide the video and set the starting time\n // before showing and playing, so that the video doesn't automatically show the first frame\n if (trial.start !== null) {\n video_element.pause();\n video_element.onseeked = () => {\n video_element.style.visibility = \"visible\";\n video_element.muted = false;\n if (trial.autoplay) {\n video_element.play();\n } else {\n video_element.pause();\n }\n video_element.onseeked = () => {};\n };\n video_element.onplaying = () => {\n video_element.currentTime = trial.start;\n video_element.onplaying = () => {};\n };\n // fix for iOS/MacOS browsers: videos aren't seekable until they start playing, so need to hide/mute, play,\n // change current time, then show/unmute\n video_element.muted = true;\n video_element.play();\n }\n\n let stopped = false;\n if (trial.stop !== null) {\n video_element.addEventListener(\"timeupdate\", (e) => {\n var currenttime = video_element.currentTime;\n if (currenttime >= trial.stop) {\n if (!trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n video_element.pause();\n if (trial.trial_ends_after_video && !stopped) {\n // this is to prevent end_trial from being called twice, because the timeupdate event\n // can fire in quick succession\n stopped = true;\n end_trial();\n }\n }\n });\n }\n\n // store response\n var response = {\n rt: null,\n key: null,\n };\n\n // function to end trial when it is time\n const end_trial = () => {\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // stop the video file if it is playing\n // remove end event listeners if they exist\n display_element\n .querySelector<HTMLVideoElement>(\"#jspsych-video-keyboard-response-stimulus\")\n .pause();\n display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n ).onended = () => {};\n\n // gather the data to store for the trial\n var trial_data = {\n rt: response.rt,\n stimulus: trial.stimulus,\n response: response.key,\n };\n\n // move on to the next trial\n this.jsPsych.finishTrial(trial_data);\n };\n\n // function to handle responses by the subject\n var after_response = (info) => {\n // after a valid response, the stimulus will have the CSS class 'responded'\n // which can be used to provide visual feedback that a response was recorded\n display_element.querySelector(\"#jspsych-video-keyboard-response-stimulus\").className +=\n \" responded\";\n\n // only record the first response\n if (response.key == null) {\n response = info;\n }\n\n if (trial.response_ends_trial) {\n end_trial();\n }\n };\n\n // start the response listener\n if (trial.choices != \"NO_KEYS\" && trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(end_trial, trial.trial_duration);\n }\n }\n\n simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n this.jsPsych.finishTrial(data);\n }\n\n private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n this.trial(display_element, trial);\n load_callback();\n\n const video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-button-response-stimulus\"\n );\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n if (!trial.response_allowed_while_playing) {\n video_element.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default VideoKeyboardResponsePlugin;\n"],"names":["version","info"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@jspsych/plugin-video-keyboard-response\",\n \"version\": \"2.1.1\",\n \"description\": \"jsPsych plugin for playing a video file and getting a keyboard response\",\n \"type\": \"module\",\n \"main\": \"dist/index.cjs\",\n \"exports\": {\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n },\n \"typings\": \"dist/index.d.ts\",\n \"unpkg\": \"dist/index.browser.min.js\",\n \"files\": [\n \"src\",\n \"dist\"\n ],\n \"source\": \"src/index.ts\",\n \"scripts\": {\n \"test\": \"jest --passWithNoTests\",\n \"test:watch\": \"npm test -- --watch\",\n \"tsc\": \"tsc\",\n \"build\": \"rollup --config\",\n \"build:watch\": \"npm run build -- --watch\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/jspsych/jsPsych.git\",\n \"directory\": \"packages/plugin-video-keyboard-response\"\n },\n \"author\": \"Josh de Leeuw\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/jspsych/jsPsych/issues\"\n },\n \"homepage\": \"https://www.jspsych.org/latest/plugins/video-keyboard-response\",\n \"peerDependencies\": {\n \"jspsych\": \">=7.1.0\"\n },\n \"devDependencies\": {\n \"@jspsych/config\": \"^3.3.1\",\n \"@jspsych/test-utils\": \"^1.2.0\"\n }\n}\n","import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"video-keyboard-response\",\n version: version,\n parameters: {\n /** Array of the video file(s) to play. Video can be provided in multiple file formats for better cross-browser support. */\n stimulus: {\n type: ParameterType.VIDEO,\n pretty_name: \"Video\",\n default: undefined,\n array: true,\n },\n /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */\n choices: {\n type: ParameterType.KEYS,\n pretty_name: \"Choices\",\n default: \"ALL_KEYS\",\n },\n /** Any content here will be displayed below the stimulus. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** The width of the video display in pixels. If `null`, the video will take the original video's dimensions, \n * or properly scaled with the aspect ratio if the height is also specified.\n */\n width: {\n type: ParameterType.INT,\n pretty_name: \"Width\",\n default: \"\",\n },\n /** The height of the video display in pixels. If `null`, the video will take the original video's dimensions,\n * or properly scaled with the aspect ratio if the width is also specified.\n */\n height: {\n type: ParameterType.INT,\n pretty_name: \"Height\",\n default: \"\",\n },\n /** If true, the video will begin playing as soon as it has loaded. */\n autoplay: {\n type: ParameterType.BOOL,\n pretty_name: \"Autoplay\",\n default: true,\n },\n /** If true, the subject will be able to pause the video or move the playback to any point in the video. */\n controls: {\n type: ParameterType.BOOL,\n pretty_name: \"Controls\",\n default: false,\n },\n /** Time to start the clip. If null (default), video will start at the beginning of the file. */\n start: {\n type: ParameterType.FLOAT,\n pretty_name: \"Start\",\n default: null,\n },\n /** Time to stop the clip. If null (default), video will stop at the end of the file. */\n stop: {\n type: ParameterType.FLOAT,\n pretty_name: \"Stop\",\n default: null,\n },\n /** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */\n rate: {\n type: ParameterType.FLOAT,\n pretty_name: \"Rate\",\n default: 1,\n },\n /** If true, the trial will end immediately after the video finishes playing. */\n trial_ends_after_video: {\n type: ParameterType.BOOL,\n pretty_name: \"End trial after video finishes\",\n default: false,\n },\n /** How long to show trial before it ends. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, the trial will end when subject makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then responses are allowed while the video is playing. If false, then the video must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the\n * stimulus first appears on the screen until the participant's response.\n */\n rt: {\n type: ParameterType.INT,\n },\n /** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */\n stimulus: {\n type: ParameterType.STRING,\n array: true,\n },\n },\n // prettier-ignore\n citations: '__CITATIONS__',\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays a video file and records a keyboard response. The stimulus can be displayed until a response is\n * given, or for a pre-determined amount of time. The trial can be ended automatically when the participant responds,\n * when the video file has finished playing, or if the participant has failed to respond within a fixed length of time.\n * You can also prevent a keyboard response from being recorded before the video has finished playing.\n *\n * Video files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are\n * using timeline variables or another dynamic method to specify the video stimulus, you will need to\n * [manually preload](../overview/media-preloading.md#manual-preloading) the videos. Also note that video preloading\n * is disabled when the experiment is running as a file (i.e. opened directly in the browser, rather than through a\n * server), in order to prevent CORS errors - see the section on [Running Experiments](../overview/running-experiments.md)\n * for more information.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/video-keyboard-response/ video-keyboard-response plugin documentation on jspsych.org}\n */\nclass VideoKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>) {\n // setup stimulus\n var video_html = \"<div>\";\n video_html += '<video id=\"jspsych-video-keyboard-response-stimulus\"';\n\n if (trial.width) {\n video_html += ' width=\"' + trial.width + '\"';\n }\n if (trial.height) {\n video_html += ' height=\"' + trial.height + '\"';\n }\n if (trial.autoplay && trial.start == null) {\n // if autoplay is true and the start time is specified, then the video will start automatically\n // via the play() method, rather than the autoplay attribute, to prevent showing the first frame\n video_html += \" autoplay \";\n }\n if (trial.controls) {\n video_html += \" controls \";\n }\n if (trial.start !== null) {\n // hide video element when page loads if the start time is specified,\n // to prevent the video element from showing the first frame\n video_html += ' style=\"visibility: hidden;\"';\n }\n video_html += \">\";\n\n var video_preload_blob = this.jsPsych.pluginAPI.getVideoBuffer(trial.stimulus[0]);\n if (!video_preload_blob) {\n for (var i = 0; i < trial.stimulus.length; i++) {\n var file_name = trial.stimulus[i];\n if (file_name.indexOf(\"?\") > -1) {\n file_name = file_name.substring(0, file_name.indexOf(\"?\"));\n }\n var type = file_name.substr(file_name.lastIndexOf(\".\") + 1);\n type = type.toLowerCase();\n if (type == \"mov\") {\n console.warn(\n \"Warning: video-keyboard-response plugin does not reliably support .mov files.\"\n );\n }\n video_html += '<source src=\"' + file_name + '\" type=\"video/' + type + '\">';\n }\n }\n video_html += \"</video>\";\n video_html += \"</div>\";\n\n // add prompt if there is one\n if (trial.prompt !== null) {\n video_html += trial.prompt;\n }\n\n display_element.innerHTML = video_html;\n\n var video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n );\n\n if (video_preload_blob) {\n video_element.src = video_preload_blob;\n }\n\n video_element.onended = () => {\n if (trial.trial_ends_after_video) {\n end_trial();\n }\n if (trial.response_allowed_while_playing == false && !trial.trial_ends_after_video) {\n // start keyboard listener\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n };\n\n video_element.playbackRate = trial.rate;\n\n // if video start time is specified, hide the video and set the starting time\n // before showing and playing, so that the video doesn't automatically show the first frame\n if (trial.start !== null) {\n video_element.pause();\n video_element.onseeked = () => {\n video_element.style.visibility = \"visible\";\n video_element.muted = false;\n if (trial.autoplay) {\n video_element.play();\n } else {\n video_element.pause();\n }\n video_element.onseeked = () => {};\n };\n video_element.onplaying = () => {\n video_element.currentTime = trial.start;\n video_element.onplaying = () => {};\n };\n // fix for iOS/MacOS browsers: videos aren't seekable until they start playing, so need to hide/mute, play,\n // change current time, then show/unmute\n video_element.muted = true;\n video_element.play();\n }\n\n let stopped = false;\n if (trial.stop !== null) {\n video_element.addEventListener(\"timeupdate\", (e) => {\n var currenttime = video_element.currentTime;\n if (currenttime >= trial.stop) {\n if (!trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n video_element.pause();\n if (trial.trial_ends_after_video && !stopped) {\n // this is to prevent end_trial from being called twice, because the timeupdate event\n // can fire in quick succession\n stopped = true;\n end_trial();\n }\n }\n });\n }\n\n // store response\n var response = {\n rt: null,\n key: null,\n };\n\n // function to end trial when it is time\n const end_trial = () => {\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // stop the video file if it is playing\n // remove end event listeners if they exist\n display_element\n .querySelector<HTMLVideoElement>(\"#jspsych-video-keyboard-response-stimulus\")\n .pause();\n display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-keyboard-response-stimulus\"\n ).onended = () => {};\n\n // gather the data to store for the trial\n var trial_data = {\n rt: response.rt,\n stimulus: trial.stimulus,\n response: response.key,\n };\n\n // move on to the next trial\n this.jsPsych.finishTrial(trial_data);\n };\n\n // function to handle responses by the subject\n var after_response = (info) => {\n // after a valid response, the stimulus will have the CSS class 'responded'\n // which can be used to provide visual feedback that a response was recorded\n display_element.querySelector(\"#jspsych-video-keyboard-response-stimulus\").className +=\n \" responded\";\n\n // only record the first response\n if (response.key == null) {\n response = info;\n }\n\n if (trial.response_ends_trial) {\n end_trial();\n }\n };\n\n // start the response listener\n if (trial.choices != \"NO_KEYS\" && trial.response_allowed_while_playing) {\n var keyboardListener = this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(end_trial, trial.trial_duration);\n }\n }\n\n simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n this.jsPsych.finishTrial(data);\n }\n\n private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n this.trial(display_element, trial);\n load_callback();\n\n const video_element = display_element.querySelector<HTMLVideoElement>(\n \"#jspsych-video-button-response-stimulus\"\n );\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n if (!trial.response_allowed_while_playing) {\n video_element.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default VideoKeyboardResponsePlugin;\n"],"names":[],"mappings":";;AAEE,IAAW,OAAA,GAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECkHA,SAAA,EAAA;AAAA;;GAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jspsych/plugin-video-keyboard-response",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "jsPsych plugin for playing a video file and getting a keyboard response",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"jspsych": ">=7.1.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@jspsych/config": "^3.
|
|
40
|
+
"@jspsych/config": "^3.3.1",
|
|
41
41
|
"@jspsych/test-utils": "^1.2.0"
|
|
42
42
|
}
|
|
43
43
|
}
|
package/src/index.spec.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -25,13 +25,17 @@ const info = <const>{
|
|
|
25
25
|
pretty_name: "Prompt",
|
|
26
26
|
default: null,
|
|
27
27
|
},
|
|
28
|
-
/** The width of the video in pixels.
|
|
28
|
+
/** The width of the video display in pixels. If `null`, the video will take the original video's dimensions,
|
|
29
|
+
* or properly scaled with the aspect ratio if the height is also specified.
|
|
30
|
+
*/
|
|
29
31
|
width: {
|
|
30
32
|
type: ParameterType.INT,
|
|
31
33
|
pretty_name: "Width",
|
|
32
34
|
default: "",
|
|
33
35
|
},
|
|
34
|
-
/** The height of the video display in pixels.
|
|
36
|
+
/** The height of the video display in pixels. If `null`, the video will take the original video's dimensions,
|
|
37
|
+
* or properly scaled with the aspect ratio if the width is also specified.
|
|
38
|
+
*/
|
|
35
39
|
height: {
|
|
36
40
|
type: ParameterType.INT,
|
|
37
41
|
pretty_name: "Height",
|
|
@@ -99,7 +103,7 @@ const info = <const>{
|
|
|
99
103
|
},
|
|
100
104
|
/** The response time in milliseconds for the participant to make a response. The time is measured from when the
|
|
101
105
|
* stimulus first appears on the screen until the participant's response.
|
|
102
|
-
|
|
106
|
+
*/
|
|
103
107
|
rt: {
|
|
104
108
|
type: ParameterType.INT,
|
|
105
109
|
},
|
|
@@ -109,6 +113,8 @@ const info = <const>{
|
|
|
109
113
|
array: true,
|
|
110
114
|
},
|
|
111
115
|
},
|
|
116
|
+
// prettier-ignore
|
|
117
|
+
citations: '__CITATIONS__',
|
|
112
118
|
};
|
|
113
119
|
|
|
114
120
|
type Info = typeof info;
|
|
@@ -135,14 +141,6 @@ class VideoKeyboardResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
135
141
|
constructor(private jsPsych: JsPsych) {}
|
|
136
142
|
|
|
137
143
|
trial(display_element: HTMLElement, trial: TrialType<Info>) {
|
|
138
|
-
// catch mistake where stimuli are not an array
|
|
139
|
-
if (!Array.isArray(trial.stimulus)) {
|
|
140
|
-
throw new Error(`
|
|
141
|
-
The stimulus property for the video-keyboard-response plugin must be an array
|
|
142
|
-
of files. See https://www.jspsych.org/latest/plugins/video-keyboard-response/#parameters
|
|
143
|
-
`);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
144
|
// setup stimulus
|
|
147
145
|
var video_html = "<div>";
|
|
148
146
|
video_html += '<video id="jspsych-video-keyboard-response-stimulus"';
|