@jspsych/plugin-video-keyboard-response 2.0.0 → 2.1.0

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,92 @@
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.0";
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 in pixels. */
68
30
  width: {
69
31
  type: jspsych.ParameterType.INT,
70
32
  pretty_name: "Width",
71
33
  default: ""
72
34
  },
35
+ /** The height of the video display in pixels. */
73
36
  height: {
74
37
  type: jspsych.ParameterType.INT,
75
38
  pretty_name: "Height",
76
39
  default: ""
77
40
  },
41
+ /** If true, the video will begin playing as soon as it has loaded. */
78
42
  autoplay: {
79
43
  type: jspsych.ParameterType.BOOL,
80
44
  pretty_name: "Autoplay",
81
45
  default: true
82
46
  },
47
+ /** If true, the subject will be able to pause the video or move the playback to any point in the video. */
83
48
  controls: {
84
49
  type: jspsych.ParameterType.BOOL,
85
50
  pretty_name: "Controls",
86
51
  default: false
87
52
  },
53
+ /** Time to start the clip. If null (default), video will start at the beginning of the file. */
88
54
  start: {
89
55
  type: jspsych.ParameterType.FLOAT,
90
56
  pretty_name: "Start",
91
57
  default: null
92
58
  },
59
+ /** Time to stop the clip. If null (default), video will stop at the end of the file. */
93
60
  stop: {
94
61
  type: jspsych.ParameterType.FLOAT,
95
62
  pretty_name: "Stop",
96
63
  default: null
97
64
  },
65
+ /** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */
98
66
  rate: {
99
67
  type: jspsych.ParameterType.FLOAT,
100
68
  pretty_name: "Rate",
101
69
  default: 1
102
70
  },
71
+ /** If true, the trial will end immediately after the video finishes playing. */
103
72
  trial_ends_after_video: {
104
73
  type: jspsych.ParameterType.BOOL,
105
74
  pretty_name: "End trial after video finishes",
106
75
  default: false
107
76
  },
77
+ /** How long to show trial before it ends. */
108
78
  trial_duration: {
109
79
  type: jspsych.ParameterType.INT,
110
80
  pretty_name: "Trial duration",
111
81
  default: null
112
82
  },
83
+ /** If true, the trial will end when subject makes a response. */
113
84
  response_ends_trial: {
114
85
  type: jspsych.ParameterType.BOOL,
115
86
  pretty_name: "Response ends trial",
116
87
  default: true
117
88
  },
89
+ /** 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
90
  response_allowed_while_playing: {
119
91
  type: jspsych.ParameterType.BOOL,
120
92
  pretty_name: "Response allowed while playing",
@@ -122,23 +94,35 @@ var jsPsychVideoKeyboardResponse = (function (jspsych) {
122
94
  }
123
95
  },
124
96
  data: {
97
+ /** Indicates which key the participant pressed. */
125
98
  response: {
126
99
  type: jspsych.ParameterType.STRING
127
100
  },
101
+ /** The response time in milliseconds for the participant to make a response. The time is measured from when the
102
+ * stimulus first appears on the screen until the participant's response.
103
+ * */
128
104
  rt: {
129
105
  type: jspsych.ParameterType.INT
130
106
  },
107
+ /** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */
131
108
  stimulus: {
132
109
  type: jspsych.ParameterType.STRING,
133
110
  array: true
134
111
  }
112
+ },
113
+ // prettier-ignore
114
+ citations: {
115
+ "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 ",
116
+ "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
117
  }
136
118
  };
137
119
  class VideoKeyboardResponsePlugin {
138
120
  constructor(jsPsych) {
139
121
  this.jsPsych = jsPsych;
140
122
  }
141
- static info = info;
123
+ static {
124
+ this.info = info;
125
+ }
142
126
  trial(display_element, trial) {
143
127
  if (!Array.isArray(trial.stimulus)) {
144
128
  throw new Error(`
@@ -338,4 +322,4 @@ var jsPsychVideoKeyboardResponse = (function (jspsych) {
338
322
  return VideoKeyboardResponsePlugin;
339
323
 
340
324
  })(jsPsychModule);
341
- //# sourceMappingURL=https://unpkg.com/@jspsych/plugin-video-keyboard-response@2.0.0/dist/index.browser.js.map
325
+ //# sourceMappingURL=https://unpkg.com/@jspsych/plugin-video-keyboard-response@2.1.0/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.0\",\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.2.0\",\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 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 // 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 // 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":[],"mappings":";;;EAEE,IAAW,OAAA,GAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IC8GA,SAAA,EAAA;EAAA;;KAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,5 +1,5 @@
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(`
1
+ var jsPsychVideoKeyboardResponse=function(r){"use strict";var c="2.1.0";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(s){this.jsPsych=s}trial(s,e){if(!Array.isArray(e.stimulus))throw new Error(`
2
2
  The stimulus property for the video-keyboard-response plugin must be an array
3
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
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 o=e.stimulus[n];o.indexOf("?")>-1&&(o=o.substring(0,o.indexOf("?")));var l=o.substr(o.lastIndexOf(".")+1);l=l.toLowerCase(),l=="mov"&&console.warn("Warning: video-keyboard-response plugin does not reliably support .mov files."),t+='<source src="'+o+'" type="video/'+l+'">'}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 h=!1;e.stop!==null&&a.addEventListener("timeupdate",p=>{var v=a.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});a.pause(),e.trial_ends_after_video&&!h&&(h=!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 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(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 o=n.querySelector("#jspsych-video-button-response-stimulus"),l=()=>{i.rt!==null&&this.jsPsych.pluginAPI.pressKey(i.response,i.rt)};s.response_allowed_while_playing?l():o.addEventListener("ended",l)}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 m.info=_,m}(jsPsychModule);
5
+ //# sourceMappingURL=https://unpkg.com/@jspsych/plugin-video-keyboard-response@2.1.0/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.0\",\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.2.0\",\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 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 // 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 // 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"],"mappings":"0DAEEA,IAAAA,EAAW,kxCC8GA,UAAA,iuBAAe;;;"}
package/dist/index.cjs CHANGED
@@ -2,120 +2,92 @@
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.0";
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 in pixels. */
69
31
  width: {
70
32
  type: jspsych.ParameterType.INT,
71
33
  pretty_name: "Width",
72
34
  default: ""
73
35
  },
36
+ /** The height of the video display in pixels. */
74
37
  height: {
75
38
  type: jspsych.ParameterType.INT,
76
39
  pretty_name: "Height",
77
40
  default: ""
78
41
  },
42
+ /** If true, the video will begin playing as soon as it has loaded. */
79
43
  autoplay: {
80
44
  type: jspsych.ParameterType.BOOL,
81
45
  pretty_name: "Autoplay",
82
46
  default: true
83
47
  },
48
+ /** If true, the subject will be able to pause the video or move the playback to any point in the video. */
84
49
  controls: {
85
50
  type: jspsych.ParameterType.BOOL,
86
51
  pretty_name: "Controls",
87
52
  default: false
88
53
  },
54
+ /** Time to start the clip. If null (default), video will start at the beginning of the file. */
89
55
  start: {
90
56
  type: jspsych.ParameterType.FLOAT,
91
57
  pretty_name: "Start",
92
58
  default: null
93
59
  },
60
+ /** Time to stop the clip. If null (default), video will stop at the end of the file. */
94
61
  stop: {
95
62
  type: jspsych.ParameterType.FLOAT,
96
63
  pretty_name: "Stop",
97
64
  default: null
98
65
  },
66
+ /** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */
99
67
  rate: {
100
68
  type: jspsych.ParameterType.FLOAT,
101
69
  pretty_name: "Rate",
102
70
  default: 1
103
71
  },
72
+ /** If true, the trial will end immediately after the video finishes playing. */
104
73
  trial_ends_after_video: {
105
74
  type: jspsych.ParameterType.BOOL,
106
75
  pretty_name: "End trial after video finishes",
107
76
  default: false
108
77
  },
78
+ /** How long to show trial before it ends. */
109
79
  trial_duration: {
110
80
  type: jspsych.ParameterType.INT,
111
81
  pretty_name: "Trial duration",
112
82
  default: null
113
83
  },
84
+ /** If true, the trial will end when subject makes a response. */
114
85
  response_ends_trial: {
115
86
  type: jspsych.ParameterType.BOOL,
116
87
  pretty_name: "Response ends trial",
117
88
  default: true
118
89
  },
90
+ /** 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
91
  response_allowed_while_playing: {
120
92
  type: jspsych.ParameterType.BOOL,
121
93
  pretty_name: "Response allowed while playing",
@@ -123,22 +95,35 @@ const info = {
123
95
  }
124
96
  },
125
97
  data: {
98
+ /** Indicates which key the participant pressed. */
126
99
  response: {
127
100
  type: jspsych.ParameterType.STRING
128
101
  },
102
+ /** The response time in milliseconds for the participant to make a response. The time is measured from when the
103
+ * stimulus first appears on the screen until the participant's response.
104
+ * */
129
105
  rt: {
130
106
  type: jspsych.ParameterType.INT
131
107
  },
108
+ /** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */
132
109
  stimulus: {
133
110
  type: jspsych.ParameterType.STRING,
134
111
  array: true
135
112
  }
113
+ },
114
+ // prettier-ignore
115
+ citations: {
116
+ "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 ",
117
+ "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
118
  }
137
119
  };
138
120
  class VideoKeyboardResponsePlugin {
139
121
  constructor(jsPsych) {
140
122
  this.jsPsych = jsPsych;
141
123
  }
124
+ static {
125
+ this.info = info;
126
+ }
142
127
  trial(display_element, trial) {
143
128
  if (!Array.isArray(trial.stimulus)) {
144
129
  throw new Error(`
@@ -334,7 +319,6 @@ class VideoKeyboardResponsePlugin {
334
319
  return data;
335
320
  }
336
321
  }
337
- VideoKeyboardResponsePlugin.info = info;
338
322
 
339
323
  module.exports = VideoKeyboardResponsePlugin;
340
324
  //# 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.0\",\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.2.0\",\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 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 // 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 // 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":[],"mappings":";;;;AAEE,IAAW,OAAA,GAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EC8GA,SAAA,EAAA;AAAA;;GAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -107,6 +107,7 @@ declare const info: {
107
107
  readonly array: true;
108
108
  };
109
109
  };
110
+ readonly citations: "__CITATIONS__";
110
111
  };
111
112
  type Info = typeof info;
112
113
  /**
@@ -234,6 +235,7 @@ declare class VideoKeyboardResponsePlugin implements JsPsychPlugin<Info> {
234
235
  readonly array: true;
235
236
  };
236
237
  };
238
+ readonly citations: "__CITATIONS__";
237
239
  };
238
240
  constructor(jsPsych: JsPsych);
239
241
  trial(display_element: HTMLElement, trial: TrialType<Info>): void;
package/dist/index.js CHANGED
@@ -1,119 +1,91 @@
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.0";
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 in pixels. */
67
29
  width: {
68
30
  type: ParameterType.INT,
69
31
  pretty_name: "Width",
70
32
  default: ""
71
33
  },
34
+ /** The height of the video display in pixels. */
72
35
  height: {
73
36
  type: ParameterType.INT,
74
37
  pretty_name: "Height",
75
38
  default: ""
76
39
  },
40
+ /** If true, the video will begin playing as soon as it has loaded. */
77
41
  autoplay: {
78
42
  type: ParameterType.BOOL,
79
43
  pretty_name: "Autoplay",
80
44
  default: true
81
45
  },
46
+ /** If true, the subject will be able to pause the video or move the playback to any point in the video. */
82
47
  controls: {
83
48
  type: ParameterType.BOOL,
84
49
  pretty_name: "Controls",
85
50
  default: false
86
51
  },
52
+ /** Time to start the clip. If null (default), video will start at the beginning of the file. */
87
53
  start: {
88
54
  type: ParameterType.FLOAT,
89
55
  pretty_name: "Start",
90
56
  default: null
91
57
  },
58
+ /** Time to stop the clip. If null (default), video will stop at the end of the file. */
92
59
  stop: {
93
60
  type: ParameterType.FLOAT,
94
61
  pretty_name: "Stop",
95
62
  default: null
96
63
  },
64
+ /** The playback rate of the video. 1 is normal, <1 is slower, >1 is faster. */
97
65
  rate: {
98
66
  type: ParameterType.FLOAT,
99
67
  pretty_name: "Rate",
100
68
  default: 1
101
69
  },
70
+ /** If true, the trial will end immediately after the video finishes playing. */
102
71
  trial_ends_after_video: {
103
72
  type: ParameterType.BOOL,
104
73
  pretty_name: "End trial after video finishes",
105
74
  default: false
106
75
  },
76
+ /** How long to show trial before it ends. */
107
77
  trial_duration: {
108
78
  type: ParameterType.INT,
109
79
  pretty_name: "Trial duration",
110
80
  default: null
111
81
  },
82
+ /** If true, the trial will end when subject makes a response. */
112
83
  response_ends_trial: {
113
84
  type: ParameterType.BOOL,
114
85
  pretty_name: "Response ends trial",
115
86
  default: true
116
87
  },
88
+ /** 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
89
  response_allowed_while_playing: {
118
90
  type: ParameterType.BOOL,
119
91
  pretty_name: "Response allowed while playing",
@@ -121,22 +93,35 @@ const info = {
121
93
  }
122
94
  },
123
95
  data: {
96
+ /** Indicates which key the participant pressed. */
124
97
  response: {
125
98
  type: ParameterType.STRING
126
99
  },
100
+ /** The response time in milliseconds for the participant to make a response. The time is measured from when the
101
+ * stimulus first appears on the screen until the participant's response.
102
+ * */
127
103
  rt: {
128
104
  type: ParameterType.INT
129
105
  },
106
+ /** The `stimulus` array. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */
130
107
  stimulus: {
131
108
  type: ParameterType.STRING,
132
109
  array: true
133
110
  }
111
+ },
112
+ // prettier-ignore
113
+ citations: {
114
+ "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 ",
115
+ "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
116
  }
135
117
  };
136
118
  class VideoKeyboardResponsePlugin {
137
119
  constructor(jsPsych) {
138
120
  this.jsPsych = jsPsych;
139
121
  }
122
+ static {
123
+ this.info = info;
124
+ }
140
125
  trial(display_element, trial) {
141
126
  if (!Array.isArray(trial.stimulus)) {
142
127
  throw new Error(`
@@ -332,7 +317,6 @@ class VideoKeyboardResponsePlugin {
332
317
  return data;
333
318
  }
334
319
  }
335
- VideoKeyboardResponsePlugin.info = info;
336
320
 
337
321
  export { VideoKeyboardResponsePlugin as default };
338
322
  //# 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,MAAM,IAAc,GAAA;AAAA,EAClB,IAAM,EAAA,yBAAA;AAAA,WACNA,gBAAA;AAAA,EACA,UAAY,EAAA;AAAA,IAEV,QAAU,EAAA;AAAA,MACR,MAAM,aAAc,CAAA,KAAA;AAAA,MACpB,WAAa,EAAA,OAAA;AAAA,MACb,OAAS,EAAA,KAAA,CAAA;AAAA,MACT,KAAO,EAAA,IAAA;AAAA,KACT;AAAA,IAEA,OAAS,EAAA;AAAA,MACP,MAAM,aAAc,CAAA,IAAA;AAAA,MACpB,WAAa,EAAA,SAAA;AAAA,MACb,OAAS,EAAA,UAAA;AAAA,KACX;AAAA,IAEA,MAAQ,EAAA;AAAA,MACN,MAAM,aAAc,CAAA,WAAA;AAAA,MACpB,WAAa,EAAA,QAAA;AAAA,MACb,OAAS,EAAA,IAAA;AAAA,KACX;AAAA,IAEA,KAAO,EAAA;AAAA,MACL,MAAM,aAAc,CAAA,GAAA;AAAA,MACpB,WAAa,EAAA,OAAA;AAAA,MACb,OAAS,EAAA,EAAA;AAAA,KACX;AAAA,IAEA,MAAQ,EAAA;AAAA,MACN,MAAM,aAAc,CAAA,GAAA;AAAA,MACpB,WAAa,EAAA,QAAA;AAAA,MACb,OAAS,EAAA,EAAA;AAAA,KACX;AAAA,IAEA,QAAU,EAAA;AAAA,MACR,MAAM,aAAc,CAAA,IAAA;AAAA,MACpB,WAAa,EAAA,UAAA;AAAA,MACb,OAAS,EAAA,IAAA;AAAA,KACX;AAAA,IAEA,QAAU,EAAA;AAAA,MACR,MAAM,aAAc,CAAA,IAAA;AAAA,MACpB,WAAa,EAAA,UAAA;AAAA,MACb,OAAS,EAAA,KAAA;AAAA,KACX;AAAA,IAEA,KAAO,EAAA;AAAA,MACL,MAAM,aAAc,CAAA,KAAA;AAAA,MACpB,WAAa,EAAA,OAAA;AAAA,MACb,OAAS,EAAA,IAAA;AAAA,KACX;AAAA,IAEA,IAAM,EAAA;AAAA,MACJ,MAAM,aAAc,CAAA,KAAA;AAAA,MACpB,WAAa,EAAA,MAAA;AAAA,MACb,OAAS,EAAA,IAAA;AAAA,KACX;AAAA,IAEA,IAAM,EAAA;AAAA,MACJ,MAAM,aAAc,CAAA,KAAA;AAAA,MACpB,WAAa,EAAA,MAAA;AAAA,MACb,OAAS,EAAA,CAAA;AAAA,KACX;AAAA,IAEA,sBAAwB,EAAA;AAAA,MACtB,MAAM,aAAc,CAAA,IAAA;AAAA,MACpB,WAAa,EAAA,gCAAA;AAAA,MACb,OAAS,EAAA,KAAA;AAAA,KACX;AAAA,IAEA,cAAgB,EAAA;AAAA,MACd,MAAM,aAAc,CAAA,GAAA;AAAA,MACpB,WAAa,EAAA,gBAAA;AAAA,MACb,OAAS,EAAA,IAAA;AAAA,KACX;AAAA,IAEA,mBAAqB,EAAA;AAAA,MACnB,MAAM,aAAc,CAAA,IAAA;AAAA,MACpB,WAAa,EAAA,qBAAA;AAAA,MACb,OAAS,EAAA,IAAA;AAAA,KACX;AAAA,IAEA,8BAAgC,EAAA;AAAA,MAC9B,MAAM,aAAc,CAAA,IAAA;AAAA,MACpB,WAAa,EAAA,gCAAA;AAAA,MACb,OAAS,EAAA,IAAA;AAAA,KACX;AAAA,GACF;AAAA,EACA,IAAM,EAAA;AAAA,IAEJ,QAAU,EAAA;AAAA,MACR,MAAM,aAAc,CAAA,MAAA;AAAA,KACtB;AAAA,IAIA,EAAI,EAAA;AAAA,MACF,MAAM,aAAc,CAAA,GAAA;AAAA,KACtB;AAAA,IAEA,QAAU,EAAA;AAAA,MACR,MAAM,aAAc,CAAA,MAAA;AAAA,MACpB,KAAO,EAAA,IAAA;AAAA,KACT;AAAA,GACF;AACF,CAAA,CAAA;AAoBA,MAAM,2BAA2D,CAAA;AAAA,EAG/D,YAAoB,OAAkB,EAAA;AAAlB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA,CAAA;AAAA,GAAmB;AAAA,EAEvC,KAAA,CAAM,iBAA8B,KAAwB,EAAA;AAE1D,IAAA,IAAI,CAAC,KAAA,CAAM,OAAQ,CAAA,KAAA,CAAM,QAAQ,CAAG,EAAA;AAClC,MAAA,MAAM,IAAI,KAAM,CAAA,CAAA;AAAA;AAAA;AAAA,MAGf,CAAA,CAAA,CAAA;AAAA,KACH;AAGA,IAAA,IAAI,UAAa,GAAA,OAAA,CAAA;AACjB,IAAc,UAAA,IAAA,sDAAA,CAAA;AAEd,IAAA,IAAI,MAAM,KAAO,EAAA;AACf,MAAc,UAAA,IAAA,UAAA,GAAa,MAAM,KAAQ,GAAA,GAAA,CAAA;AAAA,KAC3C;AACA,IAAA,IAAI,MAAM,MAAQ,EAAA;AAChB,MAAc,UAAA,IAAA,WAAA,GAAc,MAAM,MAAS,GAAA,GAAA,CAAA;AAAA,KAC7C;AACA,IAAA,IAAI,KAAM,CAAA,QAAA,IAAY,KAAM,CAAA,KAAA,IAAS,IAAM,EAAA;AAGzC,MAAc,UAAA,IAAA,YAAA,CAAA;AAAA,KAChB;AACA,IAAA,IAAI,MAAM,QAAU,EAAA;AAClB,MAAc,UAAA,IAAA,YAAA,CAAA;AAAA,KAChB;AACA,IAAI,IAAA,KAAA,CAAM,UAAU,IAAM,EAAA;AAGxB,MAAc,UAAA,IAAA,8BAAA,CAAA;AAAA,KAChB;AACA,IAAc,UAAA,IAAA,GAAA,CAAA;AAEd,IAAA,IAAI,qBAAqB,IAAK,CAAA,OAAA,CAAQ,UAAU,cAAe,CAAA,KAAA,CAAM,SAAS,CAAE,CAAA,CAAA,CAAA;AAChF,IAAA,IAAI,CAAC,kBAAoB,EAAA;AACvB,MAAA,KAAA,IAAS,IAAI,CAAG,EAAA,CAAA,GAAI,KAAM,CAAA,QAAA,CAAS,QAAQ,CAAK,EAAA,EAAA;AAC9C,QAAI,IAAA,SAAA,GAAY,MAAM,QAAS,CAAA,CAAA,CAAA,CAAA;AAC/B,QAAA,IAAI,SAAU,CAAA,OAAA,CAAQ,GAAG,CAAA,GAAI,CAAI,CAAA,EAAA;AAC/B,UAAA,SAAA,GAAY,UAAU,SAAU,CAAA,CAAA,EAAG,SAAU,CAAA,OAAA,CAAQ,GAAG,CAAC,CAAA,CAAA;AAAA,SAC3D;AACA,QAAA,IAAI,OAAO,SAAU,CAAA,MAAA,CAAO,UAAU,WAAY,CAAA,GAAG,IAAI,CAAC,CAAA,CAAA;AAC1D,QAAA,IAAA,GAAO,KAAK,WAAY,EAAA,CAAA;AACxB,QAAA,IAAI,QAAQ,KAAO,EAAA;AACjB,UAAQ,OAAA,CAAA,IAAA;AAAA,YACN,+EAAA;AAAA,WACF,CAAA;AAAA,SACF;AACA,QAAc,UAAA,IAAA,eAAA,GAAkB,SAAY,GAAA,gBAAA,GAAmB,IAAO,GAAA,IAAA,CAAA;AAAA,OACxE;AAAA,KACF;AACA,IAAc,UAAA,IAAA,UAAA,CAAA;AACd,IAAc,UAAA,IAAA,QAAA,CAAA;AAGd,IAAI,IAAA,KAAA,CAAM,WAAW,IAAM,EAAA;AACzB,MAAA,UAAA,IAAc,KAAM,CAAA,MAAA,CAAA;AAAA,KACtB;AAEA,IAAA,eAAA,CAAgB,SAAY,GAAA,UAAA,CAAA;AAE5B,IAAA,IAAI,gBAAgB,eAAgB,CAAA,aAAA;AAAA,MAClC,2CAAA;AAAA,KACF,CAAA;AAEA,IAAA,IAAI,kBAAoB,EAAA;AACtB,MAAA,aAAA,CAAc,GAAM,GAAA,kBAAA,CAAA;AAAA,KACtB;AAEA,IAAA,aAAA,CAAc,UAAU,MAAM;AAC5B,MAAA,IAAI,MAAM,sBAAwB,EAAA;AAChC,QAAU,SAAA,EAAA,CAAA;AAAA,OACZ;AACA,MAAA,IAAI,KAAM,CAAA,8BAAA,IAAkC,KAAS,IAAA,CAAC,MAAM,sBAAwB,EAAA;AAElF,QAAuB,IAAA,CAAK,OAAQ,CAAA,SAAA,CAAU,mBAAoB,CAAA;AAAA,UAChE,iBAAmB,EAAA,cAAA;AAAA,UACnB,iBAAiB,KAAM,CAAA,OAAA;AAAA,UACvB,SAAW,EAAA,aAAA;AAAA,UACX,OAAS,EAAA,KAAA;AAAA,UACT,cAAgB,EAAA,KAAA;AAAA,SACjB,EAAA;AAAA,OACH;AAAA,KACF,CAAA;AAEA,IAAA,aAAA,CAAc,eAAe,KAAM,CAAA,IAAA,CAAA;AAInC,IAAI,IAAA,KAAA,CAAM,UAAU,IAAM,EAAA;AACxB,MAAA,aAAA,CAAc,KAAM,EAAA,CAAA;AACpB,MAAA,aAAA,CAAc,WAAW,MAAM;AAC7B,QAAA,aAAA,CAAc,MAAM,UAAa,GAAA,SAAA,CAAA;AACjC,QAAA,aAAA,CAAc,KAAQ,GAAA,KAAA,CAAA;AACtB,QAAA,IAAI,MAAM,QAAU,EAAA;AAClB,UAAA,aAAA,CAAc,IAAK,EAAA,CAAA;AAAA,SACd,MAAA;AACL,UAAA,aAAA,CAAc,KAAM,EAAA,CAAA;AAAA,SACtB;AACA,QAAA,aAAA,CAAc,WAAW,MAAM;AAAA,SAAC,CAAA;AAAA,OAClC,CAAA;AACA,MAAA,aAAA,CAAc,YAAY,MAAM;AAC9B,QAAA,aAAA,CAAc,cAAc,KAAM,CAAA,KAAA,CAAA;AAClC,QAAA,aAAA,CAAc,YAAY,MAAM;AAAA,SAAC,CAAA;AAAA,OACnC,CAAA;AAGA,MAAA,aAAA,CAAc,KAAQ,GAAA,IAAA,CAAA;AACtB,MAAA,aAAA,CAAc,IAAK,EAAA,CAAA;AAAA,KACrB;AAEA,IAAA,IAAI,OAAU,GAAA,KAAA,CAAA;AACd,IAAI,IAAA,KAAA,CAAM,SAAS,IAAM,EAAA;AACvB,MAAc,aAAA,CAAA,gBAAA,CAAiB,YAAc,EAAA,CAAC,CAAM,KAAA;AAClD,QAAA,IAAI,cAAc,aAAc,CAAA,WAAA,CAAA;AAChC,QAAI,IAAA,WAAA,IAAe,MAAM,IAAM,EAAA;AAC7B,UAAI,IAAA,CAAC,MAAM,8BAAgC,EAAA;AACzC,YAAuB,IAAA,CAAK,OAAQ,CAAA,SAAA,CAAU,mBAAoB,CAAA;AAAA,cAChE,iBAAmB,EAAA,cAAA;AAAA,cACnB,iBAAiB,KAAM,CAAA,OAAA;AAAA,cACvB,SAAW,EAAA,aAAA;AAAA,cACX,OAAS,EAAA,KAAA;AAAA,cACT,cAAgB,EAAA,KAAA;AAAA,aACjB,EAAA;AAAA,WACH;AACA,UAAA,aAAA,CAAc,KAAM,EAAA,CAAA;AACpB,UAAI,IAAA,KAAA,CAAM,sBAA0B,IAAA,CAAC,OAAS,EAAA;AAG5C,YAAU,OAAA,GAAA,IAAA,CAAA;AACV,YAAU,SAAA,EAAA,CAAA;AAAA,WACZ;AAAA,SACF;AAAA,OACD,CAAA,CAAA;AAAA,KACH;AAGA,IAAA,IAAI,QAAW,GAAA;AAAA,MACb,EAAI,EAAA,IAAA;AAAA,MACJ,GAAK,EAAA,IAAA;AAAA,KACP,CAAA;AAGA,IAAA,MAAM,YAAY,MAAM;AAEtB,MAAK,IAAA,CAAA,OAAA,CAAQ,UAAU,0BAA2B,EAAA,CAAA;AAIlD,MACG,eAAA,CAAA,aAAA,CAAgC,2CAA2C,CAAA,CAC3E,KAAM,EAAA,CAAA;AACT,MAAgB,eAAA,CAAA,aAAA;AAAA,QACd,2CAAA;AAAA,OACF,CAAE,UAAU,MAAM;AAAA,OAAC,CAAA;AAGnB,MAAA,IAAI,UAAa,GAAA;AAAA,QACf,IAAI,QAAS,CAAA,EAAA;AAAA,QACb,UAAU,KAAM,CAAA,QAAA;AAAA,QAChB,UAAU,QAAS,CAAA,GAAA;AAAA,OACrB,CAAA;AAGA,MAAK,IAAA,CAAA,OAAA,CAAQ,YAAY,UAAU,CAAA,CAAA;AAAA,KACrC,CAAA;AAGA,IAAI,IAAA,cAAA,GAAiB,CAACC,KAAS,KAAA;AAG7B,MAAgB,eAAA,CAAA,aAAA,CAAc,2CAA2C,CAAA,CAAE,SACzE,IAAA,YAAA,CAAA;AAGF,MAAI,IAAA,QAAA,CAAS,OAAO,IAAM,EAAA;AACxB,QAAWA,QAAAA,GAAAA,KAAAA,CAAAA;AAAA,OACb;AAEA,MAAA,IAAI,MAAM,mBAAqB,EAAA;AAC7B,QAAU,SAAA,EAAA,CAAA;AAAA,OACZ;AAAA,KACF,CAAA;AAGA,IAAA,IAAI,KAAM,CAAA,OAAA,IAAW,SAAa,IAAA,KAAA,CAAM,8BAAgC,EAAA;AACtE,MAAuB,IAAA,CAAK,OAAQ,CAAA,SAAA,CAAU,mBAAoB,CAAA;AAAA,QAChE,iBAAmB,EAAA,cAAA;AAAA,QACnB,iBAAiB,KAAM,CAAA,OAAA;AAAA,QACvB,SAAW,EAAA,aAAA;AAAA,QACX,OAAS,EAAA,KAAA;AAAA,QACT,cAAgB,EAAA,KAAA;AAAA,OACjB,EAAA;AAAA,KACH;AAGA,IAAI,IAAA,KAAA,CAAM,mBAAmB,IAAM,EAAA;AACjC,MAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,CAAU,UAAW,CAAA,SAAA,EAAW,MAAM,cAAc,CAAA,CAAA;AAAA,KACnE;AAAA,GACF;AAAA,EAEA,QACE,CAAA,KAAA,EACA,eACA,EAAA,kBAAA,EACA,aACA,EAAA;AACA,IAAA,IAAI,mBAAmB,WAAa,EAAA;AAClC,MAAc,aAAA,EAAA,CAAA;AACd,MAAK,IAAA,CAAA,kBAAA,CAAmB,OAAO,kBAAkB,CAAA,CAAA;AAAA,KACnD;AACA,IAAA,IAAI,mBAAmB,QAAU,EAAA;AAC/B,MAAK,IAAA,CAAA,eAAA,CAAgB,KAAO,EAAA,kBAAA,EAAoB,aAAa,CAAA,CAAA;AAAA,KAC/D;AAAA,GACF;AAAA,EAEQ,kBAAA,CAAmB,OAAwB,kBAAoB,EAAA;AACrE,IAAA,MAAM,IAAO,GAAA,IAAA,CAAK,sBAAuB,CAAA,KAAA,EAAO,kBAAkB,CAAA,CAAA;AAElE,IAAK,IAAA,CAAA,OAAA,CAAQ,YAAY,IAAI,CAAA,CAAA;AAAA,GAC/B;AAAA,EAEQ,eAAA,CAAgB,KAAwB,EAAA,kBAAA,EAAoB,aAA2B,EAAA;AAC7F,IAAA,MAAM,IAAO,GAAA,IAAA,CAAK,sBAAuB,CAAA,KAAA,EAAO,kBAAkB,CAAA,CAAA;AAElE,IAAM,MAAA,eAAA,GAAkB,IAAK,CAAA,OAAA,CAAQ,iBAAkB,EAAA,CAAA;AAEvD,IAAK,IAAA,CAAA,KAAA,CAAM,iBAAiB,KAAK,CAAA,CAAA;AACjC,IAAc,aAAA,EAAA,CAAA;AAEd,IAAA,MAAM,gBAAgB,eAAgB,CAAA,aAAA;AAAA,MACpC,yCAAA;AAAA,KACF,CAAA;AAEA,IAAA,MAAM,UAAU,MAAM;AACpB,MAAI,IAAA,IAAA,CAAK,OAAO,IAAM,EAAA;AACpB,QAAA,IAAA,CAAK,QAAQ,SAAU,CAAA,QAAA,CAAS,IAAK,CAAA,QAAA,EAAU,KAAK,EAAE,CAAA,CAAA;AAAA,OACxD;AAAA,KACF,CAAA;AAEA,IAAI,IAAA,CAAC,MAAM,8BAAgC,EAAA;AACzC,MAAc,aAAA,CAAA,gBAAA,CAAiB,SAAS,OAAO,CAAA,CAAA;AAAA,KAC1C,MAAA;AACL,MAAQ,OAAA,EAAA,CAAA;AAAA,KACV;AAAA,GACF;AAAA,EAEQ,sBAAA,CAAuB,OAAwB,kBAAoB,EAAA;AACzE,IAAA,MAAM,YAAe,GAAA;AAAA,MACnB,UAAU,KAAM,CAAA,QAAA;AAAA,MAChB,EAAA,EAAI,KAAK,OAAQ,CAAA,aAAA,CAAc,iBAAiB,GAAK,EAAA,EAAA,EAAI,CAAI,GAAA,GAAA,EAAK,IAAI,CAAA;AAAA,MACtE,UAAU,IAAK,CAAA,OAAA,CAAQ,SAAU,CAAA,WAAA,CAAY,MAAM,OAAO,CAAA;AAAA,KAC5D,CAAA;AAEA,IAAA,MAAM,OAAO,IAAK,CAAA,OAAA,CAAQ,SAAU,CAAA,mBAAA,CAAoB,cAAc,kBAAkB,CAAA,CAAA;AAExF,IAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,CAAU,+BAAgC,CAAA,KAAA,EAAO,IAAI,CAAA,CAAA;AAElE,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AACF,CAAA;AAzQM,2BAAA,CACG,IAAO,GAAA,IAAA;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@jspsych/plugin-video-keyboard-response\",\n \"version\": \"2.1.0\",\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.2.0\",\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 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 // 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 // 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":[],"mappings":";;AAEE,IAAW,OAAA,GAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EC8GA,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.0",
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.2.0",
41
41
  "@jspsych/test-utils": "^1.2.0"
42
42
  }
43
43
  }
package/src/index.ts CHANGED
@@ -109,6 +109,8 @@ const info = <const>{
109
109
  array: true,
110
110
  },
111
111
  },
112
+ // prettier-ignore
113
+ citations: '__CITATIONS__',
112
114
  };
113
115
 
114
116
  type Info = typeof info;