@jspsych/plugin-audio-slider-response 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -111,7 +111,6 @@ var jsPsychAudioSliderResponse = (function (jspsych) {
111
111
  var half_thumb_width = 7.5;
112
112
  // setup stimulus
113
113
  var context = this.jsPsych.pluginAPI.audioContext();
114
- var audio;
115
114
  // record webaudio context start time
116
115
  var startTime;
117
116
  // for storing data related to response
@@ -119,30 +118,30 @@ var jsPsychAudioSliderResponse = (function (jspsych) {
119
118
  // load audio file
120
119
  this.jsPsych.pluginAPI
121
120
  .getAudioBuffer(trial.stimulus)
122
- .then(function (buffer) {
121
+ .then((buffer) => {
123
122
  if (context !== null) {
124
- audio = context.createBufferSource();
125
- audio.buffer = buffer;
126
- audio.connect(context.destination);
123
+ this.audio = context.createBufferSource();
124
+ this.audio.buffer = buffer;
125
+ this.audio.connect(context.destination);
127
126
  }
128
127
  else {
129
- audio = buffer;
130
- audio.currentTime = 0;
128
+ this.audio = buffer;
129
+ this.audio.currentTime = 0;
131
130
  }
132
131
  setupTrial();
133
132
  })
134
- .catch(function (err) {
133
+ .catch((err) => {
135
134
  console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
136
135
  console.error(err);
137
136
  });
138
137
  const setupTrial = () => {
139
138
  // set up end event if trial needs it
140
139
  if (trial.trial_ends_after_audio) {
141
- audio.addEventListener("ended", end_trial);
140
+ this.audio.addEventListener("ended", end_trial);
142
141
  }
143
142
  // enable slider after audio ends if necessary
144
143
  if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {
145
- audio.addEventListener("ended", enable_slider);
144
+ this.audio.addEventListener("ended", enable_slider);
146
145
  }
147
146
  var html = '<div id="jspsych-audio-slider-response-wrapper" style="margin: 100px 0px;">';
148
147
  html +=
@@ -223,10 +222,13 @@ var jsPsychAudioSliderResponse = (function (jspsych) {
223
222
  display_element
224
223
  .querySelector("#jspsych-audio-slider-response-response")
225
224
  .addEventListener("touchstart", enable_button);
225
+ display_element
226
+ .querySelector("#jspsych-audio-slider-response-response")
227
+ .addEventListener("change", enable_button);
226
228
  }
227
229
  display_element
228
230
  .querySelector("#jspsych-audio-slider-response-next")
229
- .addEventListener("click", function () {
231
+ .addEventListener("click", () => {
230
232
  // measure response time
231
233
  var endTime = performance.now();
232
234
  var rt = Math.round(endTime - startTime);
@@ -247,14 +249,14 @@ var jsPsychAudioSliderResponse = (function (jspsych) {
247
249
  // start audio
248
250
  if (context !== null) {
249
251
  startTime = context.currentTime;
250
- audio.start(startTime);
252
+ this.audio.start(startTime);
251
253
  }
252
254
  else {
253
- audio.play();
255
+ this.audio.play();
254
256
  }
255
257
  // end trial if trial_duration is set
256
258
  if (trial.trial_duration !== null) {
257
- this.jsPsych.pluginAPI.setTimeout(function () {
259
+ this.jsPsych.pluginAPI.setTimeout(() => {
258
260
  end_trial();
259
261
  }, trial.trial_duration);
260
262
  }
@@ -275,13 +277,13 @@ var jsPsychAudioSliderResponse = (function (jspsych) {
275
277
  // stop the audio file if it is playing
276
278
  // remove end event listeners if they exist
277
279
  if (context !== null) {
278
- audio.stop();
280
+ this.audio.stop();
279
281
  }
280
282
  else {
281
- audio.pause();
283
+ this.audio.pause();
282
284
  }
283
- audio.removeEventListener("ended", end_trial);
284
- audio.removeEventListener("ended", enable_slider);
285
+ this.audio.removeEventListener("ended", end_trial);
286
+ this.audio.removeEventListener("ended", enable_slider);
285
287
  // save data
286
288
  var trialdata = {
287
289
  rt: response.rt,
@@ -298,6 +300,53 @@ var jsPsychAudioSliderResponse = (function (jspsych) {
298
300
  trial_complete = resolve;
299
301
  });
300
302
  }
303
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
304
+ if (simulation_mode == "data-only") {
305
+ load_callback();
306
+ this.simulate_data_only(trial, simulation_options);
307
+ }
308
+ if (simulation_mode == "visual") {
309
+ this.simulate_visual(trial, simulation_options, load_callback);
310
+ }
311
+ }
312
+ create_simulation_data(trial, simulation_options) {
313
+ const default_data = {
314
+ stimulus: trial.stimulus,
315
+ slider_start: trial.slider_start,
316
+ response: this.jsPsych.randomization.randomInt(trial.min, trial.max),
317
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
318
+ };
319
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
320
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
321
+ return data;
322
+ }
323
+ simulate_data_only(trial, simulation_options) {
324
+ const data = this.create_simulation_data(trial, simulation_options);
325
+ this.jsPsych.finishTrial(data);
326
+ }
327
+ simulate_visual(trial, simulation_options, load_callback) {
328
+ const data = this.create_simulation_data(trial, simulation_options);
329
+ const display_element = this.jsPsych.getDisplayElement();
330
+ const respond = () => {
331
+ if (data.rt !== null) {
332
+ const el = display_element.querySelector("input[type='range']");
333
+ setTimeout(() => {
334
+ this.jsPsych.pluginAPI.clickTarget(el);
335
+ el.valueAsNumber = data.response;
336
+ }, data.rt / 2);
337
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("button"), data.rt);
338
+ }
339
+ };
340
+ this.trial(display_element, trial, () => {
341
+ load_callback();
342
+ if (!trial.response_allowed_while_playing) {
343
+ this.audio.addEventListener("ended", respond);
344
+ }
345
+ else {
346
+ respond();
347
+ }
348
+ });
349
+ }
301
350
  }
302
351
  AudioSliderResponsePlugin.info = info;
303
352
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.browser.js","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nconst info = <const>{\n name: \"audio-slider-response\",\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n pretty_name: \"Stimulus\",\n default: undefined,\n },\n /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, trial will end when user makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * **audio-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n let trial_complete;\n\n // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n var audio;\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then(function (buffer) {\n if (context !== null) {\n audio = context.createBufferSource();\n audio.buffer = buffer;\n audio.connect(context.destination);\n } else {\n audio = buffer;\n audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch(function (err) {\n console.error(\n `Failed to load audio file \"${trial.stimulus}\". Try checking the file path. We recommend using the preload plugin to load audio files.`\n );\n console.error(err);\n });\n\n const setupTrial = () => {\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", end_trial);\n }\n\n // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", function () {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n audio.start(startTime);\n } else {\n audio.play();\n }\n\n // end trial if trial_duration is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(function () {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\n const end_trial = () => {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n // remove end event listeners if they exist\n if (context !== null) {\n audio.stop();\n } else {\n audio.pause();\n }\n\n audio.removeEventListener(\"ended\", end_trial);\n audio.removeEventListener(\"ended\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":["ParameterType"],"mappings":"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"}
1
+ {"version":3,"file":"index.browser.js","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nconst info = <const>{\n name: \"audio-slider-response\",\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n pretty_name: \"Stimulus\",\n default: undefined,\n },\n /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, trial will end when user makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * **audio-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n let trial_complete;\n\n // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then((buffer) => {\n if (context !== null) {\n this.audio = context.createBufferSource();\n this.audio.buffer = buffer;\n this.audio.connect(context.destination);\n } else {\n this.audio = buffer;\n this.audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch((err) => {\n console.error(\n `Failed to load audio file \"${trial.stimulus}\". Try checking the file path. We recommend using the preload plugin to load audio files.`\n );\n console.error(err);\n });\n\n const setupTrial = () => {\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", end_trial);\n }\n\n // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"change\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", () => {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n this.audio.start(startTime);\n } else {\n this.audio.play();\n }\n\n // end trial if trial_duration is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\n const end_trial = () => {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n // remove end event listeners if they exist\n if (context !== null) {\n this.audio.stop();\n } else {\n this.audio.pause();\n }\n\n this.audio.removeEventListener(\"ended\", end_trial);\n this.audio.removeEventListener(\"ended\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n\n simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: this.jsPsych.randomization.randomInt(trial.min, trial.max),\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\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 const el = display_element.querySelector<HTMLInputElement>(\"input[type='range']\");\n\n setTimeout(() => {\n this.jsPsych.pluginAPI.clickTarget(el);\n el.valueAsNumber = data.response;\n }, data.rt / 2);\n\n this.jsPsych.pluginAPI.clickTarget(display_element.querySelector(\"button\"), data.rt);\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":["ParameterType"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- var jsPsychAudioSliderResponse=function(e){"use strict";const t={name:"audio-slider-response",parameters:{stimulus:{type:e.ParameterType.AUDIO,pretty_name:"Stimulus",default:void 0},min:{type:e.ParameterType.INT,pretty_name:"Min slider",default:0},max:{type:e.ParameterType.INT,pretty_name:"Max slider",default:100},slider_start:{type:e.ParameterType.INT,pretty_name:"Slider starting value",default:50},step:{type:e.ParameterType.INT,pretty_name:"Step",default:1},labels:{type:e.ParameterType.HTML_STRING,pretty_name:"Labels",default:[],array:!0},slider_width:{type:e.ParameterType.INT,pretty_name:"Slider width",default:null},button_label:{type:e.ParameterType.STRING,pretty_name:"Button label",default:"Continue",array:!1},require_movement:{type:e.ParameterType.BOOL,pretty_name:"Require movement",default:!1},prompt:{type:e.ParameterType.HTML_STRING,pretty_name:"Prompt",default:null},trial_duration:{type:e.ParameterType.INT,pretty_name:"Trial duration",default:null},response_ends_trial:{type:e.ParameterType.BOOL,pretty_name:"Response ends trial",default:!0},trial_ends_after_audio:{type:e.ParameterType.BOOL,pretty_name:"Trial ends after audio",default:!1},response_allowed_while_playing:{type:e.ParameterType.BOOL,pretty_name:"Response allowed while playing",default:!0}}};class r{constructor(e){this.jsPsych=e}trial(e,t,r){let s;var a,n,l,i=this.jsPsych.pluginAPI.audioContext();this.jsPsych.pluginAPI.getAudioBuffer(t.stimulus).then((function(e){null!==i?((a=i.createBufferSource()).buffer=e,a.connect(i.destination)):(a=e).currentTime=0,o()})).catch((function(e){console.error(`Failed to load audio file "${t.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`),console.error(e)}));const o=()=>{t.trial_ends_after_audio&&a.addEventListener("ended",p),t.response_allowed_while_playing||t.trial_ends_after_audio||a.addEventListener("ended",d);var s='<div id="jspsych-audio-slider-response-wrapper" style="margin: 100px 0px;">';s+='<div class="jspsych-audio-slider-response-container" style="position:relative; margin: 0 auto 3em auto; width:',null!==t.slider_width?s+=t.slider_width+"px;":s+="auto;",s+='">',s+='<input type="range" class="jspsych-slider" value="'+t.slider_start+'" min="'+t.min+'" max="'+t.max+'" step="'+t.step+'" id="jspsych-audio-slider-response-response"',t.response_allowed_while_playing||(s+=" disabled"),s+="></input><div>";for(var o=0;o<t.labels.length;o++){var u=100/(t.labels.length-1),y=o*(100/(t.labels.length-1));s+='<div style="border: 1px solid transparent; display: inline-block; position: absolute; left:calc('+y+"% - ("+u+"% / 2) - "+7.5*((y-50)/50*100)/100+"px); text-align: center; width: "+u+'%;">',s+='<span style="text-align: center; font-size: 80%;">'+t.labels[o]+"</span>",s+="</div>"}s+="</div>",s+="</div>",s+="</div>",null!==t.prompt&&(s+=t.prompt);var c="";if(!t.require_movement&&t.response_allowed_while_playing||(c="disabled"),s+='<button id="jspsych-audio-slider-response-next" class="jspsych-btn" '+c+">"+t.button_label+"</button>",e.innerHTML=s,l={rt:null,response:null},t.response_allowed_while_playing||(e.querySelector("#jspsych-audio-slider-response-response").disabled=!0,e.querySelector("#jspsych-audio-slider-response-next").disabled=!0),t.require_movement){const t=()=>{e.querySelector("#jspsych-audio-slider-response-next").disabled=!1};e.querySelector("#jspsych-audio-slider-response-response").addEventListener("mousedown",t),e.querySelector("#jspsych-audio-slider-response-response").addEventListener("touchstart",t)}e.querySelector("#jspsych-audio-slider-response-next").addEventListener("click",(function(){var r=performance.now(),s=Math.round(r-n);null!==i&&(r=i.currentTime,s=Math.round(1e3*(r-n))),l.rt=s,l.response=e.querySelector("#jspsych-audio-slider-response-response").valueAsNumber,t.response_ends_trial?p():e.querySelector("#jspsych-audio-slider-response-next").disabled=!0})),n=performance.now(),null!==i?(n=i.currentTime,a.start(n)):a.play(),null!==t.trial_duration&&this.jsPsych.pluginAPI.setTimeout((function(){p()}),t.trial_duration),r()};function d(){document.querySelector("#jspsych-audio-slider-response-response").disabled=!1,t.require_movement||(document.querySelector("#jspsych-audio-slider-response-next").disabled=!1)}const p=()=>{this.jsPsych.pluginAPI.clearAllTimeouts(),null!==i?a.stop():a.pause(),a.removeEventListener("ended",p),a.removeEventListener("ended",d);var r={rt:l.rt,stimulus:t.stimulus,slider_start:t.slider_start,response:l.response};e.innerHTML="",this.jsPsych.finishTrial(r),s()};return new Promise((e=>{s=e}))}}return r.info=t,r}(jsPsychModule);
1
+ var jsPsychAudioSliderResponse=function(e){"use strict";const t={name:"audio-slider-response",parameters:{stimulus:{type:e.ParameterType.AUDIO,pretty_name:"Stimulus",default:void 0},min:{type:e.ParameterType.INT,pretty_name:"Min slider",default:0},max:{type:e.ParameterType.INT,pretty_name:"Max slider",default:100},slider_start:{type:e.ParameterType.INT,pretty_name:"Slider starting value",default:50},step:{type:e.ParameterType.INT,pretty_name:"Step",default:1},labels:{type:e.ParameterType.HTML_STRING,pretty_name:"Labels",default:[],array:!0},slider_width:{type:e.ParameterType.INT,pretty_name:"Slider width",default:null},button_label:{type:e.ParameterType.STRING,pretty_name:"Button label",default:"Continue",array:!1},require_movement:{type:e.ParameterType.BOOL,pretty_name:"Require movement",default:!1},prompt:{type:e.ParameterType.HTML_STRING,pretty_name:"Prompt",default:null},trial_duration:{type:e.ParameterType.INT,pretty_name:"Trial duration",default:null},response_ends_trial:{type:e.ParameterType.BOOL,pretty_name:"Response ends trial",default:!0},trial_ends_after_audio:{type:e.ParameterType.BOOL,pretty_name:"Trial ends after audio",default:!1},response_allowed_while_playing:{type:e.ParameterType.BOOL,pretty_name:"Response allowed while playing",default:!0}}};class s{constructor(e){this.jsPsych=e}trial(e,t,s){let r;var i,a,n=this.jsPsych.pluginAPI.audioContext();this.jsPsych.pluginAPI.getAudioBuffer(t.stimulus).then((e=>{null!==n?(this.audio=n.createBufferSource(),this.audio.buffer=e,this.audio.connect(n.destination)):(this.audio=e,this.audio.currentTime=0),l()})).catch((e=>{console.error(`Failed to load audio file "${t.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`),console.error(e)}));const l=()=>{t.trial_ends_after_audio&&this.audio.addEventListener("ended",d),t.response_allowed_while_playing||t.trial_ends_after_audio||this.audio.addEventListener("ended",o);var r='<div id="jspsych-audio-slider-response-wrapper" style="margin: 100px 0px;">';r+='<div class="jspsych-audio-slider-response-container" style="position:relative; margin: 0 auto 3em auto; width:',null!==t.slider_width?r+=t.slider_width+"px;":r+="auto;",r+='">',r+='<input type="range" class="jspsych-slider" value="'+t.slider_start+'" min="'+t.min+'" max="'+t.max+'" step="'+t.step+'" id="jspsych-audio-slider-response-response"',t.response_allowed_while_playing||(r+=" disabled"),r+="></input><div>";for(var l=0;l<t.labels.length;l++){var u=100/(t.labels.length-1),p=l*(100/(t.labels.length-1));r+='<div style="border: 1px solid transparent; display: inline-block; position: absolute; left:calc('+p+"% - ("+u+"% / 2) - "+7.5*((p-50)/50*100)/100+"px); text-align: center; width: "+u+'%;">',r+='<span style="text-align: center; font-size: 80%;">'+t.labels[l]+"</span>",r+="</div>"}r+="</div>",r+="</div>",r+="</div>",null!==t.prompt&&(r+=t.prompt);var y="";if(!t.require_movement&&t.response_allowed_while_playing||(y="disabled"),r+='<button id="jspsych-audio-slider-response-next" class="jspsych-btn" '+y+">"+t.button_label+"</button>",e.innerHTML=r,a={rt:null,response:null},t.response_allowed_while_playing||(e.querySelector("#jspsych-audio-slider-response-response").disabled=!0,e.querySelector("#jspsych-audio-slider-response-next").disabled=!0),t.require_movement){const t=()=>{e.querySelector("#jspsych-audio-slider-response-next").disabled=!1};e.querySelector("#jspsych-audio-slider-response-response").addEventListener("mousedown",t),e.querySelector("#jspsych-audio-slider-response-response").addEventListener("touchstart",t),e.querySelector("#jspsych-audio-slider-response-response").addEventListener("change",t)}e.querySelector("#jspsych-audio-slider-response-next").addEventListener("click",(()=>{var s=performance.now(),r=Math.round(s-i);null!==n&&(s=n.currentTime,r=Math.round(1e3*(s-i))),a.rt=r,a.response=e.querySelector("#jspsych-audio-slider-response-response").valueAsNumber,t.response_ends_trial?d():e.querySelector("#jspsych-audio-slider-response-next").disabled=!0})),i=performance.now(),null!==n?(i=n.currentTime,this.audio.start(i)):this.audio.play(),null!==t.trial_duration&&this.jsPsych.pluginAPI.setTimeout((()=>{d()}),t.trial_duration),s()};function o(){document.querySelector("#jspsych-audio-slider-response-response").disabled=!1,t.require_movement||(document.querySelector("#jspsych-audio-slider-response-next").disabled=!1)}const d=()=>{this.jsPsych.pluginAPI.clearAllTimeouts(),null!==n?this.audio.stop():this.audio.pause(),this.audio.removeEventListener("ended",d),this.audio.removeEventListener("ended",o);var s={rt:a.rt,stimulus:t.stimulus,slider_start:t.slider_start,response:a.response};e.innerHTML="",this.jsPsych.finishTrial(s),r()};return new Promise((e=>{r=e}))}simulate(e,t,s,r){"data-only"==t&&(r(),this.simulate_data_only(e,s)),"visual"==t&&this.simulate_visual(e,s,r)}create_simulation_data(e,t){const s={stimulus:e.stimulus,slider_start:e.slider_start,response:this.jsPsych.randomization.randomInt(e.min,e.max),rt:this.jsPsych.randomization.sampleExGaussian(500,50,1/150,!0)},r=this.jsPsych.pluginAPI.mergeSimulationData(s,t);return this.jsPsych.pluginAPI.ensureSimulationDataConsistency(e,r),r}simulate_data_only(e,t){const s=this.create_simulation_data(e,t);this.jsPsych.finishTrial(s)}simulate_visual(e,t,s){const r=this.create_simulation_data(e,t),i=this.jsPsych.getDisplayElement(),a=()=>{if(null!==r.rt){const e=i.querySelector("input[type='range']");setTimeout((()=>{this.jsPsych.pluginAPI.clickTarget(e),e.valueAsNumber=r.response}),r.rt/2),this.jsPsych.pluginAPI.clickTarget(i.querySelector("button"),r.rt)}};this.trial(i,e,(()=>{s(),e.response_allowed_while_playing?a():this.audio.addEventListener("ended",a)}))}}return s.info=t,s}(jsPsychModule);
2
2
  //# sourceMappingURL=index.browser.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.browser.min.js","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nconst info = <const>{\n name: \"audio-slider-response\",\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n pretty_name: \"Stimulus\",\n default: undefined,\n },\n /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, trial will end when user makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * **audio-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n let trial_complete;\n\n // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n var audio;\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then(function (buffer) {\n if (context !== null) {\n audio = context.createBufferSource();\n audio.buffer = buffer;\n audio.connect(context.destination);\n } else {\n audio = buffer;\n audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch(function (err) {\n console.error(\n `Failed to load audio file \"${trial.stimulus}\". Try checking the file path. We recommend using the preload plugin to load audio files.`\n );\n console.error(err);\n });\n\n const setupTrial = () => {\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", end_trial);\n }\n\n // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", function () {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n audio.start(startTime);\n } else {\n audio.play();\n }\n\n // end trial if trial_duration is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(function () {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\n const end_trial = () => {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n // remove end event listeners if they exist\n if (context !== null) {\n audio.stop();\n } else {\n audio.pause();\n }\n\n audio.removeEventListener(\"ended\", end_trial);\n audio.removeEventListener(\"ended\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":["info","name","parameters","stimulus","type","ParameterType","AUDIO","pretty_name","default","undefined","min","INT","max","slider_start","step","labels","HTML_STRING","array","slider_width","button_label","STRING","require_movement","BOOL","prompt","trial_duration","response_ends_trial","trial_ends_after_audio","response_allowed_while_playing","AudioSliderResponsePlugin","constructor","jsPsych","this","trial","display_element","on_load","trial_complete","audio","startTime","response","context","pluginAPI","audioContext","getAudioBuffer","then","buffer","createBufferSource","connect","destination","currentTime","setupTrial","catch","err","console","error","addEventListener","end_trial","enable_slider","html","j","length","label_width_perc","percent_of_range","next_disabled_attribute","innerHTML","rt","querySelector","disabled","enable_button","endTime","performance","now","Math","round","valueAsNumber","start","play","setTimeout","document","clearAllTimeouts","stop","pause","removeEventListener","trialdata","finishTrial","Promise","resolve"],"mappings":"wDAEA,MAAMA,EAAc,CAClBC,KAAM,wBACNC,WAAY,CAEVC,SAAU,CACRC,KAAMC,gBAAcC,MACpBC,YAAa,WACbC,aAASC,GAGXC,IAAK,CACHN,KAAMC,gBAAcM,IACpBJ,YAAa,aACbC,QAAS,GAGXI,IAAK,CACHR,KAAMC,gBAAcM,IACpBJ,YAAa,aACbC,QAAS,KAGXK,aAAc,CACZT,KAAMC,gBAAcM,IACpBJ,YAAa,wBACbC,QAAS,IAGXM,KAAM,CACJV,KAAMC,gBAAcM,IACpBJ,YAAa,OACbC,QAAS,GAGXO,OAAQ,CACNX,KAAMC,gBAAcW,YACpBT,YAAa,SACbC,QAAS,GACTS,OAAO,GAGTC,aAAc,CACZd,KAAMC,gBAAcM,IACpBJ,YAAa,eACbC,QAAS,MAGXW,aAAc,CACZf,KAAMC,gBAAce,OACpBb,YAAa,eACbC,QAAS,WACTS,OAAO,GAGTI,iBAAkB,CAChBjB,KAAMC,gBAAciB,KACpBf,YAAa,mBACbC,SAAS,GAGXe,OAAQ,CACNnB,KAAMC,gBAAcW,YACpBT,YAAa,SACbC,QAAS,MAGXgB,eAAgB,CACdpB,KAAMC,gBAAcM,IACpBJ,YAAa,iBACbC,QAAS,MAGXiB,oBAAqB,CACnBrB,KAAMC,gBAAciB,KACpBf,YAAa,sBACbC,SAAS,GAGXkB,uBAAwB,CACtBtB,KAAMC,gBAAciB,KACpBf,YAAa,yBACbC,SAAS,GAGXmB,+BAAgC,CAC9BvB,KAAMC,gBAAciB,KACpBf,YAAa,iCACbC,SAAS,KAef,MAAMoB,EAGJC,YAAoBC,GAAAC,aAAAD,EAEpBE,MAAMC,EAA8BD,EAAwBE,GAE1D,IAAIC,EAGJ,IAIIC,EAGAC,EAGAC,EAPAC,EAAUR,KAAKD,QAAQU,UAAUC,eAUrCV,KAAKD,QAAQU,UACVE,eAAeV,EAAM7B,UACrBwC,MAAK,SAAUC,GACE,OAAZL,IACFH,EAAQG,EAAQM,sBACVD,OAASA,EACfR,EAAMU,QAAQP,EAAQQ,eAEtBX,EAAQQ,GACFI,YAAc,EAEtBC,OAEDC,OAAM,SAAUC,GACfC,QAAQC,MACN,8BAA8BrB,EAAM7B,qGAEtCiD,QAAQC,MAAMF,MAGlB,MAAMF,EAAa,KAEbjB,EAAMN,wBACRU,EAAMkB,iBAAiB,QAASC,GAI7BvB,EAAML,gCAAmCK,EAAMN,wBAClDU,EAAMkB,iBAAiB,QAASE,GAGlC,IAAIC,EAAO,8EACXA,GACE,iHACyB,OAAvBzB,EAAMd,aACRuC,GAAQzB,EAAMd,aAAe,MAE7BuC,GAAQ,QAEVA,GAAQ,KACRA,GACE,qDACAzB,EAAMnB,aACN,UACAmB,EAAMtB,IACN,UACAsB,EAAMpB,IACN,WACAoB,EAAMlB,KACN,gDACGkB,EAAML,iCACT8B,GAAQ,aAEVA,GAAQ,iBACR,IAAK,IAAIC,EAAI,EAAGA,EAAI1B,EAAMjB,OAAO4C,OAAQD,IAAK,CAC5C,IAAIE,EAAmB,KAAO5B,EAAMjB,OAAO4C,OAAS,GAChDE,EAAmBH,GAAK,KAAO1B,EAAMjB,OAAO4C,OAAS,IAGzDF,GACE,mGAEAI,EACA,QACAD,EACA,YA9EiB,MAsEcC,EAAmB,IAAM,GAAM,KACH,IAS3D,mCACAD,EACA,OACFH,GAAQ,qDAAuDzB,EAAMjB,OAAO2C,GAAK,UACjFD,GAAQ,SAEVA,GAAQ,SACRA,GAAQ,SACRA,GAAQ,SAEa,OAAjBzB,EAAMT,SACRkC,GAAQzB,EAAMT,QAIhB,IAAIuC,EAA0B,GA2B9B,IA1BI9B,EAAMX,kBAAqBW,EAAML,iCACnCmC,EAA0B,YAE5BL,GACE,uEACAK,EACA,IACA9B,EAAMb,aACN,YAEFc,EAAgB8B,UAAYN,EAE5BnB,EAAW,CACT0B,GAAI,KACJ1B,SAAU,MAGPN,EAAML,iCACTM,EAAgBgC,cACd,2CACAC,UAAW,EACbjC,EAAgBgC,cACd,uCACAC,UAAW,GAGXlC,EAAMX,iBAAkB,CAC1B,MAAM8C,EAAgB,KACpBlC,EAAgBgC,cACd,uCACAC,UAAW,GAGfjC,EACGgC,cAAc,2CACdX,iBAAiB,YAAaa,GAEjClC,EACGgC,cAAc,2CACdX,iBAAiB,aAAca,GAGpClC,EACGgC,cAAc,uCACdX,iBAAiB,SAAS,WAEzB,IAAIc,EAAUC,YAAYC,MACtBN,EAAKO,KAAKC,MAAMJ,EAAU/B,GACd,OAAZE,IACF6B,EAAU7B,EAAQS,YAClBgB,EAAKO,KAAKC,MAA8B,KAAvBJ,EAAU/B,KAE7BC,EAAS0B,GAAKA,EACd1B,EAASA,SAAWL,EAAgBgC,cAClC,2CACAQ,cAEEzC,EAAMP,oBACR8B,IAEAtB,EAAgBgC,cACd,uCACAC,UAAW,KAInB7B,EAAYgC,YAAYC,MAER,OAAZ/B,GACFF,EAAYE,EAAQS,YACpBZ,EAAMsC,MAAMrC,IAEZD,EAAMuC,OAIqB,OAAzB3C,EAAMR,gBACRO,KAAKD,QAAQU,UAAUoC,YAAW,WAChCrB,MACCvB,EAAMR,gBAGXU,KAIF,SAASsB,IACPqB,SAASZ,cAAgC,2CAA2CC,UAClF,EACGlC,EAAMX,mBACTwD,SAASZ,cAAiC,uCAAuCC,UAC/E,GAIN,MAAMX,EAAY,KAEhBxB,KAAKD,QAAQU,UAAUsC,mBAIP,OAAZvC,EACFH,EAAM2C,OAEN3C,EAAM4C,QAGR5C,EAAM6C,oBAAoB,QAAS1B,GACnCnB,EAAM6C,oBAAoB,QAASzB,GAGnC,IAAI0B,EAAY,CACdlB,GAAI1B,EAAS0B,GACb7D,SAAU6B,EAAM7B,SAChBU,aAAcmB,EAAMnB,aACpByB,SAAUA,EAASA,UAGrBL,EAAgB8B,UAAY,GAG5BhC,KAAKD,QAAQqD,YAAYD,GAEzB/C,KAGF,OAAO,IAAIiD,SAASC,IAClBlD,EAAiBkD,aAxOdzD,OAAO5B"}
1
+ {"version":3,"file":"index.browser.min.js","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nconst info = <const>{\n name: \"audio-slider-response\",\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n pretty_name: \"Stimulus\",\n default: undefined,\n },\n /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, trial will end when user makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * **audio-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n let trial_complete;\n\n // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then((buffer) => {\n if (context !== null) {\n this.audio = context.createBufferSource();\n this.audio.buffer = buffer;\n this.audio.connect(context.destination);\n } else {\n this.audio = buffer;\n this.audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch((err) => {\n console.error(\n `Failed to load audio file \"${trial.stimulus}\". Try checking the file path. We recommend using the preload plugin to load audio files.`\n );\n console.error(err);\n });\n\n const setupTrial = () => {\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", end_trial);\n }\n\n // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"change\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", () => {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n this.audio.start(startTime);\n } else {\n this.audio.play();\n }\n\n // end trial if trial_duration is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\n const end_trial = () => {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n // remove end event listeners if they exist\n if (context !== null) {\n this.audio.stop();\n } else {\n this.audio.pause();\n }\n\n this.audio.removeEventListener(\"ended\", end_trial);\n this.audio.removeEventListener(\"ended\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n\n simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: this.jsPsych.randomization.randomInt(trial.min, trial.max),\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\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 const el = display_element.querySelector<HTMLInputElement>(\"input[type='range']\");\n\n setTimeout(() => {\n this.jsPsych.pluginAPI.clickTarget(el);\n el.valueAsNumber = data.response;\n }, data.rt / 2);\n\n this.jsPsych.pluginAPI.clickTarget(display_element.querySelector(\"button\"), data.rt);\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":["info","name","parameters","stimulus","type","ParameterType","AUDIO","pretty_name","default","undefined","min","INT","max","slider_start","step","labels","HTML_STRING","array","slider_width","button_label","STRING","require_movement","BOOL","prompt","trial_duration","response_ends_trial","trial_ends_after_audio","response_allowed_while_playing","AudioSliderResponsePlugin","constructor","jsPsych","this","trial","display_element","on_load","trial_complete","startTime","response","context","pluginAPI","audioContext","getAudioBuffer","then","buffer","audio","createBufferSource","connect","destination","currentTime","setupTrial","catch","err","console","error","addEventListener","end_trial","enable_slider","html","j","length","label_width_perc","percent_of_range","next_disabled_attribute","innerHTML","rt","querySelector","disabled","enable_button","endTime","performance","now","Math","round","valueAsNumber","start","play","setTimeout","document","clearAllTimeouts","stop","pause","removeEventListener","trialdata","finishTrial","Promise","resolve","simulate","simulation_mode","simulation_options","load_callback","simulate_data_only","simulate_visual","create_simulation_data","default_data","randomization","randomInt","sampleExGaussian","data","mergeSimulationData","ensureSimulationDataConsistency","getDisplayElement","respond","el","clickTarget"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -112,7 +112,6 @@ class AudioSliderResponsePlugin {
112
112
  var half_thumb_width = 7.5;
113
113
  // setup stimulus
114
114
  var context = this.jsPsych.pluginAPI.audioContext();
115
- var audio;
116
115
  // record webaudio context start time
117
116
  var startTime;
118
117
  // for storing data related to response
@@ -120,30 +119,30 @@ class AudioSliderResponsePlugin {
120
119
  // load audio file
121
120
  this.jsPsych.pluginAPI
122
121
  .getAudioBuffer(trial.stimulus)
123
- .then(function (buffer) {
122
+ .then((buffer) => {
124
123
  if (context !== null) {
125
- audio = context.createBufferSource();
126
- audio.buffer = buffer;
127
- audio.connect(context.destination);
124
+ this.audio = context.createBufferSource();
125
+ this.audio.buffer = buffer;
126
+ this.audio.connect(context.destination);
128
127
  }
129
128
  else {
130
- audio = buffer;
131
- audio.currentTime = 0;
129
+ this.audio = buffer;
130
+ this.audio.currentTime = 0;
132
131
  }
133
132
  setupTrial();
134
133
  })
135
- .catch(function (err) {
134
+ .catch((err) => {
136
135
  console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
137
136
  console.error(err);
138
137
  });
139
138
  const setupTrial = () => {
140
139
  // set up end event if trial needs it
141
140
  if (trial.trial_ends_after_audio) {
142
- audio.addEventListener("ended", end_trial);
141
+ this.audio.addEventListener("ended", end_trial);
143
142
  }
144
143
  // enable slider after audio ends if necessary
145
144
  if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {
146
- audio.addEventListener("ended", enable_slider);
145
+ this.audio.addEventListener("ended", enable_slider);
147
146
  }
148
147
  var html = '<div id="jspsych-audio-slider-response-wrapper" style="margin: 100px 0px;">';
149
148
  html +=
@@ -224,10 +223,13 @@ class AudioSliderResponsePlugin {
224
223
  display_element
225
224
  .querySelector("#jspsych-audio-slider-response-response")
226
225
  .addEventListener("touchstart", enable_button);
226
+ display_element
227
+ .querySelector("#jspsych-audio-slider-response-response")
228
+ .addEventListener("change", enable_button);
227
229
  }
228
230
  display_element
229
231
  .querySelector("#jspsych-audio-slider-response-next")
230
- .addEventListener("click", function () {
232
+ .addEventListener("click", () => {
231
233
  // measure response time
232
234
  var endTime = performance.now();
233
235
  var rt = Math.round(endTime - startTime);
@@ -248,14 +250,14 @@ class AudioSliderResponsePlugin {
248
250
  // start audio
249
251
  if (context !== null) {
250
252
  startTime = context.currentTime;
251
- audio.start(startTime);
253
+ this.audio.start(startTime);
252
254
  }
253
255
  else {
254
- audio.play();
256
+ this.audio.play();
255
257
  }
256
258
  // end trial if trial_duration is set
257
259
  if (trial.trial_duration !== null) {
258
- this.jsPsych.pluginAPI.setTimeout(function () {
260
+ this.jsPsych.pluginAPI.setTimeout(() => {
259
261
  end_trial();
260
262
  }, trial.trial_duration);
261
263
  }
@@ -276,13 +278,13 @@ class AudioSliderResponsePlugin {
276
278
  // stop the audio file if it is playing
277
279
  // remove end event listeners if they exist
278
280
  if (context !== null) {
279
- audio.stop();
281
+ this.audio.stop();
280
282
  }
281
283
  else {
282
- audio.pause();
284
+ this.audio.pause();
283
285
  }
284
- audio.removeEventListener("ended", end_trial);
285
- audio.removeEventListener("ended", enable_slider);
286
+ this.audio.removeEventListener("ended", end_trial);
287
+ this.audio.removeEventListener("ended", enable_slider);
286
288
  // save data
287
289
  var trialdata = {
288
290
  rt: response.rt,
@@ -299,6 +301,53 @@ class AudioSliderResponsePlugin {
299
301
  trial_complete = resolve;
300
302
  });
301
303
  }
304
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
305
+ if (simulation_mode == "data-only") {
306
+ load_callback();
307
+ this.simulate_data_only(trial, simulation_options);
308
+ }
309
+ if (simulation_mode == "visual") {
310
+ this.simulate_visual(trial, simulation_options, load_callback);
311
+ }
312
+ }
313
+ create_simulation_data(trial, simulation_options) {
314
+ const default_data = {
315
+ stimulus: trial.stimulus,
316
+ slider_start: trial.slider_start,
317
+ response: this.jsPsych.randomization.randomInt(trial.min, trial.max),
318
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
319
+ };
320
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
321
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
322
+ return data;
323
+ }
324
+ simulate_data_only(trial, simulation_options) {
325
+ const data = this.create_simulation_data(trial, simulation_options);
326
+ this.jsPsych.finishTrial(data);
327
+ }
328
+ simulate_visual(trial, simulation_options, load_callback) {
329
+ const data = this.create_simulation_data(trial, simulation_options);
330
+ const display_element = this.jsPsych.getDisplayElement();
331
+ const respond = () => {
332
+ if (data.rt !== null) {
333
+ const el = display_element.querySelector("input[type='range']");
334
+ setTimeout(() => {
335
+ this.jsPsych.pluginAPI.clickTarget(el);
336
+ el.valueAsNumber = data.response;
337
+ }, data.rt / 2);
338
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("button"), data.rt);
339
+ }
340
+ };
341
+ this.trial(display_element, trial, () => {
342
+ load_callback();
343
+ if (!trial.response_allowed_while_playing) {
344
+ this.audio.addEventListener("ended", respond);
345
+ }
346
+ else {
347
+ respond();
348
+ }
349
+ });
350
+ }
302
351
  }
303
352
  AudioSliderResponsePlugin.info = info;
304
353
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nconst info = <const>{\n name: \"audio-slider-response\",\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n pretty_name: \"Stimulus\",\n default: undefined,\n },\n /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, trial will end when user makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * **audio-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n let trial_complete;\n\n // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n var audio;\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then(function (buffer) {\n if (context !== null) {\n audio = context.createBufferSource();\n audio.buffer = buffer;\n audio.connect(context.destination);\n } else {\n audio = buffer;\n audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch(function (err) {\n console.error(\n `Failed to load audio file \"${trial.stimulus}\". Try checking the file path. We recommend using the preload plugin to load audio files.`\n );\n console.error(err);\n });\n\n const setupTrial = () => {\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", end_trial);\n }\n\n // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", function () {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n audio.start(startTime);\n } else {\n audio.play();\n }\n\n // end trial if trial_duration is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(function () {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\n const end_trial = () => {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n // remove end event listeners if they exist\n if (context !== null) {\n audio.stop();\n } else {\n audio.pause();\n }\n\n audio.removeEventListener(\"ended\", end_trial);\n audio.removeEventListener(\"ended\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":["ParameterType"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nconst info = <const>{\n name: \"audio-slider-response\",\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n pretty_name: \"Stimulus\",\n default: undefined,\n },\n /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, trial will end when user makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * **audio-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n let trial_complete;\n\n // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then((buffer) => {\n if (context !== null) {\n this.audio = context.createBufferSource();\n this.audio.buffer = buffer;\n this.audio.connect(context.destination);\n } else {\n this.audio = buffer;\n this.audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch((err) => {\n console.error(\n `Failed to load audio file \"${trial.stimulus}\". Try checking the file path. We recommend using the preload plugin to load audio files.`\n );\n console.error(err);\n });\n\n const setupTrial = () => {\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", end_trial);\n }\n\n // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"change\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", () => {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n this.audio.start(startTime);\n } else {\n this.audio.play();\n }\n\n // end trial if trial_duration is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\n const end_trial = () => {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n // remove end event listeners if they exist\n if (context !== null) {\n this.audio.stop();\n } else {\n this.audio.pause();\n }\n\n this.audio.removeEventListener(\"ended\", end_trial);\n this.audio.removeEventListener(\"ended\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n\n simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: this.jsPsych.randomization.randomInt(trial.min, trial.max),\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\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 const el = display_element.querySelector<HTMLInputElement>(\"input[type='range']\");\n\n setTimeout(() => {\n this.jsPsych.pluginAPI.clickTarget(el);\n el.valueAsNumber = data.response;\n }, data.rt / 2);\n\n this.jsPsych.pluginAPI.clickTarget(display_element.querySelector(\"button\"), data.rt);\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":["ParameterType"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -192,7 +192,12 @@ declare class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
192
192
  };
193
193
  };
194
194
  };
195
+ private audio;
195
196
  constructor(jsPsych: JsPsych);
196
197
  trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void): Promise<unknown>;
198
+ simulate(trial: TrialType<Info>, simulation_mode: any, simulation_options: any, load_callback: () => void): void;
199
+ private create_simulation_data;
200
+ private simulate_data_only;
201
+ private simulate_visual;
197
202
  }
198
203
  export default AudioSliderResponsePlugin;
package/dist/index.js CHANGED
@@ -110,7 +110,6 @@ class AudioSliderResponsePlugin {
110
110
  var half_thumb_width = 7.5;
111
111
  // setup stimulus
112
112
  var context = this.jsPsych.pluginAPI.audioContext();
113
- var audio;
114
113
  // record webaudio context start time
115
114
  var startTime;
116
115
  // for storing data related to response
@@ -118,30 +117,30 @@ class AudioSliderResponsePlugin {
118
117
  // load audio file
119
118
  this.jsPsych.pluginAPI
120
119
  .getAudioBuffer(trial.stimulus)
121
- .then(function (buffer) {
120
+ .then((buffer) => {
122
121
  if (context !== null) {
123
- audio = context.createBufferSource();
124
- audio.buffer = buffer;
125
- audio.connect(context.destination);
122
+ this.audio = context.createBufferSource();
123
+ this.audio.buffer = buffer;
124
+ this.audio.connect(context.destination);
126
125
  }
127
126
  else {
128
- audio = buffer;
129
- audio.currentTime = 0;
127
+ this.audio = buffer;
128
+ this.audio.currentTime = 0;
130
129
  }
131
130
  setupTrial();
132
131
  })
133
- .catch(function (err) {
132
+ .catch((err) => {
134
133
  console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
135
134
  console.error(err);
136
135
  });
137
136
  const setupTrial = () => {
138
137
  // set up end event if trial needs it
139
138
  if (trial.trial_ends_after_audio) {
140
- audio.addEventListener("ended", end_trial);
139
+ this.audio.addEventListener("ended", end_trial);
141
140
  }
142
141
  // enable slider after audio ends if necessary
143
142
  if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {
144
- audio.addEventListener("ended", enable_slider);
143
+ this.audio.addEventListener("ended", enable_slider);
145
144
  }
146
145
  var html = '<div id="jspsych-audio-slider-response-wrapper" style="margin: 100px 0px;">';
147
146
  html +=
@@ -222,10 +221,13 @@ class AudioSliderResponsePlugin {
222
221
  display_element
223
222
  .querySelector("#jspsych-audio-slider-response-response")
224
223
  .addEventListener("touchstart", enable_button);
224
+ display_element
225
+ .querySelector("#jspsych-audio-slider-response-response")
226
+ .addEventListener("change", enable_button);
225
227
  }
226
228
  display_element
227
229
  .querySelector("#jspsych-audio-slider-response-next")
228
- .addEventListener("click", function () {
230
+ .addEventListener("click", () => {
229
231
  // measure response time
230
232
  var endTime = performance.now();
231
233
  var rt = Math.round(endTime - startTime);
@@ -246,14 +248,14 @@ class AudioSliderResponsePlugin {
246
248
  // start audio
247
249
  if (context !== null) {
248
250
  startTime = context.currentTime;
249
- audio.start(startTime);
251
+ this.audio.start(startTime);
250
252
  }
251
253
  else {
252
- audio.play();
254
+ this.audio.play();
253
255
  }
254
256
  // end trial if trial_duration is set
255
257
  if (trial.trial_duration !== null) {
256
- this.jsPsych.pluginAPI.setTimeout(function () {
258
+ this.jsPsych.pluginAPI.setTimeout(() => {
257
259
  end_trial();
258
260
  }, trial.trial_duration);
259
261
  }
@@ -274,13 +276,13 @@ class AudioSliderResponsePlugin {
274
276
  // stop the audio file if it is playing
275
277
  // remove end event listeners if they exist
276
278
  if (context !== null) {
277
- audio.stop();
279
+ this.audio.stop();
278
280
  }
279
281
  else {
280
- audio.pause();
282
+ this.audio.pause();
281
283
  }
282
- audio.removeEventListener("ended", end_trial);
283
- audio.removeEventListener("ended", enable_slider);
284
+ this.audio.removeEventListener("ended", end_trial);
285
+ this.audio.removeEventListener("ended", enable_slider);
284
286
  // save data
285
287
  var trialdata = {
286
288
  rt: response.rt,
@@ -297,6 +299,53 @@ class AudioSliderResponsePlugin {
297
299
  trial_complete = resolve;
298
300
  });
299
301
  }
302
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
303
+ if (simulation_mode == "data-only") {
304
+ load_callback();
305
+ this.simulate_data_only(trial, simulation_options);
306
+ }
307
+ if (simulation_mode == "visual") {
308
+ this.simulate_visual(trial, simulation_options, load_callback);
309
+ }
310
+ }
311
+ create_simulation_data(trial, simulation_options) {
312
+ const default_data = {
313
+ stimulus: trial.stimulus,
314
+ slider_start: trial.slider_start,
315
+ response: this.jsPsych.randomization.randomInt(trial.min, trial.max),
316
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
317
+ };
318
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
319
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
320
+ return data;
321
+ }
322
+ simulate_data_only(trial, simulation_options) {
323
+ const data = this.create_simulation_data(trial, simulation_options);
324
+ this.jsPsych.finishTrial(data);
325
+ }
326
+ simulate_visual(trial, simulation_options, load_callback) {
327
+ const data = this.create_simulation_data(trial, simulation_options);
328
+ const display_element = this.jsPsych.getDisplayElement();
329
+ const respond = () => {
330
+ if (data.rt !== null) {
331
+ const el = display_element.querySelector("input[type='range']");
332
+ setTimeout(() => {
333
+ this.jsPsych.pluginAPI.clickTarget(el);
334
+ el.valueAsNumber = data.response;
335
+ }, data.rt / 2);
336
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("button"), data.rt);
337
+ }
338
+ };
339
+ this.trial(display_element, trial, () => {
340
+ load_callback();
341
+ if (!trial.response_allowed_while_playing) {
342
+ this.audio.addEventListener("ended", respond);
343
+ }
344
+ else {
345
+ respond();
346
+ }
347
+ });
348
+ }
300
349
  }
301
350
  AudioSliderResponsePlugin.info = info;
302
351
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nconst info = <const>{\n name: \"audio-slider-response\",\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n pretty_name: \"Stimulus\",\n default: undefined,\n },\n /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, trial will end when user makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * **audio-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n let trial_complete;\n\n // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n var audio;\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then(function (buffer) {\n if (context !== null) {\n audio = context.createBufferSource();\n audio.buffer = buffer;\n audio.connect(context.destination);\n } else {\n audio = buffer;\n audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch(function (err) {\n console.error(\n `Failed to load audio file \"${trial.stimulus}\". Try checking the file path. We recommend using the preload plugin to load audio files.`\n );\n console.error(err);\n });\n\n const setupTrial = () => {\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", end_trial);\n }\n\n // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", function () {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n audio.start(startTime);\n } else {\n audio.play();\n }\n\n // end trial if trial_duration is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(function () {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\n const end_trial = () => {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n // remove end event listeners if they exist\n if (context !== null) {\n audio.stop();\n } else {\n audio.pause();\n }\n\n audio.removeEventListener(\"ended\", end_trial);\n audio.removeEventListener(\"ended\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nconst info = <const>{\n name: \"audio-slider-response\",\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n pretty_name: \"Stimulus\",\n default: undefined,\n },\n /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, trial will end when user makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * **audio-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n let trial_complete;\n\n // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then((buffer) => {\n if (context !== null) {\n this.audio = context.createBufferSource();\n this.audio.buffer = buffer;\n this.audio.connect(context.destination);\n } else {\n this.audio = buffer;\n this.audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch((err) => {\n console.error(\n `Failed to load audio file \"${trial.stimulus}\". Try checking the file path. We recommend using the preload plugin to load audio files.`\n );\n console.error(err);\n });\n\n const setupTrial = () => {\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", end_trial);\n }\n\n // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"change\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", () => {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n this.audio.start(startTime);\n } else {\n this.audio.play();\n }\n\n // end trial if trial_duration is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\n const end_trial = () => {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n // remove end event listeners if they exist\n if (context !== null) {\n this.audio.stop();\n } else {\n this.audio.pause();\n }\n\n this.audio.removeEventListener(\"ended\", end_trial);\n this.audio.removeEventListener(\"ended\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n\n simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: this.jsPsych.randomization.randomInt(trial.min, trial.max),\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\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 const el = display_element.querySelector<HTMLInputElement>(\"input[type='range']\");\n\n setTimeout(() => {\n this.jsPsych.pluginAPI.clickTarget(el);\n el.valueAsNumber = data.response;\n }, data.rt / 2);\n\n this.jsPsych.pluginAPI.clickTarget(display_element.querySelector(\"button\"), data.rt);\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jspsych/plugin-audio-slider-response",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -34,10 +34,10 @@
34
34
  },
35
35
  "homepage": "https://www.jspsych.org/latest/plugins/audio-slider-response",
36
36
  "peerDependencies": {
37
- "jspsych": ">=7.0.0"
37
+ "jspsych": ">=7.1.0"
38
38
  },
39
39
  "devDependencies": {
40
- "@jspsych/config": "^1.0.0",
41
- "@jspsych/test-utils": "^1.0.0"
40
+ "@jspsych/config": "^1.1.0",
41
+ "@jspsych/test-utils": "^1.1.0"
42
42
  }
43
43
  }
@@ -0,0 +1,57 @@
1
+ import { pressKey, simulateTimeline, startTimeline } from "@jspsych/test-utils";
2
+ import { initJsPsych } from "jspsych";
3
+
4
+ import audioSliderResponse from ".";
5
+
6
+ jest.useFakeTimers();
7
+
8
+ describe("audio-slider-response simulation", () => {
9
+ test("data mode works", async () => {
10
+ const timeline = [
11
+ {
12
+ type: audioSliderResponse,
13
+ stimulus: "foo.mp3",
14
+ },
15
+ ];
16
+
17
+ const { expectFinished, getData } = await simulateTimeline(timeline);
18
+
19
+ await expectFinished();
20
+
21
+ expect(getData().values()[0].rt).toBeGreaterThan(0);
22
+ expect(getData().values()[0].response).toBeGreaterThanOrEqual(0);
23
+ expect(getData().values()[0].response).toBeLessThanOrEqual(100);
24
+ });
25
+
26
+ // can't run this until we mock Audio elements.
27
+ test.skip("visual mode works", async () => {
28
+ const jsPsych = initJsPsych({ use_webaudio: false });
29
+
30
+ const timeline = [
31
+ {
32
+ type: audioSliderResponse,
33
+ stimulus: "foo.mp3",
34
+ prompt: "foo",
35
+ },
36
+ ];
37
+
38
+ const { expectFinished, expectRunning, getHTML, getData } = await simulateTimeline(
39
+ timeline,
40
+ "visual",
41
+ {},
42
+ jsPsych
43
+ );
44
+
45
+ await expectRunning();
46
+
47
+ expect(getHTML()).toContain("foo");
48
+
49
+ jest.runAllTimers();
50
+
51
+ await expectFinished();
52
+
53
+ expect(getData().values()[0].rt).toBeGreaterThan(0);
54
+ expect(getData().values()[0].response).toBeGreaterThanOrEqual(0);
55
+ expect(getData().values()[0].response).toBeLessThanOrEqual(100);
56
+ });
57
+ });
package/src/index.ts CHANGED
@@ -104,6 +104,7 @@ type Info = typeof info;
104
104
  */
105
105
  class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
106
106
  static info = info;
107
+ private audio;
107
108
 
108
109
  constructor(private jsPsych: JsPsych) {}
109
110
 
@@ -116,7 +117,6 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
116
117
 
117
118
  // setup stimulus
118
119
  var context = this.jsPsych.pluginAPI.audioContext();
119
- var audio;
120
120
 
121
121
  // record webaudio context start time
122
122
  var startTime;
@@ -127,18 +127,18 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
127
127
  // load audio file
128
128
  this.jsPsych.pluginAPI
129
129
  .getAudioBuffer(trial.stimulus)
130
- .then(function (buffer) {
130
+ .then((buffer) => {
131
131
  if (context !== null) {
132
- audio = context.createBufferSource();
133
- audio.buffer = buffer;
134
- audio.connect(context.destination);
132
+ this.audio = context.createBufferSource();
133
+ this.audio.buffer = buffer;
134
+ this.audio.connect(context.destination);
135
135
  } else {
136
- audio = buffer;
137
- audio.currentTime = 0;
136
+ this.audio = buffer;
137
+ this.audio.currentTime = 0;
138
138
  }
139
139
  setupTrial();
140
140
  })
141
- .catch(function (err) {
141
+ .catch((err) => {
142
142
  console.error(
143
143
  `Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`
144
144
  );
@@ -148,12 +148,12 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
148
148
  const setupTrial = () => {
149
149
  // set up end event if trial needs it
150
150
  if (trial.trial_ends_after_audio) {
151
- audio.addEventListener("ended", end_trial);
151
+ this.audio.addEventListener("ended", end_trial);
152
152
  }
153
153
 
154
154
  // enable slider after audio ends if necessary
155
155
  if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {
156
- audio.addEventListener("ended", enable_slider);
156
+ this.audio.addEventListener("ended", enable_slider);
157
157
  }
158
158
 
159
159
  var html = '<div id="jspsych-audio-slider-response-wrapper" style="margin: 100px 0px;">';
@@ -248,11 +248,15 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
248
248
  display_element
249
249
  .querySelector("#jspsych-audio-slider-response-response")
250
250
  .addEventListener("touchstart", enable_button);
251
+
252
+ display_element
253
+ .querySelector("#jspsych-audio-slider-response-response")
254
+ .addEventListener("change", enable_button);
251
255
  }
252
256
 
253
257
  display_element
254
258
  .querySelector("#jspsych-audio-slider-response-next")
255
- .addEventListener("click", function () {
259
+ .addEventListener("click", () => {
256
260
  // measure response time
257
261
  var endTime = performance.now();
258
262
  var rt = Math.round(endTime - startTime);
@@ -278,14 +282,14 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
278
282
  // start audio
279
283
  if (context !== null) {
280
284
  startTime = context.currentTime;
281
- audio.start(startTime);
285
+ this.audio.start(startTime);
282
286
  } else {
283
- audio.play();
287
+ this.audio.play();
284
288
  }
285
289
 
286
290
  // end trial if trial_duration is set
287
291
  if (trial.trial_duration !== null) {
288
- this.jsPsych.pluginAPI.setTimeout(function () {
292
+ this.jsPsych.pluginAPI.setTimeout(() => {
289
293
  end_trial();
290
294
  }, trial.trial_duration);
291
295
  }
@@ -310,13 +314,13 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
310
314
  // stop the audio file if it is playing
311
315
  // remove end event listeners if they exist
312
316
  if (context !== null) {
313
- audio.stop();
317
+ this.audio.stop();
314
318
  } else {
315
- audio.pause();
319
+ this.audio.pause();
316
320
  }
317
321
 
318
- audio.removeEventListener("ended", end_trial);
319
- audio.removeEventListener("ended", enable_slider);
322
+ this.audio.removeEventListener("ended", end_trial);
323
+ this.audio.removeEventListener("ended", enable_slider);
320
324
 
321
325
  // save data
322
326
  var trialdata = {
@@ -338,6 +342,71 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
338
342
  trial_complete = resolve;
339
343
  });
340
344
  }
345
+
346
+ simulate(
347
+ trial: TrialType<Info>,
348
+ simulation_mode,
349
+ simulation_options: any,
350
+ load_callback: () => void
351
+ ) {
352
+ if (simulation_mode == "data-only") {
353
+ load_callback();
354
+ this.simulate_data_only(trial, simulation_options);
355
+ }
356
+ if (simulation_mode == "visual") {
357
+ this.simulate_visual(trial, simulation_options, load_callback);
358
+ }
359
+ }
360
+
361
+ private create_simulation_data(trial: TrialType<Info>, simulation_options) {
362
+ const default_data = {
363
+ stimulus: trial.stimulus,
364
+ slider_start: trial.slider_start,
365
+ response: this.jsPsych.randomization.randomInt(trial.min, trial.max),
366
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
367
+ };
368
+
369
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
370
+
371
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
372
+
373
+ return data;
374
+ }
375
+
376
+ private simulate_data_only(trial: TrialType<Info>, simulation_options) {
377
+ const data = this.create_simulation_data(trial, simulation_options);
378
+
379
+ this.jsPsych.finishTrial(data);
380
+ }
381
+
382
+ private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {
383
+ const data = this.create_simulation_data(trial, simulation_options);
384
+
385
+ const display_element = this.jsPsych.getDisplayElement();
386
+
387
+ const respond = () => {
388
+ if (data.rt !== null) {
389
+ const el = display_element.querySelector<HTMLInputElement>("input[type='range']");
390
+
391
+ setTimeout(() => {
392
+ this.jsPsych.pluginAPI.clickTarget(el);
393
+ el.valueAsNumber = data.response;
394
+ }, data.rt / 2);
395
+
396
+ this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("button"), data.rt);
397
+ }
398
+ };
399
+
400
+ this.trial(display_element, trial, () => {
401
+ load_callback();
402
+
403
+ if (!trial.response_allowed_while_playing) {
404
+ this.audio.addEventListener("ended", respond);
405
+ } else {
406
+ respond();
407
+ }
408
+ });
409
+ }
341
410
  }
342
411
 
343
412
  export default AudioSliderResponsePlugin;