@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.
@@ -1,120 +1,96 @@
1
1
  var jsPsychVideoKeyboardResponse = (function (jspsych) {
2
2
  'use strict';
3
3
 
4
- var _package = {
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: _package.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 info = info;
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.0.0/dist/index.browser.js.map
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 h={name:"@jspsych/plugin-video-keyboard-response",version:"2.0.0",description:"jsPsych plugin for playing a video file and getting a keyboard response",type:"module",main:"dist/index.cjs",exports:{import:"./dist/index.js",require:"./dist/index.cjs"},typings:"dist/index.d.ts",unpkg:"dist/index.browser.min.js",files:["src","dist"],source:"src/index.ts",scripts:{test:"jest --passWithNoTests","test:watch":"npm test -- --watch",tsc:"tsc",build:"rollup --config","build:watch":"npm run build -- --watch"},repository:{type:"git",url:"git+https://github.com/jspsych/jsPsych.git",directory:"packages/plugin-video-keyboard-response"},author:"Josh de Leeuw",license:"MIT",bugs:{url:"https://github.com/jspsych/jsPsych/issues"},homepage:"https://www.jspsych.org/latest/plugins/video-keyboard-response",peerDependencies:{jspsych:">=7.1.0"},devDependencies:{"@jspsych/config":"^3.0.0","@jspsych/test-utils":"^1.2.0"}};const _={name:"video-keyboard-response",version:h.version,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}}};class c{constructor(s){this.jsPsych=s}trial(s,e){if(!Array.isArray(e.stimulus))throw new Error(`
2
- The stimulus property for the video-keyboard-response plugin must be an array
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 _package = {
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: _package.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
@@ -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 _package = {
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: _package.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.0.0",
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.0.0",
40
+ "@jspsych/config": "^3.3.1",
41
41
  "@jspsych/test-utils": "^1.2.0"
42
42
  }
43
43
  }
package/src/index.spec.ts CHANGED
@@ -23,7 +23,7 @@ describe("video-keyboard-response", () => {
23
23
 
24
24
  await expect(async () => {
25
25
  await jsPsych.run(timeline);
26
- }).rejects.toThrowError();
26
+ }).rejects.toThrow();
27
27
  });
28
28
  });
29
29
 
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"';