mirror of
https://github.com/jech/galene.git
synced 2024-12-22 23:35:46 +01:00
changes for keyboard and scrennreader users
This commit is contained in:
parent
2c72a27453
commit
999ef3ba7b
5 changed files with 482 additions and 79 deletions
39
KEYBOARD
Normal file
39
KEYBOARD
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Use of the keyboard with Galene
|
||||
|
||||
People wich are not able to use a mouse need to have other possibilities.
|
||||
|
||||
Some user will use the keyboard in order to navigate to the galene Web
|
||||
Interface and will use the Tab Key to select the wanted element and
|
||||
other key as arrow up, down, space.
|
||||
|
||||
User with visual impairement, may use a screenreader.
|
||||
|
||||
For keyboard user we can use the following keys:
|
||||
|
||||
- 'u' goto user list
|
||||
- 'c' go to the chat input box
|
||||
- 'r' raise, unraise hand
|
||||
- 'm' mute, unmute
|
||||
|
||||
|
||||
- Esc close contextual menu, close the setting, close the chat box,
|
||||
close the user list
|
||||
|
||||
|
||||
For the screenreader user, it is difficult to provide shortcuts, most
|
||||
keys or keys combination are reserved from the OS the screenreader and
|
||||
the browser.
|
||||
|
||||
In order to offer a faster way for selecting the wanted element, we use
|
||||
thw header navigation (h1, h2. h3).
|
||||
|
||||
- The key 1 will got to the main Title of the page.
|
||||
- The key 2 will allow to select one of the area user list, chat box or media area.
|
||||
- The key 3 allow to navigate from message to message.
|
||||
- The Tab and Esc key work as for the normal keyboard user
|
||||
|
||||
Screenreader shall announce the error and warning textes issued by galene,
|
||||
this is also implemented.
|
||||
|
||||
|
||||
|
|
@ -1,3 +1,15 @@
|
|||
|
||||
button {
|
||||
background: none;
|
||||
border: 0;
|
||||
}
|
||||
.screenreader {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.nav-fixed .topnav {
|
||||
z-index: 1039;
|
||||
}
|
||||
|
@ -109,7 +121,7 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
.sidenav .user-logout a {
|
||||
.sidenav .user-logout button {
|
||||
font-size: 1em;
|
||||
padding: 7px 0 0;
|
||||
color: #e4157e;
|
||||
|
@ -117,7 +129,7 @@
|
|||
line-height: .7;
|
||||
}
|
||||
|
||||
.sidenav .user-logout a:hover {
|
||||
.sidenav .user-logout button:hover {
|
||||
color: #ab0659;
|
||||
}
|
||||
|
||||
|
@ -269,12 +281,12 @@
|
|||
}
|
||||
|
||||
.full-width {
|
||||
width: calc(100vw - 200px);
|
||||
/*width: calc(100vw - 200px);*/
|
||||
height: calc(var(--vh, 1vh) * 100 - 56px);
|
||||
}
|
||||
|
||||
.full-width-active {
|
||||
width: 100vw;
|
||||
/* width: 100vw;*/
|
||||
}
|
||||
|
||||
.container {
|
||||
|
@ -312,7 +324,6 @@
|
|||
resize: none;
|
||||
overflow: hidden;
|
||||
padding: 5px;
|
||||
outline: none;
|
||||
border: none;
|
||||
text-indent: 5px;
|
||||
box-shadow: none;
|
||||
|
@ -440,6 +451,13 @@ textarea.form-reply {
|
|||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.message button {
|
||||
margin: 1em;
|
||||
padding: .4em .8em;
|
||||
border-radius: .7em;
|
||||
background: #a1ceff;
|
||||
}
|
||||
|
||||
.video-container {
|
||||
height: calc(var(--vh, 1vh) * 100 - 56px);
|
||||
position: relative;
|
||||
|
@ -467,7 +485,7 @@ textarea.form-reply {
|
|||
}
|
||||
|
||||
.collapse-video {
|
||||
left: 30px;
|
||||
left: calc(-100vw);
|
||||
right: inherit;
|
||||
}
|
||||
|
||||
|
@ -518,6 +536,7 @@ textarea.form-reply {
|
|||
background: linear-gradient(180deg, rgb(0 0 0 / 20%) 0%, rgb(0 0 0 / 50%) 0%, rgb(0 0 0 / 70%) 100%);
|
||||
}
|
||||
|
||||
.peer:focus-within > .video-controls, .peer:focus-within > .top-video-controls,
|
||||
.peer:hover > .video-controls, .peer:hover > .top-video-controls {
|
||||
opacity: 1;
|
||||
}
|
||||
|
@ -529,6 +548,10 @@ textarea.form-reply {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.peer button i {
|
||||
color: #eaeaea;
|
||||
}
|
||||
|
||||
.video-controls span:last-child, .top-video-controls span:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
@ -583,6 +606,7 @@ textarea.form-reply {
|
|||
transition: opacity .5s ease-out;
|
||||
}
|
||||
|
||||
.video-controls .volume:focus-within,
|
||||
.video-controls .volume:hover {
|
||||
--ov: 1;
|
||||
--dv: inline;
|
||||
|
@ -663,7 +687,7 @@ textarea.form-reply {
|
|||
}
|
||||
|
||||
.nav-cancel, .muted, .nav-cancel label, .muted label {
|
||||
color: #d03e3e
|
||||
color: #d03e3e;
|
||||
}
|
||||
|
||||
.nav-cancel:hover, .muted:hover, .nav-cancel label:hover, .muted label:hover {
|
||||
|
@ -675,7 +699,7 @@ textarea.form-reply {
|
|||
font-size: 25px;
|
||||
}
|
||||
|
||||
.nav-more {
|
||||
#openside .nav-more {
|
||||
padding-top: 5px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
@ -786,21 +810,25 @@ h1 {
|
|||
}
|
||||
|
||||
#filterselect {
|
||||
width: 8em;
|
||||
text-align-last: center;
|
||||
margin-right: 0.4em;
|
||||
}
|
||||
|
||||
#sendselect {
|
||||
width: 8em;
|
||||
text-align-last: center;
|
||||
margin-right: 0.4em;
|
||||
}
|
||||
|
||||
#simulcastselect {
|
||||
width: 8em;
|
||||
text-align-last: center;
|
||||
margin-right: 0.4em;
|
||||
}
|
||||
|
||||
#requestselect {
|
||||
width: 8em;
|
||||
text-align-last: center;
|
||||
}
|
||||
|
||||
|
@ -864,14 +892,6 @@ h1 {
|
|||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#inputbutton:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#resizer {
|
||||
width: 4px;
|
||||
margin-left: -4px;
|
||||
|
@ -955,7 +975,7 @@ h1 {
|
|||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.sidenav a {
|
||||
.sidenav button {
|
||||
padding: 10px 20px;
|
||||
text-decoration: none;
|
||||
font-size: 30px;
|
||||
|
@ -965,7 +985,11 @@ h1 {
|
|||
line-height: 1.0;
|
||||
}
|
||||
|
||||
.sidenav a:hover {
|
||||
.sidenav button:hover {
|
||||
color: #c2a4e0;
|
||||
}
|
||||
|
||||
.sidenav button:hover {
|
||||
color: #c2a4e0;
|
||||
}
|
||||
|
||||
|
@ -1111,9 +1135,22 @@ header .collapse:hover {
|
|||
}
|
||||
|
||||
/* Shrinking the sidebar from 200px to 0px */
|
||||
#sidebarnav {
|
||||
display: none;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
#sidebarnav[open=true] {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#left-sidebar.active {
|
||||
min-width: 0;
|
||||
max-width: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#left-sidebar:not(.active) {
|
||||
display: block;
|
||||
width:200px;
|
||||
}
|
||||
|
||||
#left-sidebar .sidebar-header strong {
|
||||
|
@ -1148,17 +1185,23 @@ header .collapse:hover {
|
|||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
white-space: pre;
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#left-sidebar.active #users > div {
|
||||
#left-sidebar.active #users > button {
|
||||
padding: 10px 5px !important;
|
||||
}
|
||||
|
||||
#users > div:hover {
|
||||
#users > button:hover {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
#users > button:focus, #users > button:focus-visible {
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
#users > div::before {
|
||||
#users > button::before {
|
||||
content: "\f111";
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
color: #20b91e;
|
||||
|
@ -1166,11 +1209,11 @@ header .collapse:hover {
|
|||
font-weight: 900;
|
||||
}
|
||||
|
||||
#users > div.user-status-raisehand::before {
|
||||
#users > button.user-status-raisehand::before {
|
||||
content: "\f256";
|
||||
}
|
||||
|
||||
#users > div::after {
|
||||
#users > button::after {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
color: #808080;
|
||||
margin-left: 5px;
|
||||
|
@ -1178,11 +1221,11 @@ header .collapse:hover {
|
|||
float: right;
|
||||
}
|
||||
|
||||
#users > div.user-status-microphone::after {
|
||||
#users > button.user-status-microphone::after {
|
||||
content: "\f130";
|
||||
}
|
||||
|
||||
#users > div.user-status-camera::after {
|
||||
#users > button.user-status-camera::after {
|
||||
content: "\f030";
|
||||
}
|
||||
|
||||
|
@ -1231,6 +1274,10 @@ header .collapse:hover {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.video-on #chat {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.video-container {
|
||||
position: fixed;
|
||||
height: calc(var(--vh, 1vh) * 100 - 56px);
|
||||
|
@ -1261,7 +1308,7 @@ header .collapse:hover {
|
|||
flex: 100%;
|
||||
width: 100vw;
|
||||
/* chat is always visible here */
|
||||
display: block !important;
|
||||
display: block /*!important*/;
|
||||
}
|
||||
|
||||
.coln-right {
|
||||
|
@ -1273,6 +1320,7 @@ header .collapse:hover {
|
|||
width: 100vw;
|
||||
}
|
||||
|
||||
/*
|
||||
#left-sidebar.active {
|
||||
min-width: 200px;
|
||||
max-width: 200px;
|
||||
|
@ -1282,10 +1330,11 @@ header .collapse:hover {
|
|||
min-width: 0;
|
||||
max-width: 0;
|
||||
}
|
||||
|
||||
*/
|
||||
/* Reappearing the sidebar on toggle button click */
|
||||
#left-sidebar {
|
||||
margin-left: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#left-sidebar .sidebar-header strong {
|
||||
|
@ -1368,6 +1417,12 @@ header .collapse:hover {
|
|||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.contextualMenu button {
|
||||
outline-offset: 3px;
|
||||
display: inline-block;
|
||||
width: calc(100% - 2em);
|
||||
}
|
||||
|
||||
#invite-dialog {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
@ -1394,3 +1449,28 @@ header .collapse:hover {
|
|||
.toastify.info .toast-close {
|
||||
color: #000;
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* wrong preset for some browser! */
|
||||
video::-webkit-media-controls-enclosure {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
video::-webkit-media-controls-panel {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
video::-webkit-media-controls {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,71 +13,75 @@
|
|||
<link rel="stylesheet" type="text/css" href="/third-party/fontawesome/css/regular.min.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="/third-party/toastify/toastify.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="/third-party/contextual/contextual.css"/>
|
||||
<script src="/key.js" ></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="main" class="app">
|
||||
<div id="srSpeak" class="sr-only" aria-live="assertive" aria-atomic="true"></div>
|
||||
<main id="main" class="app">
|
||||
<div class="row full-height">
|
||||
<nav id="left-sidebar">
|
||||
<nav id="left-sidebar" class="active">
|
||||
<div class="users-header">
|
||||
<div class="galene-header">Galène</div>
|
||||
</div>
|
||||
<div class="header-sep"></div>
|
||||
<h2 class="screenreader">User list</h2>
|
||||
<div rollr="region" class="header-sep"></div>
|
||||
<div id="users"></div>
|
||||
</nav>
|
||||
<div class="container">
|
||||
<header>
|
||||
<nav class="topnav navbar navbar-expand navbar-light fixed-top">
|
||||
<div id="header">
|
||||
<div class="collapse" title="Collapse left panel" id="sidebarCollapse">
|
||||
<button class="collapse" title="Collapse left panel" id="sidebarCollapse" aria-label="Collapse left panel">
|
||||
<i class="fas fa-align-left" aria-hidden="true"></i>
|
||||
</div>
|
||||
</button>
|
||||
<h1 id="title" class="header-title">Galène</h1>
|
||||
</div>
|
||||
|
||||
<ul class="nav-menu">
|
||||
<li>
|
||||
<button id="presentbutton" class="invisible btn btn-success">
|
||||
<button id="presentbutton" class="invisible btn btn-success" aria-label=" Enable">
|
||||
<i class="fas fa-play" aria-hidden="true"></i><span class="nav-text"> Enable</span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button id="unpresentbutton" class="invisible btn btn-cancel">
|
||||
<button id="unpresentbutton" class="invisible btn btn-cancel" aria-label=" Disable">
|
||||
<i class="fas fa-stop" aria-hidden="true"></i><span class="nav-text"> Disable</span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<div id="mutebutton" class="nav-link nav-button">
|
||||
<button id="mutebutton" class="nav-link nav-button" aria-label="Mute">
|
||||
<span><i class="fas fa-microphone-slash" aria-hidden="true"></i></span>
|
||||
<label>Mute</label>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<div id="sharebutton" class="invisible nav-link nav-button">
|
||||
<button id="sharebutton" class="invisible nav-link nav-button" aria-label="Share Screen">
|
||||
<span><i class="fas fa-share-square" aria-hidden="true"></i></span>
|
||||
<label>Share Screen</label>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<div class="nav-button nav-link nav-more" id="openside">
|
||||
<button class="nav-button nav-link nav-more" id="openside" title="Open settings" aria-label="Open settings">
|
||||
<span><i class="fas fa-ellipsis-v" aria-hidden="true"></i></span>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="row full-width" id="mainrow">
|
||||
<div class="coln-left" id="left">
|
||||
<div id="chat">
|
||||
<div class="row full-width video-on" id="mainrow">
|
||||
<div class="coln-left invisible" id="left" >
|
||||
<div role="region" id="chat">
|
||||
<h2 class="screenreader">Chat box</h2>
|
||||
<div id="chatbox">
|
||||
<div class="close-chat" id="close-chat" title="Hide chat">
|
||||
<button class="close-chat" id="close-chat" title="Hide chat" aria-label="Hide chat">
|
||||
<span class="close-icon"></span>
|
||||
</div>
|
||||
<div id="box"></div>
|
||||
</button>
|
||||
<div id="box" tabindex="0" aria-label="Chat Area"></div>
|
||||
<div class="reply">
|
||||
<form id="inputform">
|
||||
<textarea id="input" class="form-reply"></textarea>
|
||||
<input id="inputbutton" type="submit" value="➤" class="btn btn-default"/>
|
||||
<textarea id="input" class="form-reply" aria-label="Enter message"></textarea>
|
||||
<input id="inputbutton" type="submit" value="➤" class="btn btn-default" aria-label="Send message"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -85,16 +89,16 @@
|
|||
</div>
|
||||
<div id="resizer"></div>
|
||||
<div class="coln-right" id="right">
|
||||
<span class="show-video blink invisible" id="show-video">
|
||||
<button class="show-video blink invisible" id="show-video" aria-label="Show video">
|
||||
<i class="fas fa-exchange-alt" aria-hidden="true"></i>
|
||||
</span>
|
||||
<div class="chat-btn show-chat invisible" id="show-chat">
|
||||
</button>
|
||||
<button class="chat-btn show-chat" id="show-chat" aria-label="Show chat">
|
||||
<i class="far fa-comment-alt icon-chat" title="Show chat"></i>
|
||||
</div>
|
||||
<div class="chat-btn collapse-video invisible" id="collapse-video">
|
||||
</button>
|
||||
<button class="chat-btn collapse-video invisible" id="collapse-video" aria-label="Hide video and show chat">
|
||||
<i class="far fa-comment-alt icon-chat" title="Hide video and show chat"></i>
|
||||
</div>
|
||||
<div class="video-container invisible" id="video-container">
|
||||
</button>
|
||||
<div class="video-container invisible" id="video-container"><h2 class="screenreader">Video area</h2>
|
||||
<div id="expand-video" class="expand-video">
|
||||
<div id="peers"></div>
|
||||
</div>
|
||||
|
@ -142,12 +146,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div id="sidebarnav" class="sidenav">
|
||||
<div id="sidebarnav" class="sidenav" aria-hidden="true">
|
||||
<div class="sidenav-header">
|
||||
<h2>Settings</h2>
|
||||
<a class="closebtn" id="clodeside"><i class="fas fa-times" aria-hidden="true"></i></a>
|
||||
<button class="closebtn" id="clodeside" title="Close settings" aria-label="Close settings"><i class="fas fa-times"></i>
|
||||
</div>
|
||||
<div class="sidenav-content" id="optionsdiv">
|
||||
<div id="profile" class="profile invisible">
|
||||
|
@ -161,10 +165,10 @@
|
|||
<span id="chpwspan" class="invisible"><a id="change-password">Change password</a></span>
|
||||
</div>
|
||||
<div class="user-logout">
|
||||
<a id="disconnectbutton">
|
||||
<button id="disconnectbutton">
|
||||
<span class="logout-icon"><i class="fas fa-sign-out-alt"></i></span>
|
||||
<span class="logout-text">Logout</span>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -260,32 +264,36 @@
|
|||
<div id="videocontrols-template" class="invisible">
|
||||
<div class="video-controls vc-overlay">
|
||||
<div class="controls-button controls-left">
|
||||
<span class="video-play" title="Play video">
|
||||
<button class="video-play" title="Play video" aria-label="Play video">
|
||||
<i class="fas fa-play"></i>
|
||||
</span>
|
||||
</button>
|
||||
<span class="volume" title="Volume">
|
||||
<i class="fas fa-volume-up volume-mute" aria-hidden="true"></i>
|
||||
<input class="volume-slider" type="range" max="100" value="100" min="0" step="5" >
|
||||
<i class="fas fa-volume-up volume-mute" tabindex="0" role='button'></i>
|
||||
<input class="volume-slider" type="range" max="100" value="100" min="0" step="5" aria-label="Volume slider">
|
||||
</span>
|
||||
</div>
|
||||
<div class="controls-button controls-right">
|
||||
<span class="pip" title="Picture In Picture">
|
||||
<button class="pip" title="Picture In Picture" aria-label="Picture In Picture">
|
||||
<i class="far fa-clone" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span class="fullscreen" title="Fullscreen">
|
||||
</button>
|
||||
<button class="fullscreen" title="Fullscreen" aria-label="Fullscreen">
|
||||
<i class="fas fa-expand" aria-hidden="true"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="topvideocontrols-template" class="invisible">
|
||||
<div class="top-video-controls">
|
||||
<div class="controls-button controls-right">
|
||||
<span class="close-icon video-stop" title="Stop video">
|
||||
</span>
|
||||
<button class="close-icon video-stop" title="Stop video" aria-label="Stop video">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sr-help invisible"> <!-- help for translation -->
|
||||
<span class='voff'>Muted</span>
|
||||
<span class='von'>On</span>
|
||||
</div>
|
||||
|
||||
<dialog id="invite-dialog">
|
||||
<form method="dialog">
|
||||
|
|
|
@ -2039,9 +2039,13 @@ function setVolumeButton(muted, button, slider) {
|
|||
if(!muted) {
|
||||
button.classList.remove("fa-volume-mute");
|
||||
button.classList.add("fa-volume-up");
|
||||
let von = document.querySelector('.sr-help .von').textContent;
|
||||
button.setAttribute("aria-label", von);
|
||||
} else {
|
||||
button.classList.remove("fa-volume-up");
|
||||
button.classList.add("fa-volume-mute");
|
||||
let voff = document.querySelector('.sr-help .voff').textContent;
|
||||
button.setAttribute("aria-label",voff);
|
||||
}
|
||||
|
||||
if(!(slider instanceof HTMLInputElement))
|
||||
|
@ -2416,7 +2420,8 @@ function userMenu(elt) {
|
|||
*/
|
||||
function addUser(id, userinfo) {
|
||||
let div = document.getElementById('users');
|
||||
let user = document.createElement('div');
|
||||
//let user = document.createElement('div');
|
||||
let user = document.createElement('button');
|
||||
user.id = 'user-' + id;
|
||||
user.classList.add("user-p");
|
||||
setUserStatus(id, user, userinfo);
|
||||
|
@ -3126,7 +3131,8 @@ function addToChatbox(id, peerId, dest, nick, time, privileged, history, kind, m
|
|||
}
|
||||
|
||||
if(doHeader) {
|
||||
let header = document.createElement('p');
|
||||
// let header = document.createElement('p');
|
||||
let header = document.createElement('h3');
|
||||
let user = document.createElement('span');
|
||||
let u = dest && serverConnection.users[dest];
|
||||
let name = (u && u.username);
|
||||
|
@ -4131,11 +4137,16 @@ document.getElementById('disconnectbutton').onclick = function(e) {
|
|||
};
|
||||
|
||||
function openNav() {
|
||||
document.getElementById("sidebarnav").style.width = "250px";
|
||||
document.getElementById("sidebarnav").setAttribute("open",'true');
|
||||
document.getElementById("sidebarnav").removeAttribute('aria-hidden');
|
||||
document.querySelector('#sidebarnav .closebtn').focus();
|
||||
}
|
||||
|
||||
function closeNav() {
|
||||
document.getElementById("sidebarnav").style.width = "0";
|
||||
document.getElementById("sidebarnav").removeAttribute('open');
|
||||
document.getElementById("sidebarnav").setAttribute("aria-hidden",'true');
|
||||
document.querySelector('#openside').focus();
|
||||
|
||||
}
|
||||
|
||||
document.getElementById('sidebarCollapse').onclick = function(e) {
|
||||
|
@ -4144,9 +4155,9 @@ document.getElementById('sidebarCollapse').onclick = function(e) {
|
|||
};
|
||||
|
||||
document.getElementById('openside').onclick = function(e) {
|
||||
e.preventDefault();
|
||||
let sidewidth = document.getElementById("sidebarnav").style.width;
|
||||
if (sidewidth !== "0px" && sidewidth !== "") {
|
||||
//e.preventDefault();
|
||||
let open = document.getElementById("sidebarnav").getAttribute('open');
|
||||
if ( open ) {
|
||||
closeNav();
|
||||
return;
|
||||
} else {
|
||||
|
@ -4165,6 +4176,7 @@ document.getElementById('collapse-video').onclick = function(e) {
|
|||
setVisibility('collapse-video', false);
|
||||
setVisibility('show-video', true);
|
||||
hideVideo(true);
|
||||
document.getElementById('mainrow').classList.remove('video-on');
|
||||
};
|
||||
|
||||
document.getElementById('show-video').onclick = function(e) {
|
||||
|
@ -4172,6 +4184,7 @@ document.getElementById('show-video').onclick = function(e) {
|
|||
setVisibility('video-container', true);
|
||||
setVisibility('collapse-video', true);
|
||||
setVisibility('show-video', false);
|
||||
document.getElementById('mainrow').classList.add('video-on');
|
||||
};
|
||||
|
||||
document.getElementById('close-chat').onclick = function(e) {
|
||||
|
|
263
static/key.js
Normal file
263
static/key.js
Normal file
|
@ -0,0 +1,263 @@
|
|||
'use strict';
|
||||
let galeneKeys = {
|
||||
from : null,
|
||||
focusAgain : function() {
|
||||
if ( galeneKeys.from ) {
|
||||
galeneKeys.from.focus();
|
||||
//galeneKeys.from = null;
|
||||
}
|
||||
else {
|
||||
document.body.focus();
|
||||
}
|
||||
},
|
||||
leave : function (event) {
|
||||
if ( event.target.classList.contains('first') ) {
|
||||
if ( event.shiftKey ) {
|
||||
let cm = document.querySelector('.contextualMenu');
|
||||
if ( cm ) {
|
||||
cm.remove();
|
||||
setTimeout(galeneKeys.focusAgain, 20);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( event.target.classList.contains('last') ) {
|
||||
if ( ! event.shiftKey ) {
|
||||
let cm = document.querySelector('.contextualMenu');
|
||||
if ( cm ) {
|
||||
cm.remove();
|
||||
setTimeout(galeneKeys.focusAgain, 20);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( event.key === 'Escape' )
|
||||
setTimeout(galeneKeys.focusAgain, 20);
|
||||
},
|
||||
setTabindexContextMenu : function (target) {
|
||||
let context = document.querySelectorAll('.contextualMenuItemTitle');
|
||||
if ( context && context.length) {
|
||||
context.forEach( c => {
|
||||
let btn = document.createElement('button');
|
||||
btn.classList.add('contextualMenuItemTitle');
|
||||
btn.classList.add('contextualJs');
|
||||
btn.textContent = c.textContent;
|
||||
btn.addEventListener('click', galeneKeys.menuClick);
|
||||
c.replaceWith(btn);
|
||||
});
|
||||
context = document.querySelectorAll('.contextualMenuItemTitle');
|
||||
context[0].classList.add('first');
|
||||
let last = context.length - 1;
|
||||
context[last].classList.add('last');
|
||||
galeneKeys.from = target;
|
||||
context[0].focus();
|
||||
let pos = target.getBoundingClientRect();
|
||||
|
||||
let contextParent = document.querySelector('.contextualMenu');
|
||||
contextParent.style.top = pos.top+20+'px';
|
||||
// the following work only if the display with is enough we may have to correct this.
|
||||
let x = 180;
|
||||
let w = window.innerWidth;
|
||||
if ( x + 200 > window.innerWidth ) {
|
||||
x = w - 205;
|
||||
}
|
||||
contextParent.style.left = x+'px';
|
||||
|
||||
let menu = document.querySelector('ul.contextualMenu');
|
||||
} else {
|
||||
// Enter pressed
|
||||
galeneKeys.focusAgain();
|
||||
}
|
||||
},
|
||||
menuClick : function(e) {
|
||||
galeneKeys.focusAgain();
|
||||
},
|
||||
processKey : function (event) {
|
||||
let target = event.target;
|
||||
let key = event.key;
|
||||
switch(key) {
|
||||
case 'Tab':
|
||||
if ( document.activeElement.classList.contains('contextualMenuItemTitle') ) {
|
||||
galeneKeys.leave(event);
|
||||
return;
|
||||
}
|
||||
if ( document.activeElement.id === 'sideBarCollapse')
|
||||
return;
|
||||
break;
|
||||
case 'Escape':
|
||||
let dialog = document.querySelector('dialog');
|
||||
if ( dialog ) {
|
||||
let open = dialog.getAttribute('open');
|
||||
if ( open == '' ) {
|
||||
dialog.close();
|
||||
galeneKeys.focusAgain();
|
||||
return;
|
||||
}
|
||||
}
|
||||
let cm = document.querySelector('.contextualMenu');
|
||||
if ( cm ) {
|
||||
cm.remove();
|
||||
galeneKeys.leave(event);
|
||||
return;
|
||||
}
|
||||
// if the setting are open close them
|
||||
let sidebar = document.querySelector('#sidebarnav[open]');
|
||||
if ( sidebar ) {
|
||||
event.preventDefault();
|
||||
sidebar.removeAttribute('open');
|
||||
return;
|
||||
}
|
||||
// check if we are within the chat
|
||||
let active = document.activeElement;
|
||||
let chat = document.querySelector('#left:not(.invisible)');
|
||||
if ( chat ) {
|
||||
event.preventDefault();
|
||||
chat.classList.add('invisible');
|
||||
let showChat = document.querySelector('#show-chat');
|
||||
showChat.classList.remove('invisible');
|
||||
return;
|
||||
}
|
||||
// finally close possibly the user list
|
||||
let userList = document.querySelector('#left-sidebar:not(.active)');
|
||||
if ( userList ) {
|
||||
event.preventDefault();
|
||||
document.querySelector('#left-sidebar').classList.add('active');
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'Enter':
|
||||
case ' ':
|
||||
if ( target.classList.contains('volume-mute') ) {
|
||||
let state =
|
||||
target.click();
|
||||
if ( translate && translate.translateList ) {
|
||||
translate.setVolumeAria();
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
switch(target.nodeName) {
|
||||
case 'INPUT':
|
||||
let type = target.getAttribute('type');
|
||||
if ( type && type === 'submit' ) {
|
||||
return;
|
||||
}
|
||||
case 'TEXTAREA':
|
||||
case 'SELECT':
|
||||
return;
|
||||
break;
|
||||
}
|
||||
let usercont = null;
|
||||
switch(key) {
|
||||
case 'u':
|
||||
event.preventDefault();
|
||||
usercont = document.querySelector('#left-sidebar');
|
||||
if ( !usercont.classList.contains('active')) {
|
||||
/* focus first user */
|
||||
let user = document.querySelector('#users .user-p');
|
||||
if ( user )
|
||||
user.focus();
|
||||
} else {
|
||||
usercont.classList.remove('active');
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
/* raise hand */
|
||||
let me = document.querySelector('#left-sidebar #users .user-p');
|
||||
if ( me ) {
|
||||
if (me.classList.contains('user-status-raisehand') )
|
||||
me.classList.remove('user-status-raisehand');
|
||||
else
|
||||
me.classList.add('user-status-raisehand');
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
let localMute = getSettings().localMute;
|
||||
localMute = !localMute;
|
||||
setLocalMute(localMute, true);
|
||||
break;
|
||||
case 'c':
|
||||
/* Chat */
|
||||
event.preventDefault();
|
||||
let chat = document.querySelector('#left:not(.invisible)');
|
||||
if (!chat) {
|
||||
setVisibility('left', true);
|
||||
setVisibility('show-chat', false);
|
||||
resizePeers();
|
||||
chat = document.querySelector('#left:not(.invisible) textarea');
|
||||
}
|
||||
if (chat)
|
||||
chat.focus();
|
||||
break;
|
||||
}
|
||||
},
|
||||
userClick : function (e) {
|
||||
galeneKeys.from = e.target;
|
||||
setTimeout(galeneKeys.setTabindexContextMenu,20, e.target);
|
||||
},
|
||||
addClickListener : function (mutationList) {
|
||||
for (const mutation of mutationList) {
|
||||
if (mutation.type === "childList") {
|
||||
if (mutation && mutation.addedNodes) {
|
||||
let idx = mutation.addedNodes.length -1;
|
||||
if (idx >= 0) {
|
||||
mutation.addedNodes[idx].addEventListener('click', galeneKeys.userClick);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
focusInput : function() {
|
||||
let input = document.querySelector('#input');
|
||||
input.focus();
|
||||
},
|
||||
collapseSidebar : function(e) {
|
||||
let sidebar = document.querySelector('#left-sidebar.active');
|
||||
if ( !sidebar ) {
|
||||
// focus the first user-p button done but no outline!
|
||||
//let user = document.querySelector('.user-p');
|
||||
let userP = document.querySelector('.user-p');
|
||||
if (userP)
|
||||
userP.focus();
|
||||
}
|
||||
},
|
||||
setSr : function (sr, toast) {
|
||||
sr.textContent = toast.textContent;
|
||||
},
|
||||
resetSr : function(sr) {
|
||||
sr.textContent = '';
|
||||
},
|
||||
setSrText : function() {
|
||||
let toast = document.querySelector('.toastify');
|
||||
let sr = document.querySelector('#srSpeak');
|
||||
if ( toast ) {
|
||||
setTimeout(galeneKeys.setSr, 50, sr, toast);
|
||||
setTimeout(galeneKeys.resetSr, 4000, sr);
|
||||
}
|
||||
},
|
||||
init : function () {
|
||||
// add mutation oberver
|
||||
let toObserve = document.querySelector('#users');
|
||||
if ( toObserve ) {
|
||||
const observer = new MutationObserver(galeneKeys.addClickListener);
|
||||
observer.observe(toObserve, {childList:true, subtree:false});
|
||||
}
|
||||
const popup = new MutationObserver(galeneKeys.setSrText);
|
||||
popup.observe(document.body, {childList: true,subtree: false});
|
||||
|
||||
// add event listener to the show-chat button
|
||||
let showChat = document.querySelector('#show-chat');
|
||||
if ( showChat ) {
|
||||
showChat.addEventListener('click',galeneKeys.focusInput);
|
||||
}
|
||||
let left = document.querySelector('#sidebarCollapse');
|
||||
if ( left ) {
|
||||
left.addEventListener('click', galeneKeys.collapseSidebar);
|
||||
}
|
||||
},
|
||||
};
|
||||
document.addEventListener('keydown', galeneKeys.processKey);
|
||||
document.addEventListener('DOMContentLoaded', galeneKeys.init);
|
||||
|
Loading…
Reference in a new issue