2023-12-27 17:25:49 +00:00
connectionsComponent = {
html : `
< div id = "connections" >
2024-01-03 14:23:34 +00:00
< i class = "gg-close-o" id = "close" onclick = "$connections.toggle()" > < / i >
2024-01-09 11:05:13 +00:00
< br >
< div class = "tab-frame" >
< input type = "radio" name = "tab" id = "login" checked >
< label for = "login" > login < / l a b e l >
< input type = "radio" name = "tab" id = "io" >
< label for = "io" > devices < / l a b e l >
2024-01-24 18:11:37 +00:00
< input type = "radio" name = "tab" id = "networks" >
< label for = "networks" > advanced < / l a b e l >
2024-01-09 11:05:13 +00:00
< div class = "tab" >
< div id = "settings" > < / d i v >
< table >
< tr >
< td > < / t d >
< td >
< button id = "connect" onclick = "network.connect( $connections )" > 📡 Connect ! < / b u t t o n >
< / t d >
< / t r >
2024-01-03 14:23:34 +00:00
< / t a b l e >
2024-01-09 11:05:13 +00:00
< / d i v >
< div class = "tab" >
2024-01-24 18:11:37 +00:00
< div id = "devices" >
< a class = "badge ruler" > Webcam and / or Audio < / a >
2024-01-09 11:05:13 +00:00
< table >
< tr >
2024-01-24 18:11:37 +00:00
< td > Video < / t d >
2024-01-09 11:05:13 +00:00
< td >
2024-01-24 18:11:37 +00:00
< select id = "videoInput" > < / s e l e c t >
2024-01-09 11:05:13 +00:00
< / t d >
< / t r >
< tr >
2024-01-24 18:11:37 +00:00
< td > Mic < / t d >
2024-01-09 11:05:13 +00:00
< td >
2024-01-24 18:11:37 +00:00
< select id = "audioInput" > < / s e l e c t >
2024-01-09 11:05:13 +00:00
< / t d >
< / t r >
2024-01-24 18:11:37 +00:00
< tr style = "display:none" > <!-- not used ( for now ) -- >
< td > Audio < / t d >
2024-01-09 11:05:13 +00:00
< td >
2024-01-24 18:11:37 +00:00
< select id = "audioOutput" > < / s e l e c t >
2024-01-09 11:05:13 +00:00
< / t d >
< / t r >
< / t a b l e >
< / d i v >
< / d i v >
< div class = "tab" >
2024-01-24 18:11:37 +00:00
< div id = "networking" >
Networking a la carte : < br >
2024-01-09 11:05:13 +00:00
< table >
< tr >
2024-01-24 18:11:37 +00:00
< td > Webcam < / t d >
2024-01-09 11:05:13 +00:00
< td >
2024-01-24 18:11:37 +00:00
< select id = "webcam" > < / s e l e c t >
2024-01-09 11:05:13 +00:00
< / t d >
< / t r >
< tr >
2024-01-24 18:11:37 +00:00
< td > Chat < / t d >
2024-01-09 11:05:13 +00:00
< td >
2024-01-24 18:11:37 +00:00
< select id = "chatnetwork" > < / s e l e c t >
2024-01-09 11:05:13 +00:00
< / t d >
< / t r >
2024-01-24 18:11:37 +00:00
< tr >
< td > World sync < / t d >
2024-01-09 11:05:13 +00:00
< td >
2024-01-24 18:11:37 +00:00
< select id = "scene" > < / s e l e c t >
2024-01-09 11:05:13 +00:00
< / t d >
< / t r >
< / t a b l e >
< / d i v >
< / d i v >
2024-01-03 14:23:34 +00:00
< / d i v >
2023-12-27 17:25:49 +00:00
< / d i v >
` ,
init : ( el ) => new Proxy ( {
2024-01-10 22:01:21 +01:00
webcam : [ { profile : { name : "No thanks" } , config : ( ) => document . createElement ( 'div' ) } ] ,
chatnetwork : [ { profile : { name : "No thanks" } , config : ( ) => document . createElement ( 'div' ) } ] ,
scene : [ { profile : { name : "No thanks" } , config : ( ) => document . createElement ( 'div' ) } ] ,
2023-12-27 21:31:42 +00:00
selectedWebcam : '' ,
selectedChatnetwork : '' ,
selectedScene : '' ,
2023-12-27 17:25:49 +00:00
$webcam : $webcam = el . querySelector ( "#webcam" ) ,
$chatnetwork : $chatnetwork = el . querySelector ( "#chatnetwork" ) ,
$scene : $scene = el . querySelector ( "#scene" ) ,
$settings : $settings = el . querySelector ( "#settings" ) ,
2024-01-03 14:23:34 +00:00
$devices : $devices = el . querySelector ( "#devices" ) ,
$connect : $connect = el . querySelector ( "#connect" ) ,
$networking : $networking = el . querySelector ( "#networking" ) ,
$audioInput : el . querySelector ( 'select#audioInput' ) ,
$audioOutput : el . querySelector ( 'select#audioOutput' ) ,
$videoInput : el . querySelector ( 'select#videoInput' ) ,
2023-12-27 17:25:49 +00:00
install ( opts ) {
2024-01-03 14:23:34 +00:00
this . opts = opts ;
( [ 'change' ] ) . map ( ( e ) => el . addEventListener ( e , ( ev ) => this [ e ] && this [ e ] ( ev . target . id , ev ) ) )
this . reactToNetwork ( )
$menu . buttons = ( [
` <a class="btn" aria-label="button" aria-title="connect button" aria-description="use this to talk or chat with other people" id="meeting" onclick=" $ connections.show()"><i class="gg-user-add"></i> connect</a><br> `
] ) . concat ( $menu . buttons )
2023-12-27 17:25:49 +00:00
2024-01-03 14:23:34 +00:00
if ( document . location . href . match ( /meet=/ ) ) this . show ( )
2023-12-27 21:31:42 +00:00
2024-01-03 14:23:34 +00:00
setTimeout ( ( ) => document . dispatchEvent ( new CustomEvent ( "$connections:ready" , { detail : opts } ) ) , 1 )
} ,
toggle ( ) {
let parent = el . closest ( '.envelope' )
parent . style . display = parent . style . display == 'none' ? parent . style . display = '' : 'none'
} ,
change ( id , e ) {
if ( id . match ( /^(webcam|chatnetwork|scene)$/ ) ) {
this . renderSettings ( ) // trigger this when 'change' event fires on children dom elements
}
2023-12-27 17:25:49 +00:00
} ,
2024-01-10 22:01:21 +01:00
show ( opts ) {
opts = opts || { }
if ( opts . hide ) {
el . parentElement . parentElement . style . display = 'none'
2024-01-03 14:23:34 +00:00
} else {
2024-01-10 22:01:21 +01:00
$chat . visible = true
// hide networking settings if entering thru meetinglink
$networking . style . display = document . location . href . match ( /meet=/ ) ? 'none' : 'block'
if ( ! network . connected ) {
if ( el . parentElement ) el . parentElement . parentElement . remove ( )
document . querySelector ( 'body > .xrf' ) . appendChild ( el )
$chat . send ( { message : "" , el , class : [ 'ui' ] } )
if ( ! network . meetinglink ) { // set default
2024-01-26 16:21:46 +00:00
$webcam . value = opts . webcam || 'Peer2Peer'
$chatnetwork . value = opts . chatnetwork || 'Peer2Peer'
$scene . value = opts . scene || 'Peer2Peer'
2024-01-10 22:01:21 +01:00
}
this . renderSettings ( )
} else {
$chat . send ( { message : "you are already connected, refresh page to create new connection" , class : [ 'info' ] } )
}
2024-01-03 14:23:34 +00:00
}
2023-12-27 21:31:42 +00:00
} ,
update ( ) {
this . selectedWebcam = $webcam . value
this . selectedChatnetwork = $chatnetwork . value
this . selectedScene = $scene . value
} ,
forSelectedPluginsDo ( cb ) {
// this function looks weird but it's handy to prevent the same plugins rendering duplicate configurations
let plugins = { }
2024-01-10 22:01:21 +01:00
let select = ( name ) => ( o ) => o . profile . name == name ? plugins [ o . profile . name ] = o : ''
2023-12-27 21:31:42 +00:00
this . webcam . find ( select ( this . selectedWebcam ) )
this . chatnetwork . find ( select ( this . selectedChatnetwork ) )
this . scene . find ( select ( this . selectedScene ) )
for ( let i in plugins ) {
try { cb ( plugins [ i ] ) } catch ( e ) { console . error ( e ) }
}
2023-12-27 17:25:49 +00:00
} ,
renderSettings ( ) {
let opts = { webcam : $webcam . value , chatnetwork : $chatnetwork . value , scene : $scene . value }
2023-12-27 21:31:42 +00:00
this . update ( )
2023-12-27 17:25:49 +00:00
$settings . innerHTML = ''
2024-01-09 11:05:13 +00:00
this . forSelectedPluginsDo ( ( plugin ) => $settings . appendChild ( plugin . config ( { ... opts , plugin } ) ) )
2024-01-03 14:23:34 +00:00
this . renderInputs ( )
} ,
renderInputs ( ) {
2024-01-26 16:21:46 +00:00
if ( ! this . selectedWebcam || this . selectedWebcam == 'No thanks' ) {
2024-01-03 14:23:34 +00:00
return this . $devices . style . display = 'none'
} else this . $devices . style . display = ''
navigator . mediaDevices . getUserMedia ( {
audio : true ,
video : true
} )
. then ( ( ) => {
const selectors = [ this . $audioInput , this . $audioOutput , this . $videoInput ] ;
const gotDevices = ( deviceInfos ) => {
// Handles being called several times to update labels. Preserve values.
const values = selectors . map ( select => select . value ) ;
selectors . forEach ( select => {
while ( select . firstChild ) {
select . removeChild ( select . firstChild ) ;
}
} ) ;
for ( let i = 0 ; i !== deviceInfos . length ; ++ i ) {
const deviceInfo = deviceInfos [ i ] ;
const option = document . createElement ( 'option' ) ;
option . value = deviceInfo . deviceId ;
if ( deviceInfo . kind === 'audioinput' ) {
option . text = deviceInfo . label || ` microphone ${ this . $audioInput . length + 1 } ` ;
this . $audioInput . appendChild ( option ) ;
} else if ( deviceInfo . kind === 'audiooutput' ) {
option . text = deviceInfo . label || ` speaker ${ this . $audioOutput . length + 1 } ` ;
this . $audioOutput . appendChild ( option ) ;
} else if ( deviceInfo . kind === 'videoinput' ) {
option . text = deviceInfo . label || ` camera this. ${ this . $videoInput . length + 1 } ` ;
this . $videoInput . appendChild ( option ) ;
} else {
console . log ( 'Some other kind of source/device: ' , deviceInfo ) ;
}
}
// hide if there's nothing to choose
let totalDevices = this . $audioInput . options . length + this . $audioOutput . options . length + this . $videoInput . options . length
this . $devices . style . display = totalDevices > 3 ? 'block' : 'none'
selectors . forEach ( ( select , selectorIndex ) => {
if ( Array . prototype . slice . call ( select . childNodes ) . some ( n => n . value === values [ selectorIndex ] ) ) {
select . value = values [ selectorIndex ] ;
}
} ) ;
}
// after getUserMedia we can enumerate
navigator . mediaDevices . enumerateDevices ( ) . then ( gotDevices ) . catch ( console . warn ) ;
2023-12-27 21:31:42 +00:00
} )
2023-12-27 18:26:42 +00:00
} ,
2024-01-10 22:01:21 +01:00
reactToNetwork ( ) { // *TODO* move to network?
2024-01-03 14:23:34 +00:00
document . addEventListener ( 'network.connect' , ( ) => {
2024-01-10 22:01:21 +01:00
this . show ( { hide : true } )
2024-01-03 14:23:34 +00:00
} )
2024-01-26 16:21:46 +00:00
document . addEventListener ( 'network.disconnect' , ( ) => {
this . connected = false
2024-01-03 14:23:34 +00:00
} )
2024-01-10 22:01:21 +01:00
2024-01-03 14:23:34 +00:00
}
2023-12-27 17:25:49 +00:00
} , {
get ( data , k , v ) { return data [ k ] } ,
set ( data , k , v ) {
data [ k ] = v
switch ( k ) {
2024-01-10 22:01:21 +01:00
case "webcam" : $webcam . innerHTML = ` <option> ${ data [ k ] . map ( ( p ) => p . profile . name ) . join ( '</option><option>' ) } </option> ` ; break ;
case "chatnetwork" : $chatnetwork . innerHTML = ` <option> ${ data [ k ] . map ( ( p ) => p . profile . name ) . join ( '</option><option>' ) } </option> ` ; break ;
case "scene" : $scene . innerHTML = ` <option> ${ data [ k ] . map ( ( p ) => p . profile . name ) . join ( '</option><option>' ) } </option> ` ; break ;
2023-12-28 09:22:54 +00:00
case "selectedScene" : $scene . value = v ; data . renderSettings ( ) ; break ;
case "selectedChatnetwork" : $chatnetwork . value = v ; data . renderSettings ( ) ; break ;
2024-01-03 14:23:34 +00:00
case "selectedWebcam" : {
$webcam . value = v ;
data . renderSettings ( ) ;
$devices . style . display = v ? 'block' : 'none'
break ;
}
2023-12-28 09:22:54 +00:00
2023-12-27 17:25:49 +00:00
}
}
} )
}
// reactify component!
2024-01-03 14:23:34 +00:00
document . addEventListener ( '$menu:ready' , ( opts ) => {
2023-12-27 17:25:49 +00:00
opts = opts . detail
document . head . innerHTML += connectionsComponent . css
2024-01-03 14:23:34 +00:00
window . $connections = document . createElement ( 'div' )
2023-12-27 17:25:49 +00:00
$connections . innerHTML = connectionsComponent . html
$connections = connectionsComponent . init ( $connections )
$connections . install ( opts )
} )
// alpine component for displaying meetings
connectionsComponent . css = `
< style type = "text/css" >
button # connect {
height : 43 px ;
2024-01-03 14:23:34 +00:00
width : 100 % ;
2023-12-27 17:25:49 +00:00
margin : 0 px ;
}
2024-01-03 14:23:34 +00:00
# messages . msg # connections {
position : relative ;
}
. connecthide {
transform : translateY ( - 1000 px ) ;
}
# close {
display : block ;
position : relative ;
float : right ;
2024-01-10 22:01:21 +01:00
top : 16 px ;
2024-01-03 14:23:34 +00:00
}
2024-01-09 11:05:13 +00:00
# messages . msg . ui div . tab - frame > div . tab { padding : 25 px 10 px 5 px 10 px ; }
2023-12-27 17:25:49 +00:00
< / s t y l e > `