@jspsych/plugin-tobii-user-position 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js ADDED
@@ -0,0 +1,337 @@
1
+ /**
2
+ * @title Tobii User Position
3
+ * @description jsPsych plugin for Tobii eye tracker user position guide. Displays real-time
4
+ * head position feedback to help participants maintain optimal positioning for eye tracking.
5
+ * @version 1.0.0
6
+ * @author jsPsych Team
7
+ * @see {@link https://github.com/jspsych/jspsych-tobii/tree/main/packages/plugin-tobii-user-position#readme Documentation}
8
+ */
9
+ import { ParameterType } from 'jspsych';
10
+ import { version } from '../package.json';
11
+ import { PositionDisplay } from './position-display';
12
+ const info = {
13
+ name: 'tobii-user-position',
14
+ version: version,
15
+ parameters: {
16
+ /** Duration to show the position guide (ms), null for manual */
17
+ duration: {
18
+ type: ParameterType.INT,
19
+ default: null,
20
+ },
21
+ /** Message to display */
22
+ message: {
23
+ type: ParameterType.STRING,
24
+ default: 'Please position yourself so the indicators are green',
25
+ },
26
+ /** Update interval (ms) */
27
+ update_interval: {
28
+ type: ParameterType.INT,
29
+ default: 100,
30
+ },
31
+ /** Show distance feedback */
32
+ show_distance_feedback: {
33
+ type: ParameterType.BOOL,
34
+ default: true,
35
+ },
36
+ /** Show position feedback */
37
+ show_position_feedback: {
38
+ type: ParameterType.BOOL,
39
+ default: true,
40
+ },
41
+ /** Button text for manual continuation */
42
+ button_text: {
43
+ type: ParameterType.STRING,
44
+ default: 'Continue',
45
+ },
46
+ /** Only show button when position is good */
47
+ require_good_position: {
48
+ type: ParameterType.BOOL,
49
+ default: false,
50
+ },
51
+ /** Background color */
52
+ background_color: {
53
+ type: ParameterType.STRING,
54
+ default: '#f0f0f0',
55
+ },
56
+ /** Good position color */
57
+ good_color: {
58
+ type: ParameterType.STRING,
59
+ default: '#28a745',
60
+ },
61
+ /** Fair position color */
62
+ fair_color: {
63
+ type: ParameterType.STRING,
64
+ default: '#ffc107',
65
+ },
66
+ /** Poor position color */
67
+ poor_color: {
68
+ type: ParameterType.STRING,
69
+ default: '#dc3545',
70
+ },
71
+ /** Button color */
72
+ button_color: {
73
+ type: ParameterType.STRING,
74
+ default: '#007bff',
75
+ },
76
+ /** Button hover color */
77
+ button_hover_color: {
78
+ type: ParameterType.STRING,
79
+ default: '#0056b3',
80
+ },
81
+ /** Font size */
82
+ font_size: {
83
+ type: ParameterType.STRING,
84
+ default: '18px',
85
+ },
86
+ /** Position offset threshold for "good" status (normalized, default 0.15) */
87
+ position_threshold_good: {
88
+ type: ParameterType.FLOAT,
89
+ default: 0.15,
90
+ },
91
+ /** Position offset threshold for "fair" status (normalized, default 0.25) */
92
+ position_threshold_fair: {
93
+ type: ParameterType.FLOAT,
94
+ default: 0.25,
95
+ },
96
+ /** Distance offset threshold for "good" status (normalized, default 0.1) */
97
+ distance_threshold_good: {
98
+ type: ParameterType.FLOAT,
99
+ default: 0.1,
100
+ },
101
+ /** Distance offset threshold for "fair" status (normalized, default 0.2) */
102
+ distance_threshold_fair: {
103
+ type: ParameterType.FLOAT,
104
+ default: 0.2,
105
+ },
106
+ },
107
+ data: {
108
+ /** Average X position during trial */
109
+ average_x: {
110
+ type: ParameterType.FLOAT,
111
+ },
112
+ /** Average Y position during trial */
113
+ average_y: {
114
+ type: ParameterType.FLOAT,
115
+ },
116
+ /** Average Z position (distance) during trial */
117
+ average_z: {
118
+ type: ParameterType.FLOAT,
119
+ },
120
+ /** Whether position was good at end */
121
+ position_good: {
122
+ type: ParameterType.BOOL,
123
+ },
124
+ /** Horizontal position status */
125
+ horizontal_status: {
126
+ type: ParameterType.STRING,
127
+ },
128
+ /** Vertical position status */
129
+ vertical_status: {
130
+ type: ParameterType.STRING,
131
+ },
132
+ /** Distance status */
133
+ distance_status: {
134
+ type: ParameterType.STRING,
135
+ },
136
+ /** Duration of trial */
137
+ rt: {
138
+ type: ParameterType.INT,
139
+ },
140
+ },
141
+ };
142
+ class TobiiUserPositionPlugin {
143
+ constructor(jsPsych) {
144
+ this.jsPsych = jsPsych;
145
+ }
146
+ static removeStyles() {
147
+ const el = document.getElementById('tobii-user-position-styles');
148
+ if (el) {
149
+ el.remove();
150
+ }
151
+ }
152
+ injectStyles(trial) {
153
+ // Remove existing styles so each trial gets its own colors
154
+ TobiiUserPositionPlugin.removeStyles();
155
+ const css = `
156
+ .tobii-user-position-container {
157
+ display: flex;
158
+ flex-direction: column;
159
+ align-items: center;
160
+ justify-content: center;
161
+ width: 100%;
162
+ height: 100vh;
163
+ font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
164
+ font-size: ${trial.font_size};
165
+ }
166
+
167
+ .tobii-user-position-message {
168
+ margin-bottom: 40px;
169
+ text-align: center;
170
+ font-weight: 500;
171
+ color: #333;
172
+ }
173
+
174
+ .tobii-user-position-guide {
175
+ position: relative;
176
+ margin-bottom: 40px;
177
+ }
178
+
179
+ .tobii-position-feedback {
180
+ text-align: center;
181
+ margin-bottom: 30px;
182
+ font-weight: 600;
183
+ font-size: 1.1em;
184
+ }
185
+
186
+ .tobii-user-position-button {
187
+ padding: 12px 32px;
188
+ font-size: 16px;
189
+ font-weight: 500;
190
+ border: none;
191
+ border-radius: 6px;
192
+ background-color: ${trial.button_color};
193
+ color: white;
194
+ cursor: pointer;
195
+ transition: all 0.2s ease;
196
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
197
+ }
198
+
199
+ .tobii-user-position-button:hover:not(:disabled) {
200
+ background-color: ${trial.button_hover_color};
201
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
202
+ transform: translateY(-1px);
203
+ }
204
+
205
+ .tobii-user-position-button:disabled {
206
+ background-color: #ccc;
207
+ cursor: not-allowed;
208
+ opacity: 0.6;
209
+ }
210
+
211
+ .tobii-center-marker {
212
+ position: absolute;
213
+ top: 50%;
214
+ left: 50%;
215
+ transform: translate(-50%, -50%);
216
+ width: 30px;
217
+ height: 30px;
218
+ border: 2px dashed #666;
219
+ border-radius: 50%;
220
+ opacity: 0.5;
221
+ }
222
+ `;
223
+ const styleElement = document.createElement('style');
224
+ styleElement.id = 'tobii-user-position-styles';
225
+ styleElement.textContent = css;
226
+ document.head.appendChild(styleElement);
227
+ }
228
+ trial(display_element, trial) {
229
+ return new Promise((resolve) => {
230
+ // Inject CSS
231
+ this.injectStyles(trial);
232
+ // Check for Tobii extension
233
+ const tobiiExtension = this.jsPsych.extensions.tobii;
234
+ if (!tobiiExtension) {
235
+ throw new Error('Tobii extension not loaded');
236
+ }
237
+ // Create container
238
+ display_element.innerHTML = `
239
+ <div class="tobii-user-position-container">
240
+ </div>
241
+ `;
242
+ const container = display_element.querySelector('.tobii-user-position-container');
243
+ // Create position display
244
+ const positionDisplay = new PositionDisplay(container, {
245
+ message: trial.message,
246
+ showDistanceFeedback: trial.show_distance_feedback,
247
+ showPositionFeedback: trial.show_position_feedback,
248
+ backgroundColor: trial.background_color,
249
+ goodColor: trial.good_color,
250
+ fairColor: trial.fair_color,
251
+ poorColor: trial.poor_color,
252
+ fontSize: trial.font_size,
253
+ positionThresholdGood: trial.position_threshold_good,
254
+ positionThresholdFair: trial.position_threshold_fair,
255
+ distanceThresholdGood: trial.distance_threshold_good,
256
+ distanceThresholdFair: trial.distance_threshold_fair,
257
+ });
258
+ // Add continue button if no duration specified
259
+ let continueButton = null;
260
+ if (trial.duration === null) {
261
+ continueButton = document.createElement('button');
262
+ continueButton.className = 'tobii-user-position-button';
263
+ continueButton.textContent = trial.button_text;
264
+ if (trial.require_good_position) {
265
+ continueButton.disabled = true;
266
+ }
267
+ container.appendChild(continueButton);
268
+ }
269
+ // Track position data
270
+ const positionSamples = [];
271
+ const startTime = performance.now();
272
+ // Update position display periodically
273
+ const updateInterval = setInterval(async () => {
274
+ try {
275
+ const positionData = await tobiiExtension.getUserPosition();
276
+ positionDisplay.updatePosition(positionData);
277
+ // Track position quality
278
+ const quality = positionDisplay.getCurrentQuality(positionData);
279
+ positionSamples.push(quality);
280
+ // Update button state if required
281
+ if (continueButton && trial.require_good_position) {
282
+ continueButton.disabled = !quality.isGoodPosition;
283
+ }
284
+ }
285
+ catch (error) {
286
+ console.error('Error updating user position:', error);
287
+ }
288
+ }, trial.update_interval);
289
+ // Cleanup helper to ensure DOM and styles are always cleaned up
290
+ const cleanup = () => {
291
+ clearInterval(updateInterval);
292
+ positionDisplay.destroy();
293
+ display_element.innerHTML = '';
294
+ TobiiUserPositionPlugin.removeStyles();
295
+ };
296
+ // Handle trial end
297
+ const endTrial = () => {
298
+ // Calculate average position
299
+ const validSamples = positionSamples.filter((s) => s.averageX !== null && s.averageY !== null && s.averageZ !== null);
300
+ let averageX = null;
301
+ let averageY = null;
302
+ let averageZ = null;
303
+ let finalQuality = null;
304
+ if (validSamples.length > 0) {
305
+ averageX = validSamples.reduce((sum, s) => sum + s.averageX, 0) / validSamples.length;
306
+ averageY = validSamples.reduce((sum, s) => sum + s.averageY, 0) / validSamples.length;
307
+ averageZ = validSamples.reduce((sum, s) => sum + s.averageZ, 0) / validSamples.length;
308
+ finalQuality = positionSamples[positionSamples.length - 1];
309
+ }
310
+ const trialData = {
311
+ average_x: averageX,
312
+ average_y: averageY,
313
+ average_z: averageZ,
314
+ position_good: finalQuality?.isGoodPosition ?? false,
315
+ horizontal_status: finalQuality?.horizontalStatus ?? 'poor',
316
+ vertical_status: finalQuality?.verticalStatus ?? 'poor',
317
+ distance_status: finalQuality?.distanceStatus ?? 'poor',
318
+ rt: Math.round(performance.now() - startTime),
319
+ };
320
+ cleanup();
321
+ this.jsPsych.finishTrial(trialData);
322
+ resolve();
323
+ };
324
+ // Set up continue button
325
+ if (continueButton) {
326
+ continueButton.addEventListener('click', endTrial);
327
+ }
328
+ // Set up duration timeout
329
+ if (trial.duration != null) {
330
+ this.jsPsych.pluginAPI.setTimeout(endTrial, trial.duration);
331
+ }
332
+ });
333
+ }
334
+ }
335
+ TobiiUserPositionPlugin.info = info;
336
+ export default TobiiUserPositionPlugin;
337
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.spec.d.ts","sourceRoot":"","sources":["index.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,102 @@
1
+ import { initJsPsych } from 'jspsych';
2
+ import TobiiUserPositionPlugin from '.';
3
+ describe('TobiiUserPositionPlugin', () => {
4
+ describe('static info', () => {
5
+ it('should have correct name', () => {
6
+ expect(TobiiUserPositionPlugin.info.name).toBe('tobii-user-position');
7
+ });
8
+ it('should have version defined', () => {
9
+ expect(TobiiUserPositionPlugin.info.version).toBeDefined();
10
+ });
11
+ });
12
+ describe('parameters', () => {
13
+ const params = TobiiUserPositionPlugin.info.parameters;
14
+ it('should have duration parameter with default null', () => {
15
+ expect(params.duration).toBeDefined();
16
+ expect(params.duration.default).toBeNull();
17
+ });
18
+ it('should have message parameter', () => {
19
+ expect(params.message).toBeDefined();
20
+ expect(typeof params.message.default).toBe('string');
21
+ });
22
+ it('should have update_interval parameter with default 100', () => {
23
+ expect(params.update_interval).toBeDefined();
24
+ expect(params.update_interval.default).toBe(100);
25
+ });
26
+ it('should have show_distance_feedback parameter', () => {
27
+ expect(params.show_distance_feedback).toBeDefined();
28
+ expect(params.show_distance_feedback.default).toBe(true);
29
+ });
30
+ it('should have show_position_feedback parameter', () => {
31
+ expect(params.show_position_feedback).toBeDefined();
32
+ expect(params.show_position_feedback.default).toBe(true);
33
+ });
34
+ it('should have button_text parameter', () => {
35
+ expect(params.button_text).toBeDefined();
36
+ expect(params.button_text.default).toBe('Continue');
37
+ });
38
+ it('should have require_good_position parameter', () => {
39
+ expect(params.require_good_position).toBeDefined();
40
+ expect(params.require_good_position.default).toBe(false);
41
+ });
42
+ it('should have color parameters', () => {
43
+ expect(params.background_color).toBeDefined();
44
+ expect(params.good_color).toBeDefined();
45
+ expect(params.fair_color).toBeDefined();
46
+ expect(params.poor_color).toBeDefined();
47
+ expect(params.button_color).toBeDefined();
48
+ expect(params.button_hover_color).toBeDefined();
49
+ });
50
+ it('should have position_threshold_good parameter with default 0.15', () => {
51
+ expect(params.position_threshold_good).toBeDefined();
52
+ expect(params.position_threshold_good.default).toBe(0.15);
53
+ });
54
+ it('should have position_threshold_fair parameter with default 0.25', () => {
55
+ expect(params.position_threshold_fair).toBeDefined();
56
+ expect(params.position_threshold_fair.default).toBe(0.25);
57
+ });
58
+ it('should have distance_threshold_good parameter with default 0.1', () => {
59
+ expect(params.distance_threshold_good).toBeDefined();
60
+ expect(params.distance_threshold_good.default).toBe(0.1);
61
+ });
62
+ it('should have distance_threshold_fair parameter with default 0.2', () => {
63
+ expect(params.distance_threshold_fair).toBeDefined();
64
+ expect(params.distance_threshold_fair.default).toBe(0.2);
65
+ });
66
+ it('should have font_size parameter with default 18px', () => {
67
+ expect(params.font_size).toBeDefined();
68
+ expect(params.font_size.default).toBe('18px');
69
+ });
70
+ });
71
+ describe('data specification', () => {
72
+ const data = TobiiUserPositionPlugin.info.data;
73
+ it('should have average_x data field', () => {
74
+ expect(data.average_x).toBeDefined();
75
+ });
76
+ it('should have average_y data field', () => {
77
+ expect(data.average_y).toBeDefined();
78
+ });
79
+ it('should have average_z data field', () => {
80
+ expect(data.average_z).toBeDefined();
81
+ });
82
+ it('should have position_good data field', () => {
83
+ expect(data.position_good).toBeDefined();
84
+ });
85
+ it('should have status data fields', () => {
86
+ expect(data.horizontal_status).toBeDefined();
87
+ expect(data.vertical_status).toBeDefined();
88
+ expect(data.distance_status).toBeDefined();
89
+ });
90
+ it('should have rt data field', () => {
91
+ expect(data.rt).toBeDefined();
92
+ });
93
+ });
94
+ describe('instantiation', () => {
95
+ it('should instantiate without errors', () => {
96
+ const jsPsych = initJsPsych();
97
+ const plugin = new TobiiUserPositionPlugin(jsPsych);
98
+ expect(plugin).toBeDefined();
99
+ });
100
+ });
101
+ });
102
+ //# sourceMappingURL=index.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.spec.js","sourceRoot":"","sources":["index.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+ import { initJsPsych } from 'jspsych';
2
+
3
+ import TobiiUserPositionPlugin from '.';
4
+
5
+ describe('TobiiUserPositionPlugin', () => {
6
+ describe('static info', () => {
7
+ it('should have correct name', () => {
8
+ expect(TobiiUserPositionPlugin.info.name).toBe('tobii-user-position');
9
+ });
10
+
11
+ it('should have version defined', () => {
12
+ expect(TobiiUserPositionPlugin.info.version).toBeDefined();
13
+ });
14
+ });
15
+
16
+ describe('parameters', () => {
17
+ const params = TobiiUserPositionPlugin.info.parameters;
18
+
19
+ it('should have duration parameter with default null', () => {
20
+ expect(params.duration).toBeDefined();
21
+ expect(params.duration.default).toBeNull();
22
+ });
23
+
24
+ it('should have message parameter', () => {
25
+ expect(params.message).toBeDefined();
26
+ expect(typeof params.message.default).toBe('string');
27
+ });
28
+
29
+ it('should have update_interval parameter with default 100', () => {
30
+ expect(params.update_interval).toBeDefined();
31
+ expect(params.update_interval.default).toBe(100);
32
+ });
33
+
34
+ it('should have show_distance_feedback parameter', () => {
35
+ expect(params.show_distance_feedback).toBeDefined();
36
+ expect(params.show_distance_feedback.default).toBe(true);
37
+ });
38
+
39
+ it('should have show_position_feedback parameter', () => {
40
+ expect(params.show_position_feedback).toBeDefined();
41
+ expect(params.show_position_feedback.default).toBe(true);
42
+ });
43
+
44
+ it('should have button_text parameter', () => {
45
+ expect(params.button_text).toBeDefined();
46
+ expect(params.button_text.default).toBe('Continue');
47
+ });
48
+
49
+ it('should have require_good_position parameter', () => {
50
+ expect(params.require_good_position).toBeDefined();
51
+ expect(params.require_good_position.default).toBe(false);
52
+ });
53
+
54
+ it('should have color parameters', () => {
55
+ expect(params.background_color).toBeDefined();
56
+ expect(params.good_color).toBeDefined();
57
+ expect(params.fair_color).toBeDefined();
58
+ expect(params.poor_color).toBeDefined();
59
+ expect(params.button_color).toBeDefined();
60
+ expect(params.button_hover_color).toBeDefined();
61
+ });
62
+
63
+ it('should have position_threshold_good parameter with default 0.15', () => {
64
+ expect(params.position_threshold_good).toBeDefined();
65
+ expect(params.position_threshold_good.default).toBe(0.15);
66
+ });
67
+
68
+ it('should have position_threshold_fair parameter with default 0.25', () => {
69
+ expect(params.position_threshold_fair).toBeDefined();
70
+ expect(params.position_threshold_fair.default).toBe(0.25);
71
+ });
72
+
73
+ it('should have distance_threshold_good parameter with default 0.1', () => {
74
+ expect(params.distance_threshold_good).toBeDefined();
75
+ expect(params.distance_threshold_good.default).toBe(0.1);
76
+ });
77
+
78
+ it('should have distance_threshold_fair parameter with default 0.2', () => {
79
+ expect(params.distance_threshold_fair).toBeDefined();
80
+ expect(params.distance_threshold_fair.default).toBe(0.2);
81
+ });
82
+
83
+ it('should have font_size parameter with default 18px', () => {
84
+ expect(params.font_size).toBeDefined();
85
+ expect(params.font_size.default).toBe('18px');
86
+ });
87
+ });
88
+
89
+ describe('data specification', () => {
90
+ const data = TobiiUserPositionPlugin.info.data;
91
+
92
+ it('should have average_x data field', () => {
93
+ expect(data.average_x).toBeDefined();
94
+ });
95
+
96
+ it('should have average_y data field', () => {
97
+ expect(data.average_y).toBeDefined();
98
+ });
99
+
100
+ it('should have average_z data field', () => {
101
+ expect(data.average_z).toBeDefined();
102
+ });
103
+
104
+ it('should have position_good data field', () => {
105
+ expect(data.position_good).toBeDefined();
106
+ });
107
+
108
+ it('should have status data fields', () => {
109
+ expect(data.horizontal_status).toBeDefined();
110
+ expect(data.vertical_status).toBeDefined();
111
+ expect(data.distance_status).toBeDefined();
112
+ });
113
+
114
+ it('should have rt data field', () => {
115
+ expect(data.rt).toBeDefined();
116
+ });
117
+ });
118
+
119
+ describe('instantiation', () => {
120
+ it('should instantiate without errors', () => {
121
+ const jsPsych = initJsPsych();
122
+ const plugin = new TobiiUserPositionPlugin(jsPsych);
123
+ expect(plugin).toBeDefined();
124
+ });
125
+ });
126
+ });