better & tab-able buttons/notifications #accessibility

This commit is contained in:
Leon van Kammen 2024-06-17 12:42:42 +00:00
parent 093de1ec6f
commit 0bc5fa86d1
9 changed files with 112129 additions and 11 deletions

View File

@ -1,5 +1,5 @@
/*
* v0.5.1 generated at Sat Jun 15 05:46:02 PM CEST 2024
* v0.5.1 generated at Mon Jun 17 12:41:29 PM UTC 2024
* https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0
*/

View File

@ -1,5 +1,5 @@
/*
* v0.5.1 generated at Sat Jun 15 05:46:02 PM CEST 2024
* v0.5.1 generated at Mon Jun 17 12:41:29 PM UTC 2024
* https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0
*/

111967
dist/xrfragment.module.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -284,6 +284,8 @@ window.accessibility = (opts) => new Proxy({
.replace(/&/,' and ')
.replace(/=/,' is ')
}
if( str == this.speak.lastStr ) return // no duplicates
this.speak.lastStr = str
let speech = window.speechSynthesis
let utterance = new SpeechSynthesisUtterance( str )
this.speak_voices = speech.getVoices().length
@ -304,6 +306,18 @@ window.accessibility = (opts) => new Proxy({
init(){
this
.speakArrowKeys()
.setupListeners()
.setupPersistance()
.setupHrefCycling()
.setupSpeechKillOnEscape()
setTimeout( () => this.initCommands(), 200 )
},
speakArrowKeys(){
// speak arrow keys
window.addEventListener('keydown', (e) => {
if( !this.speak_keyboard ) return
let k = e.key
@ -315,6 +329,60 @@ window.accessibility = (opts) => new Proxy({
}
this.speak(k,{override:true})
})
return this
},
setupSpeechKillOnEscape(){
window.addEventListener('keydown', (e) => {
if( e.key == "Escape" ){
this.speak("stop",{override:true})
}
})
},
setupHrefCycling(){
// speak arrow keys
window.addEventListener('keydown', (e) => {
if( e.key != 'Tab') return
let subScene = xrf.scene.getObjectByName( xrf.frag.pos.last )
if( !subScene ) subScene = xrf.scene
let cache = this.setupHrefCycling.cache = this.setupHrefCycling.cache || {current: 0}
let objects = []
subScene.traverse( (n) => (n.userData.href || n.userData['aria-description']) && objects.push(n) )
const highlight = (n) => {
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.computeLineDistances()
this.helper.material.linewidth = 8
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)
notify(`${n.userData['aria-description']||''}` + (n.userData.href ? `<br><b>name:</b> ${n.name}<br><b>link:</b> ${n.userData['href']}` :'') )
}
// ensure valid href
cache.current = cache.current % objects.length
highlight( objects[cache.current] )
console.log(objects[cache.current].userData.href)
// increment to next
cache.current = cache.current + 1
e.preventDefault()
return false
})
return this
},
setupListeners(){
document.addEventListener('$menu:buttons:render', (e) => {
let $ = e.detail
@ -354,8 +422,10 @@ window.accessibility = (opts) => new Proxy({
network.posName = opts.frag.pos.string
}
})
return this
},
setTimeout( () => this.initCommands(), 200 )
setupPersistance(){
// auto-enable if previously enabled
if( window.localStorage.getItem("accessibility") === 'true' || xrf.navigator.URI.XRF.accessible ){
setTimeout( () => {
@ -363,11 +433,14 @@ window.accessibility = (opts) => new Proxy({
this.setFontSize()
}, 100 )
}
return this
},
initCommands(){
document.addEventListener('chat.command.help', (e) => {
e.detail.message += `<br><b class="badge">&lt;Escape&gt;</b> silence TTS `
e.detail.message += `<br><b class="badge">&lt;Tab&gt;</b> cycle [href] buttons / silence TTS `
e.detail.message += `<br><b class="badge">/fontsize &lt;number&gt;</b> set fontsize (default=14) `
})
@ -572,6 +645,7 @@ window.frontend = (opts) => new Proxy({
</div>
`,
el: null,
notify_links: true,
plugin: {},
xrf,
@ -651,7 +725,7 @@ window.frontend = (opts) => new Proxy({
setTimeout( () => {
let instructions = AFRAME.utils.device.isMobile()
? "hold 2-3 fingers to move forward/backward"
: "use WASD-keys and mouse-drag to move around"
: "use W A S D keys and mouse-drag to move around"
window.notify(instructions,{timeout:false})
xrf.addEventListener('pos', (opts) => {
let pos = opts.frag.pos.string
@ -662,7 +736,7 @@ window.frontend = (opts) => new Proxy({
xrf.addEventListener('href', (data) => {
if( !data.selected ) return
let html = `<b class="badge">${data.mesh.isSRC && !data.mesh.portal ? 'src' : 'href'}</b>${ data.xrf ? data.xrf.string : data.mesh.userData.src}<br>`
let html = this.notify_links ? `<b class="badge">${data.mesh.isSRC && !data.mesh.portal ? 'src' : 'href'}</b>${ data.xrf ? data.xrf.string : data.mesh.userData.src}<br>` : ''
let metadata = data.mesh.userData
let meta = xrf.Parser.getMetaData()
@ -682,6 +756,7 @@ window.frontend = (opts) => new Proxy({
let transcript = xrf.sceneToTranscript(root,data.mesh)
if( transcript.length ) html += `<br><b>transcript:</b><br><div class="transcript">${transcript}</div>`
if (hasMeta && !data.mesh.portal && metadata.XRF.src ) html += `<br><br><a class="btn" style="float:right" onclick="xrf.navigator.to('${data.mesh.userData.href}')">Visit embedded scene</a>`
if( !html ) return
window.notify(html,{timeout: 7000 * (hasMeta ? 1.5 : 1) })
})

View File

@ -1,5 +1,5 @@
/*
* v0.5.1 generated at Sat Jun 15 05:46:02 PM CEST 2024
* v0.5.1 generated at Mon Jun 17 12:41:29 PM UTC 2024
* https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0
*/

View File

@ -1,5 +1,5 @@
/*
* v0.5.1 generated at Sat Jun 15 05:46:02 PM CEST 2024
* v0.5.1 generated at Mon Jun 17 12:41:29 PM UTC 2024
* https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0
*/

View File

@ -39,8 +39,9 @@
<script>
document.addEventListener('$menu:ready', (e) => {
let {$menu} = e.detail
frontend.notify_links = true // shows href/src's as notifications when hovering buttons
// add your menubuttons:
let {$menu} = e.detail
$menu.buttons = $menu.buttons.concat([
`<a class="btn" aria-label="button" aria-title="menu button" onclick="$menu.showAbout()"><i class="gg-info"></i>&nbsp;&nbsp;&nbsp;about</a><br>`
])

View File

@ -32,6 +32,8 @@ window.accessibility = (opts) => new Proxy({
.replace(/&/,' and ')
.replace(/=/,' is ')
}
if( str == this.speak.lastStr ) return // no duplicates
this.speak.lastStr = str
let speech = window.speechSynthesis
let utterance = new SpeechSynthesisUtterance( str )
this.speak_voices = speech.getVoices().length
@ -52,6 +54,18 @@ window.accessibility = (opts) => new Proxy({
init(){
this
.speakArrowKeys()
.setupListeners()
.setupPersistance()
.setupHrefCycling()
.setupSpeechKillOnEscape()
setTimeout( () => this.initCommands(), 200 )
},
speakArrowKeys(){
// speak arrow keys
window.addEventListener('keydown', (e) => {
if( !this.speak_keyboard ) return
let k = e.key
@ -63,6 +77,60 @@ window.accessibility = (opts) => new Proxy({
}
this.speak(k,{override:true})
})
return this
},
setupSpeechKillOnEscape(){
window.addEventListener('keydown', (e) => {
if( e.key == "Escape" ){
this.speak("stop",{override:true})
}
})
},
setupHrefCycling(){
// speak arrow keys
window.addEventListener('keydown', (e) => {
if( e.key != 'Tab') return
let subScene = xrf.scene.getObjectByName( xrf.frag.pos.last )
if( !subScene ) subScene = xrf.scene
let cache = this.setupHrefCycling.cache = this.setupHrefCycling.cache || {current: 0}
let objects = []
subScene.traverse( (n) => (n.userData.href || n.userData['aria-description']) && objects.push(n) )
const highlight = (n) => {
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.computeLineDistances()
this.helper.material.linewidth = 8
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)
notify(`${n.userData['aria-description']||''}` + (n.userData.href ? `<br><b>name:</b> ${n.name}<br><b>link:</b> ${n.userData['href']}` :'') )
}
// ensure valid href
cache.current = cache.current % objects.length
highlight( objects[cache.current] )
console.log(objects[cache.current].userData.href)
// increment to next
cache.current = cache.current + 1
e.preventDefault()
return false
})
return this
},
setupListeners(){
document.addEventListener('$menu:buttons:render', (e) => {
let $ = e.detail
@ -102,8 +170,10 @@ window.accessibility = (opts) => new Proxy({
network.posName = opts.frag.pos.string
}
})
return this
},
setTimeout( () => this.initCommands(), 200 )
setupPersistance(){
// auto-enable if previously enabled
if( window.localStorage.getItem("accessibility") === 'true' || xrf.navigator.URI.XRF.accessible ){
setTimeout( () => {
@ -111,11 +181,14 @@ window.accessibility = (opts) => new Proxy({
this.setFontSize()
}, 100 )
}
return this
},
initCommands(){
document.addEventListener('chat.command.help', (e) => {
e.detail.message += `<br><b class="badge">&lt;Escape&gt;</b> silence TTS `
e.detail.message += `<br><b class="badge">&lt;Tab&gt;</b> cycle [href] buttons / silence TTS `
e.detail.message += `<br><b class="badge">/fontsize &lt;number&gt;</b> set fontsize (default=14) `
})

View File

@ -13,6 +13,7 @@ window.frontend = (opts) => new Proxy({
</div>
`,
el: null,
notify_links: true,
plugin: {},
xrf,
@ -92,7 +93,7 @@ window.frontend = (opts) => new Proxy({
setTimeout( () => {
let instructions = AFRAME.utils.device.isMobile()
? "hold 2-3 fingers to move forward/backward"
: "use WASD-keys and mouse-drag to move around"
: "use W A S D keys and mouse-drag to move around"
window.notify(instructions,{timeout:false})
xrf.addEventListener('pos', (opts) => {
let pos = opts.frag.pos.string
@ -103,7 +104,7 @@ window.frontend = (opts) => new Proxy({
xrf.addEventListener('href', (data) => {
if( !data.selected ) return
let html = `<b class="badge">${data.mesh.isSRC && !data.mesh.portal ? 'src' : 'href'}</b>${ data.xrf ? data.xrf.string : data.mesh.userData.src}<br>`
let html = this.notify_links ? `<b class="badge">${data.mesh.isSRC && !data.mesh.portal ? 'src' : 'href'}</b>${ data.xrf ? data.xrf.string : data.mesh.userData.src}<br>` : ''
let metadata = data.mesh.userData
let meta = xrf.Parser.getMetaData()
@ -123,6 +124,7 @@ window.frontend = (opts) => new Proxy({
let transcript = xrf.sceneToTranscript(root,data.mesh)
if( transcript.length ) html += `<br><b>transcript:</b><br><div class="transcript">${transcript}</div>`
if (hasMeta && !data.mesh.portal && metadata.XRF.src ) html += `<br><br><a class="btn" style="float:right" onclick="xrf.navigator.to('${data.mesh.userData.href}')">Visit embedded scene</a>`
if( !html ) return
window.notify(html,{timeout: 7000 * (hasMeta ? 1.5 : 1) })
})