shethjenil commited on
Commit
c291ab9
·
verified ·
1 Parent(s): 8820233

Upload 8 files

Browse files
Files changed (8) hide show
  1. enable-threads.js +79 -0
  2. icon.svg +11 -0
  3. imhex.js +0 -0
  4. imhex.wasm +3 -0
  5. imhex.wasm.size +1 -0
  6. index.html +103 -18
  7. style.css +178 -18
  8. wasm-config.js +275 -0
enable-threads.js ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // NOTE: This file creates a service worker that cross-origin-isolates the page (read more here: https://web.dev/coop-coep/) which allows us to use wasm threads.
2
+ // Normally you would set the COOP and COEP headers on the server to do this, but Github Pages doesn't allow this, so this is a hack to do that.
3
+
4
+ /* Edited version of: coi-serviceworker v0.1.6 - Guido Zuidhof, licensed under MIT */
5
+ // From here: https://github.com/gzuidhof/coi-serviceworker
6
+ if(typeof window === 'undefined') {
7
+ self.addEventListener("install", () => self.skipWaiting());
8
+ self.addEventListener("activate", e => e.waitUntil(self.clients.claim()));
9
+
10
+ async function handleFetch(request) {
11
+ if(request.cache === "only-if-cached" && request.mode !== "same-origin") {
12
+ return;
13
+ }
14
+
15
+ if(request.mode === "no-cors") { // We need to set `credentials` to "omit" for no-cors requests, per this comment: https://bugs.chromium.org/p/chromium/issues/detail?id=1309901#c7
16
+ request = new Request(request.url, {
17
+ cache: request.cache,
18
+ credentials: "omit",
19
+ headers: request.headers,
20
+ integrity: request.integrity,
21
+ destination: request.destination,
22
+ keepalive: request.keepalive,
23
+ method: request.method,
24
+ mode: request.mode,
25
+ redirect: request.redirect,
26
+ referrer: request.referrer,
27
+ referrerPolicy: request.referrerPolicy,
28
+ signal: request.signal,
29
+ });
30
+ }
31
+
32
+ let r = await fetch(request).catch(e => console.error(e));
33
+
34
+ if(r.status === 0) {
35
+ return r;
36
+ }
37
+
38
+ const headers = new Headers(r.headers);
39
+ headers.set("Cross-Origin-Embedder-Policy", "require-corp"); // or: require-corp
40
+ headers.set("Cross-Origin-Opener-Policy", "same-origin");
41
+
42
+ return new Response(r.body, { status: r.status, statusText: r.statusText, headers });
43
+ }
44
+
45
+ self.addEventListener("fetch", function(e) {
46
+ e.respondWith(handleFetch(e.request)); // respondWith must be executed synchonously (but can be passed a Promise)
47
+ });
48
+
49
+ } else {
50
+ (async function() {
51
+ if(window.crossOriginIsolated !== false) return;
52
+
53
+ if (!('serviceWorker' in navigator)) {
54
+ alert("Your browser doesn't support service workers.\nIf you're using Firefox, you need to not be in a private window.")
55
+ }
56
+
57
+ let registration = await navigator.serviceWorker.register(window.document.currentScript.src).catch(e => console.error("COOP/COEP Service Worker failed to register:", e));
58
+ if(registration) {
59
+ console.log("COOP/COEP Service Worker registered", registration.scope);
60
+
61
+ registration.addEventListener("updatefound", () => {
62
+ console.log("Reloading page to make use of updated COOP/COEP Service Worker.");
63
+ window.location.reload();
64
+ });
65
+
66
+ // If the registration is active, but it's not controlling the page
67
+ if(registration.active && !navigator.serviceWorker.controller) {
68
+ console.log("Reloading page to make use of COOP/COEP Service Worker.");
69
+ window.location.reload();
70
+ }
71
+ }
72
+ })();
73
+ }
74
+
75
+ // Code to deregister:
76
+ // let registrations = await navigator.serviceWorker.getRegistrations();
77
+ // for(let registration of registrations) {
78
+ // await registration.unregister();
79
+ // }
icon.svg ADDED
imhex.js ADDED
The diff for this file is too large to render. See raw diff
 
imhex.wasm ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fe42b1b242f9d164291667c8fbc4cb2702c363136e8da35b601ab6145fc84577
3
+ size 31700407
imhex.wasm.size ADDED
@@ -0,0 +1 @@
 
 
1
+ 31700407
index.html CHANGED
@@ -1,19 +1,104 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <!doctype html>
2
+ <html lang="en-us">
3
+ <head>
4
+ <title>ImHex Web - Free Online Hex Editor for Reverse Engineers</title>
5
+
6
+ <link rel="manifest" href="manifest.json">
7
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
8
+ <meta name="viewport" content="width=device-width, initial-scale=1">
9
+
10
+ <!-- Primary Meta Tags -->
11
+ <meta name="title" content="ImHex">
12
+ <meta name="description" content="Free and extremely powerful Online Hex Editor for your Web Browser. ImHex is a free and open source Hex Editor for Reverse Engineers and Developers and Data Analysts.">
13
+ <link rel="icon" type="image/x-icon" href="favicon.ico">
14
+ <link rel="apple-touch-icon" href="icon.svg">
15
+
16
+ <!-- Open Graph / Facebook -->
17
+ <meta property="og:type" content="website">
18
+ <meta property="og:url" content="https://imhex.werwolv.net/">
19
+ <meta property="og:title" content="ImHex Web - Online Hex Editor">
20
+ <meta property="og:image" content="https://imhex.werwolv.net/assets/splash_wasm.png">
21
+
22
+ <!-- Twitter -->
23
+ <meta property="twitter:card" content="summary_large_image">
24
+ <meta property="twitter:url" content="https://imhex.werwolv.net/">
25
+ <meta property="twitter:title" content="ImHex Web - Online Hex Editor">
26
+ <meta property="twitter:description"
27
+ content="A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.">
28
+ <meta property="twitter:image" content="https://imhex.werwolv.net/assets/splash_wasm.png">
29
+
30
+ <link rel="stylesheet" type="text/css" href="style.css">
31
+
32
+ <script type="application/ld+json">
33
+ {
34
+ "@context": "https://schema.org",
35
+ "@type": "Organization",
36
+ "alumni": "WerWolv",
37
+ "email": "hey@werwolv.net",
38
+ "founder": "WerWolv",
39
+ "slogan": "A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.",
40
+ "url": "https://imhex.werwolv.net",
41
+ "logo": "https://imhex.werwolv.net/assets/logos/logo.svg"
42
+ }
43
+ </script>
44
+
45
+ <script type="application/ld+json">
46
+ {
47
+ "@context": "https://schema.org",
48
+ "@type": "SoftwareApplication",
49
+ "name": "ImHex Web",
50
+ "operatingSystem": "Windows, MacOS, Linux",
51
+ "applicationCategory": "DeveloperApplication",
52
+ "offers": {
53
+ "@type": "Offer",
54
+ "price": "0",
55
+ "priceCurrency": "USD"
56
+ }
57
+ }
58
+ </script>
59
+
60
+ <script src="enable-threads.js"></script>
61
+ <link rel="stylesheet" href="style.css">
62
+ </head>
63
+ <body>
64
+ <div id="loading" class="centered">
65
+ <img src="https://raw.githubusercontent.com/WerWolv/ImHex/master/plugins/builtin/romfs/assets/dark/banner.svg" id="logo" alt="ImHex Logo">
66
+ <h1>A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.</h1>
67
+ <h2>Available both natively and on the web</h2>
68
+ <h5>ImHex runs directly in your web browser with the help of Emscripten and WebAssembly.</h5>
69
+
70
+ <div style="height: 50%">
71
+ <div style="height: 30%"> </div>
72
+ <h2 id="not_working">
73
+ Not loading in your Browser? <a href="https://imhex.werwolv.net">Try the native version</a>
74
+ </h2>
75
+ <div class="progress-bar-container">
76
+ <div class="progress progress-moved">
77
+ <div class="progress-bar" id="progress-bar-content">
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </div>
82
+
83
+
84
+
85
+ <div class="loading_ripple">
86
+ <div class="lds-ripple"><div></div><div></div></div>
87
+ </div>
88
+
89
+ <div style="height: 10%">
90
+ </div>
91
+
92
+ <div class="footer">
93
+ <a href="https://imhex.werwolv.net">Homepage</a>
94
+ <p>Made with ♥️ by the ImHex Team</p>
95
+ <a href="https://github.com/WerWolv/ImHex">GitHub</a>
96
+ </div>
97
+ </div>
98
+
99
+ <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
100
+
101
+ <script src="wasm-config.js"></script>
102
+ <script async src="imhex.js"></script>
103
+ </body>
104
+ </html>
style.css CHANGED
@@ -1,28 +1,188 @@
 
 
 
 
 
 
1
  body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
 
 
 
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ html, body {
2
+ height: 100%;
3
+ margin: 0;
4
+ user-select: none;
5
+ }
6
+
7
  body {
8
+ display: flex;
9
+ align-items: center;
10
+ background-color: #121212;
11
+ overflow: hidden;
12
+ }
13
+
14
+ .emscripten {
15
+ padding-right: 0;
16
+ margin-left: auto;
17
+ margin-right: auto;
18
+ display: none;
19
+ border: 0 none;
20
+ image-rendering: smooth;
21
+ }
22
+
23
+ h1, h2, h5 {
24
+ color: #F0F0F0;
25
+ font-size: 20px;
26
+ font-family: monospace;
27
+ width: 100%;
28
+ text-align: center;
29
+ margin-top: 60px;
30
+ margin-bottom: 10px;
31
+ }
32
+
33
+ h2 {
34
+ margin-top: 15px;
35
+ font-size: 17px;
36
+ }
37
+
38
+ h5 {
39
+ margin-top: 0;
40
+ font-size: 17px;
41
+ }
42
+
43
+ #not_working {
44
+ opacity: 0;
45
+ }
46
+
47
+ #not_working.visible {
48
+ opacity: 1;
49
+ transition: opacity 2s ease;
50
+ }
51
+
52
+ a {
53
+ color: #7893ff;
54
+ text-decoration: none;
55
+ }
56
+
57
+ a:hover {
58
+ text-shadow: #3a4677 0 0 10px;
59
  }
60
 
61
+ .footer {
62
+ width: 100%;
63
+ height: 20px;
64
+ position: absolute;
65
+ bottom: 0;
66
+ text-align: center;
67
+ color: #F0F0F0;
68
+ background-color: #0A0A0A;
69
+ padding: 10px;
70
+ font-family: monospace;
71
+ font-size: 15px;
72
+
73
+ display: flex;
74
+ justify-content: center;
75
+ align-items: center;
76
+ flex-direction: row;
77
+ gap: 10%;
78
+ }
79
+
80
+ .centered {
81
+ display: flex;
82
+ flex-direction: column;
83
+ justify-content: center;
84
+ align-items: center;
85
+
86
+ width: 100%;
87
+ height: 100%;
88
+ }
89
+
90
+
91
+ .lds-ripple {
92
+ display: inline-block;
93
+ position: relative;
94
+ width: 80px;
95
+ height: 80px;
96
+ }
97
+ .lds-ripple div {
98
+ position: absolute;
99
+ border: 4px solid #fff;
100
+ opacity: 1;
101
+ border-radius: 50%;
102
+ animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
103
+ }
104
+ .lds-ripple div:nth-child(2) {
105
+ animation-delay: -0.5s;
106
  }
107
+ @keyframes lds-ripple {
108
+ 0% {
109
+ top: 36px;
110
+ left: 36px;
111
+ width: 0;
112
+ height: 0;
113
+ opacity: 0;
114
+ }
115
+ 4.9% {
116
+ top: 36px;
117
+ left: 36px;
118
+ width: 0;
119
+ height: 0;
120
+ opacity: 0;
121
+ }
122
+ 5% {
123
+ top: 36px;
124
+ left: 36px;
125
+ width: 0;
126
+ height: 0;
127
+ opacity: 1;
128
+ }
129
+ 100% {
130
+ top: 0;
131
+ left: 0;
132
+ width: 72px;
133
+ height: 72px;
134
+ opacity: 0;
135
+ }
136
+ }
137
+
138
 
139
+ :root {
140
+ --progress: 0%;
 
 
 
141
  }
142
 
143
+ .progress-bar-container {
144
+ margin: 100px auto;
145
+ width: 600px;
146
+ text-align: center;
 
 
147
  }
148
 
149
+ .progress {
150
+ padding: 6px;
151
+ border-radius: 30px;
152
+ background: rgba(0, 0, 0, 0.25);
153
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.25),
154
+ 0 1px rgba(255, 255, 255, 0.08);
155
  }
156
+
157
+ .progress-bar {
158
+ color: rgba(240, 240, 240, 0.9);
159
+ height: 18px;
160
+ border-radius: 30px;
161
+ font-size: 13px;
162
+ font-family: monospace;
163
+ font-weight: bold;
164
+ text-wrap: avoid;
165
+ white-space: nowrap;
166
+ overflow: hidden;
167
+ background-image: linear-gradient(
168
+ to bottom,
169
+ rgba(255, 255, 255, 0.2),
170
+ rgba(255, 255, 255, 0.0)
171
+ );
172
+ }
173
+
174
+ .progress-moved .progress-bar {
175
+ width: var(--progress);
176
+ background-color: #3864cb;
177
+ }
178
+
179
+ #logo {
180
+ height: 25%;
181
+ margin-top: 50px;
182
+ }
183
+
184
+ .canvas-fixed {
185
+ position: absolute;
186
+ top: 0;
187
+ left: 0;
188
+ }
wasm-config.js ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ let wasmSize = null;
2
+ // See comment in dist/web/Dockerfile about imhex.wasm.size
3
+ fetch("imhex.wasm.size").then(async (resp) => {
4
+ wasmSize = parseInt((await resp.text()).trim());
5
+ console.log(`Real WASM binary size is ${wasmSize} bytes`);
6
+ });
7
+
8
+ // Monkeypatch WebAssembly to have a progress bar
9
+ // inspired from: https://github.com/WordPress/wordpress-playground/pull/46 (but had to be modified)
10
+ function monkeyPatch(progressFun) {
11
+ const _instantiateStreaming = WebAssembly.instantiateStreaming;
12
+ WebAssembly.instantiateStreaming = async (responsePromise, ...args) => {
13
+ // Do not collect wasm content length here see above
14
+ let response = await responsePromise
15
+ const file = response.url.substring(
16
+ new URL(response.url).origin.length + 1
17
+ );
18
+ const reportingResponse = new Response(
19
+ new ReadableStream(
20
+ {
21
+ async start(controller) {
22
+ const reader = response.clone().body.getReader();
23
+ let loaded = 0;
24
+ for (; ;) {
25
+ const { done, value } = await reader.read();
26
+ if (done) {
27
+ if(wasmSize) progressFun(file, wasmSize);
28
+ break;
29
+ }
30
+ loaded += value.byteLength;
31
+ progressFun(file, loaded);
32
+ controller.enqueue(value);
33
+ }
34
+ controller.close();
35
+ }
36
+ },
37
+ {
38
+ status: response.status,
39
+ statusText: response.statusText
40
+ }
41
+ )
42
+ );
43
+ for (const pair of response.headers.entries()) {
44
+ reportingResponse.headers.set(pair[0], pair[1]);
45
+ }
46
+
47
+ return _instantiateStreaming(reportingResponse, ...args);
48
+ }
49
+ }
50
+ monkeyPatch((file, done) => {
51
+ if (!wasmSize) return;
52
+ if (done > wasmSize) {
53
+ console.warn(`Downloaded binary size ${done} is larger than expected WASM size ${wasmSize}`);
54
+ return;
55
+ }
56
+
57
+ const percent = ((done / wasmSize) * 100).toFixed(0);
58
+ const mibNow = (done / 1024**2).toFixed(1);
59
+ const mibTotal = (wasmSize / 1024**2).toFixed(1);
60
+
61
+ let root = document.querySelector(':root');
62
+ root.style.setProperty("--progress", `${percent}%`)
63
+ document.getElementById("progress-bar-content").innerHTML = `${percent}% &nbsp;[${mibNow} MiB / ${mibTotal} MiB]`;
64
+ });
65
+
66
+ function glfwSetCursorCustom(wnd, shape) {
67
+ let body = document.getElementsByTagName("body")[0]
68
+ switch (shape) {
69
+ case 0x00036001: // GLFW_ARROW_CURSOR
70
+ body.style.cursor = "default";
71
+ break;
72
+ case 0x00036002: // GLFW_IBEAM_CURSOR
73
+ body.style.cursor = "text";
74
+ break;
75
+ case 0x00036003: // GLFW_CROSSHAIR_CURSOR
76
+ body.style.cursor = "crosshair";
77
+ break;
78
+ case 0x00036004: // GLFW_HAND_CURSOR
79
+ body.style.cursor = "pointer";
80
+ break;
81
+ case 0x00036005: // GLFW_HRESIZE_CURSOR
82
+ body.style.cursor = "ew-resize";
83
+ break;
84
+ case 0x00036006: // GLFW_VRESIZE_CURSOR
85
+ body.style.cursor = "ns-resize";
86
+ break;
87
+ default:
88
+ body.style.cursor = "default";
89
+ break;
90
+ }
91
+
92
+ }
93
+
94
+ function glfwCreateStandardCursorCustom(shape) {
95
+ return shape
96
+ }
97
+
98
+ var notWorkingTimer = setTimeout(() => {
99
+ document.getElementById("not_working").classList.add("visible")
100
+ }, 5000);
101
+
102
+ var Module = {
103
+ preRun: [],
104
+ postRun: function() {
105
+ // Patch the emscripten GLFW module to send mouse and touch events in the right order
106
+ // For ImGui interactions to correctly work with touch input, MousePos events need
107
+ // to be processed first and then MouseButton events in the next frame. By default,
108
+ // GLFW does the exact opposite, which causes buttons to require two taps to register
109
+ // and windows get "stuck" to the cursor when dragged or resized
110
+ GLFW.onMousemove = event => {
111
+ if (event.type === "touchmove") {
112
+ event.preventDefault();
113
+ let primaryChanged = false;
114
+ for (let i of event.changedTouches) {
115
+ if (GLFW.primaryTouchId === i.identifier) {
116
+ Browser.setMouseCoords(i.pageX, i.pageY);
117
+ primaryChanged = true;
118
+ break;
119
+ }
120
+ }
121
+ if (!primaryChanged) {
122
+ return;
123
+ }
124
+ } else {
125
+ Browser.calculateMouseEvent(event);
126
+ }
127
+ };
128
+
129
+ GLFW.onMouseButtonChanged = (event, status) => {
130
+ if (!GLFW.active) return;
131
+ if (event.target != Module["canvas"]) return;
132
+ const isTouchType = event.type === "touchstart" || event.type === "touchend" || event.type === "touchcancel";
133
+ let eventButton = 0;
134
+ if (isTouchType) {
135
+ event.preventDefault();
136
+ let primaryChanged = false;
137
+ if (GLFW.primaryTouchId === null && event.type === "touchstart" && event.targetTouches.length > 0) {
138
+ const chosenTouch = event.targetTouches[0];
139
+ GLFW.primaryTouchId = chosenTouch.identifier;
140
+ Browser.setMouseCoords(chosenTouch.pageX, chosenTouch.pageY);
141
+ primaryChanged = true;
142
+ } else if (event.type === "touchend" || event.type === "touchcancel") {
143
+ for (let i of event.changedTouches) {
144
+ if (GLFW.primaryTouchId === i.identifier) {
145
+ GLFW.primaryTouchId = null;
146
+ primaryChanged = true;
147
+ break;
148
+ }
149
+ }
150
+ }
151
+ if (!primaryChanged) {
152
+ return;
153
+ }
154
+ } else {
155
+ Browser.calculateMouseEvent(event);
156
+ eventButton = GLFW.DOMToGLFWMouseButton(event);
157
+ }
158
+ if (status == 1) {
159
+ GLFW.active.buttons |= (1 << eventButton);
160
+ try {
161
+ event.target.setCapture();
162
+ } catch (e) {}
163
+ } else {
164
+ GLFW.active.buttons &= ~(1 << eventButton);
165
+ }
166
+
167
+ if (GLFW.active.cursorPosFunc) {
168
+ getWasmTableEntry(GLFW.active.cursorPosFunc)(GLFW.active.id, Browser.mouseX, Browser.mouseY);
169
+ }
170
+ if (GLFW.active.mouseButtonFunc) {
171
+ getWasmTableEntry(GLFW.active.mouseButtonFunc)(GLFW.active.id, eventButton, status, GLFW.getModBits(GLFW.active));
172
+ }
173
+ };
174
+ },
175
+ onRuntimeInitialized: function() {
176
+ // Triggered when the wasm module is loaded and ready to use.
177
+ document.getElementById("loading").style.display = "none"
178
+ document.getElementById("canvas").style.display = "initial"
179
+
180
+ clearTimeout(notWorkingTimer);
181
+ },
182
+ print: (function() { })(),
183
+ printErr: function(text) { },
184
+ canvas: (function() {
185
+ const canvas = document.getElementById('canvas');
186
+ canvas.addEventListener("webglcontextlost", function(e) {
187
+ alert('WebGL context lost, please reload the page');
188
+ e.preventDefault();
189
+ }, false);
190
+
191
+ // Turn long touches into right-clicks
192
+ let timer = null;
193
+ canvas.addEventListener('touchstart', event => {
194
+ timer = setTimeout(() => {
195
+ let eventArgs = {
196
+ bubbles: true,
197
+ cancelable: true,
198
+ view: window,
199
+ screenX: event.touches[0].screenX,
200
+ screenY: event.touches[0].screenY,
201
+ clientX: event.touches[0].clientX,
202
+ clientY: event.touches[0].clientY,
203
+ button: 2,
204
+ buttons: 2,
205
+ relatedTarget: event.target,
206
+ region: event.region
207
+ }
208
+
209
+ canvas.dispatchEvent(new MouseEvent('mousedown', eventArgs));
210
+ canvas.dispatchEvent(new MouseEvent('mouseup', eventArgs));
211
+ }, 400);
212
+ });
213
+
214
+ canvas.addEventListener('touchend', event => {
215
+ if (timer) {
216
+ clearTimeout(timer);
217
+ timer = null;
218
+ }
219
+ });
220
+
221
+ if (typeof WebGL2RenderingContext !== 'undefined') {
222
+ let gl = canvas.getContext('webgl2', { stencil: true });
223
+ if (!gl) {
224
+ console.error('WebGL 2 not available, falling back to WebGL');
225
+ gl = canvas.getContext('webgl', { stencil: true });
226
+ }
227
+ if (!gl) {
228
+ alert('WebGL not available with stencil buffer');
229
+ }
230
+ return canvas;
231
+ } else {
232
+ alert('WebGL 2 not supported by this browser');
233
+ }
234
+ })(),
235
+ setStatus: function(text) { },
236
+ totalDependencies: 0,
237
+ monitorRunDependencies: function(left) {
238
+ },
239
+ instantiateWasm: async function(imports, successCallback) {
240
+ imports.env.glfwSetCursor = glfwSetCursorCustom
241
+ imports.env.glfwCreateStandardCursor = glfwCreateStandardCursorCustom
242
+ let result = await instantiateAsync(null, findWasmBinary(), imports);
243
+ successCallback(result.instance, result.module)
244
+ },
245
+ arguments: []
246
+ };
247
+
248
+ // Handle passing arguments to the wasm module
249
+ const queryString = window.location.search;
250
+ const urlParams = new URLSearchParams(queryString);
251
+ if (urlParams.has("lang")) {
252
+ Module["arguments"].push("--language");
253
+ Module["arguments"].push(urlParams.get("lang"));
254
+ } else if (urlParams.has("save-editor")) {
255
+ Module["arguments"].push("--save-editor");
256
+ Module["arguments"].push("gist");
257
+ Module["arguments"].push(urlParams.get("save-editor"));
258
+ }
259
+
260
+ window.addEventListener('resize', js_resizeCanvas, false);
261
+ function js_resizeCanvas() {
262
+ let canvas = document.getElementById('canvas');
263
+
264
+ canvas.top = document.documentElement.clientTop;
265
+ canvas.left = document.documentElement.clientLeft;
266
+ canvas.width = Math.min(document.documentElement.clientWidth, window.innerWidth || 0);
267
+ canvas.height = Math.min(document.documentElement.clientHeight, window.innerHeight || 0);
268
+ }
269
+
270
+ // Prevent some default browser shortcuts from preventing ImHex ones to work
271
+ document.addEventListener('keydown', e => {
272
+ if (e.ctrlKey) {
273
+ if (e.which == 83) e.preventDefault();
274
+ }
275
+ })