Spaces:
Running
Running
Upload 8 files
Browse files- enable-threads.js +79 -0
- icon.svg +11 -0
- imhex.js +0 -0
- imhex.wasm +3 -0
- imhex.wasm.size +1 -0
- index.html +103 -18
- style.css +178 -18
- 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 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
}
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
font-size: 15px;
|
| 14 |
-
margin-bottom: 10px;
|
| 15 |
-
margin-top: 5px;
|
| 16 |
}
|
| 17 |
|
| 18 |
-
.
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
border: 1px solid lightgray;
|
| 23 |
-
border-radius: 16px;
|
| 24 |
}
|
| 25 |
|
| 26 |
-
.
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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}% [${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 |
+
})
|