@jspsych/plugin-audio-button-response 1.2.0 → 2.0.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.
- package/dist/index.browser.js +326 -319
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.min.js +2 -2
- package/dist/index.browser.min.js.map +1 -1
- package/dist/index.cjs +258 -304
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +151 -69
- package/dist/index.js +258 -304
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.spec.ts +233 -9
- package/src/index.ts +219 -223
package/dist/index.cjs
CHANGED
|
@@ -1,321 +1,275 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var autoBind = require('auto-bind');
|
|
3
4
|
var jspsych = require('jspsych');
|
|
4
5
|
|
|
6
|
+
var _package = {
|
|
7
|
+
name: "@jspsych/plugin-audio-button-response",
|
|
8
|
+
version: "2.0.0",
|
|
9
|
+
description: "jsPsych plugin for playing an audio file and getting a button response",
|
|
10
|
+
type: "module",
|
|
11
|
+
main: "dist/index.cjs",
|
|
12
|
+
exports: {
|
|
13
|
+
import: "./dist/index.js",
|
|
14
|
+
require: "./dist/index.cjs"
|
|
15
|
+
},
|
|
16
|
+
typings: "dist/index.d.ts",
|
|
17
|
+
unpkg: "dist/index.browser.min.js",
|
|
18
|
+
files: [
|
|
19
|
+
"src",
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
source: "src/index.ts",
|
|
23
|
+
scripts: {
|
|
24
|
+
test: "jest",
|
|
25
|
+
"test:watch": "npm test -- --watch",
|
|
26
|
+
tsc: "tsc",
|
|
27
|
+
build: "rollup --config",
|
|
28
|
+
"build:watch": "npm run build -- --watch"
|
|
29
|
+
},
|
|
30
|
+
repository: {
|
|
31
|
+
type: "git",
|
|
32
|
+
url: "git+https://github.com/jspsych/jsPsych.git",
|
|
33
|
+
directory: "packages/plugin-audio-button-response"
|
|
34
|
+
},
|
|
35
|
+
author: "Kristin Diep",
|
|
36
|
+
license: "MIT",
|
|
37
|
+
bugs: {
|
|
38
|
+
url: "https://github.com/jspsych/jsPsych/issues"
|
|
39
|
+
},
|
|
40
|
+
homepage: "https://www.jspsych.org/latest/plugins/audio-button-response",
|
|
41
|
+
peerDependencies: {
|
|
42
|
+
jspsych: ">=7.1.0"
|
|
43
|
+
},
|
|
44
|
+
devDependencies: {
|
|
45
|
+
"@jspsych/config": "^3.0.0",
|
|
46
|
+
"@jspsych/test-utils": "^1.2.0"
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
5
50
|
const info = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
margin_horizontal: {
|
|
48
|
-
type: jspsych.ParameterType.STRING,
|
|
49
|
-
pretty_name: "Margin horizontal",
|
|
50
|
-
default: "8px",
|
|
51
|
-
},
|
|
52
|
-
/** If true, the trial will end when user makes a response. */
|
|
53
|
-
response_ends_trial: {
|
|
54
|
-
type: jspsych.ParameterType.BOOL,
|
|
55
|
-
pretty_name: "Response ends trial",
|
|
56
|
-
default: true,
|
|
57
|
-
},
|
|
58
|
-
/** If true, then the trial will end as soon as the audio file finishes playing. */
|
|
59
|
-
trial_ends_after_audio: {
|
|
60
|
-
type: jspsych.ParameterType.BOOL,
|
|
61
|
-
pretty_name: "Trial ends after audio",
|
|
62
|
-
default: false,
|
|
63
|
-
},
|
|
64
|
-
/**
|
|
65
|
-
* If true, then responses are allowed while the audio is playing.
|
|
66
|
-
* If false, then the audio must finish playing before a response is accepted.
|
|
67
|
-
*/
|
|
68
|
-
response_allowed_while_playing: {
|
|
69
|
-
type: jspsych.ParameterType.BOOL,
|
|
70
|
-
pretty_name: "Response allowed while playing",
|
|
71
|
-
default: true,
|
|
72
|
-
},
|
|
73
|
-
/** The delay of enabling button */
|
|
74
|
-
enable_button_after: {
|
|
75
|
-
type: jspsych.ParameterType.INT,
|
|
76
|
-
pretty_name: "Enable button after",
|
|
77
|
-
default: 0,
|
|
78
|
-
},
|
|
51
|
+
name: "audio-button-response",
|
|
52
|
+
version: _package.version,
|
|
53
|
+
parameters: {
|
|
54
|
+
stimulus: {
|
|
55
|
+
type: jspsych.ParameterType.AUDIO,
|
|
56
|
+
default: void 0
|
|
57
|
+
},
|
|
58
|
+
choices: {
|
|
59
|
+
type: jspsych.ParameterType.STRING,
|
|
60
|
+
default: void 0,
|
|
61
|
+
array: true
|
|
62
|
+
},
|
|
63
|
+
button_html: {
|
|
64
|
+
type: jspsych.ParameterType.FUNCTION,
|
|
65
|
+
default: function(choice, choice_index) {
|
|
66
|
+
return `<button class="jspsych-btn">${choice}</button>`;
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
prompt: {
|
|
70
|
+
type: jspsych.ParameterType.HTML_STRING,
|
|
71
|
+
default: null
|
|
72
|
+
},
|
|
73
|
+
trial_duration: {
|
|
74
|
+
type: jspsych.ParameterType.INT,
|
|
75
|
+
default: null
|
|
76
|
+
},
|
|
77
|
+
button_layout: {
|
|
78
|
+
type: jspsych.ParameterType.STRING,
|
|
79
|
+
default: "grid"
|
|
80
|
+
},
|
|
81
|
+
grid_rows: {
|
|
82
|
+
type: jspsych.ParameterType.INT,
|
|
83
|
+
default: 1
|
|
84
|
+
},
|
|
85
|
+
grid_columns: {
|
|
86
|
+
type: jspsych.ParameterType.INT,
|
|
87
|
+
default: null
|
|
88
|
+
},
|
|
89
|
+
response_ends_trial: {
|
|
90
|
+
type: jspsych.ParameterType.BOOL,
|
|
91
|
+
default: true
|
|
79
92
|
},
|
|
93
|
+
trial_ends_after_audio: {
|
|
94
|
+
type: jspsych.ParameterType.BOOL,
|
|
95
|
+
default: false
|
|
96
|
+
},
|
|
97
|
+
response_allowed_while_playing: {
|
|
98
|
+
type: jspsych.ParameterType.BOOL,
|
|
99
|
+
default: true
|
|
100
|
+
},
|
|
101
|
+
enable_button_after: {
|
|
102
|
+
type: jspsych.ParameterType.INT,
|
|
103
|
+
default: 0
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
data: {
|
|
107
|
+
rt: {
|
|
108
|
+
type: jspsych.ParameterType.INT
|
|
109
|
+
},
|
|
110
|
+
response: {
|
|
111
|
+
type: jspsych.ParameterType.INT
|
|
112
|
+
}
|
|
113
|
+
}
|
|
80
114
|
};
|
|
81
|
-
/**
|
|
82
|
-
* **audio-button-response**
|
|
83
|
-
*
|
|
84
|
-
* jsPsych plugin for playing an audio file and getting a button response
|
|
85
|
-
*
|
|
86
|
-
* @author Kristin Diep
|
|
87
|
-
* @see {@link https://www.jspsych.org/plugins/jspsych-audio-button-response/ audio-button-response plugin documentation on jspsych.org}
|
|
88
|
-
*/
|
|
89
115
|
class AudioButtonResponsePlugin {
|
|
90
|
-
|
|
91
|
-
|
|
116
|
+
constructor(jsPsych) {
|
|
117
|
+
this.jsPsych = jsPsych;
|
|
118
|
+
this.buttonElements = [];
|
|
119
|
+
this.response = { rt: null, button: null };
|
|
120
|
+
this.disable_buttons = () => {
|
|
121
|
+
for (const button of this.buttonElements) {
|
|
122
|
+
button.setAttribute("disabled", "disabled");
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
this.enable_buttons_without_delay = () => {
|
|
126
|
+
for (const button of this.buttonElements) {
|
|
127
|
+
button.removeAttribute("disabled");
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
this.enable_buttons_with_delay = (delay) => {
|
|
131
|
+
this.jsPsych.pluginAPI.setTimeout(this.enable_buttons_without_delay, delay);
|
|
132
|
+
};
|
|
133
|
+
this.after_response = (choice) => {
|
|
134
|
+
var endTime = performance.now();
|
|
135
|
+
var rt = Math.round(endTime - this.startTime);
|
|
136
|
+
if (this.context !== null) {
|
|
137
|
+
endTime = this.context.currentTime;
|
|
138
|
+
rt = Math.round((endTime - this.startTime) * 1e3);
|
|
139
|
+
}
|
|
140
|
+
this.response.button = parseInt(choice);
|
|
141
|
+
this.response.rt = rt;
|
|
142
|
+
this.disable_buttons();
|
|
143
|
+
if (this.params.response_ends_trial) {
|
|
144
|
+
this.end_trial();
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
this.end_trial = () => {
|
|
148
|
+
this.audio.stop();
|
|
149
|
+
this.audio.removeEventListener("ended", this.end_trial);
|
|
150
|
+
this.audio.removeEventListener("ended", this.enable_buttons);
|
|
151
|
+
var trial_data = {
|
|
152
|
+
rt: this.response.rt,
|
|
153
|
+
stimulus: this.params.stimulus,
|
|
154
|
+
response: this.response.button
|
|
155
|
+
};
|
|
156
|
+
this.trial_complete(trial_data);
|
|
157
|
+
};
|
|
158
|
+
autoBind(this);
|
|
159
|
+
}
|
|
160
|
+
async trial(display_element, trial, on_load) {
|
|
161
|
+
this.trial_complete;
|
|
162
|
+
this.params = trial;
|
|
163
|
+
this.display = display_element;
|
|
164
|
+
this.context = this.jsPsych.pluginAPI.audioContext();
|
|
165
|
+
this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);
|
|
166
|
+
if (trial.trial_ends_after_audio) {
|
|
167
|
+
this.audio.addEventListener("ended", this.end_trial);
|
|
168
|
+
}
|
|
169
|
+
if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {
|
|
170
|
+
this.audio.addEventListener("ended", this.enable_buttons);
|
|
171
|
+
}
|
|
172
|
+
const buttonGroupElement = document.createElement("div");
|
|
173
|
+
buttonGroupElement.id = "jspsych-audio-button-response-btngroup";
|
|
174
|
+
if (trial.button_layout === "grid") {
|
|
175
|
+
buttonGroupElement.classList.add("jspsych-btn-group-grid");
|
|
176
|
+
if (trial.grid_rows === null && trial.grid_columns === null) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
"You cannot set `grid_rows` to `null` without providing a value for `grid_columns`."
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
const n_cols = trial.grid_columns === null ? Math.ceil(trial.choices.length / trial.grid_rows) : trial.grid_columns;
|
|
182
|
+
const n_rows = trial.grid_rows === null ? Math.ceil(trial.choices.length / trial.grid_columns) : trial.grid_rows;
|
|
183
|
+
buttonGroupElement.style.gridTemplateColumns = `repeat(${n_cols}, 1fr)`;
|
|
184
|
+
buttonGroupElement.style.gridTemplateRows = `repeat(${n_rows}, 1fr)`;
|
|
185
|
+
} else if (trial.button_layout === "flex") {
|
|
186
|
+
buttonGroupElement.classList.add("jspsych-btn-group-flex");
|
|
187
|
+
}
|
|
188
|
+
for (const [choiceIndex, choice] of trial.choices.entries()) {
|
|
189
|
+
buttonGroupElement.insertAdjacentHTML("beforeend", trial.button_html(choice, choiceIndex));
|
|
190
|
+
const buttonElement = buttonGroupElement.lastChild;
|
|
191
|
+
buttonElement.dataset.choice = choiceIndex.toString();
|
|
192
|
+
buttonElement.addEventListener("click", () => {
|
|
193
|
+
this.after_response(choiceIndex);
|
|
194
|
+
});
|
|
195
|
+
this.buttonElements.push(buttonElement);
|
|
196
|
+
}
|
|
197
|
+
display_element.appendChild(buttonGroupElement);
|
|
198
|
+
if (trial.prompt !== null) {
|
|
199
|
+
display_element.insertAdjacentHTML("beforeend", trial.prompt);
|
|
92
200
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
rt: null,
|
|
101
|
-
button: null,
|
|
102
|
-
};
|
|
103
|
-
// record webaudio context start time
|
|
104
|
-
var startTime;
|
|
105
|
-
// load audio file
|
|
106
|
-
this.jsPsych.pluginAPI
|
|
107
|
-
.getAudioBuffer(trial.stimulus)
|
|
108
|
-
.then((buffer) => {
|
|
109
|
-
if (context !== null) {
|
|
110
|
-
this.audio = context.createBufferSource();
|
|
111
|
-
this.audio.buffer = buffer;
|
|
112
|
-
this.audio.connect(context.destination);
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
this.audio = buffer;
|
|
116
|
-
this.audio.currentTime = 0;
|
|
117
|
-
}
|
|
118
|
-
setupTrial();
|
|
119
|
-
})
|
|
120
|
-
.catch((err) => {
|
|
121
|
-
console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
|
|
122
|
-
console.error(err);
|
|
123
|
-
});
|
|
124
|
-
const setupTrial = () => {
|
|
125
|
-
// set up end event if trial needs it
|
|
126
|
-
if (trial.trial_ends_after_audio) {
|
|
127
|
-
this.audio.addEventListener("ended", end_trial);
|
|
128
|
-
}
|
|
129
|
-
// enable buttons after audio ends if necessary
|
|
130
|
-
if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {
|
|
131
|
-
this.audio.addEventListener("ended", enable_buttons);
|
|
132
|
-
}
|
|
133
|
-
//display buttons
|
|
134
|
-
var buttons = [];
|
|
135
|
-
if (Array.isArray(trial.button_html)) {
|
|
136
|
-
if (trial.button_html.length == trial.choices.length) {
|
|
137
|
-
buttons = trial.button_html;
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
console.error("Error in audio-button-response plugin. The length of the button_html array does not equal the length of the choices array");
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
for (var i = 0; i < trial.choices.length; i++) {
|
|
145
|
-
buttons.push(trial.button_html);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
var html = '<div id="jspsych-audio-button-response-btngroup">';
|
|
149
|
-
for (var i = 0; i < trial.choices.length; i++) {
|
|
150
|
-
var str = buttons[i].replace(/%choice%/g, trial.choices[i]);
|
|
151
|
-
html +=
|
|
152
|
-
'<div class="jspsych-audio-button-response-button" style="cursor: pointer; display: inline-block; margin:' +
|
|
153
|
-
trial.margin_vertical +
|
|
154
|
-
" " +
|
|
155
|
-
trial.margin_horizontal +
|
|
156
|
-
'" id="jspsych-audio-button-response-button-' +
|
|
157
|
-
i +
|
|
158
|
-
'" data-choice="' +
|
|
159
|
-
i +
|
|
160
|
-
'">' +
|
|
161
|
-
str +
|
|
162
|
-
"</div>";
|
|
163
|
-
}
|
|
164
|
-
html += "</div>";
|
|
165
|
-
//show prompt if there is one
|
|
166
|
-
if (trial.prompt !== null) {
|
|
167
|
-
html += trial.prompt;
|
|
168
|
-
}
|
|
169
|
-
display_element.innerHTML = html;
|
|
170
|
-
if (trial.response_allowed_while_playing) {
|
|
171
|
-
disable_buttons();
|
|
172
|
-
enable_buttons();
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
disable_buttons();
|
|
176
|
-
}
|
|
177
|
-
// start time
|
|
178
|
-
startTime = performance.now();
|
|
179
|
-
// start audio
|
|
180
|
-
if (context !== null) {
|
|
181
|
-
startTime = context.currentTime;
|
|
182
|
-
this.audio.start(startTime);
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
this.audio.play();
|
|
186
|
-
}
|
|
187
|
-
// end trial if time limit is set
|
|
188
|
-
if (trial.trial_duration !== null) {
|
|
189
|
-
this.jsPsych.pluginAPI.setTimeout(() => {
|
|
190
|
-
end_trial();
|
|
191
|
-
}, trial.trial_duration);
|
|
192
|
-
}
|
|
193
|
-
on_load();
|
|
194
|
-
};
|
|
195
|
-
// function to handle responses by the subject
|
|
196
|
-
function after_response(choice) {
|
|
197
|
-
// measure rt
|
|
198
|
-
var endTime = performance.now();
|
|
199
|
-
var rt = Math.round(endTime - startTime);
|
|
200
|
-
if (context !== null) {
|
|
201
|
-
endTime = context.currentTime;
|
|
202
|
-
rt = Math.round((endTime - startTime) * 1000);
|
|
203
|
-
}
|
|
204
|
-
response.button = parseInt(choice);
|
|
205
|
-
response.rt = rt;
|
|
206
|
-
// disable all the buttons after a response
|
|
207
|
-
disable_buttons();
|
|
208
|
-
if (trial.response_ends_trial) {
|
|
209
|
-
end_trial();
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
// function to end trial when it is time
|
|
213
|
-
const end_trial = () => {
|
|
214
|
-
// kill any remaining setTimeout handlers
|
|
215
|
-
this.jsPsych.pluginAPI.clearAllTimeouts();
|
|
216
|
-
// stop the audio file if it is playing
|
|
217
|
-
// remove end event listeners if they exist
|
|
218
|
-
if (context !== null) {
|
|
219
|
-
this.audio.stop();
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
this.audio.pause();
|
|
223
|
-
}
|
|
224
|
-
this.audio.removeEventListener("ended", end_trial);
|
|
225
|
-
this.audio.removeEventListener("ended", enable_buttons);
|
|
226
|
-
// gather the data to store for the trial
|
|
227
|
-
var trial_data = {
|
|
228
|
-
rt: response.rt,
|
|
229
|
-
stimulus: trial.stimulus,
|
|
230
|
-
response: response.button,
|
|
231
|
-
};
|
|
232
|
-
// clear the display
|
|
233
|
-
display_element.innerHTML = "";
|
|
234
|
-
// move on to the next trial
|
|
235
|
-
this.jsPsych.finishTrial(trial_data);
|
|
236
|
-
trial_complete();
|
|
237
|
-
};
|
|
238
|
-
const enable_buttons_with_delay = (delay) => {
|
|
239
|
-
this.jsPsych.pluginAPI.setTimeout(enable_buttons_without_delay, delay);
|
|
240
|
-
};
|
|
241
|
-
function button_response(e) {
|
|
242
|
-
var choice = e.currentTarget.getAttribute("data-choice"); // don't use dataset for jsdom compatibility
|
|
243
|
-
after_response(choice);
|
|
244
|
-
}
|
|
245
|
-
function disable_buttons() {
|
|
246
|
-
var btns = document.querySelectorAll(".jspsych-audio-button-response-button");
|
|
247
|
-
for (var i = 0; i < btns.length; i++) {
|
|
248
|
-
var btn_el = btns[i].querySelector("button");
|
|
249
|
-
if (btn_el) {
|
|
250
|
-
btn_el.disabled = true;
|
|
251
|
-
}
|
|
252
|
-
btns[i].removeEventListener("click", button_response);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
function enable_buttons_without_delay() {
|
|
256
|
-
var btns = document.querySelectorAll(".jspsych-audio-button-response-button");
|
|
257
|
-
for (var i = 0; i < btns.length; i++) {
|
|
258
|
-
var btn_el = btns[i].querySelector("button");
|
|
259
|
-
if (btn_el) {
|
|
260
|
-
btn_el.disabled = false;
|
|
261
|
-
}
|
|
262
|
-
btns[i].addEventListener("click", button_response);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
function enable_buttons() {
|
|
266
|
-
if (trial.enable_button_after > 0) {
|
|
267
|
-
enable_buttons_with_delay(trial.enable_button_after);
|
|
268
|
-
}
|
|
269
|
-
else {
|
|
270
|
-
enable_buttons_without_delay();
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
return new Promise((resolve) => {
|
|
274
|
-
trial_complete = resolve;
|
|
275
|
-
});
|
|
201
|
+
if (trial.response_allowed_while_playing) {
|
|
202
|
+
if (trial.enable_button_after > 0) {
|
|
203
|
+
this.disable_buttons();
|
|
204
|
+
this.enable_buttons();
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
this.disable_buttons();
|
|
276
208
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (simulation_mode == "visual") {
|
|
283
|
-
this.simulate_visual(trial, simulation_options, load_callback);
|
|
284
|
-
}
|
|
209
|
+
this.startTime = performance.now();
|
|
210
|
+
if (trial.trial_duration !== null) {
|
|
211
|
+
this.jsPsych.pluginAPI.setTimeout(() => {
|
|
212
|
+
this.end_trial();
|
|
213
|
+
}, trial.trial_duration);
|
|
285
214
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
215
|
+
on_load();
|
|
216
|
+
this.audio.play();
|
|
217
|
+
return new Promise((resolve) => {
|
|
218
|
+
this.trial_complete = resolve;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
enable_buttons() {
|
|
222
|
+
if (this.params.enable_button_after > 0) {
|
|
223
|
+
this.enable_buttons_with_delay(this.params.enable_button_after);
|
|
224
|
+
} else {
|
|
225
|
+
this.enable_buttons_without_delay();
|
|
296
226
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
227
|
+
}
|
|
228
|
+
async simulate(trial, simulation_mode, simulation_options, load_callback) {
|
|
229
|
+
if (simulation_mode == "data-only") {
|
|
230
|
+
load_callback();
|
|
231
|
+
this.simulate_data_only(trial, simulation_options);
|
|
300
232
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const display_element = this.jsPsych.getDisplayElement();
|
|
304
|
-
const respond = () => {
|
|
305
|
-
if (data.rt !== null) {
|
|
306
|
-
this.jsPsych.pluginAPI.clickTarget(display_element.querySelector(`div[data-choice="${data.response}"] button`), data.rt);
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
this.trial(display_element, trial, () => {
|
|
310
|
-
load_callback();
|
|
311
|
-
if (!trial.response_allowed_while_playing) {
|
|
312
|
-
this.audio.addEventListener("ended", respond);
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
respond();
|
|
316
|
-
}
|
|
317
|
-
});
|
|
233
|
+
if (simulation_mode == "visual") {
|
|
234
|
+
this.simulate_visual(trial, simulation_options, load_callback);
|
|
318
235
|
}
|
|
236
|
+
}
|
|
237
|
+
create_simulation_data(trial, simulation_options) {
|
|
238
|
+
const default_data = {
|
|
239
|
+
stimulus: trial.stimulus,
|
|
240
|
+
rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true) + trial.enable_button_after,
|
|
241
|
+
response: this.jsPsych.randomization.randomInt(0, trial.choices.length - 1)
|
|
242
|
+
};
|
|
243
|
+
const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
|
|
244
|
+
this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
|
|
245
|
+
return data;
|
|
246
|
+
}
|
|
247
|
+
simulate_data_only(trial, simulation_options) {
|
|
248
|
+
const data = this.create_simulation_data(trial, simulation_options);
|
|
249
|
+
this.jsPsych.finishTrial(data);
|
|
250
|
+
}
|
|
251
|
+
simulate_visual(trial, simulation_options, load_callback) {
|
|
252
|
+
const data = this.create_simulation_data(trial, simulation_options);
|
|
253
|
+
const display_element = this.jsPsych.getDisplayElement();
|
|
254
|
+
const respond = () => {
|
|
255
|
+
if (data.rt !== null) {
|
|
256
|
+
this.jsPsych.pluginAPI.clickTarget(
|
|
257
|
+
display_element.querySelector(
|
|
258
|
+
`#jspsych-audio-button-response-btngroup [data-choice="${data.response}"]`
|
|
259
|
+
),
|
|
260
|
+
data.rt
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
this.trial(display_element, trial, () => {
|
|
265
|
+
load_callback();
|
|
266
|
+
if (!trial.response_allowed_while_playing) {
|
|
267
|
+
this.audio.addEventListener("ended", respond);
|
|
268
|
+
} else {
|
|
269
|
+
respond();
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
319
273
|
}
|
|
320
274
|
AudioButtonResponsePlugin.info = info;
|
|
321
275
|
|