diff --git a/config/mime.json b/config/mime.json index 30991cfa..1906fc92 100644 --- a/config/mime.json +++ b/config/mime.json @@ -57,6 +57,8 @@ "gltf": "model/gltf+json", "glb": "model/gltf-binary", "gpx": "application/gpx+xml", + "gp4": "application/x-guitar-pro", + "gp5": "application/x-guitar-pro", "gz": "application/x-gzip", "heic": "image/heic", "heif": "image/heic", @@ -92,6 +94,7 @@ "mef": "image/x-mamiya-mef", "mid": "audio/midi", "midi": "application/x-midi", + "mjs": "application/javascript", "mkv": "video/x-matroska", "mml": "text/mathml", "mng": "video/x-mng", @@ -118,6 +121,7 @@ "ogv": "application/ogg", "orf": "image/x-olympus-orf", "org": "text/org", + "otf": "font/otf", "pdb": "application/x-pilot", "pdf": "application/pdf", "pef": "image/x-pentax-pef", @@ -169,6 +173,7 @@ "tiff": "image/tiff", "tk": "application/x-tcl", "ts": "text/plain", + "ttf": "application/x-font-ttf", "txt": "text/plain", "url": "application/x-url", "vcf": "text/vcard", @@ -185,7 +190,8 @@ "wmlc": "application/vnd.wap.wmlc", "wms": "application/vnd.ogc.wms_xml", "wmv": "video/x-ms-wmv", - "woff": "application/font-woff", + "woff": "font/woff", + "woff2": "font/woff2", "wrl": "x-world/x-vrml", "x3d": "model/x3d+xml", "x3dv": "model/x3d-vrml", diff --git a/public/assets/helpers/loader_wasm.js b/public/assets/helpers/loader_wasm.js index 8a859c53..7051abfe 100644 --- a/public/assets/helpers/loader_wasm.js +++ b/public/assets/helpers/loader_wasm.js @@ -1,3 +1,6 @@ +const DEBUG = false; +const log = (msg) => DEBUG && console.log(msg); + export default async function(baseURL, path, opts = {}) { const wasi = new Wasi(); const wasm = await WebAssembly.instantiateStreaming( @@ -43,11 +46,18 @@ export function readFS(fd) { return file.buffer.subarray(0, end); } +export function clearFS() { + Object.keys(FS).forEach((key) => { + if (key > 2) delete FS[key]; + }); + nextFd = 3; +} + function getFile(path) { const allFds = Object.keys(FS); for (let i=allFds.length - 1; i>0; i--) { if (FS[allFds[i]].path === path) { - console.log(`fileopen fd=${i} path=${path}`); + log(` fileopen fd=${i} path=${path}`); return FS[allFds[i]]; } } @@ -84,7 +94,7 @@ export const syscalls = { console.log(`Stubbed __syscall_stat64 called with pathPtr=${pathPtr}, bufPtr=${bufPtr}`); return 0; // Return 0 for a successful call }, - __syscall_lstat64: () => { + __syscall_lstat64: () => { console.log(`Stubbed __syscall_lstat64 called`); return -1; }, @@ -182,7 +192,14 @@ export class Wasi { if (fd === 1 || fd === 2) { let msg = fd === 1? "stdout: " : "stderr: "; msg += new TextDecoder().decode(readFS(fd)); - FS[fd] = { buffer: new Uint8Array(0), position: 0, path: "" }; + console.log(msg); + FS[fd] = { + buffer: new Uint8Array(0), + position: 0, + path: FS[fd].path || "", + }; + } else { + log(`wasi::fd_write fd=${fd}`); } return 0; } @@ -204,7 +221,7 @@ export class Wasi { length, file.buffer.length - file.position, ); - if (bytesToRead <= 0) { + if (bytesToRead < 0) { break; } memory.set( @@ -214,6 +231,7 @@ export class Wasi { file.position += bytesToRead; totalBytesRead += bytesToRead; } + log(`wasi::fd_read fd=${fd} iovs_len=${iovs_len} totalBytesRead=${totalBytesRead}`); new DataView(this.#instance.exports.memory.buffer).setUint32( nread, totalBytesRead, @@ -222,7 +240,8 @@ export class Wasi { return 0; } - fd_seek(fd, offsetBigInt, whence) { + fd_seek(fd, offsetBigInt, _, whence) { + log(`wasi::fd_seek fd=${fd} offset=${offsetBigInt} whence=${whence}`); const offset = Number(offsetBigInt); const file = FS[fd]; if (!file) { @@ -283,7 +302,6 @@ export class Wasi { } __syscall_openat(dirFd, pathPtr, flags, mode) { - console.debug(`openat called with dirFd=${dirFd}, pathPtr=${pathPtr}, flags=${flags}, mode=${mode}`); const memory = new Uint8Array(this.#instance.exports.memory.buffer); let path = ""; for (let i = pathPtr; memory[i] !== 0; i++) { @@ -292,15 +310,15 @@ export class Wasi { const allFds = Object.keys(FS); for (let i=allFds.length - 1; i>0; i--) { if (FS[allFds[i]].path === path) { - console.log(`fileopen fd=${i} path=${path}`); + log(` syscall::openat::result fd=${i} path=${path}`); return i; } } - return -1; + throw new Error("Unknown file for __syscall_openat"); } __syscall_stat64(pathPtr, buf) { - console.log(`stat64`); + log(` syscall::stat64 pathPtr=${pathPtr}, bufPtr=${buf}`); const memory = new Uint8Array(this.#instance.exports.memory.buffer); let path = ""; for (let i = pathPtr; memory[i] !== 0; i++) { @@ -309,11 +327,6 @@ export class Wasi { const file = getFile(path); const HEAP32 = new Int32Array(this.#instance.exports.memory.buffer); const HEAPU32 = new Uint32Array(this.#instance.exports.memory.buffer); - - const tempI64 = [0, 0]; - const tempDouble = 0; - - // Dummy stat data const stat = { dev: 1, ino: 42, @@ -329,47 +342,32 @@ export class Wasi { mtime: new Date(), ctime: new Date(), }; - // Fill the buffer HEAP32[(buf >> 2)] = stat.dev; HEAP32[((buf + 4) >> 2)] = stat.mode; HEAPU32[((buf + 8) >> 2)] = stat.nlink; HEAP32[((buf + 12) >> 2)] = stat.uid; HEAP32[((buf + 16) >> 2)] = stat.gid; HEAP32[((buf + 20) >> 2)] = stat.rdev; - HEAP32[((buf + 24) >> 2)] = stat.size & 0xFFFFFFFF; // Lower 32 bits - HEAP32[((buf + 28) >> 2)] = Math.floor(stat.size / 4294967296); // Upper 32 bits + HEAP32[((buf + 24) >> 2)] = stat.size & 0xFFFFFFFF; + HEAP32[((buf + 28) >> 2)] = Math.floor(stat.size / 4294967296); HEAP32[((buf + 32) >> 2)] = stat.blksize; HEAP32[((buf + 36) >> 2)] = stat.blocks; - - // Write timestamps - const atimeSeconds = Math.floor(stat.atime.getTime() / 1000); - const atimeNanos = (stat.atime.getTime() % 1000) * 1e6; - HEAP32[((buf + 40) >> 2)] = atimeSeconds; - HEAP32[((buf + 44) >> 2)] = 0; // Upper 32 bits of atime - HEAP32[((buf + 48) >> 2)] = atimeNanos; - - const mtimeSeconds = Math.floor(stat.mtime.getTime() / 1000); - const mtimeNanos = (stat.mtime.getTime() % 1000) * 1e6; - HEAP32[((buf + 56) >> 2)] = mtimeSeconds; - HEAP32[((buf + 60) >> 2)] = 0; // Upper 32 bits of mtime - HEAP32[((buf + 64) >> 2)] = mtimeNanos; - - const ctimeSeconds = Math.floor(stat.ctime.getTime() / 1000); - const ctimeNanos = (stat.ctime.getTime() % 1000) * 1e6; - HEAP32[((buf + 72) >> 2)] = ctimeSeconds; - HEAP32[((buf + 76) >> 2)] = 0; // Upper 32 bits of ctime - HEAP32[((buf + 80) >> 2)] = ctimeNanos; - - // Dummy inode - HEAP32[((buf + 88) >> 2)] = stat.ino & 0xFFFFFFFF; // Lower 32 bits - HEAP32[((buf + 92) >> 2)] = Math.floor(stat.ino / 4294967296); // Upper 32 bits - - console.debug(`Stubbed __syscall_stat64 called with pathPtr=${pathPtr}, bufPtr=${buf}`); + HEAP32[((buf + 40) >> 2)] = Math.floor(stat.atime.getTime() / 1000); + HEAP32[((buf + 44) >> 2)] = 0; + HEAP32[((buf + 48) >> 2)] = (stat.atime.getTime() % 1000) * 1e6; + HEAP32[((buf + 56) >> 2)] = Math.floor(stat.mtime.getTime() / 1000); + HEAP32[((buf + 60) >> 2)] = 0; + HEAP32[((buf + 64) >> 2)] = (stat.mtime.getTime() % 1000) * 1e6; + HEAP32[((buf + 72) >> 2)] = Math.floor(stat.ctime.getTime() / 1000); + HEAP32[((buf + 76) >> 2)] = 0; + HEAP32[((buf + 80) >> 2)] = (stat.ctime.getTime() % 1000) * 1e6; + HEAP32[((buf + 88) >> 2)] = stat.ino & 0xFFFFFFFF; + HEAP32[((buf + 92) >> 2)] = Math.floor(stat.ino / 4294967296); return 0; } __cxa_throw(ptr, type, destructor) { - console.error(`Exception thrown at ptr=${ptr}, type=${type}, destructor=${destructor}`); + console.error(` syscall::cxa_throw ptr=${ptr}, type=${type}, destructor=${destructor}`); throw new Error("WebAssembly exception"); } diff --git a/public/assets/model/plugin.js b/public/assets/model/plugin.js index 8d49305d..04ae98b7 100644 --- a/public/assets/model/plugin.js +++ b/public/assets/model/plugin.js @@ -22,7 +22,7 @@ export function get(mime) { export async function load(mime) { const specs = plugins[mime]; if (!specs) return null; - const [_, url] = specs; + const [, url] = specs; const module = await import(url); return module.default; } diff --git a/public/assets/pages/viewerpage/application_3d.js b/public/assets/pages/viewerpage/application_3d.js index 8f6998f5..3ccf3edb 100644 --- a/public/assets/pages/viewerpage/application_3d.js +++ b/public/assets/pages/viewerpage/application_3d.js @@ -5,7 +5,6 @@ import { AjaxError } from "../../lib/error.js"; import { load as loadPlugin } from "../../model/plugin.js"; import { loadCSS } from "../../helpers/loader.js"; import { createLoader } from "../../components/loader.js"; -import t from "../../locales/index.js"; import ctrlError from "../ctrl_error.js"; import componentDownloader, { init as initDownloader } from "./application_downloader.js"; @@ -18,7 +17,6 @@ import withCube from "./application_3d/scene_cube.js"; import ctrlToolbar from "./application_3d/toolbar.js"; class I3DLoader { - constructor() {} load() { throw new Error("NOT_IMPLEMENTED"); } transform() { throw new Error("NOT_IMPLEMENTED"); } is2D() { return false; } @@ -45,7 +43,7 @@ export default async function(render, { mime, acl$, getDownloadUrl = nop, getFil const removeLoader = createLoader($draw); await effect(rxjs.from(loadPlugin(mime)).pipe( - rxjs.mergeMap(async (loader) => { + rxjs.mergeMap(async(loader) => { if (!loader) { componentDownloader(render, { mime, acl$, getFilename, getDownloadUrl }); return rxjs.EMPTY; @@ -59,11 +57,7 @@ export default async function(render, { mime, acl$, getDownloadUrl = nop, getFil (err) => observer.error(err), )).pipe( removeLoader, - rxjs.mergeMap((mesh) => create3DScene({ - mesh, - $draw, $toolbar, $menubar, - hasCube, mime, is2D: loader.is2D, - })), + rxjs.mergeMap((mesh) => create3DScene({ mime, mesh, is2D: loader.is2D, $draw, $toolbar, $menubar, hasCube })), )), rxjs.catchError((err) => { let _err = err; diff --git a/public/assets/pages/viewerpage/application_skeleton.js b/public/assets/pages/viewerpage/application_skeleton.js index 55f3c2ac..e18afafc 100644 --- a/public/assets/pages/viewerpage/application_skeleton.js +++ b/public/assets/pages/viewerpage/application_skeleton.js @@ -5,6 +5,7 @@ import { load as loadPlugin } from "../../model/plugin.js"; import { loadCSS } from "../../helpers/loader.js"; import { createLoader } from "../../components/loader.js"; import ctrlError from "../ctrl_error.js"; +import componentDownloader, { init as initDownloader } from "./application_downloader.js"; export default function(render, { mime, getFilename, getDownloadUrl, acl$ }) { const $page = createElement(` @@ -35,5 +36,6 @@ export default function(render, { mime, getFilename, getDownloadUrl, acl$ }) { export function init() { return Promise.all([ loadCSS(import.meta.url, "./application_skeleton.css"), + initDownloader(), ]); } diff --git a/public/assets/pages/viewerpage/application_table.js b/public/assets/pages/viewerpage/application_table.js index 80a4995b..7838cd4e 100644 --- a/public/assets/pages/viewerpage/application_table.js +++ b/public/assets/pages/viewerpage/application_table.js @@ -51,7 +51,7 @@ export default async function(render, { mime, getDownloadUrl = nop, getFilename rxjs.mergeMap(async({ response }) => { const loader = getPlugin(mime); if (!loader) throw new TypeError(`unsupported mimetype "${mime}"`); - const [_, url] = loader; + const [, url] = loader; const module = await import(url); const table = new (await module.default(ITable))(response, { $menubar }); STATE.header = table.getHeader(); @@ -98,7 +98,7 @@ export default async function(render, { mime, getDownloadUrl = nop, getFilename // feature: infinite scroll effect(rxjs.fromEvent($dom.tbody, "scroll").pipe( - rxjs.mergeMap(async (e) => { + rxjs.mergeMap(async(e) => { const scrollBottom = e.target.scrollHeight - (e.target.scrollTop + e.target.clientHeight); if (scrollBottom > 0) return; else if (STATE.rows.length <= MAX_ROWS) return; @@ -115,7 +115,7 @@ export default async function(render, { mime, getDownloadUrl = nop, getFilename init$, init$.pipe( rxjs.mergeMap(() => rxjs.fromEvent(window, "resize")), - rxjs.debounce((e) => e.debounce === false ? rxjs.of(null) : rxjs.timer(100)), + rxjs.debounce((e) => e["debounce"] === false ? rxjs.of(null) : rxjs.timer(100)), ), ).pipe( rxjs.tap(() => resizeLastColumnIfNeeded({ @@ -159,7 +159,7 @@ async function buildRows(rows, legends, $tbody, padding, isInit, withClear) { `)); if (!isInit) { const e = new Event("resize"); - e.debounce = false; + e["debounce"] = false; window.dispatchEvent(e); await new Promise(requestAnimationFrame); } @@ -219,7 +219,7 @@ function sortBy(rows, ascending, key) { const o = ascending ? 1 : -1; return rows.sort((a, b) => { if (a[key] === b[key]) return 0; - else if (a[key] < b[key]) return -o + else if (a[key] < b[key]) return -o; return o; }); } diff --git a/public/assets/pages/viewerpage/component_menubar.js b/public/assets/pages/viewerpage/component_menubar.js index c0f47595..d3f93f2e 100644 --- a/public/assets/pages/viewerpage/component_menubar.js +++ b/public/assets/pages/viewerpage/component_menubar.js @@ -81,20 +81,21 @@ export function buttonDownload(name, link) { } export function buttonFullscreen($screen, fullscreen = null) { + let fullscreenHandler = null; if (fullscreen === null) { if ("webkitRequestFullscreen" in document.body) { - fullscreen = () => $screen.webkitRequestFullscreen(); + fullscreenHandler = () => $screen.webkitRequestFullscreen(); } else if ("mozRequestFullScreen" in document.body) { - fullscreen = () => $screen.mozRequestFullScreen(); + fullscreenHandler = () => $screen.mozRequestFullScreen(); } } - if (!fullscreen) return; + if (!fullscreenHandler) return; const $el = createElement(` fullscreen `); - $el.onclick = fullscreen; + $el.onclick = fullscreenHandler; return $el; } diff --git a/server/generator/constants.go b/server/generator/constants.go index 3da30e02..9f03f8ec 100644 --- a/server/generator/constants.go +++ b/server/generator/constants.go @@ -26,6 +26,6 @@ func init() { BUILD_REF = "%s" BUILD_DATE = "%s" } - `, b.String(), time.Now().Format("20060102")))) + `, strings.TrimSpace(b.String()), time.Now().Format("20060102")))) f.Close() }