@easyeyes_threshold/runtime 1.20260909.84286
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/LICENSE +21 -0
- package/README.md +69 -0
- package/coi-serviceworker.js +133 -0
- package/components/images/favicon.ico +0 -0
- package/components/images/ios_settings.png +0 -0
- package/components/multiple-displays/multipleDisplay.css +22 -0
- package/components/multiple-displays/peripheralDisplay.html +17 -0
- package/components/multiple-displays/peripheralDisplay.js +37 -0
- package/index-stepper-bool.html +152 -0
- package/index.html +153 -0
- package/js/easyeyes_wasm.js +2 -0
- package/js/easyeyes_wasm.min.js.map +1 -0
- package/js/first.min.js +2 -0
- package/js/first.min.js.map +1 -0
- package/js/index.js +7 -0
- package/js/index.min.js.map +1 -0
- package/js/threshold.css +1 -0
- package/js/threshold.min.js +327 -0
- package/js/threshold.min.js.map +1 -0
- package/models/README +6 -0
- package/models/detector/group1-shard1of1.bin +0 -0
- package/models/detector/model.json +1 -0
- package/models/landmark/group1-shard1of1.bin +0 -0
- package/models/landmark/model.json +1 -0
- package/models/landmark_attention/group1-shard1of1.bin +0 -0
- package/models/landmark_attention/model.json +1 -0
- package/package.json +51 -0
- package/psychojs/LICENSE.md +7 -0
- package/recruitmentServiceConfig.csv +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 New York University (Denis Pelli and the EasyEyes team)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# EasyEyes Threshold
|
|
2
|
+
|
|
3
|
+
📖 [**Manual**](https://docs.google.com/document/d/e/2PACX-1vTTrqaSyva2afVupLchBjfTHc_YW5jAbEexGbudXMJ9xMKPBDA3nxQmHXa4wjnAoSVabeEA8T9CGIMa/pub)<br />
|
|
4
|
+
📝 [**Parameter Glossary**](https://docs.google.com/spreadsheets/d/e/2PACX-1vQ8QswX_5h_oNS2Ly6VgoONGIxJHqDFjdZqWY_HUxH2Nr_LNkGDBL8FXz74l9BxVNR2AIXGhHir9GAd/pub?gid=1287694458&single=true&output=pdf)<br />
|
|
5
|
+
🌏 [**Webapp**](https://easyeyes.app/experiment/) (beta)
|
|
6
|
+
|
|
7
|
+
A PsychoJS-based experiment generator for the measuring various psychometric thresholds, e.g., crowding. Please visit [https://easyeyes.app/experiment](https://easyeyes.app/experiment/) for more instructions.
|
|
8
|
+
|
|
9
|
+
## Development
|
|
10
|
+
|
|
11
|
+
To clone this repo, use
|
|
12
|
+
|
|
13
|
+
```shell
|
|
14
|
+
git clone --recurse-submodules https://github.com/EasyEyes/threshold.git
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then, run the following for the initial setup
|
|
18
|
+
|
|
19
|
+
```shell
|
|
20
|
+
npm install
|
|
21
|
+
npm run build
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
To develop based on a specific experiment table, use
|
|
25
|
+
|
|
26
|
+
```shell
|
|
27
|
+
npm run examples [tableName].[xlsx] # you may also run `npm run examples` to prepare for all tables in your examples/tables folder
|
|
28
|
+
npm start # interactive selection of built examples
|
|
29
|
+
npm start -- --name=[tableName] # start hot-reload development for specific example
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
You need to rerun `npm run examples [your table name]` every time you edit the HTML or table files.
|
|
33
|
+
|
|
34
|
+
## PsychoJS Version
|
|
35
|
+
|
|
36
|
+
We build EasyEyes Threshold with PsychoJS `2022.1.3`.
|
|
37
|
+
|
|
38
|
+
## Vite Build System (Experimental)
|
|
39
|
+
|
|
40
|
+
We've added Vite as an experimental alternative to Webpack for faster builds. The existing Webpack setup remains fully functional.
|
|
41
|
+
|
|
42
|
+
### New Scripts
|
|
43
|
+
|
|
44
|
+
- `npm run vite-start` - Start Vite development server
|
|
45
|
+
- `npm run vite-build` - Build with Vite
|
|
46
|
+
- `npm run vite-preview` - Preview Vite production build
|
|
47
|
+
- `npm run compare-builds` - Compare Webpack vs Vite build performance (requires Rust)
|
|
48
|
+
|
|
49
|
+
### Performance Comparison
|
|
50
|
+
|
|
51
|
+
To compare build performance between Webpack and Vite:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm run compare-builds
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This Rust-based tool runs each build 3 times and provides detailed statistics including:
|
|
58
|
+
|
|
59
|
+
- Average build times
|
|
60
|
+
- Standard deviation
|
|
61
|
+
- Statistical significance
|
|
62
|
+
- Percentage improvement
|
|
63
|
+
|
|
64
|
+
### Notes
|
|
65
|
+
|
|
66
|
+
- Vite configuration is in `vite.config.js`
|
|
67
|
+
- Both build systems output to the same `js/` directory
|
|
68
|
+
- The Rust comparison tool requires Rust to be installed
|
|
69
|
+
- Existing Webpack scripts (`npm start`, `npm run build`) remain unchanged
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// /*! coi-serviceworker v0.1.6 - Guido Zuidhof, licensed under MIT */
|
|
2
|
+
// let coepCredentialless = false;
|
|
3
|
+
// if (typeof window === "undefined") {
|
|
4
|
+
// self.addEventListener("install", () => self.skipWaiting());
|
|
5
|
+
// self.addEventListener("activate", (event) =>
|
|
6
|
+
// event.waitUntil(self.clients.claim()),
|
|
7
|
+
// );
|
|
8
|
+
|
|
9
|
+
// self.addEventListener("message", (ev) => {
|
|
10
|
+
// if (!ev.data) {
|
|
11
|
+
// return;
|
|
12
|
+
// } else if (ev.data.type === "deregister") {
|
|
13
|
+
// self.registration
|
|
14
|
+
// .unregister()
|
|
15
|
+
// .then(() => {
|
|
16
|
+
// return self.clients.matchAll();
|
|
17
|
+
// })
|
|
18
|
+
// .then((clients) => {
|
|
19
|
+
// clients.forEach((client) => client.navigate(client.url));
|
|
20
|
+
// });
|
|
21
|
+
// } else if (ev.data.type === "coepCredentialless") {
|
|
22
|
+
// coepCredentialless = ev.data.value;
|
|
23
|
+
// }
|
|
24
|
+
// });
|
|
25
|
+
|
|
26
|
+
// self.addEventListener("fetch", function (event) {
|
|
27
|
+
// const r = event.request;
|
|
28
|
+
// const request =
|
|
29
|
+
// coepCredentialless && r.mode === "no-cors"
|
|
30
|
+
// ? new Request(r, {
|
|
31
|
+
// credentials: "omit",
|
|
32
|
+
// })
|
|
33
|
+
// : r;
|
|
34
|
+
|
|
35
|
+
// if (r.cache === "only-if-cached" && r.mode !== "same-origin") {
|
|
36
|
+
// return;
|
|
37
|
+
// }
|
|
38
|
+
|
|
39
|
+
// event.respondWith(
|
|
40
|
+
// fetch(request)
|
|
41
|
+
// .then((response) => {
|
|
42
|
+
// if (response.status === 0) {
|
|
43
|
+
// return response;
|
|
44
|
+
// }
|
|
45
|
+
|
|
46
|
+
// const newHeaders = new Headers(response.headers);
|
|
47
|
+
// newHeaders.set(
|
|
48
|
+
// "Cross-Origin-Embedder-Policy",
|
|
49
|
+
// coepCredentialless ? "credentialless" : "require-corp",
|
|
50
|
+
// );
|
|
51
|
+
// newHeaders.set("Cross-Origin-Opener-Policy", "same-origin");
|
|
52
|
+
|
|
53
|
+
// return new Response(response.body, {
|
|
54
|
+
// status: response.status,
|
|
55
|
+
// statusText: response.statusText,
|
|
56
|
+
// headers: newHeaders,
|
|
57
|
+
// });
|
|
58
|
+
// })
|
|
59
|
+
// .catch((e) => console.error(e)),
|
|
60
|
+
// );
|
|
61
|
+
// });
|
|
62
|
+
// } else {
|
|
63
|
+
// (() => {
|
|
64
|
+
// // You can customize the behavior of this script through a global `coi` variable.
|
|
65
|
+
// const coi = {
|
|
66
|
+
// shouldRegister: () => true,
|
|
67
|
+
// shouldDeregister: () => false,
|
|
68
|
+
// coepCredentialless: () => false,
|
|
69
|
+
// doReload: () => window.location.reload(),
|
|
70
|
+
// quiet: false,
|
|
71
|
+
// ...window.coi,
|
|
72
|
+
// };
|
|
73
|
+
|
|
74
|
+
// const n = navigator;
|
|
75
|
+
|
|
76
|
+
// if (n.serviceWorker && n.serviceWorker.controller) {
|
|
77
|
+
// n.serviceWorker.controller.postMessage({
|
|
78
|
+
// type: "coepCredentialless",
|
|
79
|
+
// value: coi.coepCredentialless(),
|
|
80
|
+
// });
|
|
81
|
+
|
|
82
|
+
// if (coi.shouldDeregister()) {
|
|
83
|
+
// n.serviceWorker.controller.postMessage({ type: "deregister" });
|
|
84
|
+
// }
|
|
85
|
+
// }
|
|
86
|
+
|
|
87
|
+
// // If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are
|
|
88
|
+
// // already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here.
|
|
89
|
+
// if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return;
|
|
90
|
+
|
|
91
|
+
// if (!window.isSecureContext) {
|
|
92
|
+
// !coi.quiet &&
|
|
93
|
+
// console.log(
|
|
94
|
+
// "COOP/COEP Service Worker not registered, a secure context is required.",
|
|
95
|
+
// );
|
|
96
|
+
// return;
|
|
97
|
+
// }
|
|
98
|
+
|
|
99
|
+
// // In some environments (e.g. Chrome incognito mode) this won't be available
|
|
100
|
+
// if (n.serviceWorker) {
|
|
101
|
+
// n.serviceWorker.register(window.document.currentScript.src).then(
|
|
102
|
+
// (registration) => {
|
|
103
|
+
// !coi.quiet &&
|
|
104
|
+
// console.log(
|
|
105
|
+
// "COOP/COEP Service Worker registered",
|
|
106
|
+
// registration.scope,
|
|
107
|
+
// );
|
|
108
|
+
|
|
109
|
+
// registration.addEventListener("updatefound", () => {
|
|
110
|
+
// !coi.quiet &&
|
|
111
|
+
// console.log(
|
|
112
|
+
// "Reloading page to make use of updated COOP/COEP Service Worker.",
|
|
113
|
+
// );
|
|
114
|
+
// coi.doReload();
|
|
115
|
+
// });
|
|
116
|
+
|
|
117
|
+
// // If the registration is active, but it's not controlling the page
|
|
118
|
+
// if (registration.active && !n.serviceWorker.controller) {
|
|
119
|
+
// !coi.quiet &&
|
|
120
|
+
// console.log(
|
|
121
|
+
// "Reloading page to make use of COOP/COEP Service Worker.",
|
|
122
|
+
// );
|
|
123
|
+
// coi.doReload();
|
|
124
|
+
// }
|
|
125
|
+
// },
|
|
126
|
+
// (err) => {
|
|
127
|
+
// !coi.quiet &&
|
|
128
|
+
// console.error("COOP/COEP Service Worker failed to register:", err);
|
|
129
|
+
// },
|
|
130
|
+
// );
|
|
131
|
+
// }
|
|
132
|
+
// })();
|
|
133
|
+
// }
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.form-input-btn {
|
|
2
|
+
font-size: 1.2rem;
|
|
3
|
+
margin: 0 0rem;
|
|
4
|
+
/* width: 5rem; */
|
|
5
|
+
height: 3rem !important;
|
|
6
|
+
outline: none;
|
|
7
|
+
border: none !important;
|
|
8
|
+
border-radius: 7px !important;
|
|
9
|
+
font-weight: 700 !important;
|
|
10
|
+
background-color: #ddd !important;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.form-input-btn:hover {
|
|
14
|
+
outline: 2px solid #999 !important;
|
|
15
|
+
outline-offset: 0.2rem !important;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.form-input-btn:active {
|
|
19
|
+
outline: 2px solid #999 !important;
|
|
20
|
+
outline-offset: 0.2rem !important;
|
|
21
|
+
background-color: #bbb !important;
|
|
22
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Peripheral Display</title>
|
|
7
|
+
<link rel="stylesheet" href="multipleDisplay.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body style="margin: 0">
|
|
10
|
+
<script src="https://cdn.jsdelivr.net/npm/remote-calibrator@0.9.146"></script>
|
|
11
|
+
<script src="./peripheralDisplay.js" type="module"></script>
|
|
12
|
+
<!-- <div
|
|
13
|
+
id="rc-panel-holder"
|
|
14
|
+
style="z-index: 99999; width: calc(100% - 2rem); margin: 0 1rem"
|
|
15
|
+
></div> -->
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
let rc = null;
|
|
2
|
+
|
|
3
|
+
async function runRemoteCalibrator() {
|
|
4
|
+
rc = new RemoteCalibrator();
|
|
5
|
+
|
|
6
|
+
rc.init();
|
|
7
|
+
console.log("RC LD", rc.languageData);
|
|
8
|
+
const tasks = [
|
|
9
|
+
{
|
|
10
|
+
name: "screenSize",
|
|
11
|
+
options: { fullscreen: true, check: false },
|
|
12
|
+
},
|
|
13
|
+
];
|
|
14
|
+
await new Promise(async (resolve) => {
|
|
15
|
+
console.log("Remote Calibrator is running...");
|
|
16
|
+
//sleep for 2 seconds to make sure rc is loaded
|
|
17
|
+
await new Promise((res) => setTimeout(res, 2000));
|
|
18
|
+
rc.panel(
|
|
19
|
+
tasks,
|
|
20
|
+
"#rc-panel-holder",
|
|
21
|
+
{
|
|
22
|
+
debug: false,
|
|
23
|
+
i18n: true,
|
|
24
|
+
},
|
|
25
|
+
() => {
|
|
26
|
+
rc.removePanel();
|
|
27
|
+
rc._removeBackground();
|
|
28
|
+
console.log("Remote Calibrator is done");
|
|
29
|
+
resolve();
|
|
30
|
+
},
|
|
31
|
+
null,
|
|
32
|
+
null,
|
|
33
|
+
null,
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
// await runRemoteCalibrator();
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
|
6
|
+
|
|
7
|
+
<!-- Load serviceWorker as soon as possible -->
|
|
8
|
+
<!-- We explicity enable COOP/COEP to use ffmpeg.wasm -->
|
|
9
|
+
<!-- https://dev.to/stefnotch/enabling-coop-coep-without-touching-the-server-2d3n -->
|
|
10
|
+
<!-- https://github.com/gzuidhof/coi-serviceworker -->
|
|
11
|
+
<script src="coi-serviceworker.js"></script>
|
|
12
|
+
<script
|
|
13
|
+
src="https://js.sentry-cdn.com/8d5c414335e8ff6ebf585b7204830e5f.min.js"
|
|
14
|
+
crossorigin="anonymous"
|
|
15
|
+
data-lazy="no"
|
|
16
|
+
></script>
|
|
17
|
+
|
|
18
|
+
<title>EasyEyes Study</title>
|
|
19
|
+
<link rel="icon" type="image/x-icon" href="components/images/favicon.ico" />
|
|
20
|
+
|
|
21
|
+
<!-- styles -->
|
|
22
|
+
<link
|
|
23
|
+
rel="stylesheet"
|
|
24
|
+
href="https://cdn.jsdelivr.net/npm/jquery-ui-dist@1.12.1/jquery-ui.min.css"
|
|
25
|
+
/>
|
|
26
|
+
|
|
27
|
+
<link
|
|
28
|
+
rel="stylesheet"
|
|
29
|
+
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
|
|
30
|
+
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
|
|
31
|
+
crossorigin="anonymous"
|
|
32
|
+
/>
|
|
33
|
+
<link
|
|
34
|
+
rel="stylesheet"
|
|
35
|
+
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.6.1/font/bootstrap-icons.css"
|
|
36
|
+
/>
|
|
37
|
+
<script
|
|
38
|
+
src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js"
|
|
39
|
+
integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF"
|
|
40
|
+
crossorigin="anonymous"
|
|
41
|
+
></script>
|
|
42
|
+
<meta
|
|
43
|
+
http-equiv="Delegate-CH"
|
|
44
|
+
content="sec-ch-ua-full-version-list https://cloud.51degrees.com; sec-ch-ua-model https://cloud.51degrees.com; sec-ch-ua-platform https://cloud.51degrees.com; sec-ch-ua-platform-version https://cloud.51degrees.com"
|
|
45
|
+
/>
|
|
46
|
+
</head>
|
|
47
|
+
<body>
|
|
48
|
+
<!-- ----------------------------------------------------------------------- -->
|
|
49
|
+
<!-- valid DOM elements -->
|
|
50
|
+
<div id="esc-key-handling-div"></div>
|
|
51
|
+
<div id="rc-panel-holder"></div>
|
|
52
|
+
<div id="root"></div>
|
|
53
|
+
|
|
54
|
+
<!-- Global error handling for script loading -->
|
|
55
|
+
<script>
|
|
56
|
+
(function () {
|
|
57
|
+
// Track failed scripts
|
|
58
|
+
window._failedScripts = [];
|
|
59
|
+
|
|
60
|
+
// Catch JavaScript errors
|
|
61
|
+
window.onerror = function (message, source, lineno, colno, error) {
|
|
62
|
+
console.error("[EasyEyes Error]", {
|
|
63
|
+
message: message,
|
|
64
|
+
source: source,
|
|
65
|
+
line: lineno,
|
|
66
|
+
column: colno,
|
|
67
|
+
error: error,
|
|
68
|
+
});
|
|
69
|
+
return false; // Let default handler run too
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Catch unhandled promise rejections
|
|
73
|
+
window.onunhandledrejection = function (event) {
|
|
74
|
+
console.error("[EasyEyes Unhandled Promise Rejection]", event.reason);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Listen for script/resource load errors
|
|
78
|
+
window.addEventListener(
|
|
79
|
+
"error",
|
|
80
|
+
function (event) {
|
|
81
|
+
if (event.target && event.target.tagName === "SCRIPT") {
|
|
82
|
+
const src = event.target.src || "unknown";
|
|
83
|
+
window._failedScripts.push(src);
|
|
84
|
+
console.error("[EasyEyes Script Load Failed]", src);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
true,
|
|
88
|
+
); // Use capture phase to catch errors before they bubble
|
|
89
|
+
|
|
90
|
+
// Log ready state changes
|
|
91
|
+
document.addEventListener("readystatechange", function () {
|
|
92
|
+
console.log("[EasyEyes] document.readyState:", document.readyState);
|
|
93
|
+
if (document.readyState === "complete") {
|
|
94
|
+
if (window._failedScripts.length > 0) {
|
|
95
|
+
console.error(
|
|
96
|
+
"[EasyEyes] The following scripts failed to load:",
|
|
97
|
+
window._failedScripts,
|
|
98
|
+
);
|
|
99
|
+
} else {
|
|
100
|
+
console.log("[EasyEyes] All scripts loaded successfully.");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
})();
|
|
105
|
+
</script>
|
|
106
|
+
|
|
107
|
+
<!-- external libraries -->
|
|
108
|
+
<script src="js/experimentLanguage.js"></script>
|
|
109
|
+
<script src="https://cdn.jsdelivr.net/npm/remote-calibrator@0.8.881/lib/RemoteCalibrator.min.js"></script>
|
|
110
|
+
<script
|
|
111
|
+
src="https://cdn.jsdelivr.net/npm/speaker-calibration@2.2.269/dist/main.js"
|
|
112
|
+
crossorigin="anonymous"
|
|
113
|
+
></script>
|
|
114
|
+
<!-- <script src="https://www.keypad.website/ExperimentPeer.js"></script> -->
|
|
115
|
+
<!-- <script src="ExperimentPeer.js"></script> -->
|
|
116
|
+
<script
|
|
117
|
+
id="virtual-keypad-peer"
|
|
118
|
+
src="https://cdn.jsdelivr.net/gh/EasyEyes/virtual-keypad/dist/ExperimentPeer.js"
|
|
119
|
+
></script>
|
|
120
|
+
<!-- <script src="main.js" ></script> -->
|
|
121
|
+
<script
|
|
122
|
+
crossorigin
|
|
123
|
+
src="https://cloud.51degrees.com/api/v4/AQSjtocC5XcfFwKc20g.js"
|
|
124
|
+
id="51DegreesScript"
|
|
125
|
+
></script>
|
|
126
|
+
<script src="https://cdn.jsdelivr.net/npm/marked@4.0.7/marked.min.js"></script>
|
|
127
|
+
<!-- PsychoJS originals -->
|
|
128
|
+
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
|
|
129
|
+
<script src="https://cdn.jsdelivr.net/npm/jquery-ui-dist@1.12.1/jquery-ui.min.js"></script>
|
|
130
|
+
<script src="https://cdn.jsdelivr.net/npm/preloadjs@1.0.1/lib/preloadjs.min.js"></script>
|
|
131
|
+
<!-- -------------------------------- TEMP --------------------------------- -->
|
|
132
|
+
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
133
|
+
|
|
134
|
+
<!-- compatibility-check -->
|
|
135
|
+
<script
|
|
136
|
+
src="https://peer.easyeyes.app/main.js"
|
|
137
|
+
type="module"
|
|
138
|
+
crossorigin
|
|
139
|
+
></script>
|
|
140
|
+
|
|
141
|
+
<script
|
|
142
|
+
src="https://connection-manager-14ac1ef82705.herokuapp.com/main.js"
|
|
143
|
+
type="module"
|
|
144
|
+
crossorigin
|
|
145
|
+
></script>
|
|
146
|
+
|
|
147
|
+
<!-- initial load -->
|
|
148
|
+
<script type="module" src="js/first.min.js"></script>
|
|
149
|
+
<!-- experiment -->
|
|
150
|
+
<script type="module" src="js/threshold.min.js" defer></script>
|
|
151
|
+
</body>
|
|
152
|
+
</html>
|
package/index.html
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
|
6
|
+
|
|
7
|
+
<!-- Load serviceWorker as soon as possible -->
|
|
8
|
+
<!-- We explicity enable COOP/COEP to use ffmpeg.wasm -->
|
|
9
|
+
<!-- https://dev.to/stefnotch/enabling-coop-coep-without-touching-the-server-2d3n -->
|
|
10
|
+
<!-- https://github.com/gzuidhof/coi-serviceworker -->
|
|
11
|
+
<script src="coi-serviceworker.js"></script>
|
|
12
|
+
|
|
13
|
+
<script
|
|
14
|
+
src="https://js.sentry-cdn.com/8d5c414335e8ff6ebf585b7204830e5f.min.js"
|
|
15
|
+
crossorigin="anonymous"
|
|
16
|
+
data-lazy="no"
|
|
17
|
+
></script>
|
|
18
|
+
|
|
19
|
+
<title>EasyEyes Study</title>
|
|
20
|
+
<link rel="icon" type="image/x-icon" href="components/images/favicon.ico" />
|
|
21
|
+
|
|
22
|
+
<!-- styles -->
|
|
23
|
+
<link
|
|
24
|
+
rel="stylesheet"
|
|
25
|
+
href="https://cdn.jsdelivr.net/npm/jquery-ui-dist@1.12.1/jquery-ui.min.css"
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
<link
|
|
29
|
+
rel="stylesheet"
|
|
30
|
+
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
|
|
31
|
+
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
|
|
32
|
+
crossorigin="anonymous"
|
|
33
|
+
/>
|
|
34
|
+
<link
|
|
35
|
+
rel="stylesheet"
|
|
36
|
+
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.6.1/font/bootstrap-icons.css"
|
|
37
|
+
/>
|
|
38
|
+
<script
|
|
39
|
+
src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js"
|
|
40
|
+
integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF"
|
|
41
|
+
crossorigin="anonymous"
|
|
42
|
+
></script>
|
|
43
|
+
<meta
|
|
44
|
+
http-equiv="Delegate-CH"
|
|
45
|
+
content="sec-ch-ua-full-version-list https://cloud.51degrees.com; sec-ch-ua-model https://cloud.51degrees.com; sec-ch-ua-platform https://cloud.51degrees.com; sec-ch-ua-platform-version https://cloud.51degrees.com"
|
|
46
|
+
/>
|
|
47
|
+
</head>
|
|
48
|
+
<body>
|
|
49
|
+
<!-- ----------------------------------------------------------------------- -->
|
|
50
|
+
<!-- valid DOM elements -->
|
|
51
|
+
<div id="esc-key-handling-div"></div>
|
|
52
|
+
<div id="rc-panel-holder"></div>
|
|
53
|
+
<div id="root"></div>
|
|
54
|
+
|
|
55
|
+
<!-- Global error handling for script loading -->
|
|
56
|
+
<script>
|
|
57
|
+
(function () {
|
|
58
|
+
// Track failed scripts
|
|
59
|
+
window._failedScripts = [];
|
|
60
|
+
|
|
61
|
+
// Catch JavaScript errors
|
|
62
|
+
window.onerror = function (message, source, lineno, colno, error) {
|
|
63
|
+
console.error("[EasyEyes Error]", {
|
|
64
|
+
message: message,
|
|
65
|
+
source: source,
|
|
66
|
+
line: lineno,
|
|
67
|
+
column: colno,
|
|
68
|
+
error: error,
|
|
69
|
+
});
|
|
70
|
+
return false; // Let default handler run too
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Catch unhandled promise rejections
|
|
74
|
+
window.onunhandledrejection = function (event) {
|
|
75
|
+
console.error("[EasyEyes Unhandled Promise Rejection]", event.reason);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Listen for script/resource load errors
|
|
79
|
+
window.addEventListener(
|
|
80
|
+
"error",
|
|
81
|
+
function (event) {
|
|
82
|
+
if (event.target && event.target.tagName === "SCRIPT") {
|
|
83
|
+
const src = event.target.src || "unknown";
|
|
84
|
+
window._failedScripts.push(src);
|
|
85
|
+
console.error("[EasyEyes Script Load Failed]", src);
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
true,
|
|
89
|
+
); // Use capture phase to catch errors before they bubble
|
|
90
|
+
|
|
91
|
+
// Log ready state changes
|
|
92
|
+
document.addEventListener("readystatechange", function () {
|
|
93
|
+
console.log("[EasyEyes] document.readyState:", document.readyState);
|
|
94
|
+
if (document.readyState === "complete") {
|
|
95
|
+
if (window._failedScripts.length > 0) {
|
|
96
|
+
console.error(
|
|
97
|
+
"[EasyEyes] The following scripts failed to load:",
|
|
98
|
+
window._failedScripts,
|
|
99
|
+
);
|
|
100
|
+
} else {
|
|
101
|
+
console.log("[EasyEyes] All scripts loaded successfully.");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
})();
|
|
106
|
+
</script>
|
|
107
|
+
|
|
108
|
+
<!-- external libraries -->
|
|
109
|
+
<script src="js/experimentLanguage.js"></script>
|
|
110
|
+
<script src="https://cdn.jsdelivr.net/npm/remote-calibrator@0.9.161"></script>
|
|
111
|
+
<script
|
|
112
|
+
src="https://cdn.jsdelivr.net/npm/speaker-calibration@2.2.272/dist/main.js"
|
|
113
|
+
crossorigin="anonymous"
|
|
114
|
+
></script>
|
|
115
|
+
<!-- <script src="https://www.keypad.website/ExperimentPeer.js"></script> -->
|
|
116
|
+
<!-- <script src="ExperimentPeer.js"></script> -->
|
|
117
|
+
<script
|
|
118
|
+
id="virtual-keypad-peer"
|
|
119
|
+
src="https://cdn.jsdelivr.net/gh/EasyEyes/virtual-keypad/dist/ExperimentPeer.js"
|
|
120
|
+
></script>
|
|
121
|
+
<!-- <script src="main.js" ></script> -->
|
|
122
|
+
<script
|
|
123
|
+
crossorigin
|
|
124
|
+
src="https://cloud.51degrees.com/api/v4/AQSjtocC5XcfFwKc20g.js"
|
|
125
|
+
id="51DegreesScript"
|
|
126
|
+
></script>
|
|
127
|
+
<script src="https://cdn.jsdelivr.net/npm/marked@4.0.7/marked.min.js"></script>
|
|
128
|
+
<!-- PsychoJS originals -->
|
|
129
|
+
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
|
|
130
|
+
<script src="https://cdn.jsdelivr.net/npm/jquery-ui-dist@1.12.1/jquery-ui.min.js"></script>
|
|
131
|
+
<script src="https://cdn.jsdelivr.net/npm/preloadjs@1.0.1/lib/preloadjs.min.js"></script>
|
|
132
|
+
<!-- -------------------------------- TEMP --------------------------------- -->
|
|
133
|
+
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
134
|
+
|
|
135
|
+
<!-- compatibility-check -->
|
|
136
|
+
<script
|
|
137
|
+
src="https://peer.easyeyes.app/main.js"
|
|
138
|
+
type="module"
|
|
139
|
+
crossorigin
|
|
140
|
+
></script>
|
|
141
|
+
|
|
142
|
+
<script
|
|
143
|
+
src="https://connection-manager-14ac1ef82705.herokuapp.com/main.js"
|
|
144
|
+
type="module"
|
|
145
|
+
crossorigin
|
|
146
|
+
></script>
|
|
147
|
+
|
|
148
|
+
<!-- initial load -->
|
|
149
|
+
<script type="module" src="js/first.min.js"></script>
|
|
150
|
+
<!-- experiment -->
|
|
151
|
+
<script type="module" src="js/threshold.min.js" defer></script>
|
|
152
|
+
</body>
|
|
153
|
+
</html>
|