An easy-to-use platform for EEG experimentation in the classroom
1import * as lab from 'lab.js';
2
3export function initMultitaskingResponseHandlers(
4 this: lab.flow.Loop<Record<string, unknown>>
5) {
6 if (!this.options.events) return;
7
8 this.options.events.keydown = (e: Event) => {
9 const { code } = e as unknown as { code: string };
10 if (code === 'KeyQ') {
11 this.data.skipTraining = true;
12 this.end();
13 }
14
15 if (code === 'ArrowLeft' || code === 'ArrowRight') {
16 const instructions =
17 document.querySelectorAll<HTMLElement>('div.instruction');
18 let notFound = true;
19 instructions.forEach((i) => {
20 if (i.style.display === 'block' && notFound) {
21 const cur_id = parseInt(i.id.split('screen_')[1], 10);
22 let next_id;
23 if (code === 'ArrowLeft') {
24 next_id = cur_id - 1;
25 }
26 if (code === 'ArrowRight') {
27 next_id = cur_id + 1;
28 }
29 if (next_id > 0 && next_id <= 10) {
30 i.style.display = 'none';
31 next_id = `screen_${next_id}`;
32
33 const nextElement = document.querySelector<HTMLElement>(
34 `#${next_id}`
35 );
36 if (nextElement) {
37 nextElement.style.display = 'block';
38 }
39 notFound = false;
40 }
41 }
42 });
43 }
44 };
45}
46
47export function initTasks(this: lab.flow.Loop<Record<string, unknown>>) {
48 function shuffle(a) {
49 let j;
50 let x;
51 for (let i = a.length - 1; i > 0; i--) {
52 j = Math.floor(Math.random() * (i + 1));
53 x = a[i];
54 a[i] = a[j];
55 a[j] = x;
56 }
57 return a;
58 }
59
60 let tasksParameters: {
61 type: string;
62 dots: number;
63 form: string;
64 cor_response: string;
65 }[] = [];
66 const blocks: string[] =
67 this.parameters.block === 'mixed'
68 ? ['shape', 'filling']
69 : [this.parameters.block, this.parameters.block];
70
71 function trialConstructor(
72 block: string,
73 dots: number,
74 form: string,
75 cor_response: string
76 ) {
77 return {
78 type: block,
79 dots,
80 form,
81 cor_response,
82 };
83 }
84
85 const numberBlocks = Math.ceil(this.parameters.num_trials / 4);
86
87 for (let i = 1; i <= numberBlocks; i++) {
88 for (const block of blocks) {
89 if (block === 'shape') {
90 tasksParameters = tasksParameters.concat([
91 trialConstructor(block, 2, 'diamond', 'b'),
92 trialConstructor(block, 2, 'square', 'n'),
93 trialConstructor(block, 3, 'diamond', 'b'),
94 trialConstructor(block, 3, 'square', 'n'),
95 ]);
96 } else if (block === 'filling') {
97 tasksParameters = tasksParameters.concat([
98 trialConstructor(block, 2, 'diamond', 'b'),
99 trialConstructor(block, 2, 'square', 'b'),
100 trialConstructor(block, 3, 'diamond', 'n'),
101 trialConstructor(block, 3, 'square', 'n'),
102 ]);
103 }
104 }
105 }
106
107 const tasksParametersShuffled = shuffle(tasksParameters);
108 // assign options values to parameters of this task
109 this.options.templateParameters = tasksParametersShuffled.slice(
110 0,
111 this.parameters.num_trials
112 );
113}
114
115export function triggerEEGCallback(this: lab.core.Component) {
116 this.parameters.callbackForEEG(
117 // TODO: is this parameter ever handled?
118 this.parameters.cond === 'Switching' ? 1 : 2
119 );
120 this.data.correct = 'empty';
121}
122
123export function initTaskScreen(this: lab.core.Component) {
124 const { id } = this.options;
125 if (!id) return;
126
127 this.data.trial_number =
128 1 + parseInt(id.split('_')[id.split('_').length - 2], 10);
129 this.data.condition = this.parameters.cond;
130 this.data.reaction_time = this.state.duration;
131
132 if (this.state.response === this.parameters.cor_response) {
133 this.data.correct_response = true;
134 } else {
135 this.data.correct_response = false;
136 }
137
138 if (this.parameters.task === 'main') {
139 this.data.response_given = this.state.correct === 'empty' ? 'no' : 'yes';
140 } else {
141 this.data.phase = 'practice';
142 }
143}