@jspsych/plugin-audio-button-response 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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.0",
54
+ version: "2.0.1",
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",
@@ -173,7 +173,6 @@ var jsPsychAudioButtonResponse = (function (jspsych) {
173
173
  startTime;
174
174
  trial_complete;
175
175
  async trial(display_element, trial, on_load) {
176
- this.trial_complete;
177
176
  this.params = trial;
178
177
  this.display = display_element;
179
178
  this.context = this.jsPsych.pluginAPI.audioContext();
@@ -221,13 +220,16 @@ var jsPsychAudioButtonResponse = (function (jspsych) {
221
220
  } else {
222
221
  this.disable_buttons();
223
222
  }
224
- this.startTime = performance.now();
225
223
  if (trial.trial_duration !== null) {
226
224
  this.jsPsych.pluginAPI.setTimeout(() => {
227
225
  this.end_trial();
228
226
  }, trial.trial_duration);
229
227
  }
230
228
  on_load();
229
+ this.startTime = performance.now();
230
+ if (this.context !== null) {
231
+ this.startTime = this.context.currentTime;
232
+ }
231
233
  this.audio.play();
232
234
  return new Promise((resolve) => {
233
235
  this.trial_complete = resolve;
@@ -328,4 +330,4 @@ var jsPsychAudioButtonResponse = (function (jspsych) {
328
330
  return AudioButtonResponsePlugin;
329
331
 
330
332
  })(jsPsychModule);
331
- //# sourceMappingURL=https://unpkg.com/@jspsych/plugin-audio-button-response@2.0.0/dist/index.browser.js.map
333
+ //# sourceMappingURL=https://unpkg.com/@jspsych/plugin-audio-button-response@2.0.1/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":";;;;;;;CAEA;CACA,MAAM,gBAAgB,GAAG,MAAM,IAAI;CACnC,CAAC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAE,CAAC;AAC9B;CACA,CAAC,GAAG;CACJ,EAAE,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;CAC7C,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;CACjC,GAAG;CACH,EAAE,QAAQ,CAAC,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,MAAM,KAAK,MAAM,CAAC,SAAS,EAAE;AACpF;CACA,CAAC,OAAO,UAAU,CAAC;CACnB,CAAC,CAAC;AACF;KACA,QAAc,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK;CACpD,CAAC,MAAM,MAAM,GAAG,GAAG,IAAI;CACvB,EAAE,MAAM,KAAK,GAAG,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,GAAG,GAAG,KAAK,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7F;CACA,EAAE,IAAI,OAAO,EAAE;CACf,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;CAC9B,GAAG;AACH;CACA,EAAE,IAAI,OAAO,EAAE;CACf,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;CAC/B,GAAG;AACH;CACA,EAAE,OAAO,IAAI,CAAC;CACd,EAAE,CAAC;AACH;CACA,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE;CAC3E,EAAE,IAAI,GAAG,KAAK,aAAa,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;CAC7C,GAAG,SAAS;CACZ,GAAG;AACH;CACA,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC,wBAAwB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACnE,EAAE,IAAI,UAAU,IAAI,OAAO,UAAU,CAAC,KAAK,KAAK,UAAU,EAAE;CAC5D,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;CACpC,GAAG;CACH,EAAE;AACF;CACA,CAAC,OAAO,IAAI,CAAC;CACb,CAAC,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CCpCD,MAAM,IAAc,GAAA;CAAA,EAClB,IAAM,EAAA,uBAAA;CAAA,WACNA,gBAAA;CAAA,EACA,UAAY,EAAA;CAAA,IAEV,QAAU,EAAA;CAAA,MACR,MAAMC,qBAAc,CAAA,KAAA;CAAA,MACpB,OAAS,EAAA,KAAA,CAAA;CAAA,KACX;CAAA,IAEA,OAAS,EAAA;CAAA,MACP,MAAMA,qBAAc,CAAA,MAAA;CAAA,MACpB,OAAS,EAAA,KAAA,CAAA;CAAA,MACT,KAAO,EAAA,IAAA;CAAA,KACT;CAAA,IAOA,WAAa,EAAA;CAAA,MACX,MAAMA,qBAAc,CAAA,QAAA;CAAA,MACpB,OAAA,EAAS,SAAU,MAAA,EAAgB,YAAsB,EAAA;CACvD,QAAA,OAAO,CAA+B,4BAAA,EAAA,MAAA,CAAA,SAAA,CAAA,CAAA;CAAA,OACxC;CAAA,KACF;CAAA,IAIA,MAAQ,EAAA;CAAA,MACN,MAAMA,qBAAc,CAAA,WAAA;CAAA,MACpB,OAAS,EAAA,IAAA;CAAA,KACX;CAAA,IAKA,cAAgB,EAAA;CAAA,MACd,MAAMA,qBAAc,CAAA,GAAA;CAAA,MACpB,OAAS,EAAA,IAAA;CAAA,KACX;CAAA,IAKA,aAAe,EAAA;CAAA,MACb,MAAMA,qBAAc,CAAA,MAAA;CAAA,MACpB,OAAS,EAAA,MAAA;CAAA,KACX;CAAA,IAIA,SAAW,EAAA;CAAA,MACT,MAAMA,qBAAc,CAAA,GAAA;CAAA,MACpB,OAAS,EAAA,CAAA;CAAA,KACX;CAAA,IAKA,YAAc,EAAA;CAAA,MACZ,MAAMA,qBAAc,CAAA,GAAA;CAAA,MACpB,OAAS,EAAA,IAAA;CAAA,KACX;CAAA,IAKA,mBAAqB,EAAA;CAAA,MACnB,MAAMA,qBAAc,CAAA,IAAA;CAAA,MACpB,OAAS,EAAA,IAAA;CAAA,KACX;CAAA,IAEA,sBAAwB,EAAA;CAAA,MACtB,MAAMA,qBAAc,CAAA,IAAA;CAAA,MACpB,OAAS,EAAA,KAAA;CAAA,KACX;CAAA,IAOA,8BAAgC,EAAA;CAAA,MAC9B,MAAMA,qBAAc,CAAA,IAAA;CAAA,MACpB,OAAS,EAAA,IAAA;CAAA,KACX;CAAA,IAGA,mBAAqB,EAAA;CAAA,MACnB,MAAMA,qBAAc,CAAA,GAAA;CAAA,MACpB,OAAS,EAAA,CAAA;CAAA,KACX;CAAA,GACF;CAAA,EACA,IAAM,EAAA;CAAA,IAGJ,EAAI,EAAA;CAAA,MACF,MAAMA,qBAAc,CAAA,GAAA;CAAA,KACtB;CAAA,IAEA,QAAU,EAAA;CAAA,MACR,MAAMA,qBAAc,CAAA,GAAA;CAAA,KACtB;CAAA,GACF;CACF,CAAA,CAAA;CAqBA,MAAM,yBAAyD,CAAA;CAAA,EAW7D,YAAoB,OAAkB,EAAA;CAAlB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA,CAAA;CAClB,IAAAC,UAAA,CAAS,IAAI,CAAA,CAAA;CAAA,GACf;CAAA,EAZA,OAAO,IAAO,GAAA,IAAA,CAAA;CAAA,EACN,KAAA,CAAA;CAAA,EACA,MAAA,CAAA;CAAA,EACA,iBAAgC,EAAC,CAAA;CAAA,EACjC,OAAA,CAAA;CAAA,EACA,QAA2C,GAAA,EAAE,EAAI,EAAA,IAAA,EAAM,QAAQ,IAAK,EAAA,CAAA;CAAA,EACpE,OAAA,CAAA;CAAA,EACA,SAAA,CAAA;CAAA,EACA,cAAA,CAAA;CAAA,EAMR,MAAM,KAAA,CAAM,eAA8B,EAAA,KAAA,EAAwB,OAAqB,EAAA;CAErF,IAAK,IAAA,CAAA,cAAA,CAAA;CACL,IAAA,IAAA,CAAK,MAAS,GAAA,KAAA,CAAA;CACd,IAAA,IAAA,CAAK,OAAU,GAAA,eAAA,CAAA;CAEf,IAAA,IAAA,CAAK,OAAU,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,CAAU,YAAa,EAAA,CAAA;CAGnD,IAAA,IAAA,CAAK,QAAQ,MAAM,IAAA,CAAK,QAAQ,SAAU,CAAA,cAAA,CAAe,MAAM,QAAQ,CAAA,CAAA;CAGvE,IAAA,IAAI,MAAM,sBAAwB,EAAA;CAChC,MAAA,IAAA,CAAK,KAAM,CAAA,gBAAA,CAAiB,OAAS,EAAA,IAAA,CAAK,SAAS,CAAA,CAAA;CAAA,KACrD;CAGA,IAAA,IAAI,CAAC,KAAA,CAAM,8BAAkC,IAAA,CAAC,MAAM,sBAAwB,EAAA;CAC1E,MAAA,IAAA,CAAK,KAAM,CAAA,gBAAA,CAAiB,OAAS,EAAA,IAAA,CAAK,cAAc,CAAA,CAAA;CAAA,KAC1D;CAGA,IAAM,MAAA,kBAAA,GAAqB,QAAS,CAAA,aAAA,CAAc,KAAK,CAAA,CAAA;CACvD,IAAA,kBAAA,CAAmB,EAAK,GAAA,wCAAA,CAAA;CACxB,IAAI,IAAA,KAAA,CAAM,kBAAkB,MAAQ,EAAA;CAClC,MAAmB,kBAAA,CAAA,SAAA,CAAU,IAAI,wBAAwB,CAAA,CAAA;CACzD,MAAA,IAAI,KAAM,CAAA,SAAA,KAAc,IAAQ,IAAA,KAAA,CAAM,iBAAiB,IAAM,EAAA;CAC3D,QAAA,MAAM,IAAI,KAAA;CAAA,UACR,oFAAA;CAAA,SACF,CAAA;CAAA,OACF;CACA,MAAA,MAAM,MACJ,GAAA,KAAA,CAAM,YAAiB,KAAA,IAAA,GACnB,IAAK,CAAA,IAAA,CAAK,KAAM,CAAA,OAAA,CAAQ,MAAS,GAAA,KAAA,CAAM,SAAS,CAAA,GAChD,KAAM,CAAA,YAAA,CAAA;CACZ,MAAA,MAAM,MACJ,GAAA,KAAA,CAAM,SAAc,KAAA,IAAA,GAChB,IAAK,CAAA,IAAA,CAAK,KAAM,CAAA,OAAA,CAAQ,MAAS,GAAA,KAAA,CAAM,YAAY,CAAA,GACnD,KAAM,CAAA,SAAA,CAAA;CACZ,MAAmB,kBAAA,CAAA,KAAA,CAAM,sBAAsB,CAAU,OAAA,EAAA,MAAA,CAAA,MAAA,CAAA,CAAA;CACzD,MAAmB,kBAAA,CAAA,KAAA,CAAM,mBAAmB,CAAU,OAAA,EAAA,MAAA,CAAA,MAAA,CAAA,CAAA;CAAA,KACxD,MAAA,IAAW,KAAM,CAAA,aAAA,KAAkB,MAAQ,EAAA;CACzC,MAAmB,kBAAA,CAAA,SAAA,CAAU,IAAI,wBAAwB,CAAA,CAAA;CAAA,KAC3D;CAEA,IAAA,KAAA,MAAW,CAAC,WAAa,EAAA,MAAM,KAAK,KAAM,CAAA,OAAA,CAAQ,SAAW,EAAA;CAC3D,MAAA,kBAAA,CAAmB,mBAAmB,WAAa,EAAA,KAAA,CAAM,WAAY,CAAA,MAAA,EAAQ,WAAW,CAAC,CAAA,CAAA;CACzF,MAAA,MAAM,gBAAgB,kBAAmB,CAAA,SAAA,CAAA;CACzC,MAAc,aAAA,CAAA,OAAA,CAAQ,MAAS,GAAA,WAAA,CAAY,QAAS,EAAA,CAAA;CACpD,MAAc,aAAA,CAAA,gBAAA,CAAiB,SAAS,MAAM;CAC5C,QAAA,IAAA,CAAK,eAAe,WAAW,CAAA,CAAA;CAAA,OAChC,CAAA,CAAA;CACD,MAAK,IAAA,CAAA,cAAA,CAAe,KAAK,aAAa,CAAA,CAAA;CAAA,KACxC;CAEA,IAAA,eAAA,CAAgB,YAAY,kBAAkB,CAAA,CAAA;CAG9C,IAAI,IAAA,KAAA,CAAM,WAAW,IAAM,EAAA;CACzB,MAAgB,eAAA,CAAA,kBAAA,CAAmB,WAAa,EAAA,KAAA,CAAM,MAAM,CAAA,CAAA;CAAA,KAC9D;CAEA,IAAA,IAAI,MAAM,8BAAgC,EAAA;CACxC,MAAI,IAAA,KAAA,CAAM,sBAAsB,CAAG,EAAA;CACjC,QAAA,IAAA,CAAK,eAAgB,EAAA,CAAA;CACrB,QAAA,IAAA,CAAK,cAAe,EAAA,CAAA;CAAA,OACtB;CAAA,KACK,MAAA;CACL,MAAA,IAAA,CAAK,eAAgB,EAAA,CAAA;CAAA,KACvB;CAGA,IAAK,IAAA,CAAA,SAAA,GAAY,YAAY,GAAI,EAAA,CAAA;CAGjC,IAAI,IAAA,KAAA,CAAM,mBAAmB,IAAM,EAAA;CACjC,MAAK,IAAA,CAAA,OAAA,CAAQ,SAAU,CAAA,UAAA,CAAW,MAAM;CACtC,QAAA,IAAA,CAAK,SAAU,EAAA,CAAA;CAAA,OACjB,EAAG,MAAM,cAAc,CAAA,CAAA;CAAA,KACzB;CAEA,IAAQ,OAAA,EAAA,CAAA;CAER,IAAA,IAAA,CAAK,MAAM,IAAK,EAAA,CAAA;CAEhB,IAAO,OAAA,IAAI,OAAQ,CAAA,CAAC,OAAY,KAAA;CAC9B,MAAA,IAAA,CAAK,cAAiB,GAAA,OAAA,CAAA;CAAA,KACvB,CAAA,CAAA;CAAA,GACH;CAAA,EAEQ,kBAAkB,MAAM;CAC9B,IAAW,KAAA,MAAA,MAAA,IAAU,KAAK,cAAgB,EAAA;CACxC,MAAO,MAAA,CAAA,YAAA,CAAa,YAAY,UAAU,CAAA,CAAA;CAAA,KAC5C;CAAA,GACF,CAAA;CAAA,EAEQ,+BAA+B,MAAM;CAC3C,IAAW,KAAA,MAAA,MAAA,IAAU,KAAK,cAAgB,EAAA;CACxC,MAAA,MAAA,CAAO,gBAAgB,UAAU,CAAA,CAAA;CAAA,KACnC;CAAA,GACF,CAAA;CAAA,EAEQ,yBAAA,GAA4B,CAAC,KAAkB,KAAA;CACrD,IAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,CAAU,UAAW,CAAA,IAAA,CAAK,8BAA8B,KAAK,CAAA,CAAA;CAAA,GAC5E,CAAA;CAAA,EAEQ,cAAiB,GAAA;CACvB,IAAI,IAAA,IAAA,CAAK,MAAO,CAAA,mBAAA,GAAsB,CAAG,EAAA;CACvC,MAAK,IAAA,CAAA,yBAAA,CAA0B,IAAK,CAAA,MAAA,CAAO,mBAAmB,CAAA,CAAA;CAAA,KACzD,MAAA;CACL,MAAA,IAAA,CAAK,4BAA6B,EAAA,CAAA;CAAA,KACpC;CAAA,GACF;CAAA,EAGQ,cAAA,GAAiB,CAAC,MAAW,KAAA;CAEnC,IAAI,IAAA,OAAA,GAAU,YAAY,GAAI,EAAA,CAAA;CAC9B,IAAA,IAAI,EAAK,GAAA,IAAA,CAAK,KAAM,CAAA,OAAA,GAAU,KAAK,SAAS,CAAA,CAAA;CAC5C,IAAI,IAAA,IAAA,CAAK,YAAY,IAAM,EAAA;CACzB,MAAA,OAAA,GAAU,KAAK,OAAQ,CAAA,WAAA,CAAA;CACvB,MAAA,EAAA,GAAK,IAAK,CAAA,KAAA,CAAA,CAAO,OAAU,GAAA,IAAA,CAAK,aAAa,GAAI,CAAA,CAAA;CAAA,KACnD;CACA,IAAK,IAAA,CAAA,QAAA,CAAS,MAAS,GAAA,QAAA,CAAS,MAAM,CAAA,CAAA;CACtC,IAAA,IAAA,CAAK,SAAS,EAAK,GAAA,EAAA,CAAA;CAGnB,IAAA,IAAA,CAAK,eAAgB,EAAA,CAAA;CAErB,IAAI,IAAA,IAAA,CAAK,OAAO,mBAAqB,EAAA;CACnC,MAAA,IAAA,CAAK,SAAU,EAAA,CAAA;CAAA,KACjB;CAAA,GACF,CAAA;CAAA,EAGQ,YAAY,MAAM;CAExB,IAAA,IAAA,CAAK,MAAM,IAAK,EAAA,CAAA;CAGhB,IAAA,IAAA,CAAK,KAAM,CAAA,mBAAA,CAAoB,OAAS,EAAA,IAAA,CAAK,SAAS,CAAA,CAAA;CACtD,IAAA,IAAA,CAAK,KAAM,CAAA,mBAAA,CAAoB,OAAS,EAAA,IAAA,CAAK,cAAc,CAAA,CAAA;CAG3D,IAAA,IAAI,UAAa,GAAA;CAAA,MACf,EAAA,EAAI,KAAK,QAAS,CAAA,EAAA;CAAA,MAClB,QAAA,EAAU,KAAK,MAAO,CAAA,QAAA;CAAA,MACtB,QAAA,EAAU,KAAK,QAAS,CAAA,MAAA;CAAA,KAC1B,CAAA;CAGA,IAAA,IAAA,CAAK,eAAe,UAAU,CAAA,CAAA;CAAA,GAChC,CAAA;CAAA,EAEA,MAAM,QAAA,CACJ,KACA,EAAA,eAAA,EACA,oBACA,aACA,EAAA;CACA,IAAA,IAAI,mBAAmB,WAAa,EAAA;CAClC,MAAc,aAAA,EAAA,CAAA;CACd,MAAK,IAAA,CAAA,kBAAA,CAAmB,OAAO,kBAAkB,CAAA,CAAA;CAAA,KACnD;CACA,IAAA,IAAI,mBAAmB,QAAU,EAAA;CAC/B,MAAK,IAAA,CAAA,eAAA,CAAgB,KAAO,EAAA,kBAAA,EAAoB,aAAa,CAAA,CAAA;CAAA,KAC/D;CAAA,GACF;CAAA,EAEQ,sBAAA,CAAuB,OAAwB,kBAAoB,EAAA;CACzE,IAAA,MAAM,YAAe,GAAA;CAAA,MACnB,UAAU,KAAM,CAAA,QAAA;CAAA,MAChB,EAAA,EACE,IAAK,CAAA,OAAA,CAAQ,aAAc,CAAA,gBAAA,CAAiB,GAAK,EAAA,EAAA,EAAI,CAAI,GAAA,GAAA,EAAK,IAAI,CAAA,GAClE,KAAM,CAAA,mBAAA;CAAA,MACR,QAAA,EAAU,KAAK,OAAQ,CAAA,aAAA,CAAc,UAAU,CAAG,EAAA,KAAA,CAAM,OAAQ,CAAA,MAAA,GAAS,CAAC,CAAA;CAAA,KAC5E,CAAA;CAEA,IAAA,MAAM,OAAO,IAAK,CAAA,OAAA,CAAQ,SAAU,CAAA,mBAAA,CAAoB,cAAc,kBAAkB,CAAA,CAAA;CAExF,IAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,CAAU,+BAAgC,CAAA,KAAA,EAAO,IAAI,CAAA,CAAA;CAElE,IAAO,OAAA,IAAA,CAAA;CAAA,GACT;CAAA,EAEQ,kBAAA,CAAmB,OAAwB,kBAAoB,EAAA;CACrE,IAAA,MAAM,IAAO,GAAA,IAAA,CAAK,sBAAuB,CAAA,KAAA,EAAO,kBAAkB,CAAA,CAAA;CAElE,IAAK,IAAA,CAAA,OAAA,CAAQ,YAAY,IAAI,CAAA,CAAA;CAAA,GAC/B;CAAA,EAEQ,eAAA,CAAgB,KAAwB,EAAA,kBAAA,EAAoB,aAA2B,EAAA;CAC7F,IAAA,MAAM,IAAO,GAAA,IAAA,CAAK,sBAAuB,CAAA,KAAA,EAAO,kBAAkB,CAAA,CAAA;CAElE,IAAM,MAAA,eAAA,GAAkB,IAAK,CAAA,OAAA,CAAQ,iBAAkB,EAAA,CAAA;CAEvD,IAAA,MAAM,UAAU,MAAM;CACpB,MAAI,IAAA,IAAA,CAAK,OAAO,IAAM,EAAA;CACpB,QAAA,IAAA,CAAK,QAAQ,SAAU,CAAA,WAAA;CAAA,UACrB,eAAgB,CAAA,aAAA;CAAA,YACd,yDAAyD,IAAK,CAAA,QAAA,CAAA,EAAA,CAAA;CAAA,WAChE;CAAA,UACA,IAAK,CAAA,EAAA;CAAA,SACP,CAAA;CAAA,OACF;CAAA,KACF,CAAA;CAEA,IAAK,IAAA,CAAA,KAAA,CAAM,eAAiB,EAAA,KAAA,EAAO,MAAM;CACvC,MAAc,aAAA,EAAA,CAAA;CACd,MAAI,IAAA,CAAC,MAAM,8BAAgC,EAAA;CACzC,QAAK,IAAA,CAAA,KAAA,CAAM,gBAAiB,CAAA,OAAA,EAAS,OAAO,CAAA,CAAA;CAAA,OACvC,MAAA;CACL,QAAQ,OAAA,EAAA,CAAA;CAAA,OACV;CAAA,KACD,CAAA,CAAA;CAAA,GACH;CACF;;;;;;;;"}
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 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(a){"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=o=>typeof o=="string"?e===o:o.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 o=Reflect.getOwnPropertyDescriptor(e,n);o&&typeof o.value=="function"&&(i[n]=i[n].bind(i))}return i},y=c(m),b={name:"@jspsych/plugin-audio-button-response",version:"2.0.0",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)}},o=u=>{try{l(t.throw(u))}catch(d){e(d)}},l=u=>u.done?r(u.value):Promise.resolve(u.value).then(n,o);l((t=t.apply(i,s)).next())});const g={name:"audio-button-response",version:b.version,parameters:{stimulus:{type:a.ParameterType.AUDIO,default:void 0},choices:{type:a.ParameterType.STRING,default:void 0,array:!0},button_html:{type:a.ParameterType.FUNCTION,default:function(i,s){return`<button class="jspsych-btn">${i}</button>`}},prompt:{type:a.ParameterType.HTML_STRING,default:null},trial_duration:{type:a.ParameterType.INT,default:null},button_layout:{type:a.ParameterType.STRING,default:"grid"},grid_rows:{type:a.ParameterType.INT,default:1},grid_columns:{type:a.ParameterType.INT,default:null},response_ends_trial:{type:a.ParameterType.BOOL,default:!0},trial_ends_after_audio:{type:a.ParameterType.BOOL,default:!1},response_allowed_while_playing:{type:a.ParameterType.BOOL,default:!0},enable_button_after:{type:a.ParameterType.INT,default:0}},data:{rt:{type:a.ParameterType.INT},response:{type:a.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.trial_complete,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,o=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(${o}, 1fr)`}else t.button_layout==="flex"&&e.classList.add("jspsych-btn-group-flex");for(const[n,o]of t.choices.entries()){e.insertAdjacentHTML("beforeend",t.button_html(o,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(),this.startTime=performance.now(),t.trial_duration!==null&&this.jsPsych.pluginAPI.setTimeout(()=>{this.end_trial()},t.trial_duration),r(),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(),o=()=>{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?o():this.audio.addEventListener("ended",o)})}}return h.info=g,h}(jsPsychModule);
2
- //# sourceMappingURL=https://unpkg.com/@jspsych/plugin-audio-button-response@2.0.0/dist/index.browser.min.js.map
1
+ var jsPsychAudioButtonResponse=function(a){"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=o=>typeof o=="string"?e===o:o.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 o=Reflect.getOwnPropertyDescriptor(e,n);o&&typeof o.value=="function"&&(i[n]=i[n].bind(i))}return i},y=c(m),b={name:"@jspsych/plugin-audio-button-response",version:"2.0.1",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)}},o=u=>{try{l(t.throw(u))}catch(d){e(d)}},l=u=>u.done?r(u.value):Promise.resolve(u.value).then(n,o);l((t=t.apply(i,s)).next())});const g={name:"audio-button-response",version:b.version,parameters:{stimulus:{type:a.ParameterType.AUDIO,default:void 0},choices:{type:a.ParameterType.STRING,default:void 0,array:!0},button_html:{type:a.ParameterType.FUNCTION,default:function(i,s){return`<button class="jspsych-btn">${i}</button>`}},prompt:{type:a.ParameterType.HTML_STRING,default:null},trial_duration:{type:a.ParameterType.INT,default:null},button_layout:{type:a.ParameterType.STRING,default:"grid"},grid_rows:{type:a.ParameterType.INT,default:1},grid_columns:{type:a.ParameterType.INT,default:null},response_ends_trial:{type:a.ParameterType.BOOL,default:!0},trial_ends_after_audio:{type:a.ParameterType.BOOL,default:!1},response_allowed_while_playing:{type:a.ParameterType.BOOL,default:!0},enable_button_after:{type:a.ParameterType.INT,default:0}},data:{rt:{type:a.ParameterType.INT},response:{type:a.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,o=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(${o}, 1fr)`}else t.button_layout==="flex"&&e.classList.add("jspsych-btn-group-flex");for(const[n,o]of t.choices.entries()){e.insertAdjacentHTML("beforeend",t.button_html(o,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(),o=()=>{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?o():this.audio.addEventListener("ended",o)})}}return h.info=g,h}(jsPsychModule);
2
+ //# sourceMappingURL=https://unpkg.com/@jspsych/plugin-audio-button-response@2.0.1/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":"4JAGA,MAAMA,EAAmBC,GAAU,CAClC,MAAMC,EAAa,IAAI,IAEvB,EACC,WAAWC,KAAO,QAAQ,QAAQF,CAAM,EACvCC,EAAW,IAAI,CAACD,EAAQE,CAAG,CAAC,SAEpBF,EAAS,QAAQ,eAAeA,CAAM,IAAMA,IAAW,OAAO,WAExE,OAAOC,CACR,MAEAE,EAAiB,CAACC,EAAM,CAAC,QAAAC,EAAS,QAAAC,CAAO,EAAI,CAAA,IAAO,CACnD,MAAMC,EAASL,GAAO,CACrB,MAAMM,EAAQC,GAAW,OAAOA,GAAY,SAAWP,IAAQO,EAAUA,EAAQ,KAAKP,CAAG,EAEzF,OAAIG,EACIA,EAAQ,KAAKG,CAAK,EAGtBF,EACI,CAACA,EAAQ,KAAKE,CAAK,EAGpB,EACT,EAEC,SAAW,CAACR,EAAQE,CAAG,IAAKH,EAAiBK,EAAK,YAAY,SAAS,EAAG,CACzE,GAAIF,IAAQ,eAAiB,CAACK,EAAOL,CAAG,EACvC,SAGD,MAAMQ,EAAa,QAAQ,yBAAyBV,EAAQE,CAAG,EAC3DQ,GAAc,OAAOA,EAAW,OAAU,aAC7CN,EAAKF,GAAOE,EAAKF,GAAK,KAAKE,CAAI,EAEhC,CAED,OAAOA,CACR,23BC1CA,EAAA,CAAAO,EAAAC,EAAA,IAAA,IAAA,QAAA,CAAAC,EAAAC,IAAA,CAAA,IAAAC,EAAA,GAAA,CAAA,GAAA,CAAAC,EAAA,EAAA,KAAA,CAAA,CAAA,CAAA,OAAA,EAAA,CAAAF,EAAA,CAAA,CAAA,CAAA,EAAAG,EAAA,GAAA,CAAA,GAAA,CAAAD,EAAA,EAAA,MAAA,CAAA,CAAA,CAAA,OAAA,EAAA,CAAAF,EAAA,CAAA,CAAA,CAAA,EAAAE,EAAA,GAAA,EAAA,KAAAH,EAAA,EAAA,KAAA,EAAA,QAAA,QAAA,EAAA,KAAA,EAAA,KAAAE,EAAAE,CAAA,EAAAD,GAAA,EAAA,EAAA,MAAAL,EAAAC,CAAA,GAAA,KAAA,CAAA,CAAA,CAAA,EAMA,MAAMM,EAAc,CAClB,KAAM,wBACN,QAASC,UACT,WAAY,CAEV,SAAU,CACR,KAAMC,EAAAA,cAAc,MACpB,QAAS,MACX,EAEA,QAAS,CACP,KAAMA,EAAAA,cAAc,OACpB,QAAS,OACT,MAAO,EACT,EAOA,YAAa,CACX,KAAMA,EAAAA,cAAc,SACpB,QAAS,SAAUC,EAAgBC,EAAsB,CACvD,MAAO,+BAA+BD,YACxC,CACF,EAIA,OAAQ,CACN,KAAMD,gBAAc,YACpB,QAAS,IACX,EAKA,eAAgB,CACd,KAAMA,gBAAc,IACpB,QAAS,IACX,EAKA,cAAe,CACb,KAAMA,EAAAA,cAAc,OACpB,QAAS,MACX,EAIA,UAAW,CACT,KAAMA,EAAc,cAAA,IACpB,QAAS,CACX,EAKA,aAAc,CACZ,KAAMA,EAAc,cAAA,IACpB,QAAS,IACX,EAKA,oBAAqB,CACnB,KAAMA,gBAAc,KACpB,QAAS,EACX,EAEA,uBAAwB,CACtB,KAAMA,gBAAc,KACpB,QAAS,EACX,EAOA,+BAAgC,CAC9B,KAAMA,gBAAc,KACpB,QAAS,EACX,EAGA,oBAAqB,CACnB,KAAMA,EAAAA,cAAc,IACpB,QAAS,CACX,CACF,EACA,KAAM,CAGJ,GAAI,CACF,KAAMA,EAAc,cAAA,GACtB,EAEA,SAAU,CACR,KAAMA,EAAAA,cAAc,GACtB,CACF,CACF,EAqBA,MAAMG,CAAyD,CAW7D,YAAoBC,EAAkB,CAAlB,KAAA,QAAAA,EAPpB,KAAQ,eAAgC,CAExC,EAAA,KAAQ,SAA2C,CAAE,GAAI,KAAM,OAAQ,IAAK,EAmG5E,KAAQ,gBAAkB,IAAM,CAC9B,UAAWC,KAAU,KAAK,eACxBA,EAAO,aAAa,WAAY,UAAU,CAE9C,EAEA,KAAQ,6BAA+B,IAAM,CAC3C,UAAWA,KAAU,KAAK,eACxBA,EAAO,gBAAgB,UAAU,CAErC,EAEA,KAAQ,0BAA6BC,GAAkB,CACrD,KAAK,QAAQ,UAAU,WAAW,KAAK,6BAA8BA,CAAK,CAC5E,EAWA,KAAQ,eAAkBL,GAAW,CAEnC,IAAIM,EAAU,YAAY,MACtBC,EAAK,KAAK,MAAMD,EAAU,KAAK,SAAS,EACxC,KAAK,UAAY,OACnBA,EAAU,KAAK,QAAQ,YACvBC,EAAK,KAAK,OAAOD,EAAU,KAAK,WAAa,GAAI,GAEnD,KAAK,SAAS,OAAS,SAASN,CAAM,EACtC,KAAK,SAAS,GAAKO,EAGnB,KAAK,gBAED,EAAA,KAAK,OAAO,qBACd,KAAK,UAET,CAAA,EAGA,KAAQ,UAAY,IAAM,CAExB,KAAK,MAAM,KAGX,EAAA,KAAK,MAAM,oBAAoB,QAAS,KAAK,SAAS,EACtD,KAAK,MAAM,oBAAoB,QAAS,KAAK,cAAc,EAG3D,IAAIC,EAAa,CACf,GAAI,KAAK,SAAS,GAClB,SAAU,KAAK,OAAO,SACtB,SAAU,KAAK,SAAS,MAC1B,EAGA,KAAK,eAAeA,CAAU,CAChC,EA3JE1B,EAAS,IAAI,CACf,CAEM,MAAM2B,EAA8BC,EAAwBC,EAAqB,QAAAC,EAAA,KAAA,KAAA,WAAA,CAErF,KAAK,eACL,KAAK,OAASF,EACd,KAAK,QAAUD,EAEf,KAAK,QAAU,KAAK,QAAQ,UAAU,eAGtC,KAAK,MAAQ,MAAM,KAAK,QAAQ,UAAU,eAAeC,EAAM,QAAQ,EAGnEA,EAAM,wBACR,KAAK,MAAM,iBAAiB,QAAS,KAAK,SAAS,EAIjD,CAACA,EAAM,gCAAkC,CAACA,EAAM,wBAClD,KAAK,MAAM,iBAAiB,QAAS,KAAK,cAAc,EAI1D,MAAMG,EAAqB,SAAS,cAAc,KAAK,EAEvD,GADAA,EAAmB,GAAK,yCACpBH,EAAM,gBAAkB,OAAQ,CAElC,GADAG,EAAmB,UAAU,IAAI,wBAAwB,EACrDH,EAAM,YAAc,MAAQA,EAAM,eAAiB,KACrD,MAAM,IAAI,MACR,oFACF,EAEF,MAAMI,EACJJ,EAAM,eAAiB,KACnB,KAAK,KAAKA,EAAM,QAAQ,OAASA,EAAM,SAAS,EAChDA,EAAM,aACNK,EACJL,EAAM,YAAc,KAChB,KAAK,KAAKA,EAAM,QAAQ,OAASA,EAAM,YAAY,EACnDA,EAAM,UACZG,EAAmB,MAAM,oBAAsB,UAAUC,UACzDD,EAAmB,MAAM,iBAAmB,UAAUE,SACxD,MAAWL,EAAM,gBAAkB,QACjCG,EAAmB,UAAU,IAAI,wBAAwB,EAG3D,SAAW,CAACG,EAAahB,CAAM,IAAKU,EAAM,QAAQ,UAAW,CAC3DG,EAAmB,mBAAmB,YAAaH,EAAM,YAAYV,EAAQgB,CAAW,CAAC,EACzF,MAAMC,EAAgBJ,EAAmB,UACzCI,EAAc,QAAQ,OAASD,EAAY,SAAA,EAC3CC,EAAc,iBAAiB,QAAS,IAAM,CAC5C,KAAK,eAAeD,CAAW,CACjC,CAAC,EACD,KAAK,eAAe,KAAKC,CAAa,CACxC,CAEA,OAAAR,EAAgB,YAAYI,CAAkB,EAG1CH,EAAM,SAAW,MACnBD,EAAgB,mBAAmB,YAAaC,EAAM,MAAM,EAG1DA,EAAM,+BACJA,EAAM,oBAAsB,IAC9B,KAAK,gBAAgB,EACrB,KAAK,eAAe,GAGtB,KAAK,gBAAA,EAIP,KAAK,UAAY,YAAY,MAGzBA,EAAM,iBAAmB,MAC3B,KAAK,QAAQ,UAAU,WAAW,IAAM,CACtC,KAAK,UAAA,CACP,EAAGA,EAAM,cAAc,EAGzBC,IAEA,KAAK,MAAM,OAEJ,IAAI,QAASO,GAAY,CAC9B,KAAK,eAAiBA,CACxB,CAAC,CACH,GAkBQ,gBAAiB,CACnB,KAAK,OAAO,oBAAsB,EACpC,KAAK,0BAA0B,KAAK,OAAO,mBAAmB,EAE9D,KAAK,6BAET,CAAA,CA0CM,SACJR,EACAS,EACAC,EACAC,EACA,QAAAT,EAAA,KAAA,KAAA,WAAA,CACIO,GAAmB,cACrBE,IACA,KAAK,mBAAmBX,EAAOU,CAAkB,GAE/CD,GAAmB,UACrB,KAAK,gBAAgBT,EAAOU,EAAoBC,CAAa,CAEjE,GAEQ,uBAAuBX,EAAwBU,EAAoB,CACzE,MAAME,EAAe,CACnB,SAAUZ,EAAM,SAChB,GACE,KAAK,QAAQ,cAAc,iBAAiB,IAAK,GAAI,oBAAS,EAAI,EAClEA,EAAM,oBACR,SAAU,KAAK,QAAQ,cAAc,UAAU,EAAGA,EAAM,QAAQ,OAAS,CAAC,CAC5E,EAEMa,EAAO,KAAK,QAAQ,UAAU,oBAAoBD,EAAcF,CAAkB,EAExF,OAAK,KAAA,QAAQ,UAAU,gCAAgCV,EAAOa,CAAI,EAE3DA,CACT,CAEQ,mBAAmBb,EAAwBU,EAAoB,CACrE,MAAMG,EAAO,KAAK,uBAAuBb,EAAOU,CAAkB,EAElE,KAAK,QAAQ,YAAYG,CAAI,CAC/B,CAEQ,gBAAgBb,EAAwBU,EAAoBC,EAA2B,CAC7F,MAAME,EAAO,KAAK,uBAAuBb,EAAOU,CAAkB,EAE5DX,EAAkB,KAAK,QAAQ,kBAAA,EAE/Be,EAAU,IAAM,CAChBD,EAAK,KAAO,MACd,KAAK,QAAQ,UAAU,YACrBd,EAAgB,cACd,yDAAyDc,EAAK,YAChE,EACAA,EAAK,EACP,CAEJ,EAEA,KAAK,MAAMd,EAAiBC,EAAO,IAAM,CACvCW,IACKX,EAAM,+BAGTc,EAAQ,EAFR,KAAK,MAAM,iBAAiB,QAASA,CAAO,CAIhD,CAAC,CACH,CACF,CAvOMtB,SACG,KAAOL"}
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 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","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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"}
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.0",
8
+ version: "2.0.1",
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",
@@ -158,7 +158,6 @@ class AudioButtonResponsePlugin {
158
158
  autoBind(this);
159
159
  }
160
160
  async trial(display_element, trial, on_load) {
161
- this.trial_complete;
162
161
  this.params = trial;
163
162
  this.display = display_element;
164
163
  this.context = this.jsPsych.pluginAPI.audioContext();
@@ -206,13 +205,16 @@ class AudioButtonResponsePlugin {
206
205
  } else {
207
206
  this.disable_buttons();
208
207
  }
209
- this.startTime = performance.now();
210
208
  if (trial.trial_duration !== null) {
211
209
  this.jsPsych.pluginAPI.setTimeout(() => {
212
210
  this.end_trial();
213
211
  }, trial.trial_duration);
214
212
  }
215
213
  on_load();
214
+ this.startTime = performance.now();
215
+ if (this.context !== null) {
216
+ this.startTime = this.context.currentTime;
217
+ }
216
218
  this.audio.play();
217
219
  return new Promise((resolve) => {
218
220
  this.trial_complete = resolve;
@@ -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 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.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.0",
6
+ version: "2.0.1",
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",
@@ -156,7 +156,6 @@ class AudioButtonResponsePlugin {
156
156
  autoBind(this);
157
157
  }
158
158
  async trial(display_element, trial, on_load) {
159
- this.trial_complete;
160
159
  this.params = trial;
161
160
  this.display = display_element;
162
161
  this.context = this.jsPsych.pluginAPI.audioContext();
@@ -204,13 +203,16 @@ class AudioButtonResponsePlugin {
204
203
  } else {
205
204
  this.disable_buttons();
206
205
  }
207
- this.startTime = performance.now();
208
206
  if (trial.trial_duration !== null) {
209
207
  this.jsPsych.pluginAPI.setTimeout(() => {
210
208
  this.end_trial();
211
209
  }, trial.trial_duration);
212
210
  }
213
211
  on_load();
212
+ this.startTime = performance.now();
213
+ if (this.context !== null) {
214
+ this.startTime = this.context.currentTime;
215
+ }
214
216
  this.audio.play();
215
217
  return new Promise((resolve) => {
216
218
  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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jspsych/plugin-audio-button-response",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "jsPsych plugin for playing an audio file and getting a button response",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
package/src/index.ts CHANGED
@@ -147,8 +147,6 @@ class AudioButtonResponsePlugin implements JsPsychPlugin<Info> {
147
147
  }
148
148
 
149
149
  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
150
  this.params = trial;
153
151
  this.display = display_element;
154
152
  // setup stimulus
@@ -217,9 +215,6 @@ class AudioButtonResponsePlugin implements JsPsychPlugin<Info> {
217
215
  this.disable_buttons();
218
216
  }
219
217
 
220
- // start time
221
- this.startTime = performance.now();
222
-
223
218
  // end trial if time limit is set
224
219
  if (trial.trial_duration !== null) {
225
220
  this.jsPsych.pluginAPI.setTimeout(() => {
@@ -229,9 +224,17 @@ class AudioButtonResponsePlugin implements JsPsychPlugin<Info> {
229
224
 
230
225
  on_load();
231
226
 
227
+ // start time
228
+ this.startTime = performance.now();
229
+ if (this.context !== null) {
230
+ this.startTime = this.context.currentTime;
231
+ }
232
+
233
+ // start audio
232
234
  this.audio.play();
233
235
 
234
236
  return new Promise((resolve) => {
237
+ // hold the .resolve() function from the Promise that ends the trial
235
238
  this.trial_complete = resolve;
236
239
  });
237
240
  }