@jspsych/plugin-tobii-calibration 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/README.md +137 -0
- package/dist/index.browser.js +840 -0
- package/dist/index.browser.js.map +1 -0
- package/dist/index.browser.min.js +206 -0
- package/dist/index.browser.min.js.map +1 -0
- package/dist/index.cjs +839 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +300 -0
- package/dist/index.js +837 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
- package/src/calibration-display.d.ts +78 -0
- package/src/calibration-display.d.ts.map +1 -0
- package/src/calibration-display.js +293 -0
- package/src/calibration-display.js.map +1 -0
- package/src/calibration-display.ts +359 -0
- package/src/index.d.ts +297 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.js +597 -0
- package/src/index.js.map +1 -0
- package/src/index.spec.d.ts +2 -0
- package/src/index.spec.d.ts.map +1 -0
- package/src/index.spec.js +125 -0
- package/src/index.spec.js.map +1 -0
- package/src/index.spec.ts +156 -0
- package/src/index.ts +646 -0
- package/src/types.d.ts +52 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.js +5 -0
- package/src/types.js.map +1 -0
- package/src/types.ts +53 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calibration display component
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CalibrationPoint, CalibrationParameters } from './types';
|
|
6
|
+
|
|
7
|
+
export class CalibrationDisplay {
|
|
8
|
+
private container: HTMLElement;
|
|
9
|
+
private currentPoint: HTMLElement | null = null;
|
|
10
|
+
private progressElement: HTMLElement | null = null;
|
|
11
|
+
private currentX: number = 0.5; // Start at center
|
|
12
|
+
private currentY: number = 0.5;
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
private displayElement: HTMLElement,
|
|
16
|
+
private params: CalibrationParameters
|
|
17
|
+
) {
|
|
18
|
+
this.container = this.createContainer();
|
|
19
|
+
this.displayElement.appendChild(this.container);
|
|
20
|
+
|
|
21
|
+
if (params.show_progress) {
|
|
22
|
+
this.progressElement = this.createProgressIndicator();
|
|
23
|
+
this.displayElement.appendChild(this.progressElement);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create container element
|
|
29
|
+
*/
|
|
30
|
+
private createContainer(): HTMLElement {
|
|
31
|
+
const container = document.createElement('div');
|
|
32
|
+
container.className = 'tobii-calibration-container';
|
|
33
|
+
return container;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Create progress indicator
|
|
38
|
+
*/
|
|
39
|
+
private createProgressIndicator(): HTMLElement {
|
|
40
|
+
const progress = document.createElement('div');
|
|
41
|
+
progress.className = 'tobii-calibration-progress';
|
|
42
|
+
progress.setAttribute('role', 'status');
|
|
43
|
+
progress.setAttribute('aria-live', 'polite');
|
|
44
|
+
return progress;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Show instructions
|
|
49
|
+
*/
|
|
50
|
+
async showInstructions(): Promise<void> {
|
|
51
|
+
const wrapper = document.createElement('div');
|
|
52
|
+
wrapper.className = 'tobii-calibration-instructions';
|
|
53
|
+
wrapper.setAttribute('role', 'dialog');
|
|
54
|
+
wrapper.setAttribute('aria-label', 'Eye tracker calibration instructions');
|
|
55
|
+
|
|
56
|
+
const content = document.createElement('div');
|
|
57
|
+
content.className = 'instructions-content';
|
|
58
|
+
|
|
59
|
+
const heading = document.createElement('h2');
|
|
60
|
+
heading.textContent = 'Eye Tracker Calibration';
|
|
61
|
+
content.appendChild(heading);
|
|
62
|
+
|
|
63
|
+
const paragraph = document.createElement('p');
|
|
64
|
+
paragraph.innerHTML =
|
|
65
|
+
this.params.instructions || 'Look at each point and follow the instructions.';
|
|
66
|
+
content.appendChild(paragraph);
|
|
67
|
+
|
|
68
|
+
if (this.params.calibration_mode === 'click') {
|
|
69
|
+
const button = document.createElement('button');
|
|
70
|
+
button.className = 'calibration-start-btn';
|
|
71
|
+
button.textContent = this.params.button_text || 'Start Calibration';
|
|
72
|
+
content.appendChild(button);
|
|
73
|
+
} else {
|
|
74
|
+
const autoMsg = document.createElement('p');
|
|
75
|
+
autoMsg.textContent = 'Starting in a moment...';
|
|
76
|
+
content.appendChild(autoMsg);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
wrapper.appendChild(content);
|
|
80
|
+
this.container.appendChild(wrapper);
|
|
81
|
+
|
|
82
|
+
if (this.params.calibration_mode === 'click') {
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
const button = wrapper.querySelector('button');
|
|
85
|
+
button?.addEventListener('click', () => {
|
|
86
|
+
wrapper.remove();
|
|
87
|
+
resolve();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
} else {
|
|
91
|
+
await this.delay(this.params.instruction_display_duration || 3000);
|
|
92
|
+
wrapper.remove();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Initialize the traveling point at screen center
|
|
98
|
+
*/
|
|
99
|
+
async initializePoint(): Promise<void> {
|
|
100
|
+
if (this.currentPoint) return;
|
|
101
|
+
|
|
102
|
+
this.currentPoint = document.createElement('div');
|
|
103
|
+
this.currentPoint.className = 'tobii-calibration-point';
|
|
104
|
+
this.currentPoint.setAttribute('role', 'img');
|
|
105
|
+
this.currentPoint.setAttribute('aria-label', 'Calibration target point');
|
|
106
|
+
|
|
107
|
+
// Start at center
|
|
108
|
+
const x = 0.5 * window.innerWidth;
|
|
109
|
+
const y = 0.5 * window.innerHeight;
|
|
110
|
+
this.currentX = 0.5;
|
|
111
|
+
this.currentY = 0.5;
|
|
112
|
+
|
|
113
|
+
Object.assign(this.currentPoint.style, {
|
|
114
|
+
left: `${x}px`,
|
|
115
|
+
top: `${y}px`,
|
|
116
|
+
width: `${this.params.point_size || 20}px`,
|
|
117
|
+
height: `${this.params.point_size || 20}px`,
|
|
118
|
+
backgroundColor: this.params.point_color || '#ff0000',
|
|
119
|
+
transition: 'none',
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
this.container.appendChild(this.currentPoint);
|
|
123
|
+
|
|
124
|
+
// Brief pause to show point at center before traveling
|
|
125
|
+
await this.delay(this.params.zoom_duration || 300);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Travel to the next point location with smooth animation
|
|
130
|
+
*/
|
|
131
|
+
async travelToPoint(point: CalibrationPoint, index: number, total: number): Promise<void> {
|
|
132
|
+
if (!this.currentPoint) {
|
|
133
|
+
await this.initializePoint();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Update progress
|
|
137
|
+
if (this.progressElement) {
|
|
138
|
+
this.progressElement.textContent = `Point ${index + 1} of ${total}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Update aria-label with current point number
|
|
142
|
+
this.currentPoint!.setAttribute(
|
|
143
|
+
'aria-label',
|
|
144
|
+
`Calibration target point ${index + 1} of ${total}`
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Calculate travel distance for dynamic duration
|
|
148
|
+
const dx = point.x - this.currentX;
|
|
149
|
+
const dy = point.y - this.currentY;
|
|
150
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
151
|
+
|
|
152
|
+
// Travel duration: 150ms base + 200ms per normalized unit distance (quick travel)
|
|
153
|
+
const travelDuration = Math.max(150, Math.min(400, 150 + distance * 200));
|
|
154
|
+
|
|
155
|
+
// Convert normalized coordinates to pixels
|
|
156
|
+
const x = point.x * window.innerWidth;
|
|
157
|
+
const y = point.y * window.innerHeight;
|
|
158
|
+
|
|
159
|
+
// Set up travel transition
|
|
160
|
+
this.currentPoint!.style.transition = `left ${travelDuration}ms ease-in-out, top ${travelDuration}ms ease-in-out`;
|
|
161
|
+
this.currentPoint!.classList.remove(
|
|
162
|
+
'animation-explosion',
|
|
163
|
+
'animation-shrink',
|
|
164
|
+
'animation-pulse',
|
|
165
|
+
'animation-zoom-out',
|
|
166
|
+
'animation-zoom-in'
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
// Move to new position
|
|
170
|
+
this.currentPoint!.style.left = `${x}px`;
|
|
171
|
+
this.currentPoint!.style.top = `${y}px`;
|
|
172
|
+
|
|
173
|
+
// Update current position
|
|
174
|
+
this.currentX = point.x;
|
|
175
|
+
this.currentY = point.y;
|
|
176
|
+
|
|
177
|
+
// Wait for travel to complete
|
|
178
|
+
await this.delay(travelDuration);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Play zoom out animation (point grows larger)
|
|
183
|
+
*/
|
|
184
|
+
async playZoomOut(): Promise<void> {
|
|
185
|
+
if (!this.currentPoint) return;
|
|
186
|
+
|
|
187
|
+
this.currentPoint.style.transition = 'none';
|
|
188
|
+
this.currentPoint.classList.remove(
|
|
189
|
+
'animation-shrink',
|
|
190
|
+
'animation-pulse',
|
|
191
|
+
'animation-explosion',
|
|
192
|
+
'animation-zoom-in'
|
|
193
|
+
);
|
|
194
|
+
this.currentPoint.classList.add('animation-zoom-out');
|
|
195
|
+
|
|
196
|
+
await this.delay(this.params.zoom_duration || 300);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Play zoom in animation (point shrinks to fixation size)
|
|
201
|
+
*/
|
|
202
|
+
async playZoomIn(): Promise<void> {
|
|
203
|
+
if (!this.currentPoint) return;
|
|
204
|
+
|
|
205
|
+
this.currentPoint.classList.remove('animation-zoom-out');
|
|
206
|
+
this.currentPoint.classList.add('animation-zoom-in');
|
|
207
|
+
|
|
208
|
+
await this.delay(this.params.zoom_duration || 300);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Play explosion animation on the current point
|
|
213
|
+
*/
|
|
214
|
+
async playExplosion(success: boolean): Promise<void> {
|
|
215
|
+
if (!this.currentPoint) return;
|
|
216
|
+
|
|
217
|
+
// Remove any existing animation classes
|
|
218
|
+
this.currentPoint.classList.remove('animation-pulse', 'animation-shrink');
|
|
219
|
+
|
|
220
|
+
// Add explosion animation class
|
|
221
|
+
this.currentPoint.classList.add('animation-explosion');
|
|
222
|
+
|
|
223
|
+
// Change color based on success
|
|
224
|
+
if (success) {
|
|
225
|
+
this.currentPoint.style.backgroundColor = '#4ade80'; // green
|
|
226
|
+
} else {
|
|
227
|
+
this.currentPoint.style.backgroundColor = '#f87171'; // red
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Wait for animation to complete
|
|
231
|
+
await this.delay(this.params.explosion_duration || 400);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Hide current calibration point (removes element)
|
|
236
|
+
*/
|
|
237
|
+
async hidePoint(): Promise<void> {
|
|
238
|
+
if (this.currentPoint) {
|
|
239
|
+
this.currentPoint.remove();
|
|
240
|
+
this.currentPoint = null;
|
|
241
|
+
}
|
|
242
|
+
await this.delay(200);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Reset point state after explosion (keeps element for continued travel)
|
|
247
|
+
*/
|
|
248
|
+
async resetPointAfterExplosion(): Promise<void> {
|
|
249
|
+
if (!this.currentPoint) return;
|
|
250
|
+
|
|
251
|
+
// Reset to normal size and opacity for next travel
|
|
252
|
+
this.currentPoint.classList.remove('animation-explosion');
|
|
253
|
+
this.currentPoint.style.transform = 'translate(-50%, -50%) scale(1)';
|
|
254
|
+
this.currentPoint.style.opacity = '1';
|
|
255
|
+
this.currentPoint.style.backgroundColor = this.params.point_color || '#ff0000';
|
|
256
|
+
|
|
257
|
+
await this.delay(50);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Wait for user click (in click mode)
|
|
262
|
+
*/
|
|
263
|
+
waitForClick(): Promise<void> {
|
|
264
|
+
return new Promise((resolve) => {
|
|
265
|
+
const handleClick = () => {
|
|
266
|
+
document.removeEventListener('click', handleClick);
|
|
267
|
+
resolve();
|
|
268
|
+
};
|
|
269
|
+
document.addEventListener('click', handleClick);
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Show calibration result
|
|
275
|
+
* @param success Whether calibration succeeded
|
|
276
|
+
* @param canRetry Whether a retry button should be shown on failure
|
|
277
|
+
* @returns 'retry' if user chose to retry, 'continue' otherwise
|
|
278
|
+
*/
|
|
279
|
+
async showResult(success: boolean, canRetry: boolean = false): Promise<'retry' | 'continue'> {
|
|
280
|
+
const result = document.createElement('div');
|
|
281
|
+
result.className = 'tobii-calibration-result';
|
|
282
|
+
result.setAttribute('role', 'alert');
|
|
283
|
+
result.setAttribute('aria-live', 'assertive');
|
|
284
|
+
|
|
285
|
+
if (success) {
|
|
286
|
+
result.innerHTML = `
|
|
287
|
+
<div class="tobii-calibration-result-content success">
|
|
288
|
+
<h2>Calibration Successful!</h2>
|
|
289
|
+
<p>Continuing automatically...</p>
|
|
290
|
+
</div>
|
|
291
|
+
`;
|
|
292
|
+
|
|
293
|
+
this.container.appendChild(result);
|
|
294
|
+
|
|
295
|
+
// Auto-advance after showing result
|
|
296
|
+
await this.delay(this.params.success_display_duration || 2000);
|
|
297
|
+
result.remove();
|
|
298
|
+
return 'continue';
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Failure case: show buttons
|
|
302
|
+
const buttonsHTML = canRetry
|
|
303
|
+
? `<button class="calibration-retry-btn">Retry</button>
|
|
304
|
+
<button class="calibration-continue-btn" style="margin-left: 10px;">Continue</button>`
|
|
305
|
+
: `<button class="calibration-continue-btn">Continue</button>`;
|
|
306
|
+
|
|
307
|
+
result.innerHTML = `
|
|
308
|
+
<div class="tobii-calibration-result-content error">
|
|
309
|
+
<h2>Calibration Failed</h2>
|
|
310
|
+
<p>Please try again or continue.</p>
|
|
311
|
+
${buttonsHTML}
|
|
312
|
+
</div>
|
|
313
|
+
`;
|
|
314
|
+
|
|
315
|
+
this.container.appendChild(result);
|
|
316
|
+
|
|
317
|
+
return new Promise((resolve) => {
|
|
318
|
+
const retryBtn = result.querySelector('.calibration-retry-btn');
|
|
319
|
+
const continueBtn = result.querySelector('.calibration-continue-btn');
|
|
320
|
+
|
|
321
|
+
retryBtn?.addEventListener('click', () => {
|
|
322
|
+
result.remove();
|
|
323
|
+
resolve('retry');
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
continueBtn?.addEventListener('click', () => {
|
|
327
|
+
result.remove();
|
|
328
|
+
resolve('continue');
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Reset display state for a retry attempt
|
|
335
|
+
*/
|
|
336
|
+
resetForRetry(): void {
|
|
337
|
+
this.container.innerHTML = '';
|
|
338
|
+
this.currentPoint = null;
|
|
339
|
+
this.currentX = 0.5;
|
|
340
|
+
this.currentY = 0.5;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Clear display
|
|
345
|
+
*/
|
|
346
|
+
clear(): void {
|
|
347
|
+
this.container.innerHTML = '';
|
|
348
|
+
if (this.progressElement) {
|
|
349
|
+
this.progressElement.textContent = '';
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Delay helper
|
|
355
|
+
*/
|
|
356
|
+
private delay(ms: number): Promise<void> {
|
|
357
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
358
|
+
}
|
|
359
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title Tobii Calibration
|
|
3
|
+
* @description jsPsych plugin for Tobii eye tracker calibration. Provides a visual
|
|
4
|
+
* calibration procedure with animated points and real-time feedback.
|
|
5
|
+
* @version 1.0.0
|
|
6
|
+
* @author jsPsych Team
|
|
7
|
+
* @see {@link https://github.com/jspsych/jspsych-tobii/tree/main/packages/plugin-tobii-calibration#readme Documentation}
|
|
8
|
+
*/
|
|
9
|
+
import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from 'jspsych';
|
|
10
|
+
declare const info: {
|
|
11
|
+
readonly name: "tobii-calibration";
|
|
12
|
+
readonly version: string;
|
|
13
|
+
readonly parameters: {
|
|
14
|
+
/** Number of calibration points (5 or 9) */
|
|
15
|
+
readonly calibration_points: {
|
|
16
|
+
readonly type: ParameterType.INT;
|
|
17
|
+
readonly default: 9;
|
|
18
|
+
};
|
|
19
|
+
/** Calibration mode: click or view */
|
|
20
|
+
readonly calibration_mode: {
|
|
21
|
+
readonly type: ParameterType.STRING;
|
|
22
|
+
readonly default: "view";
|
|
23
|
+
};
|
|
24
|
+
/** Size of calibration points in pixels */
|
|
25
|
+
readonly point_size: {
|
|
26
|
+
readonly type: ParameterType.INT;
|
|
27
|
+
readonly default: 20;
|
|
28
|
+
};
|
|
29
|
+
/** Color of calibration points */
|
|
30
|
+
readonly point_color: {
|
|
31
|
+
readonly type: ParameterType.STRING;
|
|
32
|
+
readonly default: "#ff0000";
|
|
33
|
+
};
|
|
34
|
+
/** Duration to show each point before data collection (ms) - allows user to fixate */
|
|
35
|
+
readonly point_duration: {
|
|
36
|
+
readonly type: ParameterType.INT;
|
|
37
|
+
readonly default: 500;
|
|
38
|
+
};
|
|
39
|
+
/** Show progress indicator */
|
|
40
|
+
readonly show_progress: {
|
|
41
|
+
readonly type: ParameterType.BOOL;
|
|
42
|
+
readonly default: true;
|
|
43
|
+
};
|
|
44
|
+
/** Custom calibration points */
|
|
45
|
+
readonly custom_points: {
|
|
46
|
+
readonly type: ParameterType.COMPLEX;
|
|
47
|
+
readonly default: null;
|
|
48
|
+
};
|
|
49
|
+
/** Instructions text */
|
|
50
|
+
readonly instructions: {
|
|
51
|
+
readonly type: ParameterType.STRING;
|
|
52
|
+
readonly default: "Look at each point as it appears on the screen. Keep your gaze fixed on each point until it disappears.";
|
|
53
|
+
};
|
|
54
|
+
/** Button text for click mode */
|
|
55
|
+
readonly button_text: {
|
|
56
|
+
readonly type: ParameterType.STRING;
|
|
57
|
+
readonly default: "Start Calibration";
|
|
58
|
+
};
|
|
59
|
+
/** Background color of the calibration container */
|
|
60
|
+
readonly background_color: {
|
|
61
|
+
readonly type: ParameterType.STRING;
|
|
62
|
+
readonly default: "#808080";
|
|
63
|
+
};
|
|
64
|
+
/** Primary button color */
|
|
65
|
+
readonly button_color: {
|
|
66
|
+
readonly type: ParameterType.STRING;
|
|
67
|
+
readonly default: "#007bff";
|
|
68
|
+
};
|
|
69
|
+
/** Primary button hover color */
|
|
70
|
+
readonly button_hover_color: {
|
|
71
|
+
readonly type: ParameterType.STRING;
|
|
72
|
+
readonly default: "#0056b3";
|
|
73
|
+
};
|
|
74
|
+
/** Retry button color */
|
|
75
|
+
readonly retry_button_color: {
|
|
76
|
+
readonly type: ParameterType.STRING;
|
|
77
|
+
readonly default: "#dc3545";
|
|
78
|
+
};
|
|
79
|
+
/** Retry button hover color */
|
|
80
|
+
readonly retry_button_hover_color: {
|
|
81
|
+
readonly type: ParameterType.STRING;
|
|
82
|
+
readonly default: "#c82333";
|
|
83
|
+
};
|
|
84
|
+
/** Success message color */
|
|
85
|
+
readonly success_color: {
|
|
86
|
+
readonly type: ParameterType.STRING;
|
|
87
|
+
readonly default: "#28a745";
|
|
88
|
+
};
|
|
89
|
+
/** Error message color */
|
|
90
|
+
readonly error_color: {
|
|
91
|
+
readonly type: ParameterType.STRING;
|
|
92
|
+
readonly default: "#dc3545";
|
|
93
|
+
};
|
|
94
|
+
/** Maximum number of retry attempts allowed on calibration failure */
|
|
95
|
+
readonly max_retries: {
|
|
96
|
+
readonly type: ParameterType.INT;
|
|
97
|
+
readonly default: 1;
|
|
98
|
+
};
|
|
99
|
+
/** Duration of zoom in/out animations in ms */
|
|
100
|
+
readonly zoom_duration: {
|
|
101
|
+
readonly type: ParameterType.INT;
|
|
102
|
+
readonly default: 300;
|
|
103
|
+
};
|
|
104
|
+
/** Duration of explosion animation in ms */
|
|
105
|
+
readonly explosion_duration: {
|
|
106
|
+
readonly type: ParameterType.INT;
|
|
107
|
+
readonly default: 400;
|
|
108
|
+
};
|
|
109
|
+
/** Duration to show success result before auto-advancing in ms */
|
|
110
|
+
readonly success_display_duration: {
|
|
111
|
+
readonly type: ParameterType.INT;
|
|
112
|
+
readonly default: 2000;
|
|
113
|
+
};
|
|
114
|
+
/** Duration to show instructions before auto-advancing in view mode in ms */
|
|
115
|
+
readonly instruction_display_duration: {
|
|
116
|
+
readonly type: ParameterType.INT;
|
|
117
|
+
readonly default: 3000;
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
readonly data: {
|
|
121
|
+
/** Calibration success status */
|
|
122
|
+
readonly calibration_success: {
|
|
123
|
+
readonly type: ParameterType.BOOL;
|
|
124
|
+
};
|
|
125
|
+
/** Number of calibration points used */
|
|
126
|
+
readonly num_points: {
|
|
127
|
+
readonly type: ParameterType.INT;
|
|
128
|
+
};
|
|
129
|
+
/** Calibration mode used */
|
|
130
|
+
readonly mode: {
|
|
131
|
+
readonly type: ParameterType.STRING;
|
|
132
|
+
};
|
|
133
|
+
/** Full calibration result data */
|
|
134
|
+
readonly calibration_data: {
|
|
135
|
+
readonly type: ParameterType.COMPLEX;
|
|
136
|
+
};
|
|
137
|
+
/** Number of calibration attempts made */
|
|
138
|
+
readonly num_attempts: {
|
|
139
|
+
readonly type: ParameterType.INT;
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
type Info = typeof info;
|
|
144
|
+
declare class TobiiCalibrationPlugin implements JsPsychPlugin<Info> {
|
|
145
|
+
private jsPsych;
|
|
146
|
+
static info: {
|
|
147
|
+
readonly name: "tobii-calibration";
|
|
148
|
+
readonly version: string;
|
|
149
|
+
readonly parameters: {
|
|
150
|
+
/** Number of calibration points (5 or 9) */
|
|
151
|
+
readonly calibration_points: {
|
|
152
|
+
readonly type: ParameterType.INT;
|
|
153
|
+
readonly default: 9;
|
|
154
|
+
};
|
|
155
|
+
/** Calibration mode: click or view */
|
|
156
|
+
readonly calibration_mode: {
|
|
157
|
+
readonly type: ParameterType.STRING;
|
|
158
|
+
readonly default: "view";
|
|
159
|
+
};
|
|
160
|
+
/** Size of calibration points in pixels */
|
|
161
|
+
readonly point_size: {
|
|
162
|
+
readonly type: ParameterType.INT;
|
|
163
|
+
readonly default: 20;
|
|
164
|
+
};
|
|
165
|
+
/** Color of calibration points */
|
|
166
|
+
readonly point_color: {
|
|
167
|
+
readonly type: ParameterType.STRING;
|
|
168
|
+
readonly default: "#ff0000";
|
|
169
|
+
};
|
|
170
|
+
/** Duration to show each point before data collection (ms) - allows user to fixate */
|
|
171
|
+
readonly point_duration: {
|
|
172
|
+
readonly type: ParameterType.INT;
|
|
173
|
+
readonly default: 500;
|
|
174
|
+
};
|
|
175
|
+
/** Show progress indicator */
|
|
176
|
+
readonly show_progress: {
|
|
177
|
+
readonly type: ParameterType.BOOL;
|
|
178
|
+
readonly default: true;
|
|
179
|
+
};
|
|
180
|
+
/** Custom calibration points */
|
|
181
|
+
readonly custom_points: {
|
|
182
|
+
readonly type: ParameterType.COMPLEX;
|
|
183
|
+
readonly default: null;
|
|
184
|
+
};
|
|
185
|
+
/** Instructions text */
|
|
186
|
+
readonly instructions: {
|
|
187
|
+
readonly type: ParameterType.STRING;
|
|
188
|
+
readonly default: "Look at each point as it appears on the screen. Keep your gaze fixed on each point until it disappears.";
|
|
189
|
+
};
|
|
190
|
+
/** Button text for click mode */
|
|
191
|
+
readonly button_text: {
|
|
192
|
+
readonly type: ParameterType.STRING;
|
|
193
|
+
readonly default: "Start Calibration";
|
|
194
|
+
};
|
|
195
|
+
/** Background color of the calibration container */
|
|
196
|
+
readonly background_color: {
|
|
197
|
+
readonly type: ParameterType.STRING;
|
|
198
|
+
readonly default: "#808080";
|
|
199
|
+
};
|
|
200
|
+
/** Primary button color */
|
|
201
|
+
readonly button_color: {
|
|
202
|
+
readonly type: ParameterType.STRING;
|
|
203
|
+
readonly default: "#007bff";
|
|
204
|
+
};
|
|
205
|
+
/** Primary button hover color */
|
|
206
|
+
readonly button_hover_color: {
|
|
207
|
+
readonly type: ParameterType.STRING;
|
|
208
|
+
readonly default: "#0056b3";
|
|
209
|
+
};
|
|
210
|
+
/** Retry button color */
|
|
211
|
+
readonly retry_button_color: {
|
|
212
|
+
readonly type: ParameterType.STRING;
|
|
213
|
+
readonly default: "#dc3545";
|
|
214
|
+
};
|
|
215
|
+
/** Retry button hover color */
|
|
216
|
+
readonly retry_button_hover_color: {
|
|
217
|
+
readonly type: ParameterType.STRING;
|
|
218
|
+
readonly default: "#c82333";
|
|
219
|
+
};
|
|
220
|
+
/** Success message color */
|
|
221
|
+
readonly success_color: {
|
|
222
|
+
readonly type: ParameterType.STRING;
|
|
223
|
+
readonly default: "#28a745";
|
|
224
|
+
};
|
|
225
|
+
/** Error message color */
|
|
226
|
+
readonly error_color: {
|
|
227
|
+
readonly type: ParameterType.STRING;
|
|
228
|
+
readonly default: "#dc3545";
|
|
229
|
+
};
|
|
230
|
+
/** Maximum number of retry attempts allowed on calibration failure */
|
|
231
|
+
readonly max_retries: {
|
|
232
|
+
readonly type: ParameterType.INT;
|
|
233
|
+
readonly default: 1;
|
|
234
|
+
};
|
|
235
|
+
/** Duration of zoom in/out animations in ms */
|
|
236
|
+
readonly zoom_duration: {
|
|
237
|
+
readonly type: ParameterType.INT;
|
|
238
|
+
readonly default: 300;
|
|
239
|
+
};
|
|
240
|
+
/** Duration of explosion animation in ms */
|
|
241
|
+
readonly explosion_duration: {
|
|
242
|
+
readonly type: ParameterType.INT;
|
|
243
|
+
readonly default: 400;
|
|
244
|
+
};
|
|
245
|
+
/** Duration to show success result before auto-advancing in ms */
|
|
246
|
+
readonly success_display_duration: {
|
|
247
|
+
readonly type: ParameterType.INT;
|
|
248
|
+
readonly default: 2000;
|
|
249
|
+
};
|
|
250
|
+
/** Duration to show instructions before auto-advancing in view mode in ms */
|
|
251
|
+
readonly instruction_display_duration: {
|
|
252
|
+
readonly type: ParameterType.INT;
|
|
253
|
+
readonly default: 3000;
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
readonly data: {
|
|
257
|
+
/** Calibration success status */
|
|
258
|
+
readonly calibration_success: {
|
|
259
|
+
readonly type: ParameterType.BOOL;
|
|
260
|
+
};
|
|
261
|
+
/** Number of calibration points used */
|
|
262
|
+
readonly num_points: {
|
|
263
|
+
readonly type: ParameterType.INT;
|
|
264
|
+
};
|
|
265
|
+
/** Calibration mode used */
|
|
266
|
+
readonly mode: {
|
|
267
|
+
readonly type: ParameterType.STRING;
|
|
268
|
+
};
|
|
269
|
+
/** Full calibration result data */
|
|
270
|
+
readonly calibration_data: {
|
|
271
|
+
readonly type: ParameterType.COMPLEX;
|
|
272
|
+
};
|
|
273
|
+
/** Number of calibration attempts made */
|
|
274
|
+
readonly num_attempts: {
|
|
275
|
+
readonly type: ParameterType.INT;
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
};
|
|
279
|
+
constructor(jsPsych: JsPsych);
|
|
280
|
+
private static removeStyles;
|
|
281
|
+
private injectStyles;
|
|
282
|
+
trial(display_element: HTMLElement, trial: TrialType<Info>): Promise<void>;
|
|
283
|
+
/**
|
|
284
|
+
* Validate custom calibration points
|
|
285
|
+
*/
|
|
286
|
+
private validateCustomPoints;
|
|
287
|
+
/**
|
|
288
|
+
* Get standard calibration points for the given grid size
|
|
289
|
+
*/
|
|
290
|
+
private getCalibrationPoints;
|
|
291
|
+
/**
|
|
292
|
+
* Delay helper
|
|
293
|
+
*/
|
|
294
|
+
private delay;
|
|
295
|
+
}
|
|
296
|
+
export default TobiiCalibrationPlugin;
|
|
297
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAO3E,QAAA,MAAM,IAAI;;;;QAIN,4CAA4C;;;;;QAK5C,sCAAsC;;;;;QAKtC,2CAA2C;;;;;QAK3C,kCAAkC;;;;;QAKlC,sFAAsF;;;;;QAKtF,8BAA8B;;;;;QAK9B,gCAAgC;;;;;QAKhC,wBAAwB;;;;;QAMxB,iCAAiC;;;;;QAKjC,oDAAoD;;;;;QAKpD,2BAA2B;;;;;QAK3B,iCAAiC;;;;;QAKjC,yBAAyB;;;;;QAKzB,+BAA+B;;;;;QAK/B,4BAA4B;;;;;QAK5B,0BAA0B;;;;;QAK1B,sEAAsE;;;;;QAKtE,+CAA+C;;;;;QAK/C,4CAA4C;;;;;QAK5C,kEAAkE;;;;;QAKlE,6EAA6E;;;;;;;QAO7E,iCAAiC;;;;QAIjC,wCAAwC;;;;QAIxC,4BAA4B;;;;QAI5B,mCAAmC;;;;QAInC,0CAA0C;;;;;CAK7C,CAAC;AAEF,KAAK,IAAI,GAAG,OAAO,IAAI,CAAC;AAExB,cAAM,sBAAuB,YAAW,aAAa,CAAC,IAAI,CAAC;IAG7C,OAAO,CAAC,OAAO;IAF3B,MAAM,CAAC,IAAI;;;;YAtIT,4CAA4C;;;;;YAK5C,sCAAsC;;;;;YAKtC,2CAA2C;;;;;YAK3C,kCAAkC;;;;;YAKlC,sFAAsF;;;;;YAKtF,8BAA8B;;;;;YAK9B,gCAAgC;;;;;YAKhC,wBAAwB;;;;;YAMxB,iCAAiC;;;;;YAKjC,oDAAoD;;;;;YAKpD,2BAA2B;;;;;YAK3B,iCAAiC;;;;;YAKjC,yBAAyB;;;;;YAKzB,+BAA+B;;;;;YAK/B,4BAA4B;;;;;YAK5B,0BAA0B;;;;;YAK1B,sEAAsE;;;;;YAKtE,+CAA+C;;;;;YAK/C,4CAA4C;;;;;YAK5C,kEAAkE;;;;;YAKlE,6EAA6E;;;;;;;YAO7E,iCAAiC;;;;YAIjC,wCAAwC;;;;YAIxC,4BAA4B;;;;YAI5B,mCAAmC;;;;YAInC,0CAA0C;;;;;MAUzB;gBAEC,OAAO,EAAE,OAAO;IAEpC,OAAO,CAAC,MAAM,CAAC,YAAY;IAO3B,OAAO,CAAC,YAAY;IA4Md,KAAK,CAAC,eAAe,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAuHhF;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA0B5B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAqH5B;;OAEG;IACH,OAAO,CAAC,KAAK;CAGd;AAED,eAAe,sBAAsB,CAAC"}
|