2024-08-01 16:25:05 +02:00
/ *
2025-01-15 10:53:56 +01:00
* v0 . 5.1 generated at Wed Jan 15 10 : 52 : 05 AM CET 2025
2024-08-01 16:25:05 +02:00
* https : //xrfragment.org
* SPDX - License - Identifier : AGPL - 3.0 - or - later
* /
2024-03-19 10:53:22 +01:00
// reactive component for displaying the menu
$editorPopup = ( el ) => new Proxy ( {
html : ( opts ) => `
< div >
< b > # $ { $editor . selected . name } < / b >
< table class = "editorPopup" >
< tbody >
< tr >
< td > < b class = "badge" > href < / a > < / t d >
< td >
< input type = "text" id = "href" placeholder = "https://foo.com" maxlength = "255" list = "objects"
onkeydown = "document.querySelector('#editActions').classList.add('show')"
onkeyup = "$editor.selected.edited = $editor.selected.userData.href = this.value"
value = "${$editor.selected.userData.href||''}" / >
< / t d >
< / t r >
< tr >
< td > < b class = "badge" > src < / a > < / t d >
< td >
< input type = "text" id = "src" placeholder = "https://foo.com" maxlength = "255" list = "objects"
onkeydown = "document.querySelector('#editActions').classList.add('show')"
onkeyup = "$editor.selected.edited = $editor.selected.userData.src = this.value"
value = "${$editor.selected.userData.src||''}" / >
< / t d >
< / t r >
< tr >
< td > < b class = "badge" > tag < / a > < / t d >
< td >
< input type = "text" id = "tag" placeholder = "foo bar" maxlength = "255"
onkeydown = "document.querySelector('#editActions').classList.add('show')"
onkeyup = "$editor.selected.edited = $editor.selected.userData.tag = this.value"
value = "${$editor.selected.userData.tag||''}" / >
< / t d >
< / t r >
< / t b o d y >
< / t a b l e >
< datalist id = "objects" >
< option > https : //xrfragment.org/index.glb#pos=start</option>
< option >
$ { opts . objectNames . join ( '</option><option>' ) }
< / o p t i o n >
< / d a t a l i s t >
< br >
< div id = "editActions" >
< button class = "download" onclick = "$editor.export()" > < i class = "gg-software-download" > < / i > & n b s p ; & n b s p ; & n b s p ; d o w n l o a d s c e n e f i l e < / b u t t o n >
< br >
NOTE : updates to src - values will require reloading the scene
< / d i v >
< / d i v >
< style type = "text/css" >
table . editorPopup input {
min - width : 200 px ;
}
table . editorPopup tr td : nth - child ( 1 ) {
text - align : left ;
}
# editActions {
visibility : hidden ;
}
# editActions . show {
visibility : visible ;
}
< / s t y l e >
` ,
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 : `
< div style = "position:absolute; width:100%; text-align:right; right:166px;" >
< button class = "btn edit-btn" >
< i class = "gg-pen" > < / i >
< / b u t t o n >
< / d i v >
< style type = "text/css" >
. xrf button . edit - btn {
height : 32 px ;
width : 30 px ;
margin - top : 7 px ;
}
. edit - btn . enabled ,
. edit - btn . enabled : hover {
background : black ;
}
. edit - btn i . gg - pen {
margin - top : - 26 px ;
margin - left : 4 px ;
width : 10 px ;
color : var ( -- xrf - white ) ;
}
< / s t y l e >
` ,
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 ( )
//`<b>XR Fragment:</b> #${this.selected.name}<br><br>${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 ( ` <b># ${ n . name } </b><br> ${ this . getMetaData ( this . selected ) } ` )
}
if ( n . material ) n . addEventListener ( 'mousemove' , n . highlightOnMouseMove = highlight ( n ) )
} )
} ,
getMetaData ( n ) {
let html = ` ${ n . userData . href ? ` <b class="badge">href</b> ${ n . userData . href } <br> ` : '' } `
html += ` ${ n . userData . src ? ` <b class="badge">src</b> ${ n . userData . src } <br> ` : '' } `
html += ` ${ n . userData . tag ? ` <b class="badge">tag</b> ${ n . userData . tag } <br> ` : '' } `
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 )
} )