-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathindex.ts
More file actions
179 lines (153 loc) · 5.66 KB
/
Copy pathindex.ts
File metadata and controls
179 lines (153 loc) · 5.66 KB
1
2
3
4
5
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych";
import { version } from "../package.json";
const info = <const>{
name: "button-click-counter",
version: version,
parameters: {
/** The label to display on the button. */
button_label: {
type: ParameterType.STRING,
default: "Click me!",
},
/** The key that the participant must press to end the trial. If null, any key press will end the trial. */
key_to_advance: {
type: ParameterType.KEY,
default: null,
},
/** HTML content to display below the button and counter. Can be used to provide instructions. */
prompt: {
type: ParameterType.HTML_STRING,
default: null,
},
},
data: {
/** The number of times the button was clicked during the trial. */
button_clicks: {
type: ParameterType.INT,
},
/** The key that was pressed to end the trial. */
key_pressed: {
type: ParameterType.STRING,
},
/** The response time in milliseconds from the start of the trial until the key was pressed. */
rt: {
type: ParameterType.INT,
},
},
// When you run build on your plugin, citations will be generated here based on the information in the CITATION.cff file.
citations: '__CITATIONS__',
};
type Info = typeof info;
/**
* **button-click-counter**
*
* A jsPsych plugin that displays a button along with a running count of how many times it has been
* clicked. The trial ends when the participant presses a key. This plugin is useful for training
* contributors to understand how jsPsych plugins work.
*
* @author jsPsych Contributors
* @see {@link https://github.com/jspsych/git-training}
*/
class ButtonClickCounterPlugin implements JsPsychPlugin<Info> {
static info = info;
constructor(private jsPsych: JsPsych) {}
trial(display_element: HTMLElement, trial: TrialType<Info>) {
let click_count = 0;
const start_time = performance.now();
// Build the HTML structure using DOM APIs to avoid XSS with string parameters
const wrapper = document.createElement("div");
wrapper.id = "jspsych-button-click-counter-wrapper";
const countPara = document.createElement("p");
countPara.id = "jspsych-button-click-counter-count";
countPara.appendChild(document.createTextNode("Click till you hit 100, then press Enter to find your time in milliseconds: "));
const countSpan = document.createElement("span");
countSpan.id = "jspsych-button-click-counter-value";
countSpan.textContent = "0";
countPara.appendChild(countSpan);
wrapper.appendChild(countPara);
const btn = document.createElement("button");
btn.id = "jspsych-button-click-counter-btn";
btn.className = "jspsych-btn";
btn.textContent = trial.button_label;
wrapper.appendChild(btn);
if (trial.prompt !== null) {
const promptDiv = document.createElement("div");
promptDiv.id = "jspsych-button-click-counter-prompt";
promptDiv.innerHTML = trial.prompt;
wrapper.appendChild(promptDiv);
}
display_element.appendChild(wrapper);
btn.addEventListener("click", () => {
click_count++;
display_element.querySelector("#jspsych-button-click-counter-value").textContent =
click_count.toString();
});
// End trial function
const end_trial = (key: string) => {
this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
const rt = Math.round(performance.now() - start_time);
display_element.innerHTML = "";
this.jsPsych.finishTrial({
button_clicks: click_count,
key_pressed: key,
rt: rt,
});
};
// Listen for key press
this.jsPsych.pluginAPI.getKeyboardResponse({
callback_function: (info: { key: string; rt: number }) => {
end_trial(info.key);
},
valid_responses: trial.key_to_advance === null ? "ALL_KEYS" : [trial.key_to_advance],
rt_method: "performance",
persist: false,
});
}
simulate(
trial: TrialType<Info>,
simulation_mode,
simulation_options: any,
load_callback: () => void
) {
if (simulation_mode == "data-only") {
load_callback();
this.simulate_data_only(trial, simulation_options);
}
if (simulation_mode == "visual") {
this.simulate_visual(trial, simulation_options, load_callback);
}
}
private create_simulation_data(trial: TrialType<Info>, simulation_options) {
const default_data = {
button_clicks: this.jsPsych.randomization.randomInt(0, 10),
key_pressed:
trial.key_to_advance === null
? this.jsPsych.pluginAPI.getValidKey("ALL_KEYS")
: trial.key_to_advance,
rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
};
return this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
}
private simulate_data_only(trial: TrialType<Info>, simulation_options) {
const data = this.create_simulation_data(trial, simulation_options);
this.jsPsych.finishTrial(data);
}
private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {
const data = this.create_simulation_data(trial, simulation_options);
const display_element = this.jsPsych.getDisplayElement();
this.trial(display_element, trial);
load_callback();
// Simulate some button clicks
const btn = display_element.querySelector<HTMLButtonElement>(
"#jspsych-button-click-counter-btn"
);
for (let i = 0; i < data.button_clicks; i++) {
this.jsPsych.pluginAPI.clickTarget(btn);
}
// Simulate key press to end trial
if (data.rt !== null) {
this.jsPsych.pluginAPI.pressKey(data.key_pressed, data.rt);
}
}
}
export default ButtonClickCounterPlugin;