work in progress [might break]

This commit is contained in:
Leon van Kammen 2023-12-19 20:40:40 +01:00
parent d3b767b665
commit 688713d179
12 changed files with 281 additions and 537 deletions

File diff suppressed because one or more lines are too long

View File

@ -11,6 +11,7 @@
</head>
<body>
<script src="./../../../dist/xrfragment.aframe.js"></script>
<script src="./../../../dist/xrfragment.extras.js"></script>
<a-scene xr-mode-ui="XRMode: xr" renderer="colorManagement: true; highRefreshRate:true" light="defaultLightsEnabled: false">
<a-entity id="player" wasd-controls look-controls>
@ -33,7 +34,7 @@
XRFMENU.logo = './../../assets/logo.png'
XRFMENU.morelabel = '⚡'
// add your menubuttons:
XRFMENU.buttons = XRFMENU.buttons.concat([`<a class="btn" aria-label="button" aria-description="about menu" onclick="XRFMENU.showAbout()">💁 about</a><br>`])
XRFMENU.buttons.push(`<a class="btn" aria-label="button" aria-description="about menu" onclick="XRFMENU.showAbout()">💁 about</a><br>`)
XRFMENU.showAbout = () => window.notify(`
<h1>💁 Hi there!</h1>

9
make
View File

@ -84,14 +84,18 @@ build(){
cat dist/xrfragment.three.js \
src/3rd/js/aframe/*.js \
example/assets/js/qr.js > dist/xrfragment.aframe.js
# html extras like menu & meetings
test -f dist/alpine.min.js || wget "https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" -O dist/alpine.min.js
cat dist/alpine.min.js src/3rd/js/extra/*.js > dist/xrfragment.extras.js
# fat all-in-one standalone xrf release
test -f /tmp/xrf-aframe.js || {
wget "https://aframe.io/releases/1.5.0/aframe.min.js" -O /tmp/xrf-aframe.js
wget "https://cdn.jsdelivr.net/npm/aframe-blink-controls/dist/aframe-blink-controls.min.js" -O /tmp/xrf-blink.js
#for i in /tmp/xrf-*.js; do echo -e "\n" >> $i; done # add extra linebreak to prevent bundle issues
}
cat /tmp/xrf-*.js dist/xrfragment.aframe.js > dist/xrfragment.aframe.all.js
cat /tmp/xrf-*.js dist/xrfragment.aframe.js dist/xrfragment.extras.js > dist/xrfragment.aframe.all.js
# add license headers
for file in dist/xrfragment.{aframe,module,three,three.module,aframe.all}.js; do
@ -142,6 +146,7 @@ repos(){
# remove aframe reference
sed -i 's|<script src="https:\/\/aframe.*||g' ../xrfragment-helloworld/index.html
sed -i 's|<script src=".*extras.*||g' ../xrfragment-helloworld/index.html
sed -i 's|<script src=".*blink-controls.*||g' ../xrfragment-helloworld/index.html
sed -i 's|aframe\.js|aframe.all.js|g' ../xrfragment-helloworld/index.html
}

View File

@ -1,38 +1,8 @@
// this project uses #vanillajs #proxies #clean #noframework
//
// menu = $proxy({
// attach: 'body',
// html: (el) => `<ul><li>${el.items.join('</li><li>')}</li></lu>`,
// items: [1,2],
// on: (el,k,v) => {
// switch(k){
// default: return el.outerHTML = el.html(el)
// }
// }
// })
//
// menu.items = menu.items.concat([3,4])
// $('#foo')
// $$('.someclass').map( (el) => el.classList.toggle() )
//
$proxy = (opts) => {
let el = document.createElement('div')
el.innerHTML = opts.html(opts)
el.querySelector = el.querySelector.bind(el)
el.appendChild = el.appendChild.bind(el)
let parent = typeof opts.attach == 'string' ? document.querySelector(opts.attach) : opts.attach
parent.appendChild( el )
el.on = el.addEventListener.bind(el)
for( let i in opts ) el[i] = opts[i]
return new Proxy( el, {
get: (el,k,receiver) => el[k] || '',
set: (el,k,v) => { el[k] = v; el.on(el,k,v) }
})
}
// the core project uses #vanillajs #proxies #clean #noframework
$ = typeof $ != 'undefined' ? $ : (s) => document.querySelector(s) // respect jquery
$$ = typeof $$ != 'undefined' ? $$ : (s) => [...document.querySelectorAll(s)] // zepto etc.
$el = (html) => {
$el = (html,tag) => {
let el = document.createElement('div')
el.innerHTML = html
return el.children[0]

View File

@ -22,7 +22,8 @@ window.AFRAME.registerComponent('xrf', {
renderer: aScene.renderer,
loaders: {
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?
glb: THREE.GLTFLoader
glb: THREE.GLTFLoader,
obj: THREE.OBJLoader
}
})
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'

View File

@ -1,391 +0,0 @@
//
//AFRAME.registerComponent('meeting', {
// schema:{
// id:{ required:true, type:'string'},
// visitorname:{required:false,type:'string'},
// parentRoom:{required:false,type:'string'},
// link:{required:false,type:'string'},
// },
//
// // reactive HTML elements
// $: document.createElement('div'),
// cameras: {},
// audios: {},
// messages: [],
// placeholderInput: 'enter name here',
// hideChat: false,
// log: [],
// selfStream: null,
//
// events:{
// connect: function(e){
// if( !this.data.visitorname ){
// this.data.appendHTMLMessage("Please enter your name below",["info"])
// }else{
// if( this.data.parentRoom ) this.data.$chat.add(`leaving ${this.data.parentRoom}`,["info"]);
// }
// },
// },
//
// remove: function(){
// this.el.emit("remove",{})
// this.data.$.remove()
// },
//
// update: function(){
// setTimeout( () => {
// this.remove()
// this.initMeeting()
// },100)
// },
//
// init: function(){
//
// //// when teleport is clicked
// //AFRAME.XRF.addEventListener('href', (opts) => {
// // if( !opts.click ) return // ignore mouseovers etc
// // let url = opts.xrf.string
// // let isTeleport = url.match(/(:\/\/|pos=)/)
// // if( isTeleport ){
// // url = url[0] == '#' ? document.location.href.replace(/#.*/, opts.xrf.string ) : '?'+opts.xrf.string
// // this.notifyTeleport( url )
// // }
// //})
// this.initMeeting()
//
// },
//
// initMeeting: function(){
// this.data.link = document.location.href
// this.initHTML()
//
// // load plugins
// if( window.meeting.trystero ) new window.meeting.trystero(this.el,this,this.data)
//
// this.getMedia()
// this.emit("connect",{})
// },
//
// getMedia: async function(){
// this.data.selfStream = await navigator.mediaDevices.getUserMedia({
// audio: true,
// video: {
// width: 320,
// height: 240,
// frameRate: {
// ideal: 30,
// min: 10
// }
// }
// })
// let meVideo = this.createVideoElement(this.data.selfStream)
// meVideo.muted = true
// },
//
// // central function to broadcast stuff to chat
// send: function(opts){
// opts = { sendLocal: true, sendNetwork:true, ...opts }
// if( opts.sendNetwork ){
// if( !this.sendChat ) return
// this.sendChat({opts}) // send to network
// }
// if( opts.sendLocal ){
// this.data.$chat.add( opts ) // send to HTML screen
// }
// },
//
// createVideoElement: function(stream,id){
// let video = this.data.videos[peerId]
// const videoContainer = document.getElementById('videos')
// // if this peer hasn't sent a stream before, create a video element
// if (!video) {
// video = document.createElement('video')
// video.autoplay = true
//
// // add video element to the DOM
// videoContainer.appendChild(video)
// }
//
// video.srcObject = stream
// video.resize = (state) => {
// if( video.resize.state == undefined ) video.resize.state = false
// if( state == undefined ) state = (video.resize.state = !video.resize.state )
// video.style.width = state ? '320px' : '80px'
// video.style.height = state ? '200px' : '60px'
// }
// this.data.videos[ id || 'me' ] = video
// return video
// },
//
// initHTML: function(){
//
// // add css
// this.data.$.innerHTML += `<style type="text/css">
// #videos{
// display:grid-auto-columns;
// grid-column-gap:5px;
// margin-bottom:15px;
// position: fixed;
// top: 0;
// left: 0;
// bottom: 0;
// right: 0;
// margin: 15px;
// z-index:1500;
// }
// #videos > video{
// border-radius:7px;
// display:inline-block;
// background:black;
// width:80px;
// height:60px;
// margin-right:5px;
// margin-bottom:5px;
// vertical-align:top;
// pointer-events:all;
// }
// #videos > video:hover{
// filter: brightness(1.8);
// cursor:pointer;
// }
//
// #chatbar,
// button#showchat{
// z-index: 1500;
// position: fixed;
// bottom: 20px;
// left: 20px;
// width: 48%;
// background: white;
// padding: 0px 0px 0px 15px;
// border-radius: 30px;
// max-width: 500px;
// box-sizing: border-box;
// box-shadow: 0px 0px 5px 5px #0002;
// }
// button#showchat{
// z-index:1550;
// color:white;
// border:0;
// display:none;
// height: 44px;
// background:#07F;
// font-weight:bold;
// }
// #chatbar input{
// border:none;
// width:90%;
// box-sizing:border-box;
// }
// #chat{
// position: absolute;
// top: 100px;
// left: 0;
// right: 0;
// bottom: 88px;
// padding: 15px;
// pointer-events: none;
// overflow-y:auto;
// }
// #chat .msg{
// background: #fff;
// display: inline-block;
// padding: 6px 17px;
// border-radius: 20px;
// color: #000c;
// margin-bottom: 10px;
// line-height:23px;
// pointer-events:visible;
// border: 1px solid #ccc;
// }
// #chat .msg.info{
// background: #333;
// color: #FFF;
// font-size: 14px;
// padding: 0px 16px;
// }
// #chat .msg.info a,
// #chat .msg.info a:visited{
// color: #aaf;
// text-decoration: none;
// transition:0.3s;
// }
// #chat .msg.info a:hover,
// #chat button:hover{
// filter: brightness(1.8);
// text-decoration: underline;
// }
// #chat button {
// margin: 0px 15px 10px 0px;
// background: #07F;
// color: #FFF;
// border-radius: 7px;
// padding: 11px 15px;
// border: 0;
// font-weight: bold;
// box-shadow: 0px 0px 5px 5px #0002;
// pointer-events:all;
// }
// #chat,#chatbar,#chatbar *, #chat *{
// font-family:monospace;
// font-size:15px;
// }
// </style>
// <div id="videos" style="pointer-events:none"></div>
// <div id="chat" aria-live="assertive" aria-relevant></div>
// <div id="chatfooter"></div>
// `
// document.body.appendChild(this.data.$)
//
// // reactive logic
// this.data = new Proxy(this.data, {
// html: {
// chatbar: (data) => data.showChat
// ? `<div id="chatbar">
// <input id="chatline" type="text" placeholder="${data.placeholderInput}"></input>
// </div>`
// : `<button id="showchat" class="btn">show chat</button>`
// },
// get(data,k ){ return data[k] },
// set(data,k,v){
// data[k] = v
// switch( k ){
//
// case 'cameras':{
// data.$videos.innerHTML = ''
// for( let k in data.cameras ) el.appendChild( data.cameras[k] ) )
// return
// }
//
// case 'audios':{
// data.$videos.innerHTML = ''
// for( let k in data.cameras ) el.appendChild( data.cameras[k] ) )
// return
// }
//
// case 'showChat':{
// data.$chatfooter.innerHTML = this.html.chatbar(data)
// data.$chatline.addEventListener("keydown", this.onChatInput )
// if( v ){
// data.$chatfooter.querySelector('#chatline').focus()
// }
// return
// }
//
// }
// },
// })
//
// // trigger 1st render
// this.data.showChat = true
//
// // setup handles
// this.data.$videos = this.data.$.querySelector('#meeting #videos')
// this.data.$chatfooter = this.data.$.querySelector('#meeting #chatfooter')
// this.data.$chatline = this.data.$chatfooter.querySelector('#chatline')
// this.data.$chat = this.data.$.querySelector('#meeting #chat')
// this.data.$chat.add = this.addMessage
//
// },
//
// addMessage: function(opts){
// let {str,classes,buttons) = opts
// let el = this.data.$chat
//
// const createMsg = (str) => {
// let el = document.createElement("div")
// el.className = "msg"
// el.innerHTML = this.linkify(str)
// return el
// }
//
// if( str ){
// str = str.replace('\n', "<br>")
// .replace(/^[a-zA-Z-0-9]+?[:\.]/,'<b>$&</b>')
// let msg = this.createMsg(str)
// if( classes ) classes.map( (c) => msg.classList.add(c) )
// el.appendChild(msg) // send to screen
// el.innerHTML += '<br>'
// if( !classes ) this.data.log.push(str)
// }
// if( buttons ){
// for( let i in buttons ){
// let btn = document.createElement("button")
// btn.innerText = i
// btn.addEventListener('click', () => buttons[i]() )
// el.appendChild(btn)
// }
// el.innerHTML += '<br>'
// }
// el.scrollTop = el.scrollHeight; // scroll to bottom
// }
// },
//
// onChatInput: function(e){
// if( e.key !== "Enter" ) return
// let $chatline = this.data.$chatline
// if( !this.data.visitorname ){
// this.data.visitorname = chatline.value.toLowerCase()
// this.data.visitorname = this.data.visitorname.replace(/[^a-z]+/g,'-')
// this.send({message:"note: camera/mic access is totally optional ♥️", classes:["info"], isLocal:false})
// $chatline.setAttribute("placeholder","chat here")
// }else{
// let str = `${this.data.visitorname}: ${$chatline.value.substr(0,65515).trim()}`
// this.send({message:str})
// }
// $chatline.value = ''
// event.preventDefault();
// event.target.blur()
// },
//
// enableSmallScreen: function(){
// // on small screens/mobile make chat toggle-able
// if( window.outerWidth < 1024 ){
// let show = (state) => () => this.data.showChat = state
// $('.a-canvas').addEventListener('click', () => show(false) )
// $('.a-canvas').addEventListener('touchstart', () => show(false) )
// $('#showchat').addEventListener('touchstart', () => show(true) )
// $('#showchat').addEventListener('click', () => show(true) )
// }
// }
//
// //notifyTeleport: function(url){
// // url = url || this.roomname
// // url = url.replace(/(#|&)meet/,'')
// // let message = `${this.data.visitorname} teleported to ${url}`
// // this.send({
// // message,
// // classes: ["info"]
// // })
// //},
//
// //linkify: function(t){
// // const m = t.match(/(?<=\s|^)[a-zA-Z0-9-:/]+[?\.][a-zA-Z0-9-].+?(?=[.,;:?!-]?(?:\s|$))/g)
// // if (!m) return t
// // const a = []
// // m.forEach(x => {
// // const [t1, ...t2] = t.split(x)
// // a.push(t1)
// // t = t2.join(x)
// // let url = (!(x.match(/:\/\//)) ? 'https://' : '') + x
// // let attr = `href="${url}" target="_blank"`
// // if (isNaN(x) ){
// // let url_human = url.split('/')[2]
// // let isXRFragment = url.match(/\.(glb|gltf|obj|usdz|fbx|col)/)
// // if( isXRFragment ){ // detect xr fragments
// // isMeeting = url.match(/(#|&)meet/)
// // url_human = url.replace(/.*[?\?]/,'') // shorten xr fragment links
// // .replace(/[?\&]meet/,'')
// // let onclick = [ `xrf.navigator.to('${url}')` ]
// // if( isMeeting ) onclick.push(`$('[meeting]').coms['meeting'].update()`)
// // attr = `onclick="${onclick.join(';')}"`
// // }
// // a.push(`<a ${attr} style="pointer-events:all">${url_human}</a>`)
// // }else
// // a.push(x)
// // })
// // a.push(t)
// // return a.join('')
// //}
//
//})

View File

@ -1,49 +0,0 @@
AFRAME.registerComponent('xrf-menu', {
schema:{
id:{ required:true, type:'string'}
},
init: function(){
// add css+html
window.XRFMENU.init()
$('a-scene').addEventListener('XRF', this.onXRFready )
if( document.location.search.length > 2 ){
$('[xrf]').setAttribute('xrf', document.location.search.substr(1)+document.location.hash )
}
},
onXRFready: function(){
let XRF = window.AFRAME.XRF
return
XRFMENU.setupMenu( XRF )
// on localhost enable debugging mode for developer convenience
let loc = document.location
if( loc.host.match(/^localhost/) ){
$('a-scene').setAttribute('stats')
XRF.debug = 1
}
// add screenshot component with camera to capture bigger size equirects
// document.querySelector('a-scene').components.screenshot.capture('perspective')
$('a-scene').setAttribute("screenshot",{camera: "[camera]",width: 4096*2, height:2048*2})
if( window.outerWidth > 800 )
setTimeout( () => window.notify("use WASD-keys and mouse-drag to move around",{timeout:false}),2000 )
window.AFRAME.XRF.addEventListener('href', (data) => data.selected ? window.notify(`href: ${data.xrf.string}`) : false )
// enable user-uploaded asset files
let fileLoaders = XRFMENU.loadFile({
".gltf": (file) => file.arrayBuffer().then( (data) => xrf.navigator.to(file.name,null, (new xrf.loaders.gltf()), data) ),
".glb": (file) => file.arrayBuffer().then( (data) => xrf.navigator.to(file.name,null, (new xrf.loaders.gltf()), data) )
})
$("#overlay > input[type=submit]").addEventListener("click", fileLoaders )
}
});

View File

@ -0,0 +1,159 @@
// alpine component for displaying meetings
$XRFMEETING = $el(
`<div x-show="enabled" id="meeting" x-data="XRFMEETING">
<style type="text/css">
#videos{
display:grid-auto-columns;
grid-column-gap:5px;
margin-bottom:15px;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: 15px;
z-index:1500;
}
#videos > video{
border-radius:7px;
display:inline-block;
background:black;
width:80px;
height:60px;
margin-right:5px;
margin-bottom:5px;
vertical-align:top;
pointer-events:all;
}
#videos > video:hover{
filter: brightness(1.8);
cursor:pointer;
}
#chatbar,
button#showchat{
z-index: 1500;
position: fixed;
bottom: 20px;
left: 20px;
width: 48%;
background: white;
padding: 0px 0px 0px 15px;
border-radius: 30px;
max-width: 500px;
box-sizing: border-box;
box-shadow: 0px 0px 5px 5px #0002;
}
button#showchat{
z-index:1550;
color:white;
border:0;
display:none;
height: 44px;
background:#07F;
font-weight:bold;
}
#chatbar input{
border:none;
width:90%;
box-sizing:border-box;
}
#chat{
position: absolute;
top: 100px;
left: 0;
right: 0;
bottom: 88px;
padding: 15px;
pointer-events: none;
overflow-y:auto;
}
#chat .msg{
background: #fff;
display: inline-block;
padding: 6px 17px;
border-radius: 20px;
color: #000c;
margin-bottom: 10px;
line-height:23px;
pointer-events:visible;
border: 1px solid #ccc;
}
#chat .msg.info{
background: #333;
color: #FFF;
font-size: 14px;
padding: 0px 16px;
}
#chat .msg.info a,
#chat .msg.info a:visited{
color: #aaf;
text-decoration: none;
transition:0.3s;
}
#chat .msg.info a:hover,
#chat button:hover{
filter: brightness(1.8);
text-decoration: underline;
}
#chat button {
margin: 0px 15px 10px 0px;
background: #07F;
color: #FFF;
border-radius: 7px;
padding: 11px 15px;
border: 0;
font-weight: bold;
box-shadow: 0px 0px 5px 5px #0002;
pointer-events:all;
}
#chat,#chatbar,#chatbar *, #chat *{
font-family:monospace;
font-size:15px;
}
</style>
<div id="videos" style="pointer-events:none"></div>
<div id="chat" aria-live="assertive" aria-relevant></div>
<div id="chatfooter">
<div id="chatbar">
<input id="chatline" type="text" placeholder=""></input>
</div>
<button id="showchat" class="btn">show chat</button>
</div>
</div>`
)
window.XRFMEETING = {
scene: null,
enabled: false,
active: false,
init(){
window.XRFMEETING = this.$data // replace so we can modify state in global scope
window.meeting
},
toggle(){
this.enabled = !this.enabled
if( !this.active ) this.start()
},
install(opts){
this.scene = opts.scene
window.XRFMENU.buttons.push(`<a class="btn" aria-label="button" aria-description="start text/audio/video chat" id="meeting" onclick="XRFMEETING.toggle()" target="_blank">🧑‍🤝‍🧑 meeting</a><br>`)
document.body.appendChild( $XRFMEETING )
},
start(){
this.scene.addEventListener('meeting.peer.add', () => console.log("$meeting.peer.add") )
this.scene.addEventListener('meeting.peer.remove', () => console.log("$meeting.peer.remove") )
}
}
// finally we alpinify
document.addEventListener('XRFMENU:ready', (opts) => {
opts = opts.detail
Alpine.data('XRFMEETING', () => window.XRFMEETING)
XRFMEETING.install(opts)
})

View File

@ -1,7 +1,7 @@
// alpine component for displaying the menu
$XRFMENU = $el(
`<div id="menu" x-bind="XRFMENU">
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" type="text/javascript"></script>
`<div id="menu" x-data="XRFMENU">
<style type="text/css">
:root {
--xrf-primary: #6839dc;
@ -150,6 +150,7 @@ $XRFMENU = $el(
.menu .btn{
display:inline-block;
background: var(--xrf-primary);
border-radius: 25px;
border: 0;
@ -163,7 +164,6 @@ $XRFMENU = $el(
cursor:pointer;
min-width:107px;
text-decoration:none;
display:none;
margin-top: 15px;
line-height:36px;
margin-right:10px;
@ -369,8 +369,8 @@ $XRFMENU = $el(
}
</style>
<div id="overlay" class="xrf" style="display:none">
<div class="logo" :style="'background-image: url('+$store.XRFMENU.logo+')'"></div>
<div id="overlay" class="xrf" x-show="enabled">
<div class="logo" :style="'background-image: url('+logo+')'"></div>
<button id="navback" onclick="history.back()">&lt;</button>
<button id="navforward" onclick="history.forward()">&gt;</button>
<input type="submit" value="load 3D file"></input>
@ -378,36 +378,34 @@ $XRFMENU = $el(
</div>
<div class="xrf footer">
<div id="buttons" class="menu">
<a class="btn" id="more" style="display:inline-block" x-text="$store.XRFMENU.morelabel"></a>
<template x-for="btn in buttons.reverse()">
<div x-show="enabled" x-html="btn"></div>
</template>
<a class="btn" id="more" @click="toggle" x-text="morelabel"></a>
</div>
</div>
</div>`
)
//${window.XRFMENU.html.map( (html) => typeof html == "function" ? html() : html ).join('\n')}
window.XRFMENU = {
$: $XRFMENU,
morelabel: '⚡',
enabled: false,
logo: './../../assets/logo.png',
buttons: [
`<a class="btn" aria-label="button" aria-description="start text/audio/video chat" id="meeting" target="_blank">🧑‍🤝‍🧑 meeting</a><br>`,
`<a class="btn" aria-label="button" aria-description="share URL/screenshot/embed" id="share" target="_blank" onclick="window.share()">🔗 share</a><br>`
],
buttons: [`<a class="btn" aria-label="button" aria-description="share URL/screenshot/embed" id="share" onclick="window.share()">🔗 share</a><br>`],
init(){ window.XRFMENU = this.$data }, // replace so we can modify state in global scope
toggle(){ this.enabled = !this.enabled },
init: () => {
install(opts){
// bind object as alpine app
document.body.appendChild( XRFMENU.$ )
document.addEventListener('alpine:init', () => Alpine.bind('XRFMENU', () => XRFMENU ) )
document.body.appendChild( $XRFMENU )
document.dispatchEvent( new CustomEvent("XRFMENU:ready", {detail: opts}) )
//if( XRFMENU.logo ) $('.logo').style['background-image'] = `url(${XRFMENU.logo})`
window.notify = XRFMENU.notify(window)
window.share = XRFMENU.share
window.download = XRFMENU.download
window.notify('loading '+document.location.search.substr(1))
// reroute console messages to snackbar notifications
console.log = ( (log) => function(str){
if( String(str).match(/(:.*#|note:)/) ) window.notify(str)
@ -441,48 +439,6 @@ window.XRFMENU = {
}
},
setupMenu(XRF){
let aScene = document.querySelector('a-scene')
let urlbar = $('input#uri')
let inIframe = window.location !== window.parent.location
let els = [ ...document.querySelectorAll('.menu .btn') ]
els = els.filter( (el) => el.id != "more" ? el : false )
let showMenu = (state) => {
els.map( (el) => el.style.display = state ? 'inline-block' : 'none' )
$('a#more').style.display = state ? 'none' : 'inline-block'
$('#overlay').style.display = state ? 'inline-block' : 'none'
if( inIframe ) $('#uri').style.display = 'block'
}
els.map( (el) => el.addEventListener('click', () => showMenu(false) ) )
$('a#more').addEventListener('click', () => showMenu(true) )
$('.a-canvas').addEventListener('click', () => showMenu(false) )
// enable meetings
let startMeeting = () => {
aScene.setAttribute('meeting', 'id: xrfragments')
$('a#meeting').innerText = '🧑‍🤝‍🧑 breakout meeting'
$('a#meeting').setAttribute('aria-description','breakout room')
}
$('a#meeting').addEventListener('click', () => {
if( aScene.getAttribute('meeting') ){ // meeting already, start breakout room
let parentRoom = document.location.href
XRFMENU.updateHashPosition(true)
let meeting = $('[meeting]').components['meeting']
meeting.data.parentRoom = parentRoom
meeting.update()
}else startMeeting()
})
if( document.location.hash.match(/(#|&)meet/) ) startMeeting()
XRF.addEventListener('hash', () => reflectUrl() )
const reflectUrl = window.reflectUrl = (url) => {
urlbar.value = url || document.location.search.substr(1) + document.location.hash
}
reflectUrl()
},
SnackBar(userOptions) {
var snackbar = this || (window.snackbar = {});
var _Interval;
@ -709,3 +665,69 @@ window.XRFMENU = {
}
}
//$('a-scene').addEventListener('XRF', this.onXRFready )
//
// if( document.location.search.length > 2 ){
// $('[xrf]').setAttribute('xrf', document.location.search.substr(1)+document.location.hash )
// }
//
// },
//
// onXRFready: function(){
//
// let XRF = window.AFRAME.XRF
// //setupMenu(XRF){
// // let aScene = document.querySelector('a-scene')
// // let urlbar = $('input#uri')
// // let inIframe = window.location !== window.parent.location
// // let els = [ ...document.querySelectorAll('.menu .btn') ]
// // els = els.filter( (el) => el.id != "more" ? el : false )
//
// // // enable meetings
// // let startMeeting = () => {
// // aScene.setAttribute('meeting', 'id: xrfragments')
// // $('a#meeting').innerText = '🧑‍🤝‍🧑 breakout meeting'
// // $('a#meeting').setAttribute('aria-description','breakout room')
// // }
// // $('a#meeting').addEventListener('click', () => {
// // if( aScene.getAttribute('meeting') ){ // meeting already, start breakout room
// // let parentRoom = document.location.href
// // XRFMENU.updateHashPosition(true)
// // let meeting = $('[meeting]').components['meeting']
// // meeting.data.parentRoom = parentRoom
// // meeting.update()
// // }else startMeeting()
// // })
// // if( document.location.hash.match(/(#|&)meet/) ) startMeeting()
//
// // XRF.addEventListener('hash', () => reflectUrl() )
// // const reflectUrl = window.reflectUrl = (url) => {
// // urlbar.value = url || document.location.search.substr(1) + document.location.hash
// // }
// // reflectUrl()
// //},
//
//
// // on localhost enable debugging mode for developer convenience
// let loc = document.location
// if( loc.host.match(/^localhost/) ){
// $('a-scene').setAttribute('stats')
// XRF.debug = 1
// }
//
// // add screenshot component with camera to capture bigger size equirects
// // document.querySelector('a-scene').components.screenshot.capture('perspective')
// $('a-scene').setAttribute("screenshot",{camera: "[camera]",width: 4096*2, height:2048*2})
//
// if( window.outerWidth > 800 )
// setTimeout( () => window.notify("use WASD-keys and mouse-drag to move around",{timeout:false}),2000 )
//
// window.AFRAME.XRF.addEventListener('href', (data) => data.selected ? window.notify(`href: ${data.xrf.string}`) : false )
//
// // enable user-uploaded asset files
// let fileLoaders = XRFMENU.loadFile({
// ".gltf": (file) => file.arrayBuffer().then( (data) => xrf.navigator.to(file.name,null, (new xrf.loaders.gltf()), data) ),
// ".glb": (file) => file.arrayBuffer().then( (data) => xrf.navigator.to(file.name,null, (new xrf.loaders.gltf()), data) )
// })
// $("#overlay > input[type=submit]").addEventListener("click", fileLoaders )

View File

@ -0,0 +1,22 @@
// this orchestrates multiplayer events from the scene graph
window.meeting = (THREE, scene) => ({
peer:{
peers: [],
add(peer){
let defaults = {lastUpdated: new Date().getTime() }
peer = { ...defaults,...peer}
this.peers.push(peer)
scene.dispatchEvent({type:'meeting.peer.add', peer})
},
remove(peer){
scene.dispatchEvent({type:'meeting.peer.remove', peer})
},
send(opts){
},
receive(opts){
}
}
})

View File

@ -14,6 +14,10 @@ xrf.init = ((init) => function(opts){
xrf.navigator.init()
// return xrfragment lib as 'xrf' query functor (like jquery)
for ( let i in xrf ) xrf.query[i] = xrf[i]
// install menu+extras if available
if( typeof XRFMENU != 'undefined' ) XRFMENU.install(xrf)
return xrf.query
})(xrf.init)