/* * v0.5.1 generated at Wed Jan 15 10:52:05 AM CET 2025 * https://xrfragment.org * SPDX-License-Identifier: AGPL-3.0-or-later */ // reactive component for displaying the menu $editorPopup = (el) => new Proxy({ html: (opts) => `
#${$editor.selected.name}
href
src
tag


NOTE: updates to src-values will require reloading the scene
`, init(opts){ el.innerHTML = this.html(opts) return (this.el = el) }, },{ get(me,k,v){ return me[k] }, set(me,k,v){ me[k] = v } }) $editor = (el,opts) => new Proxy({ html: `
`, selecting: false, editing: false, helper: null, selected: null, objectNames: [], init(opts){ el.innerHTML = this.html window.frontend.el.querySelector('#topbar').appendChild(el); el.querySelector('.edit-btn').addEventListener('click', () => { if( $editor.selecting || $editor.editing ) this.reset() else{ $editor.selecting = true $editor.editing = false } }) xrf.addEventListener('export', (e) => this.updateOriginalScene(e) ) xrf.addEventListener('href', (opts) => { if( $editor.selecting || $editor.editing ) return opts.promise().reject("$editor should block hrefs while editing") // never resolve (block hrefs from interfering) }) return this }, reset(){ if( this.helper) xrf.scene.remove(this.helper) $editor.selecting = false $editor.editing = false }, export(){ window.frontend.download() this.reset() }, editNode(){ if( !this.selecting ) return console.log("not editing") this.reset() $editor.editing = true this.collectObjects() //`XR Fragment: #${this.selected.name}

${this.getMetaData(this.selected)}`),{ notify( $editorPopup( document.createElement('div') ).init(this) , { timeout:false, onclose: () => this.reset() }) }, collectObjects(){ this.objectNames = [] const escape = (str) => { let d = document.createElement('div') d.innerText = str return d.innerHTML } xrf.scene.traverse( (n) => { if( n.userData && n.userData.href ){ this.objectNames.push( escape(n.userData.href) ) } }) xrf.scene.traverse( (n) => { if( n.name ) this.objectNames.push( escape('#'+n.name) ) }) }, initEdit(scene){ if( !this.listenersInstalled ){ AFRAME.scenes[0].addEventListener('click', () => this.editNode() ) this.listenersInstalled = true } scene.traverse( (n) => { let highlight = (n) => (e) => { if( !this.selecting || this.editing ) return // do nothing if( this.helper){ if( this.helper.selected == n.uuid ) return // already selected xrf.scene.remove(this.helper) } this.selected = n this.helper = new THREE.BoxHelper( n, 0xFF00FF ) this.helper.material.linewidth = 4 this.helper.material.color = xrf.focusLine.material.color this.helper.material.dashSize = xrf.focusLine.material.dashSize this.helper.material.gapSize = xrf.focusLine.material.gapSize this.helper.selected = n.uuid xrf.scene.add(this.helper) let div = document.createElement('div') notify(`#${n.name}
${this.getMetaData(this.selected)}`) } if( n.material ) n.addEventListener('mousemove', n.highlightOnMouseMove = highlight(n) ) }) }, getMetaData(n){ let html = `${n.userData.href ? `href${n.userData.href}
`:''}` html += `${n.userData.src ? `src${n.userData.src}
` :''}` html += `${n.userData.tag ? `tag${n.userData.tag}
` :''}` return html }, updateOriginalScene(e){ const {scene,ext} = e scene.traverse( (n) => { if( !n.name ) return // overwrite node with modified userData from scene let o = xrf.scene.getObjectByName(n.name) if( o && o.edited ){ for( let i in o.userData ) n.userData[i] = o.userData[i] } }) } }, { get(me,k,v){ return me[k] }, set(me,k,v){ me[k] = v switch( k ){ case "selecting":{ lookctl = $('[look-controls]').components['look-controls'] if( v ){ lookctl.pause() // prevent click-conflict notify("click an object to reveal XR Fragment metadata") xrf.interactive.raycastAll = true me.initEdit(xrf.scene) lookctl.pause() // prevent click-conflict el.querySelector('.edit-btn').classList.add(['enabled']) }else{ lookctl.pause() // prevent click-conflict xrf.scene.traverse( (n) => { if( n.highlightOnMouseMove ){ n.removeEventListener( 'mousemove', n.highlightOnMouseMove ) } }) lookctl.play() // prevent click-conflict (resume) el.querySelector('.edit-btn').classList.remove(['enabled']) } break; } } }, }) // reactify component! document.addEventListener('frontend:ready', (e) => { window.$editor = $editor( document.createElement('div') ).init(e.detail) })