remote-calibrator 0.3.0-beta.2 → 0.3.0-beta.7
Sign up to get free protection for your applications and to get access to all the features.
- package/.eslintrc.js +1 -1
- package/.husky/pre-commit +1 -1
- package/.prettierignore +4 -0
- package/CHANGELOG.md +24 -0
- package/README.md +28 -9
- package/homepage/example.css +4 -0
- package/homepage/index.html +26 -0
- package/i18n/.eslintrc.js +12 -0
- package/i18n/fetch-languages-sheets.js +71 -0
- package/lib/RemoteCalibrator.min.js +1 -1
- package/lib/RemoteCalibrator.min.js.LICENSE.txt +1 -1
- package/lib/RemoteCalibrator.min.js.map +1 -1
- package/package.json +14 -10
- package/src/components/buttons.js +4 -3
- package/src/components/language.js +31 -0
- package/src/components/swalOptions.js +4 -2
- package/src/{helpers.js → components/utils.js} +8 -2
- package/src/const.js +4 -4
- package/src/core.js +104 -2
- package/src/css/buttons.scss +18 -2
- package/src/css/distance.scss +12 -7
- package/src/css/main.css +18 -4
- package/src/css/panel.scss +1 -6
- package/src/css/screenSize.css +19 -16
- package/src/css/swal.css +0 -1
- package/src/displaySize.js +1 -1
- package/src/distance/distance.js +19 -12
- package/src/distance/distanceTrack.js +6 -6
- package/src/gaze/gaze.js +4 -4
- package/src/gaze/gazeAccuracy.js +2 -3
- package/src/gaze/gazeCalibration.js +5 -5
- package/src/gaze/gazeTracker.js +1 -1
- package/src/i18n.js +6 -0
- package/src/index.js +1 -1
- package/src/interpupillaryDistance.js +10 -5
- package/src/{panel/panel.js → panel.js} +56 -23
- package/src/screenSize.js +46 -33
- package/src/text.json +0 -34
@@ -1,13 +1,13 @@
|
|
1
1
|
import tinycolor from 'tinycolor2'
|
2
2
|
|
3
|
-
import RemoteCalibrator from '
|
4
|
-
import
|
3
|
+
import RemoteCalibrator from './core'
|
4
|
+
import { phrases } from './i18n'
|
5
5
|
|
6
6
|
// Icons from Google Material UI
|
7
|
-
import Camera from '
|
8
|
-
import Phone from '
|
7
|
+
import Camera from './media/photo_camera.svg'
|
8
|
+
import Phone from './media/smartphone.svg'
|
9
9
|
|
10
|
-
import '
|
10
|
+
import './css/panel.scss'
|
11
11
|
|
12
12
|
RemoteCalibrator.prototype.removePanel = function () {
|
13
13
|
if (!this._hasPanel) return false
|
@@ -86,12 +86,12 @@ RemoteCalibrator.prototype.panel = async function (
|
|
86
86
|
|
87
87
|
options = Object.assign(
|
88
88
|
{
|
89
|
-
headline:
|
90
|
-
description:
|
89
|
+
headline: phrases.RC_panelTitle[this.L],
|
90
|
+
description: phrases.RC_panelIntro[this.L],
|
91
91
|
showNextButton: false,
|
92
|
-
nextHeadline:
|
93
|
-
nextDescription:
|
94
|
-
nextButton:
|
92
|
+
nextHeadline: phrases.RC_panelTitleNext[this.L],
|
93
|
+
nextDescription: phrases.RC_panelIntroNext[this.L],
|
94
|
+
nextButton: phrases.RC_panelButton[this.L],
|
95
95
|
color: '#3490de',
|
96
96
|
_demoActivateAll: false, // ! Private
|
97
97
|
},
|
@@ -119,6 +119,9 @@ RemoteCalibrator.prototype.panel = async function (
|
|
119
119
|
|
120
120
|
const panel = document.createElement('div')
|
121
121
|
panel.className = panel.id = 'rc-panel'
|
122
|
+
if (this.LD === this._CONST.RTL) panel.className += ' rc-lang-rtl'
|
123
|
+
else panel.className += ' rc-lang-ltr'
|
124
|
+
|
122
125
|
panel.innerHTML = `<h1 class="rc-panel-title" id="rc-panel-title">${options.headline}</h1>`
|
123
126
|
panel.innerHTML += `<p class="rc-panel-description" id="rc-panel-description">${options.description}</p>`
|
124
127
|
panel.innerHTML += '<div class="rc-panel-steps" id="rc-panel-steps"></div>'
|
@@ -129,17 +132,18 @@ RemoteCalibrator.prototype.panel = async function (
|
|
129
132
|
const steps = panel.querySelector('#rc-panel-steps')
|
130
133
|
|
131
134
|
// Observe panel size for adjusting steps
|
135
|
+
const RC = this
|
132
136
|
const panelObserver = new ResizeObserver(() => {
|
133
|
-
_setStepsClassesSL(steps, panel.offsetWidth)
|
137
|
+
_setStepsClassesSL(steps, panel.offsetWidth, RC.LD)
|
134
138
|
})
|
135
139
|
panelObserver.observe(panel)
|
136
|
-
_setStepsClassesSL(steps, panel.offsetWidth)
|
140
|
+
_setStepsClassesSL(steps, panel.offsetWidth, this.LD)
|
137
141
|
|
138
142
|
if (tasks.length === 0) {
|
139
143
|
steps.className += ' rc-panel-no-steps'
|
140
144
|
} else {
|
141
145
|
for (let t in tasks) {
|
142
|
-
const b = _newStepBlock(t, tasks[t], options)
|
146
|
+
const b = _newStepBlock(this, t, tasks[t], options)
|
143
147
|
steps.appendChild(b)
|
144
148
|
}
|
145
149
|
}
|
@@ -155,7 +159,21 @@ RemoteCalibrator.prototype.panel = async function (
|
|
155
159
|
this._panel.panelObserver = panelObserver
|
156
160
|
this._panel.panelTasks = tasks
|
157
161
|
this._panel.panelParent = parent
|
158
|
-
|
162
|
+
|
163
|
+
const tempOptions = { ...options }
|
164
|
+
if (options.headline === phrases.RC_panelTitle[this.L])
|
165
|
+
delete tempOptions.headline
|
166
|
+
if (options.description === phrases.RC_panelIntro[this.L])
|
167
|
+
delete tempOptions.description
|
168
|
+
if (options.nextHeadline === phrases.RC_panelTitleNext[this.L])
|
169
|
+
delete tempOptions.nextHeadline
|
170
|
+
if (options.nextDescription === phrases.RC_panelIntroNext[this.L])
|
171
|
+
delete tempOptions.nextDescription
|
172
|
+
if (options.nextButton === phrases.RC_panelButton[this.L])
|
173
|
+
delete tempOptions.nextButton
|
174
|
+
|
175
|
+
this._panel.panelOptions = tempOptions
|
176
|
+
|
159
177
|
this._panel.panelCallback = callback
|
160
178
|
this._panel.panelResolve = resolveOnFinish
|
161
179
|
|
@@ -184,27 +202,33 @@ RemoteCalibrator.prototype.panel = async function (
|
|
184
202
|
const _validTaskList = {
|
185
203
|
screenSize: {
|
186
204
|
use: 1,
|
187
|
-
name: '
|
205
|
+
name: phrases.RC_screenSize['en-US'],
|
206
|
+
phraseHandle: 'RC_screenSize',
|
188
207
|
},
|
189
208
|
displaySize: {
|
190
209
|
use: 0,
|
191
|
-
name: '
|
210
|
+
name: phrases.RC_displaySize['en-US'],
|
211
|
+
phraseHandle: 'RC_displaySize',
|
192
212
|
},
|
193
213
|
measureDistance: {
|
194
214
|
use: 2,
|
195
|
-
name: '
|
215
|
+
name: phrases.RC_viewingDistance['en-US'],
|
216
|
+
phraseHandle: 'RC_viewingDistance',
|
196
217
|
},
|
197
218
|
trackDistance: {
|
198
219
|
use: 2,
|
199
|
-
name: '
|
220
|
+
name: phrases.RC_headTracking['en-US'],
|
221
|
+
phraseHandle: 'RC_headTracking',
|
200
222
|
},
|
201
223
|
trackGaze: {
|
202
224
|
use: 2,
|
203
|
-
name: '
|
225
|
+
name: phrases.RC_gazeTracking['en-US'],
|
226
|
+
phraseHandle: 'RC_gazeTracking',
|
204
227
|
},
|
205
228
|
environment: {
|
206
229
|
use: 0,
|
207
|
-
name: '
|
230
|
+
name: phrases.RC_environment['en-US'],
|
231
|
+
phraseHandle: 'RC_environment',
|
208
232
|
},
|
209
233
|
}
|
210
234
|
const _validTaskListNames = Object.keys(_validTaskList)
|
@@ -223,7 +247,7 @@ const _validateTask = task => {
|
|
223
247
|
return true
|
224
248
|
}
|
225
249
|
|
226
|
-
const _newStepBlock = (index, task, options) => {
|
250
|
+
const _newStepBlock = (RC, index, task, options) => {
|
227
251
|
let useCode = _validTaskList[_getTaskName(task)].use
|
228
252
|
let use, useTip
|
229
253
|
|
@@ -260,7 +284,7 @@ const _newStepBlock = (index, task, options) => {
|
|
260
284
|
b.innerHTML =
|
261
285
|
(use.length ? `<p class="rc-panel-step-use">${use}</p>` : '') +
|
262
286
|
`<p class="rc-panel-step-name">${Number(index) + 1} ${
|
263
|
-
_validTaskList[_getTaskName(task)].
|
287
|
+
phrases[_validTaskList[_getTaskName(task)].phraseHandle][RC.L]
|
264
288
|
}</p>` +
|
265
289
|
(use.length ? `<p class="rc-panel-step-use-tip">${use} ${useTip}</p>` : '')
|
266
290
|
// b.disabled = true
|
@@ -276,13 +300,22 @@ const _nextStepBlock = (index, options) => {
|
|
276
300
|
return b
|
277
301
|
}
|
278
302
|
|
279
|
-
const _setStepsClassesSL = (steps, panelWidth) => {
|
303
|
+
const _setStepsClassesSL = (steps, panelWidth, LD) => {
|
280
304
|
if (panelWidth < 640) {
|
281
305
|
steps.classList.add('rc-panel-steps-s')
|
282
306
|
steps.classList.remove('rc-panel-steps-l')
|
307
|
+
|
308
|
+
steps.childNodes.forEach(e => {
|
309
|
+
e.classList.add(`rc-lang-${LD.toLowerCase()}`)
|
310
|
+
})
|
283
311
|
} else {
|
284
312
|
steps.classList.add('rc-panel-steps-l')
|
285
313
|
steps.classList.remove('rc-panel-steps-s')
|
314
|
+
|
315
|
+
steps.childNodes.forEach(e => {
|
316
|
+
e.classList.remove(`rc-lang-ltr`)
|
317
|
+
e.classList.remove(`rc-lang-rtl`)
|
318
|
+
})
|
286
319
|
}
|
287
320
|
}
|
288
321
|
|
package/src/screenSize.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import RemoteCalibrator from './core'
|
2
|
-
import { toFixedNumber, blurAll, remap } from './
|
2
|
+
import { toFixedNumber, blurAll, remap } from './components/utils'
|
3
3
|
|
4
4
|
import Card from './media/card.svg'
|
5
5
|
import Arrow from './media/arrow.svg'
|
@@ -7,7 +7,7 @@ import USBA from './media/usba.svg'
|
|
7
7
|
import USBC from './media/usbc.svg'
|
8
8
|
import { bindKeys, unbindKeys } from './components/keyBinder'
|
9
9
|
import { addButtons } from './components/buttons'
|
10
|
-
import
|
10
|
+
import { phrases } from './i18n'
|
11
11
|
|
12
12
|
// TODO Make it customizable
|
13
13
|
const defaultObj = 'usba'
|
@@ -49,19 +49,30 @@ RemoteCalibrator.prototype.screenSize = function (options = {}, callback) {
|
|
49
49
|
quitFullscreenOnFinished: false,
|
50
50
|
repeatTesting: 1,
|
51
51
|
decimalPlace: 1,
|
52
|
-
headline:
|
53
|
-
description:
|
52
|
+
headline: '🖥️ ' + phrases.RC_screenSizeTitle[this.L],
|
53
|
+
description: phrases.RC_screenSizeIntro[this.L],
|
54
54
|
},
|
55
55
|
options
|
56
56
|
)
|
57
57
|
|
58
58
|
this.getFullscreen(options.fullscreen)
|
59
59
|
|
60
|
-
options.description += `<br /><b class="rc-size-obj-selection"
|
60
|
+
options.description += `<br /><br /><b class="rc-size-obj-selection">${phrases.RC_screenSizeHave[
|
61
|
+
this.L
|
62
|
+
].replace(
|
63
|
+
'xxx',
|
64
|
+
`<select id="matching-obj"><option value="usba" selected>${
|
65
|
+
phrases.RC_screenSizeUSBA[this.L]
|
66
|
+
}</option><option value="usbc">${
|
67
|
+
phrases.RC_screenSizeUSBC[this.L]
|
68
|
+
}</option><option value="card">${
|
69
|
+
phrases.RC_screenSizeCreditCard[this.L]
|
70
|
+
}</option></select>`
|
71
|
+
)}</b>`
|
61
72
|
|
62
73
|
this._addBackground()
|
63
74
|
this._addBackgroundText(options.headline, options.description)
|
64
|
-
this._addCreditOnBackground(this.
|
75
|
+
this._addCreditOnBackground(phrases.RC_screenSizeCredit[this.L])
|
65
76
|
|
66
77
|
getSize(this, this.background, options, callback)
|
67
78
|
|
@@ -71,8 +82,8 @@ RemoteCalibrator.prototype.screenSize = function (options = {}, callback) {
|
|
71
82
|
function getSize(RC, parent, options, callback) {
|
72
83
|
// Slider
|
73
84
|
const sliderElement = document.createElement('input')
|
74
|
-
sliderElement.id = 'size-slider'
|
75
|
-
sliderElement.className = 'slider'
|
85
|
+
sliderElement.id = 'rc-size-slider'
|
86
|
+
sliderElement.className = 'rc-slider'
|
76
87
|
sliderElement.type = 'range'
|
77
88
|
sliderElement.min = 0
|
78
89
|
sliderElement.max = 100
|
@@ -83,37 +94,27 @@ function getSize(RC, parent, options, callback) {
|
|
83
94
|
sliderElement.step = 0.1
|
84
95
|
|
85
96
|
setSliderPosition(sliderElement, parent)
|
97
|
+
setSliderStyle(sliderElement)
|
86
98
|
parent.appendChild(sliderElement)
|
87
99
|
|
88
100
|
const _onDown = (e, type) => {
|
89
101
|
if (
|
90
|
-
e.target.className === 'slider' &&
|
91
|
-
e.target.id === 'size-slider' &&
|
102
|
+
e.target.className === 'rc-slider' &&
|
103
|
+
e.target.id === 'rc-size-slider' &&
|
92
104
|
((type === RC._CONST.S.CLICK_TYPE.MOUSE && e.which === 1) ||
|
93
105
|
type === RC._CONST.S.CLICK_TYPE.TOUCH)
|
94
106
|
) {
|
95
107
|
e.target.style.cursor = 'grabbing'
|
96
|
-
arrowFillElement.setAttribute('fill', RC._CONST.COLOR.
|
108
|
+
arrowFillElement.setAttribute('fill', RC._CONST.COLOR.ORANGE)
|
109
|
+
const _onEnd = () => {
|
110
|
+
sliderElement.style.cursor = 'grab'
|
111
|
+
arrowFillElement.setAttribute('fill', RC._CONST.COLOR.LIGHT_GREY)
|
112
|
+
document.removeEventListener('mouseup', _onEnd, false)
|
113
|
+
}
|
97
114
|
if (type === RC._CONST.S.CLICK_TYPE.MOUSE)
|
98
|
-
document.addEventListener(
|
99
|
-
'mouseup',
|
100
|
-
function _onMouseUp() {
|
101
|
-
sliderElement.style.cursor = 'grab'
|
102
|
-
arrowFillElement.setAttribute('fill', '#aaa')
|
103
|
-
document.removeEventListener('mouseup', _onMouseUp, false)
|
104
|
-
},
|
105
|
-
false
|
106
|
-
)
|
115
|
+
document.addEventListener('mouseup', _onEnd, false)
|
107
116
|
else if (type === RC._CONST.S.CLICK_TYPE.TOUCH)
|
108
|
-
document.addEventListener(
|
109
|
-
'touchend',
|
110
|
-
function _onTouchend() {
|
111
|
-
sliderElement.style.cursor = 'grab'
|
112
|
-
arrowFillElement.setAttribute('fill', '#aaa')
|
113
|
-
document.removeEventListener('mouseup', _onTouchend, false)
|
114
|
-
},
|
115
|
-
false
|
116
|
-
)
|
117
|
+
document.addEventListener('touchend', _onEnd, false)
|
117
118
|
}
|
118
119
|
}
|
119
120
|
|
@@ -139,7 +140,7 @@ function getSize(RC, parent, options, callback) {
|
|
139
140
|
switchMatchingObj('card', elements)
|
140
141
|
// Card & Arrow
|
141
142
|
let arrowFillElement = document.getElementById('size-arrow-fill')
|
142
|
-
arrowFillElement.setAttribute('fill',
|
143
|
+
arrowFillElement.setAttribute('fill', RC._CONST.COLOR.LIGHT_GREY)
|
143
144
|
let arrowSizes = {
|
144
145
|
width: elements.arrow.getBoundingClientRect().width,
|
145
146
|
height: elements.arrow.getBoundingClientRect().height,
|
@@ -153,6 +154,7 @@ function getSize(RC, parent, options, callback) {
|
|
153
154
|
|
154
155
|
setSizes()
|
155
156
|
const onSliderInput = () => {
|
157
|
+
setSliderStyle(sliderElement)
|
156
158
|
setSizes()
|
157
159
|
}
|
158
160
|
const resizeObserver = new ResizeObserver(() => {
|
@@ -206,6 +208,7 @@ function getSize(RC, parent, options, callback) {
|
|
206
208
|
})
|
207
209
|
|
208
210
|
addButtons(
|
211
|
+
RC.L,
|
209
212
|
RC.background,
|
210
213
|
{
|
211
214
|
go: finishFunction,
|
@@ -262,7 +265,7 @@ const addMatchingObj = (names, parent) => {
|
|
262
265
|
elements[name] = element
|
263
266
|
}
|
264
267
|
|
265
|
-
setObjectsPosition(elements, document.querySelector('#size-slider'))
|
268
|
+
setObjectsPosition(elements, document.querySelector('#rc-size-slider'))
|
266
269
|
|
267
270
|
return elements
|
268
271
|
}
|
@@ -272,8 +275,9 @@ const switchMatchingObj = (name, elements, setSizes) => {
|
|
272
275
|
if (obj === name) elements[obj].style.visibility = 'visible'
|
273
276
|
else elements[obj].style.visibility = 'hidden'
|
274
277
|
}
|
275
|
-
if (name === 'card') elements.arrow.style.visibility = 'visible'
|
276
|
-
else elements.arrow.style.visibility = 'hidden'
|
278
|
+
// if (name === 'card') elements.arrow.style.visibility = 'visible'
|
279
|
+
// else elements.arrow.style.visibility = 'hidden'
|
280
|
+
elements.arrow.style.visibility = 'hidden'
|
277
281
|
if (setSizes) setSizes()
|
278
282
|
}
|
279
283
|
|
@@ -324,3 +328,12 @@ const setObjectsPosition = (objects, slider) => {
|
|
324
328
|
for (let i in objects)
|
325
329
|
objects[i].style.top = slider.getBoundingClientRect().top + 50 + 'px'
|
326
330
|
}
|
331
|
+
|
332
|
+
/* -------------------------------------------------------------------------- */
|
333
|
+
|
334
|
+
const setSliderStyle = ele => {
|
335
|
+
const ratio = ele.value / ele.max
|
336
|
+
ele.style.background = `linear-gradient(90deg, #ffc772, #ffc772 ${
|
337
|
+
ratio * 100
|
338
|
+
}%, #fff ${ratio * 100}%)`
|
339
|
+
}
|
package/src/text.json
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"screenSize": {
|
3
|
-
"headline": "🖥️ Screen Size Calibration",
|
4
|
-
"description": "We will measure your screen size. Find a <b>USB connector</b> (like the one providing power to your laptop) or a <b>credit card</b> (or any card of the same size), place it on the screen and drag the slider to match the sizes of the physical and displayed objects. When the sizes match, press <b>RETURN</b> or click <b>OK</b>."
|
5
|
-
},
|
6
|
-
"measureDistance": {
|
7
|
-
"headline": "📏 Measure Viewing Distance",
|
8
|
-
"description": "We'll measure your viewing distance. To do this, we will perform a blind spot test. Close/Cover one of your eyes (as instructed below) and focus on the black crosshair. Press <b>SPACE</b> or click <b>OK</b> when the red circle disappears. If it doesn't disappear, try moving closer to the screen."
|
9
|
-
},
|
10
|
-
"trackDistance": {
|
11
|
-
"headline": "🙂 Set up for Head Tracking",
|
12
|
-
"description": "Now we'll set up head tracking, to monitor viewing distance. When asked, please grant permission to access your camera. We'll perform a blind spot test first. Close/Cover one of your eyes (as instructed below) and focus on the black crosshair. Please press <b>SPACE</b> or click <b>OK</b> when the red dot disappears. If it doesn't disappear, try moving closer to the screen. You'll do this twice with each eye. Once you're done, head tracking will begin."
|
13
|
-
},
|
14
|
-
"calibrateGaze": {
|
15
|
-
"headline": "👀 Set up for Gaze Tracking",
|
16
|
-
"description": "Now we'll set up gaze tracking, to monitor eye position. When asked, please grant permission to access your camera. Try to keep your face centered in the video feed. Click on the <b style=\"color: #ff005c\">pink dot</b> at each location that it stops at. Make sure that your eyes are on the dot when you click."
|
17
|
-
},
|
18
|
-
"getGazeAccuracy": {
|
19
|
-
"headline": "👀 Get Gaze Accuracy",
|
20
|
-
"description": "We will measure your gaze accuracy. Please do not move the mouse and look at the fixation at the middle of the screen for the next 5 seconds."
|
21
|
-
},
|
22
|
-
"panel": {
|
23
|
-
"headline": "Please press a button, to calibrate.",
|
24
|
-
"description": "",
|
25
|
-
"nextHeadline": "Thanks for calibrating. Hit the button to continue.",
|
26
|
-
"nextDescription": "",
|
27
|
-
"nextButton": "Done"
|
28
|
-
},
|
29
|
-
"measurePD": {
|
30
|
-
"headline": "👁️ Measure Pupillary Distance",
|
31
|
-
"description": "We'll measure the distance between the centers of your eyes. Hold the cable to your eyes, align the thumbs with the centers of your pupils. Then hold the cable against the ruler and align the left thumb with zero, check where the right thumb is. Finally, click on the ruler and click <b>OK</b> to submit your measurement. You can use the video image as a reference. If you do have a ruler with you, go ahead and use it.",
|
32
|
-
"shortDescription": "Hold the cable to your eyes, align the thumbs with the centers of your pupils. Then hold the cable against the ruler and align the left thumb with zero, check where the right thumb is. Finally, click on the ruler and click <b>OK</b> to submit your measurement. You can use the video image as a reference. If you do have a ruler with you, go ahead and use it."
|
33
|
-
}
|
34
|
-
}
|