remote-calibrator 0.3.0-beta.0 → 0.3.0-beta.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. package/.eslintrc.js +1 -1
  2. package/.husky/pre-commit +1 -1
  3. package/.prettierignore +4 -0
  4. package/CHANGELOG.md +38 -1
  5. package/README.md +33 -13
  6. package/homepage/example.css +4 -0
  7. package/homepage/example.js +1 -0
  8. package/homepage/index.html +26 -0
  9. package/i18n/.eslintrc.js +12 -0
  10. package/i18n/fetch-languages-sheets.js +62 -0
  11. package/lib/RemoteCalibrator.min.js +1 -1
  12. package/lib/RemoteCalibrator.min.js.LICENSE.txt +1 -1
  13. package/lib/RemoteCalibrator.min.js.map +1 -1
  14. package/package.json +12 -8
  15. package/src/components/buttons.js +4 -3
  16. package/src/components/language.js +31 -0
  17. package/src/components/onCanvas.js +4 -8
  18. package/src/components/swalOptions.js +25 -23
  19. package/src/{helpers.js → components/utils.js} +8 -2
  20. package/src/{video.js → components/video.js} +23 -4
  21. package/src/const.js +23 -3
  22. package/src/core.js +106 -6
  23. package/src/css/distance.scss +12 -7
  24. package/src/css/main.css +65 -10
  25. package/src/css/panel.scss +1 -1
  26. package/src/css/screenSize.css +28 -14
  27. package/src/debug.js +2 -0
  28. package/src/displaySize.js +1 -1
  29. package/src/distance/distance.js +20 -13
  30. package/src/distance/distanceTrack.js +9 -8
  31. package/src/gaze/gaze.js +22 -20
  32. package/src/gaze/gazeAccuracy.js +3 -4
  33. package/src/gaze/gazeCalibration.js +30 -22
  34. package/src/gaze/gazeTracker.js +4 -2
  35. package/src/i18n.js +6 -0
  36. package/src/index.js +1 -1
  37. package/src/interpupillaryDistance.js +34 -18
  38. package/src/{panel/panel.js → panel.js} +57 -21
  39. package/src/screenSize.js +72 -35
  40. package/src/constants.js +0 -11
  41. package/src/text.json +0 -34
package/.eslintrc.js CHANGED
@@ -19,9 +19,9 @@ module.exports = {
19
19
  ignorePatterns: [
20
20
  'webpack.config.js',
21
21
  '.eslintrc.js',
22
+ 'i18n.js',
22
23
  'WebGazer/',
23
24
  'lib/',
24
25
  'server.js',
25
- 'serverHTTPS.js',
26
26
  ],
27
27
  }
package/.husky/pre-commit CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/bin/sh
2
2
  . "$(dirname "$0")/_/husky.sh"
3
3
 
4
- npx lint-staged && npm run build && git add lib
4
+ npm run phrases && npx lint-staged && npm run build && git add lib
package/.prettierignore CHANGED
@@ -2,5 +2,9 @@
2
2
  package.json
3
3
  package-lock.json
4
4
 
5
+ i18n.js
6
+
5
7
  *WebGazer4RC/
6
8
  lib/
9
+
10
+ credentials.json
package/CHANGELOG.md CHANGED
@@ -9,9 +9,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
 
12
+ **i18n!**
13
+
14
+ - Internationalization! A full list of supported languages can be found at https://docs.google.com/spreadsheets/d/1UFfNikfLuo8bSromE34uWDuJrMPFiJG3VpoQKdCGkII/edit#gid=0.
15
+ - A few new getters related to languages:
16
+ - `.userLanguage` (as a part of `.environment()`)
17
+ - `.language` (e.g., `en-US`, `zh-CN`)
18
+ - `.languageNameEnglish` (e.g., `English`, `Chinese (Simplified)`)
19
+ - `.languageNameNative` (e.g., `简体中文`)
20
+ - `.languageDirection` (`LTR` or `RTL`)
21
+ - `.languagePhraseSource` (e.g., `Denis Pelli & Peiling Jiang 2021.10.10`)
22
+ - `.languageData` gets the whole data history of languages.
23
+ - `.supportedLanguages` gets an array of supported languages.
24
+ - `.newLanguage(lang = 'en-US')` to set a new language for the calibrator.
25
+ - Allows researchers to set language on initialization using the `language` option. Set to `AUTO` (default) will let the calibrator go with the user language.
26
+ - `.isMobile` getter.
27
+ - Call `.environment()` automatically when initializing the calibrator.
28
+ - Instructions in the viewing distance measurement (and head tracking setup) is scrollable to avoid overlapping with the canvas on small screen sizes.
29
+
30
+ ### Changed
31
+
32
+ - Improved UI and performance for small screens and mobile devices.
33
+ - Take Return instead of Space for confirming screen size measurement.
34
+
35
+ ### Removed
36
+
37
+ - The responsive arrow in the screen size calibration with credit card.
38
+
39
+ ## [0.2.3] - 2021-10-05
40
+
41
+ ### Added
42
+
43
+ - (Breaking) Callback function of gaze tracking is split into two functions: `callbackOnCalibrationEnd` that will only be called once when calibration ends, and `callbackTrack` that will be called continuously as the tracking runs (with data parameter passed in).
12
44
  - `sparkle` option (default `true`) for measuring and tracking viewing distance. The red dot sparkles at 10 Hz to make it more prominent when absent from the view.
13
45
  - Ignore the Return key in screen size calibration.
14
46
 
47
+ ### Fixed
48
+
49
+ - Panel final callback function is called multiple times if gaze tracking is the last calibration task.
50
+
15
51
  ## [0.2.2] - 2021-10-04
16
52
 
17
53
  ### Added
@@ -207,7 +243,8 @@ No new feature updates in this release. Updated dependency packages and the lice
207
243
 
208
244
  The framework and some basic functions, e.g., screen size calibration. Released for integration testing.
209
245
 
210
- [unreleased]: https://github.com/EasyEyes/remote-calibrator/compare/v0.2.2...develop
246
+ [unreleased]: https://github.com/EasyEyes/remote-calibrator/compare/v0.2.3...develop
247
+ [0.2.3]: https://github.com/EasyEyes/remote-calibrator/compare/v0.2.2...v0.2.3
211
248
  [0.2.2]: https://github.com/EasyEyes/remote-calibrator/compare/v0.2.1...v0.2.2
212
249
  [0.2.1]: https://github.com/EasyEyes/remote-calibrator/compare/v0.2.0...v0.2.1
213
250
  [0.2.0]: https://github.com/EasyEyes/remote-calibrator/compare/v0.1.1...v0.2.0
package/README.md CHANGED
@@ -52,13 +52,13 @@ RemoteCalibrator.measureDistance({}, data => {
52
52
  | [🎬 Initialize](#-initialize) | [`init()`](#-initialize) (always required) |
53
53
  | [🍱 Panel](#-panel) | [`async panel()`](#-panel) `removePanel()` `resetPanel()` |
54
54
  | [🖥️ Screen](#️-screen) | [`displaySize()`](#measure-display-pixels) [`screenSize()`](#measure-screen-size) |
55
- | [📏 Viewing Distance](#-viewing-distance) | `measureDistance()` |
55
+ | [📏 Viewing Distance](#-viewing-distance) | [`measureDistance()`](#-viewing-distance) |
56
56
  | [🙂 Head Tracking](#-head-tracking) | (viewing distance and [near point](#near-point)) [`trackDistance()`](#-head-tracking) [`async getDistanceNow()`](#async-get-distance-now) [Lifecycle](#lifecycle) [Others](#others) |
57
57
  | [👀 Gaze](#-gaze) | [`trackGaze()`](#start-tracking) [`async getGazeNow()`](#async-get-gaze-now) [`calibrateGaze()`](#calibrate) [`getGazeAccuracy()`](#get-accuracy-) [Lifecycle](#lifecycle-1) [Others](#others-1) |
58
58
  | [💻 Environment](#-environment) | [`environment()`](#-environment) |
59
59
  | [💄 Customization](#-customization) | `backgroundColor()` `videoOpacity()` `showCancelButton()` |
60
- | [📔 Other Functions](#-other-functions) | `checkInitialized()` `getFullscreen()` |
61
- | [🎣 Getters](#-getters) | [Experiment](#experiment) [Environment](#environment) [All Data](#all-data) [Others](#others-2) |
60
+ | [📔 Other Functions](#-other-functions) | `checkInitialized()` `getFullscreen()` `newLanguage()` |
61
+ | [🎣 Getters](#-getters) | [Experiment](#experiment) [Environment](#environment) [i18n](#i18n) [All Data](#all-data) [Others](#others-2) |
62
62
 
63
63
  Arguments in square brackets are optional, e.g. `init([options, [callback]])` means both `options` configuration and the `callback` function are optional, but you have to put `options`, e.g., `{}`, if you want to call the callback function. The default values of `options` are listed in each section with explanation.
64
64
 
@@ -81,8 +81,17 @@ Pass `{ value, timestamp }` (equivalent to `RemoteCalibrator.id`) to callback.
81
81
  * A random one will be generated if no value is passed into the function
82
82
  */
83
83
  id: /* Randomized value */,
84
- // Enter fullscreen if set to true
85
- // Will be ignored if already in fullscreen mode
84
+ /**
85
+ * Set the language, e.g., 'en-US', 'zh-CN'
86
+ * If set to 'AUTO' (default), the calibrator will try to follow the browser settings
87
+ * A full list of supported languages can be found at
88
+ * https://docs.google.com/spreadsheets/d/1UFfNikfLuo8bSromE34uWDuJrMPFiJG3VpoQKdCGkII/edit#gid=0
89
+ */
90
+ language: 'AUTO',
91
+ /**
92
+ * Enter full screen if set to true
93
+ * Will be ignored if already in full screen mode
94
+ */
86
95
  fullscreen: false,
87
96
  }
88
97
  ```
@@ -112,11 +121,9 @@ The `data` passed into the callback function is an [object](https://www.w3school
112
121
  ![Panel](./media/panel.png)
113
122
 
114
123
  ```js
115
- .panel(tasks, parentQuery, [options, [callback, [resolveOnFinish]]])
124
+ /* async */ .panel(tasks, parentQuery, [options, [callback, [resolveOnFinish]]])
116
125
  ```
117
126
 
118
- **Since 0.2.0:** `.panel()` is now an async function.
119
-
120
127
  `.panel()` is a powerful tool to help you set up a graphical user interface for participants to go through step-by-step and calibrate or set up tracking. It is highly customizable: tasks, task order, title, description, and "Done" button can all be customized. It is appended to the parent HTML node as set by `parentQuery`, e.g., if the parent node has id `main-area`, put `#main-area` as the `parentQuery`. Can only run once. Return a JavaScript [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) that will resolve the `resolveOnFinish` once the "Done" button is pressed.
121
128
 
122
129
  `tasks` is an array of tasks which can be a string or an object. Valid names are `screenSize`, `displaySize`, `measureDistance`, `trackDistance`, `trackGaze`, `environment` (system information).
@@ -128,7 +135,7 @@ The `data` passed into the callback function is an [object](https://www.w3school
128
135
  {
129
136
  name: 'trackGaze',
130
137
  options: { framerate: 60 }, // Same as setting the options for .trackGaze()
131
- callback: gotGaze, // Same as setting the callback for .trackGaze()
138
+ callbackTrack: gotGaze, // Same as setting the callback for .trackGaze()
132
139
  },
133
140
  // Tracking viewing distance accepts two callbacks just like .trackDistance()
134
141
  {
@@ -300,14 +307,14 @@ The value returned are the horizontal and vertical offsets, in centimeters, comp
300
307
  ![Start Gaze Tracking](./media/trackGaze.png)
301
308
 
302
309
  ```js
303
- .trackGaze([options, [callback]])
310
+ .trackGaze([options, [callbackOnCalibrationEnd, [callbackTrack]]])
304
311
  ```
305
312
 
306
313
  Use [WebGazer](https://github.com/peilingjiang-DEV/WebGazer). Pop an interface for participants to calibrate their gaze position on the screen (only when this function is called for the first time), then run in the background and continuously predict the current gaze position. Require access to the camera of the participant's computer. The callback function will be executed repeatedly **every time** there's a new prediction.
307
314
 
308
315
  This function should only be called once, unless you want to change the callback functions for every prediction.
309
316
 
310
- Pass `{ value: { x, y }, timestamp }` (equivalent to `RemoteCalibrator.gazePositionPx`) to callback.
317
+ Pass `{ value: { x, y }, timestamp }` (equivalent to `RemoteCalibrator.gazePositionPx`) to `callbackTrack` function.
311
318
 
312
319
  ```js
313
320
  /* [options] Default value */
@@ -391,7 +398,7 @@ Pop an interface for participants to calibrate their gaze position on the screen
391
398
 
392
399
  Get the setup information of the experiment, including browser type, device model, operating system family and version, etc. This function does not create its own timestamp, but use the one associated with `id`, i.e. the one created when `init()` is called.
393
400
 
394
- Pass `{ value: { browser, browserVersion, model, manufacturer, engine, system, systemFamily, description, fullDescription }, timestamp }` to callback.
401
+ Pass `{ value: { browser, browserVersion, model, manufacturer, engine, system, systemFamily, description, fullDescription, userLanguage }, timestamp }` to callback.
395
402
 
396
403
  ### 💄 Customization
397
404
 
@@ -403,6 +410,7 @@ Pass `{ value: { browser, browserVersion, model, manufacturer, engine, system, s
403
410
 
404
411
  - `.checkInitialized()` Check if the model is initialized. Return a boolean.
405
412
  - `.getFullscreen()` Get fullscreen mode.
413
+ - `.newLanguage(lang = 'en-US')` Set a new language for the calibrator.
406
414
 
407
415
  ### 🎣 Getters
408
416
 
@@ -424,10 +432,11 @@ Getters will get `null` if no data can be found, i.e. the corresponding function
424
432
 
425
433
  The associated timestamp of the following items is the one created at initiation, i.e. when `init()` is called.
426
434
 
427
- - `.bot` If the user agent is a bot or not, an empty string will be returned if no bot detected, e.g., `Googlebot (Search bot) by Google Inc.`.
435
+ - `.bot` If the user agent is a bot or not, `null` will be returned if no bot detected, e.g., `Googlebot (Search bot) by Google Inc.`.
428
436
  - `.browser` The browser type, e.g., `Safari`, `Chrome`.
429
437
  - `.browserVersion` The browser version.
430
438
  - `.deviceType` The type of device, e.g., `desktop`.
439
+ - `.isMobile` The type of device is mobile or not.
431
440
  - `.model` The model type of the device, e.g., `iPad`.
432
441
  - `.manufacturer` The device manufacturer.
433
442
  - `.engine` The browser engine, e.g., `Webkit`.
@@ -435,6 +444,16 @@ The associated timestamp of the following items is the one created at initiation
435
444
  - `.systemFamily` The family name of the device OS, e.g., `OS X`.
436
445
  - `.description` A tidy description of the current environment, e.g., `Chrome 89.0.4389.90 on OS X 11.2.1 64-bit`.
437
446
  - `.fullDescription` The full description of the current environment.
447
+ - `.userLanguage` The language used in the browser, e.g., `en`.
448
+
449
+ #### i18n
450
+
451
+ - `.language` (e.g., `en-US`, `zh-CN`)
452
+ - `.languageNameEnglish` (e.g., `English`, `Chinese (Simplified)`)
453
+ - `.languageNameNative` (e.g., `简体中文`)
454
+ - `.languageDirection` (`LTR` or `RTL`)
455
+ - `.languagePhraseSource` (e.g., `Denis Pelli & Peiling Jiang 2021.10.10`)
456
+ - `.supportedLanguages` An array of all supported languages. Can be called before initialization.
438
457
 
439
458
  #### All Data
440
459
 
@@ -448,6 +467,7 @@ Use the following keywords to retrieve the whole dataset.
448
467
  - `.gazeData`
449
468
  - `.fullScreenData`
450
469
  - `.environmentData`
470
+ - `.languageData`
451
471
 
452
472
  #### Others
453
473
 
@@ -160,6 +160,10 @@ button:active {
160
160
  border: 1px solid #ac0d0d;
161
161
  }
162
162
 
163
+ #lang-picker {
164
+ padding: 0.25rem;
165
+ }
166
+
163
167
  /* -------------------------------------------------------------------------- */
164
168
 
165
169
  @media (min-width: 721px) {
@@ -299,6 +299,7 @@ function trackGaze(e) {
299
299
  {
300
300
  showVideo: false,
301
301
  },
302
+ null,
302
303
  data => {
303
304
  gazeP.innerHTML = gotData(
304
305
  `The gaze position is [${data.value.x} px, ${
@@ -51,6 +51,8 @@
51
51
  >
52
52
  </p>
53
53
 
54
+ <div id="rc-language"></div>
55
+
54
56
  <h2>Functions</h2>
55
57
 
56
58
  <div id="functions" class="flex-wrapper"></div>
@@ -128,6 +130,12 @@
128
130
  'nearPointCm',
129
131
  'gazePositionPx',
130
132
  'isFullscreen',
133
+ 'id',
134
+ 'language',
135
+ 'languageNameEnglish',
136
+ 'languageNameNative',
137
+ 'languageDirection',
138
+ 'languagePhraseSource',
131
139
  ]
132
140
 
133
141
  const gettersEnv = [
@@ -142,6 +150,7 @@
142
150
  'systemFamily',
143
151
  'description',
144
152
  'fullDescription',
153
+ 'userLanguage',
145
154
  'version',
146
155
  ]
147
156
 
@@ -163,6 +172,23 @@
163
172
  setGetters(document.getElementById('getters-exp'), gettersExp)
164
173
  setGetters(document.getElementById('getters-env'), gettersEnv)
165
174
 
175
+ // i18n
176
+ const langPickerParent = document.getElementById('rc-language')
177
+ let langInner = '<select name="lang" id="lang-picker">'
178
+ for (let lang of RemoteCalibrator.supportedLanguages) {
179
+ langInner += `<option value="${lang.language}">${lang.languageNameNative}</option>`
180
+ }
181
+ langInner += '</select>'
182
+ langPickerParent.innerHTML = langInner
183
+
184
+ document.querySelector('#lang-picker').onchange = e => {
185
+ RemoteCalibrator.newLanguage(
186
+ document.querySelector('#lang-picker').value
187
+ )
188
+ RemoteCalibrator.resetPanel()
189
+ }
190
+ //
191
+
166
192
  initialize({ target: document.getElementById('init-button') })
167
193
  </script>
168
194
  <!-- GitHub corner -->
@@ -0,0 +1,12 @@
1
+ module.exports = {
2
+ env: {
3
+ commonjs: true,
4
+ es2021: true,
5
+ node: true,
6
+ },
7
+ extends: 'eslint:recommended',
8
+ parserOptions: {
9
+ ecmaVersion: 12,
10
+ },
11
+ rules: {},
12
+ }
@@ -0,0 +1,62 @@
1
+ const process = require('process')
2
+ const fs = require('fs')
3
+ const XLSX = require('xlsx')
4
+ const google = require('googleapis')
5
+
6
+ const auth = new google.Auth.GoogleAuth({
7
+ keyFile: `${__dirname}/credentials.json`,
8
+ scopes: 'https://www.googleapis.com/auth/spreadsheets',
9
+ })
10
+
11
+ async function processLanguageSheet() {
12
+ const spreadsheetId = '1UFfNikfLuo8bSromE34uWDuJrMPFiJG3VpoQKdCGkII'
13
+ const googleSheets = new google.sheets_v4.Sheets()
14
+ const rows = await googleSheets.spreadsheets.values.get({
15
+ auth,
16
+ spreadsheetId,
17
+ range: 'Sheet1',
18
+ })
19
+
20
+ const rowsJSON = XLSX.utils.sheet_to_json(
21
+ XLSX.utils.aoa_to_sheet(rows.data.values),
22
+ {
23
+ defval: '',
24
+ }
25
+ )
26
+
27
+ const data = {}
28
+ for (let phrase of rowsJSON) {
29
+ const { language, ...translations } = phrase
30
+ if (language.includes('RC_') || language.includes('EE_'))
31
+ data[language] = translations
32
+ }
33
+
34
+ const exportWarning = `/*
35
+ Do not modify this file! Run npm \`npm run phrases\` at ROOT of this project to fetch from the Google Sheets.
36
+ https://docs.google.com/spreadsheets/d/1UFfNikfLuo8bSromE34uWDuJrMPFiJG3VpoQKdCGkII/edit#gid=0
37
+ */\n\n`
38
+ const exportHandle = `export const phrases = `
39
+
40
+ fs.writeFile(
41
+ `${process.cwd()}/src/i18n.js`,
42
+ exportWarning + exportHandle + JSON.stringify(data) + '\n',
43
+ error => {
44
+ if (error) {
45
+ console.log("Error! Couldn't write to the file.", error)
46
+ } else {
47
+ console.log(
48
+ 'EasyEyes International Phrases fetched and written into files successfully.'
49
+ )
50
+ }
51
+ }
52
+ )
53
+ }
54
+
55
+ require('dns').resolve('www.google.com', function (err) {
56
+ if (err) {
57
+ console.log('No internet connection. Skip fetching phrases.')
58
+ } else {
59
+ console.log('Fetching up-to-date phrases...')
60
+ processLanguageSheet()
61
+ }
62
+ })