@jspsych/plugin-audio-button-response 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.browser.js +9 -4
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.min.js +2 -2
- package/dist/index.browser.min.js.map +1 -1
- package/dist/index.cjs +8 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +12 -5
package/dist/index.browser.js
CHANGED
|
@@ -51,7 +51,7 @@ var jsPsychAudioButtonResponse = (function (jspsych) {
|
|
|
51
51
|
|
|
52
52
|
var _package = {
|
|
53
53
|
name: "@jspsych/plugin-audio-button-response",
|
|
54
|
-
version: "2.0.
|
|
54
|
+
version: "2.0.2",
|
|
55
55
|
description: "jsPsych plugin for playing an audio file and getting a button response",
|
|
56
56
|
type: "module",
|
|
57
57
|
main: "dist/index.cjs",
|
|
@@ -150,6 +150,9 @@ var jsPsychAudioButtonResponse = (function (jspsych) {
|
|
|
150
150
|
}
|
|
151
151
|
},
|
|
152
152
|
data: {
|
|
153
|
+
stimulus: {
|
|
154
|
+
type: jspsych.ParameterType.STRING
|
|
155
|
+
},
|
|
153
156
|
rt: {
|
|
154
157
|
type: jspsych.ParameterType.INT
|
|
155
158
|
},
|
|
@@ -173,7 +176,6 @@ var jsPsychAudioButtonResponse = (function (jspsych) {
|
|
|
173
176
|
startTime;
|
|
174
177
|
trial_complete;
|
|
175
178
|
async trial(display_element, trial, on_load) {
|
|
176
|
-
this.trial_complete;
|
|
177
179
|
this.params = trial;
|
|
178
180
|
this.display = display_element;
|
|
179
181
|
this.context = this.jsPsych.pluginAPI.audioContext();
|
|
@@ -221,13 +223,16 @@ var jsPsychAudioButtonResponse = (function (jspsych) {
|
|
|
221
223
|
} else {
|
|
222
224
|
this.disable_buttons();
|
|
223
225
|
}
|
|
224
|
-
this.startTime = performance.now();
|
|
225
226
|
if (trial.trial_duration !== null) {
|
|
226
227
|
this.jsPsych.pluginAPI.setTimeout(() => {
|
|
227
228
|
this.end_trial();
|
|
228
229
|
}, trial.trial_duration);
|
|
229
230
|
}
|
|
230
231
|
on_load();
|
|
232
|
+
this.startTime = performance.now();
|
|
233
|
+
if (this.context !== null) {
|
|
234
|
+
this.startTime = this.context.currentTime;
|
|
235
|
+
}
|
|
231
236
|
this.audio.play();
|
|
232
237
|
return new Promise((resolve) => {
|
|
233
238
|
this.trial_complete = resolve;
|
|
@@ -328,4 +333,4 @@ var jsPsychAudioButtonResponse = (function (jspsych) {
|
|
|
328
333
|
return AudioButtonResponsePlugin;
|
|
329
334
|
|
|
330
335
|
})(jsPsychModule);
|
|
331
|
-
//# sourceMappingURL=https://unpkg.com/@jspsych/plugin-audio-button-response@2.0.
|
|
336
|
+
//# sourceMappingURL=https://unpkg.com/@jspsych/plugin-audio-button-response@2.0.2/dist/index.browser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.browser.js","sources":["../../../node_modules/auto-bind/index.js","../src/index.ts"],"sourcesContent":["'use strict';\n\n// Gets all non-builtin properties up the prototype chain\nconst getAllProperties = object => {\n\tconst properties = new Set();\n\n\tdo {\n\t\tfor (const key of Reflect.ownKeys(object)) {\n\t\t\tproperties.add([object, key]);\n\t\t}\n\t} while ((object = Reflect.getPrototypeOf(object)) && object !== Object.prototype);\n\n\treturn properties;\n};\n\nmodule.exports = (self, {include, exclude} = {}) => {\n\tconst filter = key => {\n\t\tconst match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key);\n\n\t\tif (include) {\n\t\t\treturn include.some(match);\n\t\t}\n\n\t\tif (exclude) {\n\t\t\treturn !exclude.some(match);\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tfor (const [object, key] of getAllProperties(self.constructor.prototype)) {\n\t\tif (key === 'constructor' || !filter(key)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst descriptor = Reflect.getOwnPropertyDescriptor(object, key);\n\t\tif (descriptor && typeof descriptor.value === 'function') {\n\t\t\tself[key] = self[key].bind(self);\n\t\t}\n\t}\n\n\treturn self;\n};\n","import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-button-response\",\n version: version,\n parameters: {\n /** Path to audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** Labels for the buttons. Each different string in the array will generate a different button. */\n choices: {\n type: ParameterType.STRING,\n default: undefined,\n array: true,\n },\n /**\n * A function that generates the HTML for each button in the `choices` array. The function gets the string\n * and index of the item in the `choices` array and should return valid HTML. If you want to use different\n * markup for each button, you can do that by using a conditional on either parameter. The default parameter\n * returns a button element with the text label of the choice.\n */\n button_html: {\n type: ParameterType.FUNCTION,\n default: function (choice: string, choice_index: number) {\n return `<button class=\"jspsych-btn\">${choice}</button>`;\n },\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention\n * is that it can be used to provide a reminder about the action the participant is supposed to take\n * (e.g., which key to press). */\n prompt: {\n type: ParameterType.HTML_STRING,\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, the trial\n * will wait for a response indefinitely */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** Setting to `'grid'` will make the container element have the CSS property `display: grid` and enable the\n * use of `grid_rows` and `grid_columns`. Setting to `'flex'` will make the container element have the CSS\n * property `display: flex`. You can customize how the buttons are laid out by adding inline CSS in the `button_html` parameter.\n */\n button_layout: {\n type: ParameterType.STRING,\n default: \"grid\",\n },\n /** The number of rows in the button grid. Only applicable when `button_layout` is set to `'grid'`. If null, the\n * number of rows will be determined automatically based on the number of buttons and the number of columns.\n */\n grid_rows: {\n type: ParameterType.INT,\n default: 1,\n },\n /** The number of columns in the button grid. Only applicable when `button_layout` is set to `'grid'`.\n * If null, the number of columns will be determined automatically based on the number of buttons and the\n * number of rows.\n */\n grid_columns: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force\n * the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n default: false,\n },\n /**\n * If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before the button choices are enabled and a response is accepted. Once the audio has played\n * all the way through, the buttons are enabled and a response is allowed (including while the audio is\n * being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** How long the button will delay enabling in milliseconds. If `response_allowed_while_playing` is `true`,\n * the timer will start immediately. If it is `false`, the timer will start at the end of the audio. */\n enable_button_after: {\n type: ParameterType.INT,\n default: 0,\n },\n },\n data: {\n /** The response time in milliseconds for the participant to make a response. The time is measured from\n * when the stimulus first began playing until the participant's response.*/\n rt: {\n type: ParameterType.INT,\n },\n /** Indicates which button the participant pressed. The first button in the `choices` array is 0, the second is 1, and so on. */\n response: {\n type: ParameterType.INT,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise \n * timing of the playback. The timing of responses generated is measured against the WebAudio specific clock, \n * improving the measurement of response times. If the browser does not support the WebAudio API, then the audio file is \n * played with HTML5 audio. \n\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if \n * you are using timeline variables or another dynamic method to specify the audio stimulus, you will need \n * to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant \n * has failed to respond within a fixed length of time. You can also prevent a button response from being made before the \n * audio has finished playing.\n * \n * @author Kristin Diep\n * @see {@link https://www.jspsych.org/latest/plugins/audio-button-response/ audio-button-response plugin documentation on jspsych.org}\n */\nclass AudioButtonResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private buttonElements: HTMLElement[] = [];\n private display: HTMLElement;\n private response: { rt: number; button: number } = { rt: null, button: null };\n private context: AudioContext;\n private startTime: number;\n private trial_complete: (trial_data: { rt: number; stimulus: string; response: number }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n async trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n this.trial_complete;\n this.params = trial;\n this.display = display_element;\n // setup stimulus\n this.context = this.jsPsych.pluginAPI.audioContext();\n\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // enable buttons after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.enable_buttons);\n }\n\n // Display buttons\n const buttonGroupElement = document.createElement(\"div\");\n buttonGroupElement.id = \"jspsych-audio-button-response-btngroup\";\n if (trial.button_layout === \"grid\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-grid\");\n if (trial.grid_rows === null && trial.grid_columns === null) {\n throw new Error(\n \"You cannot set `grid_rows` to `null` without providing a value for `grid_columns`.\"\n );\n }\n const n_cols =\n trial.grid_columns === null\n ? Math.ceil(trial.choices.length / trial.grid_rows)\n : trial.grid_columns;\n const n_rows =\n trial.grid_rows === null\n ? Math.ceil(trial.choices.length / trial.grid_columns)\n : trial.grid_rows;\n buttonGroupElement.style.gridTemplateColumns = `repeat(${n_cols}, 1fr)`;\n buttonGroupElement.style.gridTemplateRows = `repeat(${n_rows}, 1fr)`;\n } else if (trial.button_layout === \"flex\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-flex\");\n }\n\n for (const [choiceIndex, choice] of trial.choices.entries()) {\n buttonGroupElement.insertAdjacentHTML(\"beforeend\", trial.button_html(choice, choiceIndex));\n const buttonElement = buttonGroupElement.lastChild as HTMLElement;\n buttonElement.dataset.choice = choiceIndex.toString();\n buttonElement.addEventListener(\"click\", () => {\n this.after_response(choiceIndex);\n });\n this.buttonElements.push(buttonElement);\n }\n\n display_element.appendChild(buttonGroupElement);\n\n // Show prompt if there is one\n if (trial.prompt !== null) {\n display_element.insertAdjacentHTML(\"beforeend\", trial.prompt);\n }\n\n if (trial.response_allowed_while_playing) {\n if (trial.enable_button_after > 0) {\n this.disable_buttons();\n this.enable_buttons();\n }\n } else {\n this.disable_buttons();\n }\n\n // start time\n this.startTime = performance.now();\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n\n this.audio.play();\n\n return new Promise((resolve) => {\n this.trial_complete = resolve;\n });\n }\n\n private disable_buttons = () => {\n for (const button of this.buttonElements) {\n button.setAttribute(\"disabled\", \"disabled\");\n }\n };\n\n private enable_buttons_without_delay = () => {\n for (const button of this.buttonElements) {\n button.removeAttribute(\"disabled\");\n }\n };\n\n private enable_buttons_with_delay = (delay: number) => {\n this.jsPsych.pluginAPI.setTimeout(this.enable_buttons_without_delay, delay);\n };\n\n private enable_buttons() {\n if (this.params.enable_button_after > 0) {\n this.enable_buttons_with_delay(this.params.enable_button_after);\n } else {\n this.enable_buttons_without_delay();\n }\n }\n\n // function to handle responses by the subject\n private after_response = (choice) => {\n // measure rt\n var endTime = performance.now();\n var rt = Math.round(endTime - this.startTime);\n if (this.context !== null) {\n endTime = this.context.currentTime;\n rt = Math.round((endTime - this.startTime) * 1000);\n }\n this.response.button = parseInt(choice);\n this.response.rt = rt;\n\n // disable all the buttons after a response\n this.disable_buttons();\n\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n };\n\n // method to end trial when it is time\n private end_trial = () => {\n // stop the audio file if it is playing\n this.audio.stop();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.enable_buttons);\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n stimulus: this.params.stimulus,\n response: this.response.button,\n };\n\n // move on to the next trial\n this.trial_complete(trial_data);\n };\n\n async 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 create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt:\n this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true) +\n trial.enable_button_after,\n response: this.jsPsych.randomization.randomInt(0, trial.choices.length - 1),\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 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 const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.clickTarget(\n display_element.querySelector(\n `#jspsych-audio-button-response-btngroup [data-choice=\"${data.response}\"]`\n ),\n data.rt\n );\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioButtonResponsePlugin;\n"],"names":["version","ParameterType","autoBind"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.browser.js","sources":["../../../node_modules/auto-bind/index.js","../src/index.ts"],"sourcesContent":["'use strict';\n\n// Gets all non-builtin properties up the prototype chain\nconst getAllProperties = object => {\n\tconst properties = new Set();\n\n\tdo {\n\t\tfor (const key of Reflect.ownKeys(object)) {\n\t\t\tproperties.add([object, key]);\n\t\t}\n\t} while ((object = Reflect.getPrototypeOf(object)) && object !== Object.prototype);\n\n\treturn properties;\n};\n\nmodule.exports = (self, {include, exclude} = {}) => {\n\tconst filter = key => {\n\t\tconst match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key);\n\n\t\tif (include) {\n\t\t\treturn include.some(match);\n\t\t}\n\n\t\tif (exclude) {\n\t\t\treturn !exclude.some(match);\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tfor (const [object, key] of getAllProperties(self.constructor.prototype)) {\n\t\tif (key === 'constructor' || !filter(key)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst descriptor = Reflect.getOwnPropertyDescriptor(object, key);\n\t\tif (descriptor && typeof descriptor.value === 'function') {\n\t\t\tself[key] = self[key].bind(self);\n\t\t}\n\t}\n\n\treturn self;\n};\n","import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-button-response\",\n version: version,\n parameters: {\n /** Path to audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** Labels for the buttons. Each different string in the array will generate a different button. */\n choices: {\n type: ParameterType.STRING,\n default: undefined,\n array: true,\n },\n /**\n * A function that generates the HTML for each button in the `choices` array. The function gets the string\n * and index of the item in the `choices` array and should return valid HTML. If you want to use different\n * markup for each button, you can do that by using a conditional on either parameter. The default parameter\n * returns a button element with the text label of the choice.\n */\n button_html: {\n type: ParameterType.FUNCTION,\n default: function (choice: string, choice_index: number) {\n return `<button class=\"jspsych-btn\">${choice}</button>`;\n },\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention\n * is that it can be used to provide a reminder about the action the participant is supposed to take\n * (e.g., which key to press). */\n prompt: {\n type: ParameterType.HTML_STRING,\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, the trial\n * will wait for a response indefinitely */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** Setting to `'grid'` will make the container element have the CSS property `display: grid` and enable the\n * use of `grid_rows` and `grid_columns`. Setting to `'flex'` will make the container element have the CSS\n * property `display: flex`. You can customize how the buttons are laid out by adding inline CSS in the `button_html` parameter.\n */\n button_layout: {\n type: ParameterType.STRING,\n default: \"grid\",\n },\n /** The number of rows in the button grid. Only applicable when `button_layout` is set to `'grid'`. If null, the\n * number of rows will be determined automatically based on the number of buttons and the number of columns.\n */\n grid_rows: {\n type: ParameterType.INT,\n default: 1,\n },\n /** The number of columns in the button grid. Only applicable when `button_layout` is set to `'grid'`.\n * If null, the number of columns will be determined automatically based on the number of buttons and the\n * number of rows.\n */\n grid_columns: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force\n * the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n default: false,\n },\n /**\n * If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before the button choices are enabled and a response is accepted. Once the audio has played\n * all the way through, the buttons are enabled and a response is allowed (including while the audio is\n * being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** How long the button will delay enabling in milliseconds. If `response_allowed_while_playing` is `true`,\n * the timer will start immediately. If it is `false`, the timer will start at the end of the audio. */\n enable_button_after: {\n type: ParameterType.INT,\n default: 0,\n },\n },\n data: {\n /** The path of the audio file that was played. */\n stimulus: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from\n * when the stimulus first began playing until the participant's response.*/\n rt: {\n type: ParameterType.INT,\n },\n /** Indicates which button the participant pressed. The first button in the `choices` array is 0, the second is 1, and so on. */\n response: {\n type: ParameterType.INT,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise \n * timing of the playback. The timing of responses generated is measured against the WebAudio specific clock, \n * improving the measurement of response times. If the browser does not support the WebAudio API, then the audio file is \n * played with HTML5 audio. \n\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if \n * you are using timeline variables or another dynamic method to specify the audio stimulus, you will need \n * to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant \n * has failed to respond within a fixed length of time. You can also prevent a button response from being made before the \n * audio has finished playing.\n * \n * @author Kristin Diep\n * @see {@link https://www.jspsych.org/latest/plugins/audio-button-response/ audio-button-response plugin documentation on jspsych.org}\n */\nclass AudioButtonResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private buttonElements: HTMLElement[] = [];\n private display: HTMLElement;\n private response: { rt: number; button: number } = { rt: null, button: null };\n private context: AudioContext;\n private startTime: number;\n private trial_complete: (trial_data: { rt: number; stimulus: string; response: number }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n async trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n this.params = trial;\n this.display = display_element;\n // setup stimulus\n this.context = this.jsPsych.pluginAPI.audioContext();\n\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // enable buttons after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.enable_buttons);\n }\n\n // Display buttons\n const buttonGroupElement = document.createElement(\"div\");\n buttonGroupElement.id = \"jspsych-audio-button-response-btngroup\";\n if (trial.button_layout === \"grid\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-grid\");\n if (trial.grid_rows === null && trial.grid_columns === null) {\n throw new Error(\n \"You cannot set `grid_rows` to `null` without providing a value for `grid_columns`.\"\n );\n }\n const n_cols =\n trial.grid_columns === null\n ? Math.ceil(trial.choices.length / trial.grid_rows)\n : trial.grid_columns;\n const n_rows =\n trial.grid_rows === null\n ? Math.ceil(trial.choices.length / trial.grid_columns)\n : trial.grid_rows;\n buttonGroupElement.style.gridTemplateColumns = `repeat(${n_cols}, 1fr)`;\n buttonGroupElement.style.gridTemplateRows = `repeat(${n_rows}, 1fr)`;\n } else if (trial.button_layout === \"flex\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-flex\");\n }\n\n for (const [choiceIndex, choice] of trial.choices.entries()) {\n buttonGroupElement.insertAdjacentHTML(\"beforeend\", trial.button_html(choice, choiceIndex));\n const buttonElement = buttonGroupElement.lastChild as HTMLElement;\n buttonElement.dataset.choice = choiceIndex.toString();\n buttonElement.addEventListener(\"click\", () => {\n this.after_response(choiceIndex);\n });\n this.buttonElements.push(buttonElement);\n }\n\n display_element.appendChild(buttonGroupElement);\n\n // Show prompt if there is one\n if (trial.prompt !== null) {\n display_element.insertAdjacentHTML(\"beforeend\", trial.prompt);\n }\n\n if (trial.response_allowed_while_playing) {\n if (trial.enable_button_after > 0) {\n this.disable_buttons();\n this.enable_buttons();\n }\n } else {\n this.disable_buttons();\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n\n // start time\n this.startTime = performance.now();\n if (this.context !== null) {\n this.startTime = this.context.currentTime;\n }\n\n // start audio\n this.audio.play();\n\n return new Promise((resolve) => {\n // hold the .resolve() function from the Promise that ends the trial\n this.trial_complete = resolve;\n });\n }\n\n private disable_buttons = () => {\n for (const button of this.buttonElements) {\n button.setAttribute(\"disabled\", \"disabled\");\n }\n };\n\n private enable_buttons_without_delay = () => {\n for (const button of this.buttonElements) {\n button.removeAttribute(\"disabled\");\n }\n };\n\n private enable_buttons_with_delay = (delay: number) => {\n this.jsPsych.pluginAPI.setTimeout(this.enable_buttons_without_delay, delay);\n };\n\n private enable_buttons() {\n if (this.params.enable_button_after > 0) {\n this.enable_buttons_with_delay(this.params.enable_button_after);\n } else {\n this.enable_buttons_without_delay();\n }\n }\n\n // function to handle responses by the subject\n private after_response = (choice) => {\n // measure rt\n var endTime = performance.now();\n var rt = Math.round(endTime - this.startTime);\n if (this.context !== null) {\n endTime = this.context.currentTime;\n rt = Math.round((endTime - this.startTime) * 1000);\n }\n this.response.button = parseInt(choice);\n this.response.rt = rt;\n\n // disable all the buttons after a response\n this.disable_buttons();\n\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n };\n\n // method to end trial when it is time\n private end_trial = () => {\n // stop the audio file if it is playing\n this.audio.stop();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.enable_buttons);\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n stimulus: this.params.stimulus,\n response: this.response.button,\n };\n\n // move on to the next trial\n this.trial_complete(trial_data);\n };\n\n async 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 create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt:\n this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true) +\n trial.enable_button_after,\n response: this.jsPsych.randomization.randomInt(0, trial.choices.length - 1),\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 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 const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.clickTarget(\n display_element.querySelector(\n `#jspsych-audio-button-response-btngroup [data-choice=\"${data.response}\"]`\n ),\n data.rt\n );\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioButtonResponsePlugin;\n"],"names":["version","ParameterType","autoBind"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var jsPsychAudioButtonResponse=function(
|
|
2
|
-
//# sourceMappingURL=https://unpkg.com/@jspsych/plugin-audio-button-response@2.0.
|
|
1
|
+
var jsPsychAudioButtonResponse=function(o){"use strict";function c(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}const _=i=>{const s=new Set;do for(const t of Reflect.ownKeys(i))s.add([i,t]);while((i=Reflect.getPrototypeOf(i))&&i!==Object.prototype);return s};var m=(i,{include:s,exclude:t}={})=>{const r=e=>{const n=a=>typeof a=="string"?e===a:a.test(e);return s?s.some(n):t?!t.some(n):!0};for(const[e,n]of _(i.constructor.prototype)){if(n==="constructor"||!r(n))continue;const a=Reflect.getOwnPropertyDescriptor(e,n);a&&typeof a.value=="function"&&(i[n]=i[n].bind(i))}return i},y=c(m),b={name:"@jspsych/plugin-audio-button-response",version:"2.0.2",description:"jsPsych plugin for playing an audio file and getting a button 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","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-audio-button-response"},author:"Kristin Diep",license:"MIT",bugs:{url:"https://github.com/jspsych/jsPsych/issues"},homepage:"https://www.jspsych.org/latest/plugins/audio-button-response",peerDependencies:{jspsych:">=7.1.0"},devDependencies:{"@jspsych/config":"^3.0.0","@jspsych/test-utils":"^1.2.0"}},p=(i,s,t)=>new Promise((r,e)=>{var n=u=>{try{l(t.next(u))}catch(d){e(d)}},a=u=>{try{l(t.throw(u))}catch(d){e(d)}},l=u=>u.done?r(u.value):Promise.resolve(u.value).then(n,a);l((t=t.apply(i,s)).next())});const g={name:"audio-button-response",version:b.version,parameters:{stimulus:{type:o.ParameterType.AUDIO,default:void 0},choices:{type:o.ParameterType.STRING,default:void 0,array:!0},button_html:{type:o.ParameterType.FUNCTION,default:function(i,s){return`<button class="jspsych-btn">${i}</button>`}},prompt:{type:o.ParameterType.HTML_STRING,default:null},trial_duration:{type:o.ParameterType.INT,default:null},button_layout:{type:o.ParameterType.STRING,default:"grid"},grid_rows:{type:o.ParameterType.INT,default:1},grid_columns:{type:o.ParameterType.INT,default:null},response_ends_trial:{type:o.ParameterType.BOOL,default:!0},trial_ends_after_audio:{type:o.ParameterType.BOOL,default:!1},response_allowed_while_playing:{type:o.ParameterType.BOOL,default:!0},enable_button_after:{type:o.ParameterType.INT,default:0}},data:{stimulus:{type:o.ParameterType.STRING},rt:{type:o.ParameterType.INT},response:{type:o.ParameterType.INT}}};class h{constructor(s){this.jsPsych=s,this.buttonElements=[],this.response={rt:null,button:null},this.disable_buttons=()=>{for(const t of this.buttonElements)t.setAttribute("disabled","disabled")},this.enable_buttons_without_delay=()=>{for(const t of this.buttonElements)t.removeAttribute("disabled")},this.enable_buttons_with_delay=t=>{this.jsPsych.pluginAPI.setTimeout(this.enable_buttons_without_delay,t)},this.after_response=t=>{var r=performance.now(),e=Math.round(r-this.startTime);this.context!==null&&(r=this.context.currentTime,e=Math.round((r-this.startTime)*1e3)),this.response.button=parseInt(t),this.response.rt=e,this.disable_buttons(),this.params.response_ends_trial&&this.end_trial()},this.end_trial=()=>{this.audio.stop(),this.audio.removeEventListener("ended",this.end_trial),this.audio.removeEventListener("ended",this.enable_buttons);var t={rt:this.response.rt,stimulus:this.params.stimulus,response:this.response.button};this.trial_complete(t)},y(this)}trial(s,t,r){return p(this,null,function*(){this.params=t,this.display=s,this.context=this.jsPsych.pluginAPI.audioContext(),this.audio=yield this.jsPsych.pluginAPI.getAudioPlayer(t.stimulus),t.trial_ends_after_audio&&this.audio.addEventListener("ended",this.end_trial),!t.response_allowed_while_playing&&!t.trial_ends_after_audio&&this.audio.addEventListener("ended",this.enable_buttons);const e=document.createElement("div");if(e.id="jspsych-audio-button-response-btngroup",t.button_layout==="grid"){if(e.classList.add("jspsych-btn-group-grid"),t.grid_rows===null&&t.grid_columns===null)throw new Error("You cannot set `grid_rows` to `null` without providing a value for `grid_columns`.");const n=t.grid_columns===null?Math.ceil(t.choices.length/t.grid_rows):t.grid_columns,a=t.grid_rows===null?Math.ceil(t.choices.length/t.grid_columns):t.grid_rows;e.style.gridTemplateColumns=`repeat(${n}, 1fr)`,e.style.gridTemplateRows=`repeat(${a}, 1fr)`}else t.button_layout==="flex"&&e.classList.add("jspsych-btn-group-flex");for(const[n,a]of t.choices.entries()){e.insertAdjacentHTML("beforeend",t.button_html(a,n));const l=e.lastChild;l.dataset.choice=n.toString(),l.addEventListener("click",()=>{this.after_response(n)}),this.buttonElements.push(l)}return s.appendChild(e),t.prompt!==null&&s.insertAdjacentHTML("beforeend",t.prompt),t.response_allowed_while_playing?t.enable_button_after>0&&(this.disable_buttons(),this.enable_buttons()):this.disable_buttons(),t.trial_duration!==null&&this.jsPsych.pluginAPI.setTimeout(()=>{this.end_trial()},t.trial_duration),r(),this.startTime=performance.now(),this.context!==null&&(this.startTime=this.context.currentTime),this.audio.play(),new Promise(n=>{this.trial_complete=n})})}enable_buttons(){this.params.enable_button_after>0?this.enable_buttons_with_delay(this.params.enable_button_after):this.enable_buttons_without_delay()}simulate(s,t,r,e){return p(this,null,function*(){t=="data-only"&&(e(),this.simulate_data_only(s,r)),t=="visual"&&this.simulate_visual(s,r,e)})}create_simulation_data(s,t){const r={stimulus:s.stimulus,rt:this.jsPsych.randomization.sampleExGaussian(500,50,.006666666666666667,!0)+s.enable_button_after,response:this.jsPsych.randomization.randomInt(0,s.choices.length-1)},e=this.jsPsych.pluginAPI.mergeSimulationData(r,t);return this.jsPsych.pluginAPI.ensureSimulationDataConsistency(s,e),e}simulate_data_only(s,t){const r=this.create_simulation_data(s,t);this.jsPsych.finishTrial(r)}simulate_visual(s,t,r){const e=this.create_simulation_data(s,t),n=this.jsPsych.getDisplayElement(),a=()=>{e.rt!==null&&this.jsPsych.pluginAPI.clickTarget(n.querySelector(`#jspsych-audio-button-response-btngroup [data-choice="${e.response}"]`),e.rt)};this.trial(n,s,()=>{r(),s.response_allowed_while_playing?a():this.audio.addEventListener("ended",a)})}}return h.info=g,h}(jsPsychModule);
|
|
2
|
+
//# sourceMappingURL=https://unpkg.com/@jspsych/plugin-audio-button-response@2.0.2/dist/index.browser.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.browser.min.js","sources":["../../../node_modules/auto-bind/index.js","../src/index.ts"],"sourcesContent":["'use strict';\n\n// Gets all non-builtin properties up the prototype chain\nconst getAllProperties = object => {\n\tconst properties = new Set();\n\n\tdo {\n\t\tfor (const key of Reflect.ownKeys(object)) {\n\t\t\tproperties.add([object, key]);\n\t\t}\n\t} while ((object = Reflect.getPrototypeOf(object)) && object !== Object.prototype);\n\n\treturn properties;\n};\n\nmodule.exports = (self, {include, exclude} = {}) => {\n\tconst filter = key => {\n\t\tconst match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key);\n\n\t\tif (include) {\n\t\t\treturn include.some(match);\n\t\t}\n\n\t\tif (exclude) {\n\t\t\treturn !exclude.some(match);\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tfor (const [object, key] of getAllProperties(self.constructor.prototype)) {\n\t\tif (key === 'constructor' || !filter(key)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst descriptor = Reflect.getOwnPropertyDescriptor(object, key);\n\t\tif (descriptor && typeof descriptor.value === 'function') {\n\t\t\tself[key] = self[key].bind(self);\n\t\t}\n\t}\n\n\treturn self;\n};\n","import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-button-response\",\n version: version,\n parameters: {\n /** Path to audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** Labels for the buttons. Each different string in the array will generate a different button. */\n choices: {\n type: ParameterType.STRING,\n default: undefined,\n array: true,\n },\n /**\n * A function that generates the HTML for each button in the `choices` array. The function gets the string\n * and index of the item in the `choices` array and should return valid HTML. If you want to use different\n * markup for each button, you can do that by using a conditional on either parameter. The default parameter\n * returns a button element with the text label of the choice.\n */\n button_html: {\n type: ParameterType.FUNCTION,\n default: function (choice: string, choice_index: number) {\n return `<button class=\"jspsych-btn\">${choice}</button>`;\n },\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention\n * is that it can be used to provide a reminder about the action the participant is supposed to take\n * (e.g., which key to press). */\n prompt: {\n type: ParameterType.HTML_STRING,\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, the trial\n * will wait for a response indefinitely */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** Setting to `'grid'` will make the container element have the CSS property `display: grid` and enable the\n * use of `grid_rows` and `grid_columns`. Setting to `'flex'` will make the container element have the CSS\n * property `display: flex`. You can customize how the buttons are laid out by adding inline CSS in the `button_html` parameter.\n */\n button_layout: {\n type: ParameterType.STRING,\n default: \"grid\",\n },\n /** The number of rows in the button grid. Only applicable when `button_layout` is set to `'grid'`. If null, the\n * number of rows will be determined automatically based on the number of buttons and the number of columns.\n */\n grid_rows: {\n type: ParameterType.INT,\n default: 1,\n },\n /** The number of columns in the button grid. Only applicable when `button_layout` is set to `'grid'`.\n * If null, the number of columns will be determined automatically based on the number of buttons and the\n * number of rows.\n */\n grid_columns: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force\n * the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n default: false,\n },\n /**\n * If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before the button choices are enabled and a response is accepted. Once the audio has played\n * all the way through, the buttons are enabled and a response is allowed (including while the audio is\n * being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** How long the button will delay enabling in milliseconds. If `response_allowed_while_playing` is `true`,\n * the timer will start immediately. If it is `false`, the timer will start at the end of the audio. */\n enable_button_after: {\n type: ParameterType.INT,\n default: 0,\n },\n },\n data: {\n /** The response time in milliseconds for the participant to make a response. The time is measured from\n * when the stimulus first began playing until the participant's response.*/\n rt: {\n type: ParameterType.INT,\n },\n /** Indicates which button the participant pressed. The first button in the `choices` array is 0, the second is 1, and so on. */\n response: {\n type: ParameterType.INT,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise \n * timing of the playback. The timing of responses generated is measured against the WebAudio specific clock, \n * improving the measurement of response times. If the browser does not support the WebAudio API, then the audio file is \n * played with HTML5 audio. \n\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if \n * you are using timeline variables or another dynamic method to specify the audio stimulus, you will need \n * to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant \n * has failed to respond within a fixed length of time. You can also prevent a button response from being made before the \n * audio has finished playing.\n * \n * @author Kristin Diep\n * @see {@link https://www.jspsych.org/latest/plugins/audio-button-response/ audio-button-response plugin documentation on jspsych.org}\n */\nclass AudioButtonResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private buttonElements: HTMLElement[] = [];\n private display: HTMLElement;\n private response: { rt: number; button: number } = { rt: null, button: null };\n private context: AudioContext;\n private startTime: number;\n private trial_complete: (trial_data: { rt: number; stimulus: string; response: number }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n async trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n this.trial_complete;\n this.params = trial;\n this.display = display_element;\n // setup stimulus\n this.context = this.jsPsych.pluginAPI.audioContext();\n\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // enable buttons after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.enable_buttons);\n }\n\n // Display buttons\n const buttonGroupElement = document.createElement(\"div\");\n buttonGroupElement.id = \"jspsych-audio-button-response-btngroup\";\n if (trial.button_layout === \"grid\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-grid\");\n if (trial.grid_rows === null && trial.grid_columns === null) {\n throw new Error(\n \"You cannot set `grid_rows` to `null` without providing a value for `grid_columns`.\"\n );\n }\n const n_cols =\n trial.grid_columns === null\n ? Math.ceil(trial.choices.length / trial.grid_rows)\n : trial.grid_columns;\n const n_rows =\n trial.grid_rows === null\n ? Math.ceil(trial.choices.length / trial.grid_columns)\n : trial.grid_rows;\n buttonGroupElement.style.gridTemplateColumns = `repeat(${n_cols}, 1fr)`;\n buttonGroupElement.style.gridTemplateRows = `repeat(${n_rows}, 1fr)`;\n } else if (trial.button_layout === \"flex\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-flex\");\n }\n\n for (const [choiceIndex, choice] of trial.choices.entries()) {\n buttonGroupElement.insertAdjacentHTML(\"beforeend\", trial.button_html(choice, choiceIndex));\n const buttonElement = buttonGroupElement.lastChild as HTMLElement;\n buttonElement.dataset.choice = choiceIndex.toString();\n buttonElement.addEventListener(\"click\", () => {\n this.after_response(choiceIndex);\n });\n this.buttonElements.push(buttonElement);\n }\n\n display_element.appendChild(buttonGroupElement);\n\n // Show prompt if there is one\n if (trial.prompt !== null) {\n display_element.insertAdjacentHTML(\"beforeend\", trial.prompt);\n }\n\n if (trial.response_allowed_while_playing) {\n if (trial.enable_button_after > 0) {\n this.disable_buttons();\n this.enable_buttons();\n }\n } else {\n this.disable_buttons();\n }\n\n // start time\n this.startTime = performance.now();\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n\n this.audio.play();\n\n return new Promise((resolve) => {\n this.trial_complete = resolve;\n });\n }\n\n private disable_buttons = () => {\n for (const button of this.buttonElements) {\n button.setAttribute(\"disabled\", \"disabled\");\n }\n };\n\n private enable_buttons_without_delay = () => {\n for (const button of this.buttonElements) {\n button.removeAttribute(\"disabled\");\n }\n };\n\n private enable_buttons_with_delay = (delay: number) => {\n this.jsPsych.pluginAPI.setTimeout(this.enable_buttons_without_delay, delay);\n };\n\n private enable_buttons() {\n if (this.params.enable_button_after > 0) {\n this.enable_buttons_with_delay(this.params.enable_button_after);\n } else {\n this.enable_buttons_without_delay();\n }\n }\n\n // function to handle responses by the subject\n private after_response = (choice) => {\n // measure rt\n var endTime = performance.now();\n var rt = Math.round(endTime - this.startTime);\n if (this.context !== null) {\n endTime = this.context.currentTime;\n rt = Math.round((endTime - this.startTime) * 1000);\n }\n this.response.button = parseInt(choice);\n this.response.rt = rt;\n\n // disable all the buttons after a response\n this.disable_buttons();\n\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n };\n\n // method to end trial when it is time\n private end_trial = () => {\n // stop the audio file if it is playing\n this.audio.stop();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.enable_buttons);\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n stimulus: this.params.stimulus,\n response: this.response.button,\n };\n\n // move on to the next trial\n this.trial_complete(trial_data);\n };\n\n async 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 create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt:\n this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true) +\n trial.enable_button_after,\n response: this.jsPsych.randomization.randomInt(0, trial.choices.length - 1),\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 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 const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.clickTarget(\n display_element.querySelector(\n `#jspsych-audio-button-response-btngroup [data-choice=\"${data.response}\"]`\n ),\n data.rt\n );\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioButtonResponsePlugin;\n"],"names":["getAllProperties","object","properties","key","autoBind","self","include","exclude","filter","match","pattern","descriptor","l","e","n","s","o","r","a","info","version","ParameterType","choice","choice_index","AudioButtonResponsePlugin","jsPsych","button","delay","endTime","rt","trial_data","display_element","trial","on_load","__async","buttonGroupElement","n_cols","n_rows","choiceIndex","buttonElement","resolve","simulation_mode","simulation_options","load_callback","default_data","data","respond"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.browser.min.js","sources":["../../../node_modules/auto-bind/index.js","../src/index.ts"],"sourcesContent":["'use strict';\n\n// Gets all non-builtin properties up the prototype chain\nconst getAllProperties = object => {\n\tconst properties = new Set();\n\n\tdo {\n\t\tfor (const key of Reflect.ownKeys(object)) {\n\t\t\tproperties.add([object, key]);\n\t\t}\n\t} while ((object = Reflect.getPrototypeOf(object)) && object !== Object.prototype);\n\n\treturn properties;\n};\n\nmodule.exports = (self, {include, exclude} = {}) => {\n\tconst filter = key => {\n\t\tconst match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key);\n\n\t\tif (include) {\n\t\t\treturn include.some(match);\n\t\t}\n\n\t\tif (exclude) {\n\t\t\treturn !exclude.some(match);\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tfor (const [object, key] of getAllProperties(self.constructor.prototype)) {\n\t\tif (key === 'constructor' || !filter(key)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst descriptor = Reflect.getOwnPropertyDescriptor(object, key);\n\t\tif (descriptor && typeof descriptor.value === 'function') {\n\t\t\tself[key] = self[key].bind(self);\n\t\t}\n\t}\n\n\treturn self;\n};\n","import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-button-response\",\n version: version,\n parameters: {\n /** Path to audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** Labels for the buttons. Each different string in the array will generate a different button. */\n choices: {\n type: ParameterType.STRING,\n default: undefined,\n array: true,\n },\n /**\n * A function that generates the HTML for each button in the `choices` array. The function gets the string\n * and index of the item in the `choices` array and should return valid HTML. If you want to use different\n * markup for each button, you can do that by using a conditional on either parameter. The default parameter\n * returns a button element with the text label of the choice.\n */\n button_html: {\n type: ParameterType.FUNCTION,\n default: function (choice: string, choice_index: number) {\n return `<button class=\"jspsych-btn\">${choice}</button>`;\n },\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention\n * is that it can be used to provide a reminder about the action the participant is supposed to take\n * (e.g., which key to press). */\n prompt: {\n type: ParameterType.HTML_STRING,\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, the trial\n * will wait for a response indefinitely */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** Setting to `'grid'` will make the container element have the CSS property `display: grid` and enable the\n * use of `grid_rows` and `grid_columns`. Setting to `'flex'` will make the container element have the CSS\n * property `display: flex`. You can customize how the buttons are laid out by adding inline CSS in the `button_html` parameter.\n */\n button_layout: {\n type: ParameterType.STRING,\n default: \"grid\",\n },\n /** The number of rows in the button grid. Only applicable when `button_layout` is set to `'grid'`. If null, the\n * number of rows will be determined automatically based on the number of buttons and the number of columns.\n */\n grid_rows: {\n type: ParameterType.INT,\n default: 1,\n },\n /** The number of columns in the button grid. Only applicable when `button_layout` is set to `'grid'`.\n * If null, the number of columns will be determined automatically based on the number of buttons and the\n * number of rows.\n */\n grid_columns: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force\n * the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n default: false,\n },\n /**\n * If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before the button choices are enabled and a response is accepted. Once the audio has played\n * all the way through, the buttons are enabled and a response is allowed (including while the audio is\n * being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** How long the button will delay enabling in milliseconds. If `response_allowed_while_playing` is `true`,\n * the timer will start immediately. If it is `false`, the timer will start at the end of the audio. */\n enable_button_after: {\n type: ParameterType.INT,\n default: 0,\n },\n },\n data: {\n /** The path of the audio file that was played. */\n stimulus: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from\n * when the stimulus first began playing until the participant's response.*/\n rt: {\n type: ParameterType.INT,\n },\n /** Indicates which button the participant pressed. The first button in the `choices` array is 0, the second is 1, and so on. */\n response: {\n type: ParameterType.INT,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise \n * timing of the playback. The timing of responses generated is measured against the WebAudio specific clock, \n * improving the measurement of response times. If the browser does not support the WebAudio API, then the audio file is \n * played with HTML5 audio. \n\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if \n * you are using timeline variables or another dynamic method to specify the audio stimulus, you will need \n * to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant \n * has failed to respond within a fixed length of time. You can also prevent a button response from being made before the \n * audio has finished playing.\n * \n * @author Kristin Diep\n * @see {@link https://www.jspsych.org/latest/plugins/audio-button-response/ audio-button-response plugin documentation on jspsych.org}\n */\nclass AudioButtonResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private buttonElements: HTMLElement[] = [];\n private display: HTMLElement;\n private response: { rt: number; button: number } = { rt: null, button: null };\n private context: AudioContext;\n private startTime: number;\n private trial_complete: (trial_data: { rt: number; stimulus: string; response: number }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n async trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n this.params = trial;\n this.display = display_element;\n // setup stimulus\n this.context = this.jsPsych.pluginAPI.audioContext();\n\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // enable buttons after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.enable_buttons);\n }\n\n // Display buttons\n const buttonGroupElement = document.createElement(\"div\");\n buttonGroupElement.id = \"jspsych-audio-button-response-btngroup\";\n if (trial.button_layout === \"grid\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-grid\");\n if (trial.grid_rows === null && trial.grid_columns === null) {\n throw new Error(\n \"You cannot set `grid_rows` to `null` without providing a value for `grid_columns`.\"\n );\n }\n const n_cols =\n trial.grid_columns === null\n ? Math.ceil(trial.choices.length / trial.grid_rows)\n : trial.grid_columns;\n const n_rows =\n trial.grid_rows === null\n ? Math.ceil(trial.choices.length / trial.grid_columns)\n : trial.grid_rows;\n buttonGroupElement.style.gridTemplateColumns = `repeat(${n_cols}, 1fr)`;\n buttonGroupElement.style.gridTemplateRows = `repeat(${n_rows}, 1fr)`;\n } else if (trial.button_layout === \"flex\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-flex\");\n }\n\n for (const [choiceIndex, choice] of trial.choices.entries()) {\n buttonGroupElement.insertAdjacentHTML(\"beforeend\", trial.button_html(choice, choiceIndex));\n const buttonElement = buttonGroupElement.lastChild as HTMLElement;\n buttonElement.dataset.choice = choiceIndex.toString();\n buttonElement.addEventListener(\"click\", () => {\n this.after_response(choiceIndex);\n });\n this.buttonElements.push(buttonElement);\n }\n\n display_element.appendChild(buttonGroupElement);\n\n // Show prompt if there is one\n if (trial.prompt !== null) {\n display_element.insertAdjacentHTML(\"beforeend\", trial.prompt);\n }\n\n if (trial.response_allowed_while_playing) {\n if (trial.enable_button_after > 0) {\n this.disable_buttons();\n this.enable_buttons();\n }\n } else {\n this.disable_buttons();\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n\n // start time\n this.startTime = performance.now();\n if (this.context !== null) {\n this.startTime = this.context.currentTime;\n }\n\n // start audio\n this.audio.play();\n\n return new Promise((resolve) => {\n // hold the .resolve() function from the Promise that ends the trial\n this.trial_complete = resolve;\n });\n }\n\n private disable_buttons = () => {\n for (const button of this.buttonElements) {\n button.setAttribute(\"disabled\", \"disabled\");\n }\n };\n\n private enable_buttons_without_delay = () => {\n for (const button of this.buttonElements) {\n button.removeAttribute(\"disabled\");\n }\n };\n\n private enable_buttons_with_delay = (delay: number) => {\n this.jsPsych.pluginAPI.setTimeout(this.enable_buttons_without_delay, delay);\n };\n\n private enable_buttons() {\n if (this.params.enable_button_after > 0) {\n this.enable_buttons_with_delay(this.params.enable_button_after);\n } else {\n this.enable_buttons_without_delay();\n }\n }\n\n // function to handle responses by the subject\n private after_response = (choice) => {\n // measure rt\n var endTime = performance.now();\n var rt = Math.round(endTime - this.startTime);\n if (this.context !== null) {\n endTime = this.context.currentTime;\n rt = Math.round((endTime - this.startTime) * 1000);\n }\n this.response.button = parseInt(choice);\n this.response.rt = rt;\n\n // disable all the buttons after a response\n this.disable_buttons();\n\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n };\n\n // method to end trial when it is time\n private end_trial = () => {\n // stop the audio file if it is playing\n this.audio.stop();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.enable_buttons);\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n stimulus: this.params.stimulus,\n response: this.response.button,\n };\n\n // move on to the next trial\n this.trial_complete(trial_data);\n };\n\n async 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 create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt:\n this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true) +\n trial.enable_button_after,\n response: this.jsPsych.randomization.randomInt(0, trial.choices.length - 1),\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 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 const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.clickTarget(\n display_element.querySelector(\n `#jspsych-audio-button-response-btngroup [data-choice=\"${data.response}\"]`\n ),\n data.rt\n );\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioButtonResponsePlugin;\n"],"names":["getAllProperties","object","properties","key","autoBind","self","include","exclude","filter","match","pattern","descriptor","l","e","n","s","o","r","info","version","ParameterType","choice","choice_index","AudioButtonResponsePlugin","jsPsych","button","delay","endTime","rt","trial_data","display_element","trial","on_load","__async","buttonGroupElement","n_cols","n_rows","choiceIndex","buttonElement","resolve","simulation_mode","simulation_options","load_callback","default_data","data","respond"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -5,7 +5,7 @@ var jspsych = require('jspsych');
|
|
|
5
5
|
|
|
6
6
|
var _package = {
|
|
7
7
|
name: "@jspsych/plugin-audio-button-response",
|
|
8
|
-
version: "2.0.
|
|
8
|
+
version: "2.0.2",
|
|
9
9
|
description: "jsPsych plugin for playing an audio file and getting a button response",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.cjs",
|
|
@@ -104,6 +104,9 @@ const info = {
|
|
|
104
104
|
}
|
|
105
105
|
},
|
|
106
106
|
data: {
|
|
107
|
+
stimulus: {
|
|
108
|
+
type: jspsych.ParameterType.STRING
|
|
109
|
+
},
|
|
107
110
|
rt: {
|
|
108
111
|
type: jspsych.ParameterType.INT
|
|
109
112
|
},
|
|
@@ -158,7 +161,6 @@ class AudioButtonResponsePlugin {
|
|
|
158
161
|
autoBind(this);
|
|
159
162
|
}
|
|
160
163
|
async trial(display_element, trial, on_load) {
|
|
161
|
-
this.trial_complete;
|
|
162
164
|
this.params = trial;
|
|
163
165
|
this.display = display_element;
|
|
164
166
|
this.context = this.jsPsych.pluginAPI.audioContext();
|
|
@@ -206,13 +208,16 @@ class AudioButtonResponsePlugin {
|
|
|
206
208
|
} else {
|
|
207
209
|
this.disable_buttons();
|
|
208
210
|
}
|
|
209
|
-
this.startTime = performance.now();
|
|
210
211
|
if (trial.trial_duration !== null) {
|
|
211
212
|
this.jsPsych.pluginAPI.setTimeout(() => {
|
|
212
213
|
this.end_trial();
|
|
213
214
|
}, trial.trial_duration);
|
|
214
215
|
}
|
|
215
216
|
on_load();
|
|
217
|
+
this.startTime = performance.now();
|
|
218
|
+
if (this.context !== null) {
|
|
219
|
+
this.startTime = this.context.currentTime;
|
|
220
|
+
}
|
|
216
221
|
this.audio.play();
|
|
217
222
|
return new Promise((resolve) => {
|
|
218
223
|
this.trial_complete = resolve;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-button-response\",\n version: version,\n parameters: {\n /** Path to audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** Labels for the buttons. Each different string in the array will generate a different button. */\n choices: {\n type: ParameterType.STRING,\n default: undefined,\n array: true,\n },\n /**\n * A function that generates the HTML for each button in the `choices` array. The function gets the string\n * and index of the item in the `choices` array and should return valid HTML. If you want to use different\n * markup for each button, you can do that by using a conditional on either parameter. The default parameter\n * returns a button element with the text label of the choice.\n */\n button_html: {\n type: ParameterType.FUNCTION,\n default: function (choice: string, choice_index: number) {\n return `<button class=\"jspsych-btn\">${choice}</button>`;\n },\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention\n * is that it can be used to provide a reminder about the action the participant is supposed to take\n * (e.g., which key to press). */\n prompt: {\n type: ParameterType.HTML_STRING,\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, the trial\n * will wait for a response indefinitely */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** Setting to `'grid'` will make the container element have the CSS property `display: grid` and enable the\n * use of `grid_rows` and `grid_columns`. Setting to `'flex'` will make the container element have the CSS\n * property `display: flex`. You can customize how the buttons are laid out by adding inline CSS in the `button_html` parameter.\n */\n button_layout: {\n type: ParameterType.STRING,\n default: \"grid\",\n },\n /** The number of rows in the button grid. Only applicable when `button_layout` is set to `'grid'`. If null, the\n * number of rows will be determined automatically based on the number of buttons and the number of columns.\n */\n grid_rows: {\n type: ParameterType.INT,\n default: 1,\n },\n /** The number of columns in the button grid. Only applicable when `button_layout` is set to `'grid'`.\n * If null, the number of columns will be determined automatically based on the number of buttons and the\n * number of rows.\n */\n grid_columns: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force\n * the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n default: false,\n },\n /**\n * If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before the button choices are enabled and a response is accepted. Once the audio has played\n * all the way through, the buttons are enabled and a response is allowed (including while the audio is\n * being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** How long the button will delay enabling in milliseconds. If `response_allowed_while_playing` is `true`,\n * the timer will start immediately. If it is `false`, the timer will start at the end of the audio. */\n enable_button_after: {\n type: ParameterType.INT,\n default: 0,\n },\n },\n data: {\n /** The response time in milliseconds for the participant to make a response. The time is measured from\n * when the stimulus first began playing until the participant's response.*/\n rt: {\n type: ParameterType.INT,\n },\n /** Indicates which button the participant pressed. The first button in the `choices` array is 0, the second is 1, and so on. */\n response: {\n type: ParameterType.INT,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise \n * timing of the playback. The timing of responses generated is measured against the WebAudio specific clock, \n * improving the measurement of response times. If the browser does not support the WebAudio API, then the audio file is \n * played with HTML5 audio. \n\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if \n * you are using timeline variables or another dynamic method to specify the audio stimulus, you will need \n * to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant \n * has failed to respond within a fixed length of time. You can also prevent a button response from being made before the \n * audio has finished playing.\n * \n * @author Kristin Diep\n * @see {@link https://www.jspsych.org/latest/plugins/audio-button-response/ audio-button-response plugin documentation on jspsych.org}\n */\nclass AudioButtonResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private buttonElements: HTMLElement[] = [];\n private display: HTMLElement;\n private response: { rt: number; button: number } = { rt: null, button: null };\n private context: AudioContext;\n private startTime: number;\n private trial_complete: (trial_data: { rt: number; stimulus: string; response: number }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n async trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n this.trial_complete;\n this.params = trial;\n this.display = display_element;\n // setup stimulus\n this.context = this.jsPsych.pluginAPI.audioContext();\n\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // enable buttons after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.enable_buttons);\n }\n\n // Display buttons\n const buttonGroupElement = document.createElement(\"div\");\n buttonGroupElement.id = \"jspsych-audio-button-response-btngroup\";\n if (trial.button_layout === \"grid\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-grid\");\n if (trial.grid_rows === null && trial.grid_columns === null) {\n throw new Error(\n \"You cannot set `grid_rows` to `null` without providing a value for `grid_columns`.\"\n );\n }\n const n_cols =\n trial.grid_columns === null\n ? Math.ceil(trial.choices.length / trial.grid_rows)\n : trial.grid_columns;\n const n_rows =\n trial.grid_rows === null\n ? Math.ceil(trial.choices.length / trial.grid_columns)\n : trial.grid_rows;\n buttonGroupElement.style.gridTemplateColumns = `repeat(${n_cols}, 1fr)`;\n buttonGroupElement.style.gridTemplateRows = `repeat(${n_rows}, 1fr)`;\n } else if (trial.button_layout === \"flex\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-flex\");\n }\n\n for (const [choiceIndex, choice] of trial.choices.entries()) {\n buttonGroupElement.insertAdjacentHTML(\"beforeend\", trial.button_html(choice, choiceIndex));\n const buttonElement = buttonGroupElement.lastChild as HTMLElement;\n buttonElement.dataset.choice = choiceIndex.toString();\n buttonElement.addEventListener(\"click\", () => {\n this.after_response(choiceIndex);\n });\n this.buttonElements.push(buttonElement);\n }\n\n display_element.appendChild(buttonGroupElement);\n\n // Show prompt if there is one\n if (trial.prompt !== null) {\n display_element.insertAdjacentHTML(\"beforeend\", trial.prompt);\n }\n\n if (trial.response_allowed_while_playing) {\n if (trial.enable_button_after > 0) {\n this.disable_buttons();\n this.enable_buttons();\n }\n } else {\n this.disable_buttons();\n }\n\n // start time\n this.startTime = performance.now();\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n\n this.audio.play();\n\n return new Promise((resolve) => {\n this.trial_complete = resolve;\n });\n }\n\n private disable_buttons = () => {\n for (const button of this.buttonElements) {\n button.setAttribute(\"disabled\", \"disabled\");\n }\n };\n\n private enable_buttons_without_delay = () => {\n for (const button of this.buttonElements) {\n button.removeAttribute(\"disabled\");\n }\n };\n\n private enable_buttons_with_delay = (delay: number) => {\n this.jsPsych.pluginAPI.setTimeout(this.enable_buttons_without_delay, delay);\n };\n\n private enable_buttons() {\n if (this.params.enable_button_after > 0) {\n this.enable_buttons_with_delay(this.params.enable_button_after);\n } else {\n this.enable_buttons_without_delay();\n }\n }\n\n // function to handle responses by the subject\n private after_response = (choice) => {\n // measure rt\n var endTime = performance.now();\n var rt = Math.round(endTime - this.startTime);\n if (this.context !== null) {\n endTime = this.context.currentTime;\n rt = Math.round((endTime - this.startTime) * 1000);\n }\n this.response.button = parseInt(choice);\n this.response.rt = rt;\n\n // disable all the buttons after a response\n this.disable_buttons();\n\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n };\n\n // method to end trial when it is time\n private end_trial = () => {\n // stop the audio file if it is playing\n this.audio.stop();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.enable_buttons);\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n stimulus: this.params.stimulus,\n response: this.response.button,\n };\n\n // move on to the next trial\n this.trial_complete(trial_data);\n };\n\n async 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 create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt:\n this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true) +\n trial.enable_button_after,\n response: this.jsPsych.randomization.randomInt(0, trial.choices.length - 1),\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 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 const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.clickTarget(\n display_element.querySelector(\n `#jspsych-audio-button-response-btngroup [data-choice=\"${data.response}\"]`\n ),\n data.rt\n );\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioButtonResponsePlugin;\n"],"names":["version","ParameterType"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-button-response\",\n version: version,\n parameters: {\n /** Path to audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** Labels for the buttons. Each different string in the array will generate a different button. */\n choices: {\n type: ParameterType.STRING,\n default: undefined,\n array: true,\n },\n /**\n * A function that generates the HTML for each button in the `choices` array. The function gets the string\n * and index of the item in the `choices` array and should return valid HTML. If you want to use different\n * markup for each button, you can do that by using a conditional on either parameter. The default parameter\n * returns a button element with the text label of the choice.\n */\n button_html: {\n type: ParameterType.FUNCTION,\n default: function (choice: string, choice_index: number) {\n return `<button class=\"jspsych-btn\">${choice}</button>`;\n },\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention\n * is that it can be used to provide a reminder about the action the participant is supposed to take\n * (e.g., which key to press). */\n prompt: {\n type: ParameterType.HTML_STRING,\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, the trial\n * will wait for a response indefinitely */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** Setting to `'grid'` will make the container element have the CSS property `display: grid` and enable the\n * use of `grid_rows` and `grid_columns`. Setting to `'flex'` will make the container element have the CSS\n * property `display: flex`. You can customize how the buttons are laid out by adding inline CSS in the `button_html` parameter.\n */\n button_layout: {\n type: ParameterType.STRING,\n default: \"grid\",\n },\n /** The number of rows in the button grid. Only applicable when `button_layout` is set to `'grid'`. If null, the\n * number of rows will be determined automatically based on the number of buttons and the number of columns.\n */\n grid_rows: {\n type: ParameterType.INT,\n default: 1,\n },\n /** The number of columns in the button grid. Only applicable when `button_layout` is set to `'grid'`.\n * If null, the number of columns will be determined automatically based on the number of buttons and the\n * number of rows.\n */\n grid_columns: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force\n * the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n default: false,\n },\n /**\n * If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before the button choices are enabled and a response is accepted. Once the audio has played\n * all the way through, the buttons are enabled and a response is allowed (including while the audio is\n * being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** How long the button will delay enabling in milliseconds. If `response_allowed_while_playing` is `true`,\n * the timer will start immediately. If it is `false`, the timer will start at the end of the audio. */\n enable_button_after: {\n type: ParameterType.INT,\n default: 0,\n },\n },\n data: {\n /** The path of the audio file that was played. */\n stimulus: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from\n * when the stimulus first began playing until the participant's response.*/\n rt: {\n type: ParameterType.INT,\n },\n /** Indicates which button the participant pressed. The first button in the `choices` array is 0, the second is 1, and so on. */\n response: {\n type: ParameterType.INT,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise \n * timing of the playback. The timing of responses generated is measured against the WebAudio specific clock, \n * improving the measurement of response times. If the browser does not support the WebAudio API, then the audio file is \n * played with HTML5 audio. \n\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if \n * you are using timeline variables or another dynamic method to specify the audio stimulus, you will need \n * to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant \n * has failed to respond within a fixed length of time. You can also prevent a button response from being made before the \n * audio has finished playing.\n * \n * @author Kristin Diep\n * @see {@link https://www.jspsych.org/latest/plugins/audio-button-response/ audio-button-response plugin documentation on jspsych.org}\n */\nclass AudioButtonResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private buttonElements: HTMLElement[] = [];\n private display: HTMLElement;\n private response: { rt: number; button: number } = { rt: null, button: null };\n private context: AudioContext;\n private startTime: number;\n private trial_complete: (trial_data: { rt: number; stimulus: string; response: number }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n async trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n this.params = trial;\n this.display = display_element;\n // setup stimulus\n this.context = this.jsPsych.pluginAPI.audioContext();\n\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // enable buttons after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.enable_buttons);\n }\n\n // Display buttons\n const buttonGroupElement = document.createElement(\"div\");\n buttonGroupElement.id = \"jspsych-audio-button-response-btngroup\";\n if (trial.button_layout === \"grid\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-grid\");\n if (trial.grid_rows === null && trial.grid_columns === null) {\n throw new Error(\n \"You cannot set `grid_rows` to `null` without providing a value for `grid_columns`.\"\n );\n }\n const n_cols =\n trial.grid_columns === null\n ? Math.ceil(trial.choices.length / trial.grid_rows)\n : trial.grid_columns;\n const n_rows =\n trial.grid_rows === null\n ? Math.ceil(trial.choices.length / trial.grid_columns)\n : trial.grid_rows;\n buttonGroupElement.style.gridTemplateColumns = `repeat(${n_cols}, 1fr)`;\n buttonGroupElement.style.gridTemplateRows = `repeat(${n_rows}, 1fr)`;\n } else if (trial.button_layout === \"flex\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-flex\");\n }\n\n for (const [choiceIndex, choice] of trial.choices.entries()) {\n buttonGroupElement.insertAdjacentHTML(\"beforeend\", trial.button_html(choice, choiceIndex));\n const buttonElement = buttonGroupElement.lastChild as HTMLElement;\n buttonElement.dataset.choice = choiceIndex.toString();\n buttonElement.addEventListener(\"click\", () => {\n this.after_response(choiceIndex);\n });\n this.buttonElements.push(buttonElement);\n }\n\n display_element.appendChild(buttonGroupElement);\n\n // Show prompt if there is one\n if (trial.prompt !== null) {\n display_element.insertAdjacentHTML(\"beforeend\", trial.prompt);\n }\n\n if (trial.response_allowed_while_playing) {\n if (trial.enable_button_after > 0) {\n this.disable_buttons();\n this.enable_buttons();\n }\n } else {\n this.disable_buttons();\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n\n // start time\n this.startTime = performance.now();\n if (this.context !== null) {\n this.startTime = this.context.currentTime;\n }\n\n // start audio\n this.audio.play();\n\n return new Promise((resolve) => {\n // hold the .resolve() function from the Promise that ends the trial\n this.trial_complete = resolve;\n });\n }\n\n private disable_buttons = () => {\n for (const button of this.buttonElements) {\n button.setAttribute(\"disabled\", \"disabled\");\n }\n };\n\n private enable_buttons_without_delay = () => {\n for (const button of this.buttonElements) {\n button.removeAttribute(\"disabled\");\n }\n };\n\n private enable_buttons_with_delay = (delay: number) => {\n this.jsPsych.pluginAPI.setTimeout(this.enable_buttons_without_delay, delay);\n };\n\n private enable_buttons() {\n if (this.params.enable_button_after > 0) {\n this.enable_buttons_with_delay(this.params.enable_button_after);\n } else {\n this.enable_buttons_without_delay();\n }\n }\n\n // function to handle responses by the subject\n private after_response = (choice) => {\n // measure rt\n var endTime = performance.now();\n var rt = Math.round(endTime - this.startTime);\n if (this.context !== null) {\n endTime = this.context.currentTime;\n rt = Math.round((endTime - this.startTime) * 1000);\n }\n this.response.button = parseInt(choice);\n this.response.rt = rt;\n\n // disable all the buttons after a response\n this.disable_buttons();\n\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n };\n\n // method to end trial when it is time\n private end_trial = () => {\n // stop the audio file if it is playing\n this.audio.stop();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.enable_buttons);\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n stimulus: this.params.stimulus,\n response: this.response.button,\n };\n\n // move on to the next trial\n this.trial_complete(trial_data);\n };\n\n async 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 create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt:\n this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true) +\n trial.enable_button_after,\n response: this.jsPsych.randomization.randomInt(0, trial.choices.length - 1),\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 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 const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.clickTarget(\n display_element.querySelector(\n `#jspsych-audio-button-response-btngroup [data-choice=\"${data.response}\"]`\n ),\n data.rt\n );\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioButtonResponsePlugin;\n"],"names":["version","ParameterType"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -94,6 +94,10 @@ declare const info: {
|
|
|
94
94
|
};
|
|
95
95
|
};
|
|
96
96
|
readonly data: {
|
|
97
|
+
/** The path of the audio file that was played. */
|
|
98
|
+
readonly stimulus: {
|
|
99
|
+
readonly type: ParameterType.STRING;
|
|
100
|
+
};
|
|
97
101
|
/** The response time in milliseconds for the participant to make a response. The time is measured from
|
|
98
102
|
* when the stimulus first began playing until the participant's response.*/
|
|
99
103
|
readonly rt: {
|
|
@@ -219,6 +223,10 @@ declare class AudioButtonResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
219
223
|
};
|
|
220
224
|
};
|
|
221
225
|
readonly data: {
|
|
226
|
+
/** The path of the audio file that was played. */
|
|
227
|
+
readonly stimulus: {
|
|
228
|
+
readonly type: ParameterType.STRING;
|
|
229
|
+
};
|
|
222
230
|
/** The response time in milliseconds for the participant to make a response. The time is measured from
|
|
223
231
|
* when the stimulus first began playing until the participant's response.*/
|
|
224
232
|
readonly rt: {
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { ParameterType } from 'jspsych';
|
|
|
3
3
|
|
|
4
4
|
var _package = {
|
|
5
5
|
name: "@jspsych/plugin-audio-button-response",
|
|
6
|
-
version: "2.0.
|
|
6
|
+
version: "2.0.2",
|
|
7
7
|
description: "jsPsych plugin for playing an audio file and getting a button response",
|
|
8
8
|
type: "module",
|
|
9
9
|
main: "dist/index.cjs",
|
|
@@ -102,6 +102,9 @@ const info = {
|
|
|
102
102
|
}
|
|
103
103
|
},
|
|
104
104
|
data: {
|
|
105
|
+
stimulus: {
|
|
106
|
+
type: ParameterType.STRING
|
|
107
|
+
},
|
|
105
108
|
rt: {
|
|
106
109
|
type: ParameterType.INT
|
|
107
110
|
},
|
|
@@ -156,7 +159,6 @@ class AudioButtonResponsePlugin {
|
|
|
156
159
|
autoBind(this);
|
|
157
160
|
}
|
|
158
161
|
async trial(display_element, trial, on_load) {
|
|
159
|
-
this.trial_complete;
|
|
160
162
|
this.params = trial;
|
|
161
163
|
this.display = display_element;
|
|
162
164
|
this.context = this.jsPsych.pluginAPI.audioContext();
|
|
@@ -204,13 +206,16 @@ class AudioButtonResponsePlugin {
|
|
|
204
206
|
} else {
|
|
205
207
|
this.disable_buttons();
|
|
206
208
|
}
|
|
207
|
-
this.startTime = performance.now();
|
|
208
209
|
if (trial.trial_duration !== null) {
|
|
209
210
|
this.jsPsych.pluginAPI.setTimeout(() => {
|
|
210
211
|
this.end_trial();
|
|
211
212
|
}, trial.trial_duration);
|
|
212
213
|
}
|
|
213
214
|
on_load();
|
|
215
|
+
this.startTime = performance.now();
|
|
216
|
+
if (this.context !== null) {
|
|
217
|
+
this.startTime = this.context.currentTime;
|
|
218
|
+
}
|
|
214
219
|
this.audio.play();
|
|
215
220
|
return new Promise((resolve) => {
|
|
216
221
|
this.trial_complete = resolve;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-button-response\",\n version: version,\n parameters: {\n /** Path to audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** Labels for the buttons. Each different string in the array will generate a different button. */\n choices: {\n type: ParameterType.STRING,\n default: undefined,\n array: true,\n },\n /**\n * A function that generates the HTML for each button in the `choices` array. The function gets the string\n * and index of the item in the `choices` array and should return valid HTML. If you want to use different\n * markup for each button, you can do that by using a conditional on either parameter. The default parameter\n * returns a button element with the text label of the choice.\n */\n button_html: {\n type: ParameterType.FUNCTION,\n default: function (choice: string, choice_index: number) {\n return `<button class=\"jspsych-btn\">${choice}</button>`;\n },\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention\n * is that it can be used to provide a reminder about the action the participant is supposed to take\n * (e.g., which key to press). */\n prompt: {\n type: ParameterType.HTML_STRING,\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, the trial\n * will wait for a response indefinitely */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** Setting to `'grid'` will make the container element have the CSS property `display: grid` and enable the\n * use of `grid_rows` and `grid_columns`. Setting to `'flex'` will make the container element have the CSS\n * property `display: flex`. You can customize how the buttons are laid out by adding inline CSS in the `button_html` parameter.\n */\n button_layout: {\n type: ParameterType.STRING,\n default: \"grid\",\n },\n /** The number of rows in the button grid. Only applicable when `button_layout` is set to `'grid'`. If null, the\n * number of rows will be determined automatically based on the number of buttons and the number of columns.\n */\n grid_rows: {\n type: ParameterType.INT,\n default: 1,\n },\n /** The number of columns in the button grid. Only applicable when `button_layout` is set to `'grid'`.\n * If null, the number of columns will be determined automatically based on the number of buttons and the\n * number of rows.\n */\n grid_columns: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force\n * the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n default: false,\n },\n /**\n * If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before the button choices are enabled and a response is accepted. Once the audio has played\n * all the way through, the buttons are enabled and a response is allowed (including while the audio is\n * being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** How long the button will delay enabling in milliseconds. If `response_allowed_while_playing` is `true`,\n * the timer will start immediately. If it is `false`, the timer will start at the end of the audio. */\n enable_button_after: {\n type: ParameterType.INT,\n default: 0,\n },\n },\n data: {\n /** The response time in milliseconds for the participant to make a response. The time is measured from\n * when the stimulus first began playing until the participant's response.*/\n rt: {\n type: ParameterType.INT,\n },\n /** Indicates which button the participant pressed. The first button in the `choices` array is 0, the second is 1, and so on. */\n response: {\n type: ParameterType.INT,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise \n * timing of the playback. The timing of responses generated is measured against the WebAudio specific clock, \n * improving the measurement of response times. If the browser does not support the WebAudio API, then the audio file is \n * played with HTML5 audio. \n\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if \n * you are using timeline variables or another dynamic method to specify the audio stimulus, you will need \n * to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant \n * has failed to respond within a fixed length of time. You can also prevent a button response from being made before the \n * audio has finished playing.\n * \n * @author Kristin Diep\n * @see {@link https://www.jspsych.org/latest/plugins/audio-button-response/ audio-button-response plugin documentation on jspsych.org}\n */\nclass AudioButtonResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private buttonElements: HTMLElement[] = [];\n private display: HTMLElement;\n private response: { rt: number; button: number } = { rt: null, button: null };\n private context: AudioContext;\n private startTime: number;\n private trial_complete: (trial_data: { rt: number; stimulus: string; response: number }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n async trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n this.trial_complete;\n this.params = trial;\n this.display = display_element;\n // setup stimulus\n this.context = this.jsPsych.pluginAPI.audioContext();\n\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // enable buttons after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.enable_buttons);\n }\n\n // Display buttons\n const buttonGroupElement = document.createElement(\"div\");\n buttonGroupElement.id = \"jspsych-audio-button-response-btngroup\";\n if (trial.button_layout === \"grid\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-grid\");\n if (trial.grid_rows === null && trial.grid_columns === null) {\n throw new Error(\n \"You cannot set `grid_rows` to `null` without providing a value for `grid_columns`.\"\n );\n }\n const n_cols =\n trial.grid_columns === null\n ? Math.ceil(trial.choices.length / trial.grid_rows)\n : trial.grid_columns;\n const n_rows =\n trial.grid_rows === null\n ? Math.ceil(trial.choices.length / trial.grid_columns)\n : trial.grid_rows;\n buttonGroupElement.style.gridTemplateColumns = `repeat(${n_cols}, 1fr)`;\n buttonGroupElement.style.gridTemplateRows = `repeat(${n_rows}, 1fr)`;\n } else if (trial.button_layout === \"flex\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-flex\");\n }\n\n for (const [choiceIndex, choice] of trial.choices.entries()) {\n buttonGroupElement.insertAdjacentHTML(\"beforeend\", trial.button_html(choice, choiceIndex));\n const buttonElement = buttonGroupElement.lastChild as HTMLElement;\n buttonElement.dataset.choice = choiceIndex.toString();\n buttonElement.addEventListener(\"click\", () => {\n this.after_response(choiceIndex);\n });\n this.buttonElements.push(buttonElement);\n }\n\n display_element.appendChild(buttonGroupElement);\n\n // Show prompt if there is one\n if (trial.prompt !== null) {\n display_element.insertAdjacentHTML(\"beforeend\", trial.prompt);\n }\n\n if (trial.response_allowed_while_playing) {\n if (trial.enable_button_after > 0) {\n this.disable_buttons();\n this.enable_buttons();\n }\n } else {\n this.disable_buttons();\n }\n\n // start time\n this.startTime = performance.now();\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n\n this.audio.play();\n\n return new Promise((resolve) => {\n this.trial_complete = resolve;\n });\n }\n\n private disable_buttons = () => {\n for (const button of this.buttonElements) {\n button.setAttribute(\"disabled\", \"disabled\");\n }\n };\n\n private enable_buttons_without_delay = () => {\n for (const button of this.buttonElements) {\n button.removeAttribute(\"disabled\");\n }\n };\n\n private enable_buttons_with_delay = (delay: number) => {\n this.jsPsych.pluginAPI.setTimeout(this.enable_buttons_without_delay, delay);\n };\n\n private enable_buttons() {\n if (this.params.enable_button_after > 0) {\n this.enable_buttons_with_delay(this.params.enable_button_after);\n } else {\n this.enable_buttons_without_delay();\n }\n }\n\n // function to handle responses by the subject\n private after_response = (choice) => {\n // measure rt\n var endTime = performance.now();\n var rt = Math.round(endTime - this.startTime);\n if (this.context !== null) {\n endTime = this.context.currentTime;\n rt = Math.round((endTime - this.startTime) * 1000);\n }\n this.response.button = parseInt(choice);\n this.response.rt = rt;\n\n // disable all the buttons after a response\n this.disable_buttons();\n\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n };\n\n // method to end trial when it is time\n private end_trial = () => {\n // stop the audio file if it is playing\n this.audio.stop();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.enable_buttons);\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n stimulus: this.params.stimulus,\n response: this.response.button,\n };\n\n // move on to the next trial\n this.trial_complete(trial_data);\n };\n\n async 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 create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt:\n this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true) +\n trial.enable_button_after,\n response: this.jsPsych.randomization.randomInt(0, trial.choices.length - 1),\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 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 const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.clickTarget(\n display_element.querySelector(\n `#jspsych-audio-button-response-btngroup [data-choice=\"${data.response}\"]`\n ),\n data.rt\n );\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioButtonResponsePlugin;\n"],"names":["version"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-button-response\",\n version: version,\n parameters: {\n /** Path to audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** Labels for the buttons. Each different string in the array will generate a different button. */\n choices: {\n type: ParameterType.STRING,\n default: undefined,\n array: true,\n },\n /**\n * A function that generates the HTML for each button in the `choices` array. The function gets the string\n * and index of the item in the `choices` array and should return valid HTML. If you want to use different\n * markup for each button, you can do that by using a conditional on either parameter. The default parameter\n * returns a button element with the text label of the choice.\n */\n button_html: {\n type: ParameterType.FUNCTION,\n default: function (choice: string, choice_index: number) {\n return `<button class=\"jspsych-btn\">${choice}</button>`;\n },\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention\n * is that it can be used to provide a reminder about the action the participant is supposed to take\n * (e.g., which key to press). */\n prompt: {\n type: ParameterType.HTML_STRING,\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, the trial\n * will wait for a response indefinitely */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** Setting to `'grid'` will make the container element have the CSS property `display: grid` and enable the\n * use of `grid_rows` and `grid_columns`. Setting to `'flex'` will make the container element have the CSS\n * property `display: flex`. You can customize how the buttons are laid out by adding inline CSS in the `button_html` parameter.\n */\n button_layout: {\n type: ParameterType.STRING,\n default: \"grid\",\n },\n /** The number of rows in the button grid. Only applicable when `button_layout` is set to `'grid'`. If null, the\n * number of rows will be determined automatically based on the number of buttons and the number of columns.\n */\n grid_rows: {\n type: ParameterType.INT,\n default: 1,\n },\n /** The number of columns in the button grid. Only applicable when `button_layout` is set to `'grid'`.\n * If null, the number of columns will be determined automatically based on the number of buttons and the\n * number of rows.\n */\n grid_columns: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force\n * the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n default: false,\n },\n /**\n * If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before the button choices are enabled and a response is accepted. Once the audio has played\n * all the way through, the buttons are enabled and a response is allowed (including while the audio is\n * being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** How long the button will delay enabling in milliseconds. If `response_allowed_while_playing` is `true`,\n * the timer will start immediately. If it is `false`, the timer will start at the end of the audio. */\n enable_button_after: {\n type: ParameterType.INT,\n default: 0,\n },\n },\n data: {\n /** The path of the audio file that was played. */\n stimulus: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from\n * when the stimulus first began playing until the participant's response.*/\n rt: {\n type: ParameterType.INT,\n },\n /** Indicates which button the participant pressed. The first button in the `choices` array is 0, the second is 1, and so on. */\n response: {\n type: ParameterType.INT,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise \n * timing of the playback. The timing of responses generated is measured against the WebAudio specific clock, \n * improving the measurement of response times. If the browser does not support the WebAudio API, then the audio file is \n * played with HTML5 audio. \n\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if \n * you are using timeline variables or another dynamic method to specify the audio stimulus, you will need \n * to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant \n * has failed to respond within a fixed length of time. You can also prevent a button response from being made before the \n * audio has finished playing.\n * \n * @author Kristin Diep\n * @see {@link https://www.jspsych.org/latest/plugins/audio-button-response/ audio-button-response plugin documentation on jspsych.org}\n */\nclass AudioButtonResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private buttonElements: HTMLElement[] = [];\n private display: HTMLElement;\n private response: { rt: number; button: number } = { rt: null, button: null };\n private context: AudioContext;\n private startTime: number;\n private trial_complete: (trial_data: { rt: number; stimulus: string; response: number }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n async trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n this.params = trial;\n this.display = display_element;\n // setup stimulus\n this.context = this.jsPsych.pluginAPI.audioContext();\n\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // enable buttons after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.enable_buttons);\n }\n\n // Display buttons\n const buttonGroupElement = document.createElement(\"div\");\n buttonGroupElement.id = \"jspsych-audio-button-response-btngroup\";\n if (trial.button_layout === \"grid\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-grid\");\n if (trial.grid_rows === null && trial.grid_columns === null) {\n throw new Error(\n \"You cannot set `grid_rows` to `null` without providing a value for `grid_columns`.\"\n );\n }\n const n_cols =\n trial.grid_columns === null\n ? Math.ceil(trial.choices.length / trial.grid_rows)\n : trial.grid_columns;\n const n_rows =\n trial.grid_rows === null\n ? Math.ceil(trial.choices.length / trial.grid_columns)\n : trial.grid_rows;\n buttonGroupElement.style.gridTemplateColumns = `repeat(${n_cols}, 1fr)`;\n buttonGroupElement.style.gridTemplateRows = `repeat(${n_rows}, 1fr)`;\n } else if (trial.button_layout === \"flex\") {\n buttonGroupElement.classList.add(\"jspsych-btn-group-flex\");\n }\n\n for (const [choiceIndex, choice] of trial.choices.entries()) {\n buttonGroupElement.insertAdjacentHTML(\"beforeend\", trial.button_html(choice, choiceIndex));\n const buttonElement = buttonGroupElement.lastChild as HTMLElement;\n buttonElement.dataset.choice = choiceIndex.toString();\n buttonElement.addEventListener(\"click\", () => {\n this.after_response(choiceIndex);\n });\n this.buttonElements.push(buttonElement);\n }\n\n display_element.appendChild(buttonGroupElement);\n\n // Show prompt if there is one\n if (trial.prompt !== null) {\n display_element.insertAdjacentHTML(\"beforeend\", trial.prompt);\n }\n\n if (trial.response_allowed_while_playing) {\n if (trial.enable_button_after > 0) {\n this.disable_buttons();\n this.enable_buttons();\n }\n } else {\n this.disable_buttons();\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n\n // start time\n this.startTime = performance.now();\n if (this.context !== null) {\n this.startTime = this.context.currentTime;\n }\n\n // start audio\n this.audio.play();\n\n return new Promise((resolve) => {\n // hold the .resolve() function from the Promise that ends the trial\n this.trial_complete = resolve;\n });\n }\n\n private disable_buttons = () => {\n for (const button of this.buttonElements) {\n button.setAttribute(\"disabled\", \"disabled\");\n }\n };\n\n private enable_buttons_without_delay = () => {\n for (const button of this.buttonElements) {\n button.removeAttribute(\"disabled\");\n }\n };\n\n private enable_buttons_with_delay = (delay: number) => {\n this.jsPsych.pluginAPI.setTimeout(this.enable_buttons_without_delay, delay);\n };\n\n private enable_buttons() {\n if (this.params.enable_button_after > 0) {\n this.enable_buttons_with_delay(this.params.enable_button_after);\n } else {\n this.enable_buttons_without_delay();\n }\n }\n\n // function to handle responses by the subject\n private after_response = (choice) => {\n // measure rt\n var endTime = performance.now();\n var rt = Math.round(endTime - this.startTime);\n if (this.context !== null) {\n endTime = this.context.currentTime;\n rt = Math.round((endTime - this.startTime) * 1000);\n }\n this.response.button = parseInt(choice);\n this.response.rt = rt;\n\n // disable all the buttons after a response\n this.disable_buttons();\n\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n };\n\n // method to end trial when it is time\n private end_trial = () => {\n // stop the audio file if it is playing\n this.audio.stop();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.enable_buttons);\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n stimulus: this.params.stimulus,\n response: this.response.button,\n };\n\n // move on to the next trial\n this.trial_complete(trial_data);\n };\n\n async 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 create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt:\n this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true) +\n trial.enable_button_after,\n response: this.jsPsych.randomization.randomInt(0, trial.choices.length - 1),\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 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 const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.clickTarget(\n display_element.querySelector(\n `#jspsych-audio-button-response-btngroup [data-choice=\"${data.response}\"]`\n ),\n data.rt\n );\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioButtonResponsePlugin;\n"],"names":["version"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -100,6 +100,10 @@ const info = <const>{
|
|
|
100
100
|
},
|
|
101
101
|
},
|
|
102
102
|
data: {
|
|
103
|
+
/** The path of the audio file that was played. */
|
|
104
|
+
stimulus: {
|
|
105
|
+
type: ParameterType.STRING,
|
|
106
|
+
},
|
|
103
107
|
/** The response time in milliseconds for the participant to make a response. The time is measured from
|
|
104
108
|
* when the stimulus first began playing until the participant's response.*/
|
|
105
109
|
rt: {
|
|
@@ -147,8 +151,6 @@ class AudioButtonResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
147
151
|
}
|
|
148
152
|
|
|
149
153
|
async trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {
|
|
150
|
-
// hold the .resolve() function from the Promise that ends the trial
|
|
151
|
-
this.trial_complete;
|
|
152
154
|
this.params = trial;
|
|
153
155
|
this.display = display_element;
|
|
154
156
|
// setup stimulus
|
|
@@ -217,9 +219,6 @@ class AudioButtonResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
217
219
|
this.disable_buttons();
|
|
218
220
|
}
|
|
219
221
|
|
|
220
|
-
// start time
|
|
221
|
-
this.startTime = performance.now();
|
|
222
|
-
|
|
223
222
|
// end trial if time limit is set
|
|
224
223
|
if (trial.trial_duration !== null) {
|
|
225
224
|
this.jsPsych.pluginAPI.setTimeout(() => {
|
|
@@ -229,9 +228,17 @@ class AudioButtonResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
229
228
|
|
|
230
229
|
on_load();
|
|
231
230
|
|
|
231
|
+
// start time
|
|
232
|
+
this.startTime = performance.now();
|
|
233
|
+
if (this.context !== null) {
|
|
234
|
+
this.startTime = this.context.currentTime;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// start audio
|
|
232
238
|
this.audio.play();
|
|
233
239
|
|
|
234
240
|
return new Promise((resolve) => {
|
|
241
|
+
// hold the .resolve() function from the Promise that ends the trial
|
|
235
242
|
this.trial_complete = resolve;
|
|
236
243
|
});
|
|
237
244
|
}
|