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 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,14 +9,38 @@ 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.
12
26
  - `.isMobile` getter.
13
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
+ - Automatically minimize the mobile address bar when a calibration task starts.
14
30
 
15
31
  ### Changed
16
32
 
17
33
  - Improved UI and performance for small screens and mobile devices.
18
34
  - Take Return instead of Space for confirming screen size measurement.
19
35
 
36
+ ### Fixed
37
+
38
+ - Fatal error due to cannot detect device for VR headsets.
39
+
40
+ ### Removed
41
+
42
+ - The responsive arrow in the screen size calibration with credit card.
43
+
20
44
  ## [0.2.3] - 2021-10-05
21
45
 
22
46
  ### Added
package/README.md CHANGED
@@ -57,8 +57,8 @@ RemoteCalibrator.measureDistance({}, data => {
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).
@@ -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,7 +432,7 @@ 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`.
@@ -436,6 +444,16 @@ The associated timestamp of the following items is the one created at initiation
436
444
  - `.systemFamily` The family name of the device OS, e.g., `OS X`.
437
445
  - `.description` A tidy description of the current environment, e.g., `Chrome 89.0.4389.90 on OS X 11.2.1 64-bit`.
438
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.
439
457
 
440
458
  #### All Data
441
459
 
@@ -449,6 +467,7 @@ Use the following keywords to retrieve the whole dataset.
449
467
  - `.gazeData`
450
468
  - `.fullScreenData`
451
469
  - `.environmentData`
470
+ - `.languageData`
452
471
 
453
472
  #### Others
454
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) {
@@ -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,71 @@
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
+ for (let phrase in data) {
35
+ for (let lang in data[phrase]) {
36
+ if (data[phrase][lang].includes('XX'))
37
+ data[phrase][lang] = data[phrase][lang]
38
+ .replace('XXX', 'xxx')
39
+ .replace('XX', 'xx')
40
+ }
41
+ }
42
+
43
+ const exportWarning = `/*
44
+ Do not modify this file! Run npm \`npm run phrases\` at ROOT of this project to fetch from the Google Sheets.
45
+ https://docs.google.com/spreadsheets/d/1UFfNikfLuo8bSromE34uWDuJrMPFiJG3VpoQKdCGkII/edit#gid=0
46
+ */\n\n`
47
+ const exportHandle = `export const phrases = `
48
+
49
+ fs.writeFile(
50
+ `${process.cwd()}/src/i18n.js`,
51
+ exportWarning + exportHandle + JSON.stringify(data) + '\n',
52
+ error => {
53
+ if (error) {
54
+ console.log("Error! Couldn't write to the file.", error)
55
+ } else {
56
+ console.log(
57
+ 'EasyEyes International Phrases fetched and written into files successfully.'
58
+ )
59
+ }
60
+ }
61
+ )
62
+ }
63
+
64
+ require('dns').resolve('www.google.com', function (err) {
65
+ if (err) {
66
+ console.log('No internet connection. Skip fetching phrases.')
67
+ } else {
68
+ console.log('Fetching up-to-date phrases...')
69
+ processLanguageSheet()
70
+ }
71
+ })