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(){
 | 
			
		||||
 | 
			
		||||
  return new Proxy({
 | 
			
		||||
  const me   = new AsyncEmitter()
 | 
			
		||||
  me.player  = document.querySelector("#player"),
 | 
			
		||||
  me.src     = document.location.search.substr(1),
 | 
			
		||||
  me.ext     = {}
 | 
			
		||||
 | 
			
		||||
    backend: null,
 | 
			
		||||
    player:  document.querySelector("#player"),
 | 
			
		||||
    src:     document.location.search.substr(1),
 | 
			
		||||
    ext:     {},
 | 
			
		||||
  return new Proxy( me, {
 | 
			
		||||
    get(me,k){ return me[k] },
 | 
			
		||||
 | 
			
		||||
    init(opts){ 
 | 
			
		||||
      for( var i in opts) this[i] = opts[i]
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
    set(me,k,v){ 
 | 
			
		||||
      me[k] = v
 | 
			
		||||
      return true 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  },
 | 
			
		||||
    {
 | 
			
		||||
      get(me,k){ return me[k] },
 | 
			
		||||
 | 
			
		||||
      set(me,k,v){ 
 | 
			
		||||
        me[k] = v
 | 
			
		||||
        return true 
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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'
 | 
			
		||||
 | 
			
		||||
const btn_play = document.querySelector("#btn_play")
 | 
			
		||||
btn_play.addEventListener("click", () => {
 | 
			
		||||
  btn_play.style.display = 'none' // hide button
 | 
			
		||||
  import('aframe')
 | 
			
		||||
})
 | 
			
		||||
// extensions
 | 
			
		||||
import {default as thumb} from './../widget/thumb.js'
 | 
			
		||||
import {default as play}  from './../widget/play.js'
 | 
			
		||||
 | 
			
		||||
let   src = document.location.search.substr(1)
 | 
			
		||||
let cover = src
 | 
			
		||||
const img = /\.(png|jpg|webp)/
 | 
			
		||||
const widget = Widget()
 | 
			
		||||
 | 
			
		||||
// 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})`)
 | 
			
		||||
document.body.style.background = `url(${cover}) no-repeat center / cover`
 | 
			
		||||
  if( src.match(img) ){
 | 
			
		||||
    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": "",
 | 
			
		||||
  "description": "",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@needle-tools/three-animation-pointer": "^1.0.7",
 | 
			
		||||
    "@zip.js/zip.js": "^2.8.8",
 | 
			
		||||
    "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 = with pkgs.buildPackages; [ 
 | 
			
		||||
 | 
			
		||||
      nodejs_20
 | 
			
		||||
      monolith
 | 
			
		||||
      bun
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue