separated code into extensions
This commit is contained in:
parent
33e2b4c335
commit
520b8cd2c5
7 changed files with 177 additions and 50 deletions
46
lib/AsyncEmitter.js
Normal file
46
lib/AsyncEmitter.js
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* async Event Emitter
|
||||||
|
*
|
||||||
|
* // Example usage
|
||||||
|
* const emitter = new AsyncEmitter();
|
||||||
|
*
|
||||||
|
* // Register some async listeners
|
||||||
|
* emitter.on("data", async (msg) => {
|
||||||
|
* console.log("Listener 1 starting...");
|
||||||
|
* await new Promise((r) => setTimeout(r, 1000));
|
||||||
|
* console.log("Listener 1 done:", msg);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* emitter.on("data", async (msg) => {
|
||||||
|
* console.log("Listener 2 starting...");
|
||||||
|
* await new Promise((r) => setTimeout(r, 500));
|
||||||
|
* console.log("Listener 2 done:", msg);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* (async () => {
|
||||||
|
* console.log("Emitting...");
|
||||||
|
* await emitter.emit("data", "Hello async world!");
|
||||||
|
* console.log("All listeners finished.");
|
||||||
|
* })();
|
||||||
|
*/
|
||||||
|
|
||||||
|
class AsyncEmitter {
|
||||||
|
constructor() {
|
||||||
|
this.listeners = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
on(event, listener) {
|
||||||
|
if (!this.listeners.has(event)) this.listeners.set(event, []);
|
||||||
|
this.listeners.get(event).push( listener );
|
||||||
|
}
|
||||||
|
|
||||||
|
async emit(event, ...args) {
|
||||||
|
const listeners = this.listeners.get(event) || [];
|
||||||
|
if( document.location.hostname == 'localhost' ) console.info(`emit(${event})`)
|
||||||
|
for (const fn of listeners) {
|
||||||
|
await fn(...args); // <-- Waits for each listener to finish
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {AsyncEmitter}
|
||||||
|
|
@ -1,42 +1,21 @@
|
||||||
|
import {AsyncEmitter} from "./AsyncEmitter.js"
|
||||||
|
|
||||||
function widget(){
|
function widget(){
|
||||||
|
|
||||||
return new Proxy({
|
const me = new AsyncEmitter()
|
||||||
|
me.player = document.querySelector("#player"),
|
||||||
|
me.src = document.location.search.substr(1),
|
||||||
|
me.ext = {}
|
||||||
|
|
||||||
backend: null,
|
return new Proxy( me, {
|
||||||
player: document.querySelector("#player"),
|
get(me,k){ return me[k] },
|
||||||
src: document.location.search.substr(1),
|
|
||||||
ext: {},
|
|
||||||
|
|
||||||
init(opts){
|
set(me,k,v){
|
||||||
for( var i in opts) this[i] = opts[i]
|
me[k] = v
|
||||||
},
|
return true
|
||||||
|
|
||||||
play(){
|
|
||||||
// set URL
|
|
||||||
player.setAttribute("gltf-model", `url(${widget.src})` )
|
|
||||||
|
|
||||||
document.querySelector("#btn_play").style.display = 'none'
|
|
||||||
|
|
||||||
let script = document.createElement("script")
|
|
||||||
script.src = "https://aframe.io/releases/1.7.0/aframe.min.js"
|
|
||||||
document.head.appendChild(script)
|
|
||||||
|
|
||||||
script = document.createElement("script")
|
|
||||||
script.src = "backend.xrforge.js"
|
|
||||||
document.head.appendChild(script)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
})
|
||||||
{
|
|
||||||
get(me,k){ return me[k] },
|
|
||||||
|
|
||||||
set(me,k,v){
|
|
||||||
me[k] = v
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export {widget}
|
export {widget}
|
||||||
|
|
|
||||||
34
lib/widget/play.js
Normal file
34
lib/widget/play.js
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
export default function extension(widget){
|
||||||
|
|
||||||
|
const ext = {
|
||||||
|
|
||||||
|
async init(){
|
||||||
|
|
||||||
|
// let other extensions set this.src if needed
|
||||||
|
await widget.emit("init.play", this)
|
||||||
|
|
||||||
|
if( widget.src ){
|
||||||
|
document.querySelector('#scene').setAttribute("gltf-model",`url(${widget.src})`)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initButton()
|
||||||
|
},
|
||||||
|
|
||||||
|
initButton(){
|
||||||
|
const btn_play = document.querySelector("#btn_play")
|
||||||
|
btn_play.addEventListener("click", async () => {
|
||||||
|
btn_play.style.display = 'none' // hide button
|
||||||
|
widget.emit("play")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.ext.play = ext
|
||||||
|
|
||||||
|
// register async listeners
|
||||||
|
widget.on("init", ext.init.bind(ext) )
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
26
lib/widget/thumb.js
Normal file
26
lib/widget/thumb.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
|
||||||
|
export default function extension(widget){
|
||||||
|
|
||||||
|
const ext = {
|
||||||
|
|
||||||
|
src: "",
|
||||||
|
|
||||||
|
init: async () => {
|
||||||
|
|
||||||
|
// let other extensions set this.src if needed
|
||||||
|
await widget.emit("init.thumb", widget.ext.thumb )
|
||||||
|
|
||||||
|
if( widget.ext.thumb.src ){
|
||||||
|
document.body.style.background = `url(${widget.ext.thumb.src}) no-repeat center / cover`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.ext.thumb = ext
|
||||||
|
|
||||||
|
// register async listeners
|
||||||
|
widget.on("init", ext.init.bind(ext) )
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,24 +1,61 @@
|
||||||
import {widget} from './../widget.js'
|
import {widget as Widget} from './../widget.js'
|
||||||
import {inferSource} from './util.js'
|
import {inferSource} from './util.js'
|
||||||
|
|
||||||
const btn_play = document.querySelector("#btn_play")
|
// extensions
|
||||||
btn_play.addEventListener("click", () => {
|
import {default as thumb} from './../widget/thumb.js'
|
||||||
btn_play.style.display = 'none' // hide button
|
import {default as play} from './../widget/play.js'
|
||||||
import('aframe')
|
|
||||||
})
|
|
||||||
|
|
||||||
let src = document.location.search.substr(1)
|
const widget = Widget()
|
||||||
let cover = src
|
|
||||||
const img = /\.(png|jpg|webp)/
|
// init extensions
|
||||||
|
thumb(widget)
|
||||||
|
play(widget)
|
||||||
|
|
||||||
|
// init xrforge extension
|
||||||
|
widget.ext.xrforge = {
|
||||||
|
|
||||||
// manyfold URLs need to be de-obfuscated
|
|
||||||
if( src.match(img) ){
|
|
||||||
cover = await inferSource(src)
|
|
||||||
src = cover.replace(img,".glb")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.dir({cover,src})
|
// in case of Manyfold backend, a thumbnail is passed (not gltf src e.g.)
|
||||||
|
// so we will infer the source-url based on the thumbnail
|
||||||
|
widget.on('init.thumb', async (thumb) => {
|
||||||
|
let src = widget.src
|
||||||
|
const img = /\.(png|jpg|webp)/
|
||||||
|
|
||||||
document.querySelector('#scene').setAttribute("gltf-model",`url(${src})`)
|
if( src.match(img) ){
|
||||||
document.body.style.background = `url(${cover}) no-repeat center / cover`
|
thumb.src = src
|
||||||
|
let inferredSrc = await inferSource(src)
|
||||||
|
widget.src = inferredSrc.replace(img,".glb")
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
widget.on("play", async () => {
|
||||||
|
// initialize a specialized build of THREE/AFRAME
|
||||||
|
const AFRAME = await import('aframe')
|
||||||
|
window.THREE.DRACOLoader = await import('three/examples/jsm/loaders/DRACOLoader.js')
|
||||||
|
window.THREE.FBXLoader = await import('three/examples/jsm/loaders/FBXLoader.js')
|
||||||
|
window.THREE.USDZLoader = await import('three/examples/jsm/loaders/USDZLoader.js')
|
||||||
|
window.THREE.ColladaLoader = await import('three/examples/jsm/loaders/ColladaLoader.js')
|
||||||
|
window.THREE.MTLLoader = await import('three/examples/jsm/loaders/MTLLoader.js')
|
||||||
|
window.THREE.GLTFExporter = await import('three/examples/jsm/exporters/GLTFExporter.js')
|
||||||
|
// optional utils
|
||||||
|
window.zipjs = await import("@zip.js/zip.js")
|
||||||
|
window.webdav = await import("webdav")
|
||||||
|
window.rs = await import("remotestoragejs")
|
||||||
|
window.trystero = await import("trystero")
|
||||||
|
|
||||||
|
// include xrsh (remote for now, until the final integration is more clear)
|
||||||
|
const script = document.createElement("script")
|
||||||
|
script.src = "https://xrsh.isvery.ninja/xrsh.js"
|
||||||
|
script.addEventListener("load", function(){
|
||||||
|
const ent = document.createElement("a-entity")
|
||||||
|
ent.setAttribute("isoterminal","minimized:true")
|
||||||
|
ent.setAttribute("position","0 1.6 -0.3")
|
||||||
|
document.querySelector("a-scene").appendChild(ent)
|
||||||
|
})
|
||||||
|
document.body.appendChild(script)
|
||||||
|
})
|
||||||
|
|
||||||
|
await widget.emit("init")
|
||||||
|
console.dir(widget)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,13 @@
|
||||||
"license": "",
|
"license": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@needle-tools/three-animation-pointer": "^1.0.7",
|
||||||
|
"@zip.js/zip.js": "^2.8.8",
|
||||||
"aframe": "^1.7.1",
|
"aframe": "^1.7.1",
|
||||||
"xrf": "^0.0.1"
|
"remotestoragejs": "^2.0.0-beta.8",
|
||||||
|
"trystero": "^0.22.0",
|
||||||
|
"webdav": "^5.8.0",
|
||||||
|
"xrf": "^0.0.1",
|
||||||
|
"xrsh": "^0.0.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
# nativeBuildInputs is usually what you want -- tools you need to run
|
# nativeBuildInputs is usually what you want -- tools you need to run
|
||||||
nativeBuildInputs = with pkgs.buildPackages; [
|
nativeBuildInputs = with pkgs.buildPackages; [
|
||||||
|
|
||||||
nodejs_20
|
|
||||||
monolith
|
monolith
|
||||||
bun
|
bun
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue