Added a lot of new stuff
This commit is contained in:
parent
efb4bad182
commit
6d537bac15
66 changed files with 3564 additions and 2103 deletions
|
@ -1,4 +1,12 @@
|
||||||
# Changelog:
|
# Changelog:
|
||||||
|
* **04.04.19**
|
||||||
|
- Fixed issue with client icons not updating correctly (Showing invalid microphone state)
|
||||||
|
- Added multi server mode
|
||||||
|
- Connect URL not disconnecting from all other servers
|
||||||
|
- Open certificate accept URL in another tab
|
||||||
|
- Improved the host banner experience
|
||||||
|
- Hiding server group types in permission editor which are not editable
|
||||||
|
|
||||||
* **28.03.19**
|
* **28.03.19**
|
||||||
- Improved icon and avatar cache handling
|
- Improved icon and avatar cache handling
|
||||||
- Added an icon manager
|
- Added an icon manager
|
||||||
|
|
|
@ -13,7 +13,7 @@ html, body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: stretch;
|
||||||
|
|
||||||
.app {
|
.app {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@import "properties";
|
||||||
|
|
||||||
/* the channel tree */
|
/* the channel tree */
|
||||||
.channel-tree {
|
.channel-tree {
|
||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
|
@ -60,7 +62,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
background-color: blue;
|
background-color: $channel_tree_entry_selected;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
color: whitesmoke;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,10 +85,6 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&.selected {
|
|
||||||
background-color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-type {
|
.channel-type {
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
@ -128,6 +130,13 @@
|
||||||
border-bottom: 1px solid black;
|
border-bottom: 1px solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: $channel_tree_entry_selected;
|
||||||
|
.channel-name {
|
||||||
|
color: whitesmoke;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.show-channel-normal-only {
|
.show-channel-normal-only {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
|
@ -191,7 +200,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
background-color: blue;
|
background-color: $channel_tree_entry_selected;
|
||||||
|
|
||||||
|
.client-name {
|
||||||
|
color: whitesmoke;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
119
shared/css/static/connection_handlers.scss
Normal file
119
shared/css/static/connection_handlers.scss
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
|
||||||
|
.container-connection-handlers {
|
||||||
|
height: 35px;
|
||||||
|
background-color: lightgray;
|
||||||
|
|
||||||
|
border: 4px solid lightgray;
|
||||||
|
border-top: 1px dotted gray;
|
||||||
|
border-bottom-width: 0;
|
||||||
|
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.connection-handlers {
|
||||||
|
height: 100%;
|
||||||
|
width: fit-content;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: left;
|
||||||
|
|
||||||
|
.connection-container {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
|
||||||
|
margin-top: 5px;
|
||||||
|
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
|
||||||
|
border: 1px #2222223b solid;
|
||||||
|
border-radius: 2px 2px 0 0;
|
||||||
|
|
||||||
|
.server-icon {
|
||||||
|
align-self: center;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-name {
|
||||||
|
align-self: center;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-close {
|
||||||
|
align-self: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #e7e7e7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #FFFFFF33;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-scroll {
|
||||||
|
margin-top: 5px;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
display: none;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
&.enabled {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-scroll {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
|
||||||
|
border: 1px #2222223b solid;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: #e7e7e7;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 2px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #eeeeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
background: #9e9e9e;
|
||||||
|
&:hover {
|
||||||
|
background: #9e9e9e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.scrollbar {
|
||||||
|
.connection-handlers {
|
||||||
|
width: calc(100% - 45px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,11 @@ $background:lightgray;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
/* tmp fix for ultra small devices */
|
/* tmp fix for ultra small devices */
|
||||||
overflow-y: visible;
|
overflow-y: visible;
|
||||||
|
|
||||||
|
@ -99,8 +104,6 @@ $background:lightgray;
|
||||||
border: 2px solid $border_color_activated;
|
border: 2px solid $border_color_activated;
|
||||||
width: 230px;
|
width: 230px;
|
||||||
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
/*box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19);*/
|
/*box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19);*/
|
||||||
|
|
||||||
|
@ -136,7 +139,7 @@ $background:lightgray;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover.displayed {
|
&:hover.displayed, &.force-show {
|
||||||
.dropdown {
|
.dropdown {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,10 +66,12 @@
|
||||||
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-shrink: 2;
|
flex-shrink: 2;
|
||||||
|
|
||||||
max-height: 25%;
|
max-height: 25%;
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: stretch;
|
justify-content: stretch;
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,6 +83,12 @@
|
||||||
.hostbanner {
|
.hostbanner {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
|
||||||
|
.meta-image {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.image-container {
|
.image-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -238,6 +238,7 @@ $animation_length: .5s;
|
||||||
|
|
||||||
.app {
|
.app {
|
||||||
min-width: 350px;
|
min-width: 350px;
|
||||||
|
min-height: 330px;
|
||||||
|
|
||||||
.container-app-main {
|
.container-app-main {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -245,6 +246,7 @@ $animation_length: .5s;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: stretch;
|
justify-content: stretch;
|
||||||
|
|
||||||
|
min-height: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
@ -254,9 +256,11 @@ $animation_length: .5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container-control-bar {
|
.container-control-bar {
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
height: 45px;
|
height: 45px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 2px 0 0 0;
|
border-radius: 2px 2px 0 0;
|
||||||
border-bottom-width: 0;
|
border-bottom-width: 0;
|
||||||
background-color: lightgrey;
|
background-color: lightgrey;
|
||||||
|
|
||||||
|
@ -327,6 +331,12 @@ $animation_length: .5s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 400px), only screen and (max-height: 400px) {
|
||||||
|
.app-container {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: $small_device) {
|
@media only screen and (max-width: $small_device) {
|
||||||
.app-container {
|
.app-container {
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -334,11 +344,12 @@ $animation_length: .5s;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
||||||
transition: all $animation_length linear;
|
transition: all $animation_length linear;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app {
|
.app {
|
||||||
.container-app-main {
|
.container-app-main {
|
||||||
|
|
||||||
.container-info {
|
.container-info {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
1
shared/css/static/properties.scss
Normal file
1
shared/css/static/properties.scss
Normal file
|
@ -0,0 +1 @@
|
||||||
|
$channel_tree_entry_selected: blue;
|
|
@ -1,4 +1,4 @@
|
||||||
#chat {
|
.container-frame-chat {
|
||||||
font-family: Arial, serif;
|
font-family: Arial, serif;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
/*white-space: pre;*/
|
/*white-space: pre;*/
|
||||||
|
|
|
@ -12,12 +12,27 @@
|
||||||
<!-- navigation bar -->
|
<!-- navigation bar -->
|
||||||
<div class="container-control-bar">
|
<div class="container-control-bar">
|
||||||
<div id="control_bar" class="control_bar">
|
<div id="control_bar" class="control_bar">
|
||||||
<div class="button btn_connect" title="{{tr 'Connect to a server' /}}">
|
<div class="button btn_connect container-connect" title="{{tr 'Connect to a server' /}}">
|
||||||
<div class="icon_x32 client-connect"></div>
|
<div class="icon_x32 client-connect"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="button-dropdown container-disconnect" title="{{tr 'Disconnect from server' /}}" style="display: none">
|
||||||
|
<div class="buttons">
|
||||||
|
<div class="button icon_x32 client-disconnect btn_disconnect"></div>
|
||||||
|
<div class="button-dropdown">
|
||||||
|
<div class="arrow down"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown" style="width: 350px">
|
||||||
|
<div class="btn_disconnect"><div class="icon client-disconnect"></div><a>{{tr "Disconnect from current server" /}}</a></div>
|
||||||
|
<div class="btn_connect"><div class="icon client-connect"></div><a>{{tr "Connect to a server" /}}</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--
|
||||||
<div class="button btn_disconnect" title="{{tr 'Disconnect from server' /}}" style="display: none">
|
<div class="button btn_disconnect" title="{{tr 'Disconnect from server' /}}" style="display: none">
|
||||||
<div class="icon_x32 client-disconnect"></div>
|
<div class="icon_x32 client-disconnect"></div>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
<div class="button-dropdown btn_bookmark" title="{{tr 'Bookmarks' /}}">
|
<div class="button-dropdown btn_bookmark" title="{{tr 'Bookmarks' /}}">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
|
@ -44,9 +59,14 @@
|
||||||
<div class="arrow down"></div>
|
<div class="arrow down"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown">
|
<div class="dropdown" style="width: 350px">
|
||||||
<div class="btn_away_toggle"><div class="icon client-away"></div><a>{{tr "Toggle away status" /}}</a></div>
|
<div class="btn_away_disable"><div class="icon client-present"></div><a>{{tr "Go online" /}}</a></div>
|
||||||
<div class="btn_away_message"><div class="icon client-away"></div><a>{{tr "Set away message" /}}</a></div>
|
<div class="btn_away_enable"><div class="icon client-away"></div><a>{{tr "Set away on this server" /}}</a></div>
|
||||||
|
<div class="btn_away_message"><div class="icon client-away"></div><a>{{tr "Set away message on this server" /}}</a></div>
|
||||||
|
<hr class="btn_away_message_global"> <!-- applied to this HR this class because it needs to get hidden as well if we dont have global settings -->
|
||||||
|
<div class="btn_away_enable_global"><div class="icon client-away"></div><a>{{tr "Set away for all servers" /}}</a></div>
|
||||||
|
<div class="btn_away_message_global"><div class="icon client-away"></div><a>{{tr "Set away message for all servers" /}}</a></div>
|
||||||
|
<div class="btn_away_disable_global"><div class="icon client-present"></div><a>{{tr "Go online for all servers" /}}</a></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hide-small button btn_mute_input">
|
<div class="hide-small button btn_mute_input">
|
||||||
|
@ -154,6 +174,17 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="container-connection-handlers scrollbar" id="connection-handlers">
|
||||||
|
<div class="connection-handlers"> </div>
|
||||||
|
<div class="container-scroll">
|
||||||
|
<div class="button-scroll button-scroll-left">
|
||||||
|
<div class="icon client-arrow_left"></div>
|
||||||
|
</div>
|
||||||
|
<div class="button-scroll button-scroll-right disabled">
|
||||||
|
<div class="icon client-arrow_right"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container-app-main">
|
<div class="container-app-main">
|
||||||
<div class="container-channel-chat">
|
<div class="container-channel-chat">
|
||||||
|
@ -164,43 +195,47 @@
|
||||||
|
|
||||||
<div class="container-seperator horizontal" seperator-id="seperator-channel-chat"></div>
|
<div class="container-seperator horizontal" seperator-id="seperator-channel-chat"></div>
|
||||||
<!-- Chat window -->
|
<!-- Chat window -->
|
||||||
<div class="main_container container-chat">
|
<div class="main_container container-chat" id="chat"> </div>
|
||||||
<div id="chat">
|
|
||||||
<div class="messages">
|
|
||||||
<div class="message_box"></div>
|
|
||||||
</div>
|
|
||||||
<div class="chats"></div>
|
|
||||||
<div class="input">
|
|
||||||
<!--<div contentEditable="true" class="input_box"></div>-->
|
|
||||||
<div class="form-group">
|
|
||||||
<textarea id="input-chat-input"
|
|
||||||
type="text"
|
|
||||||
class="form-control input-message"
|
|
||||||
placeholder="{{tr 'enter a chat message...' /}}"
|
|
||||||
autocomplete="off"></textarea>
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-primary button-send">{{tr "Send" /}}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="container-seperator vertical" seperator-id="seperator-main-info"></div>
|
<div class="container-seperator vertical" seperator-id="seperator-main-info"></div>
|
||||||
<div id="select_info" class="main_container container-info">
|
<div id="select_info" class="main_container container-info"> </div> <!-- Selection info -->
|
||||||
<div class="select_info" style="width: 100%; max-width: 100%">
|
|
||||||
<button type="button" class="close" aria-label="Close">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
<div class="container-banner"></div>
|
|
||||||
<!-- <div class="container-seperator horizontal" seperator-id="seperator-hostbanner-info"></div> -->
|
|
||||||
<div class="container-select-info"></div>
|
|
||||||
</div>
|
|
||||||
</div> <!-- Selection info -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="contextMenu" class="context-menu"></div>
|
<div id="contextMenu" class="context-menu"></div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script class="jsrender-template" id="tmpl_select_info" type="text/html">
|
||||||
|
<div class="select_info" style="width: 100%; max-width: 100%">
|
||||||
|
<button type="button" class="close" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<div class="container-banner"></div>
|
||||||
|
<!-- <div class="container-seperator horizontal" seperator-id="seperator-hostbanner-info"></div> -->
|
||||||
|
<div class="container-select-info"></div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script class="jsrender-template" id="tmpl_frame_chat" type="text/html">
|
||||||
|
<div class="container-frame-chat">
|
||||||
|
<div class="messages">
|
||||||
|
<div class="message_box"></div>
|
||||||
|
</div>
|
||||||
|
<div class="chats"></div>
|
||||||
|
<div class="input">
|
||||||
|
<!--<div contentEditable="true" class="input_box"></div>-->
|
||||||
|
<div class="form-group">
|
||||||
|
<textarea id="input-chat-input"
|
||||||
|
type="text"
|
||||||
|
class="form-control input-message"
|
||||||
|
placeholder="{{tr 'enter a chat message...' /}}"
|
||||||
|
autocomplete="off"></textarea>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary button-send">{{tr "Send" /}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
<script class="jsrender-template" id="tmpl_modal" type="text/html">
|
<script class="jsrender-template" id="tmpl_modal" type="text/html">
|
||||||
<div class="modal fade" tabindex="-1" role="dialog" style="padding-right: 8%; padding-left: 8%;" aria-hidden="true">
|
<div class="modal fade" tabindex="-1" role="dialog" style="padding-right: 8%; padding-left: 8%;" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered" role="document" style="max-width: unset;">
|
<div class="modal-dialog modal-dialog-centered" role="document" style="max-width: unset;">
|
||||||
|
@ -330,7 +365,10 @@
|
||||||
</div>
|
</div>
|
||||||
</modal-body>
|
</modal-body>
|
||||||
<modal-footer>
|
<modal-footer>
|
||||||
<button type="button" class="btn btn-raised btn-success button-connect">Connect</button>
|
<button type="button" class="btn btn-raised btn-success button-connect">{{tr "Connect" /}}</button>
|
||||||
|
{{if multi_tab}}
|
||||||
|
<button type="button" class="btn btn-raised btn-success button-connect-new-tab">{{tr "Connect in a new tab" /}}</button>
|
||||||
|
{{/if}}
|
||||||
</modal-footer>
|
</modal-footer>
|
||||||
</modal>
|
</modal>
|
||||||
</script>
|
</script>
|
||||||
|
@ -2313,8 +2351,13 @@
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
<script class="jsrender-template" id="tmpl_selected_hostbanner" type="text/html">
|
<script class="jsrender-template" id="tmpl_selected_hostbanner" type="text/html">
|
||||||
<div class="hostbanner">
|
<div class="hostbanner" x-divider-require-resize="1">
|
||||||
<a class="image-container" href="{{:property_virtualserver_hostbanner_url}}" target="_blank">
|
<img class="meta-image hostbanner-image" src="{{:hostbanner_gfx_url}}"/>
|
||||||
|
<a class="image-container"
|
||||||
|
{{if property_virtualserver_hostbanner_url}}
|
||||||
|
href="{{:property_virtualserver_hostbanner_url}}"
|
||||||
|
{{/if}}
|
||||||
|
target="_blank">
|
||||||
<div
|
<div
|
||||||
style="background: center no-repeat url({{:hostbanner_gfx_url}})"
|
style="background: center no-repeat url({{:hostbanner_gfx_url}})"
|
||||||
alt="{{tr 'Host banner'/}}"
|
alt="{{tr 'Host banner'/}}"
|
||||||
|
|
215
shared/js/BrowserIPC.ts
Normal file
215
shared/js/BrowserIPC.ts
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
interface Window {
|
||||||
|
BroadcastChannel: BroadcastChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace bipc {
|
||||||
|
export interface BroadcastMessage {
|
||||||
|
timestamp: number;
|
||||||
|
receiver: string;
|
||||||
|
sender: string;
|
||||||
|
|
||||||
|
type: string;
|
||||||
|
data: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function uuidv4() {
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||||
|
const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProcessQuery {
|
||||||
|
timestamp: number
|
||||||
|
query_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProcessQueryResponse {
|
||||||
|
request_timestamp: number
|
||||||
|
request_query_id: string;
|
||||||
|
|
||||||
|
device_id: string;
|
||||||
|
protocol: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CertificateAcceptCallback {
|
||||||
|
request_id: string;
|
||||||
|
}
|
||||||
|
export interface CertificateAcceptSucceeded {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class BasicIPCHandler {
|
||||||
|
protected static readonly BROADCAST_UNIQUE_ID = "00000000-0000-4000-0000-000000000000";
|
||||||
|
protected static readonly PROTOCOL_VERSION = 1;
|
||||||
|
|
||||||
|
protected unique_id;
|
||||||
|
|
||||||
|
protected constructor() { }
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
this.unique_id = uuidv4(); /* lets get an unique identifier */
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract send_message(type: string, data: any, target?: string);
|
||||||
|
|
||||||
|
protected handle_message(message: BroadcastMessage) {
|
||||||
|
console.log("Received: %o", message);
|
||||||
|
|
||||||
|
if(message.receiver === BasicIPCHandler.BROADCAST_UNIQUE_ID) {
|
||||||
|
if(message.type == "process-query") {
|
||||||
|
log.debug(LogCategory.IPC, tr("Received a device query from %s."), message.sender);
|
||||||
|
this.send_message("process-query-response", {
|
||||||
|
request_query_id: (<ProcessQuery>message.data).query_id,
|
||||||
|
request_timestamp: (<ProcessQuery>message.data).timestamp,
|
||||||
|
|
||||||
|
device_id: this.unique_id,
|
||||||
|
protocol: BasicIPCHandler.PROTOCOL_VERSION
|
||||||
|
} as ProcessQueryResponse, message.sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if(message.receiver === this.unique_id) {
|
||||||
|
if(message.type == "process-query-response") {
|
||||||
|
const response: ProcessQueryResponse = message.data;
|
||||||
|
if(this._query_results[response.request_query_id])
|
||||||
|
this._query_results[response.request_query_id].push(response);
|
||||||
|
else {
|
||||||
|
log.warn(LogCategory.IPC, tr("Received a query response for an unknown request."));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if(message.type == "certificate-accept-callback") {
|
||||||
|
const data: CertificateAcceptCallback = message.data;
|
||||||
|
if(!this._cert_accept_callbacks[data.request_id]) {
|
||||||
|
log.warn(LogCategory.IPC, tr("Received certificate accept callback for an unknown request ID."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._cert_accept_callbacks[data.request_id]();
|
||||||
|
delete this._cert_accept_callbacks[data.request_id];
|
||||||
|
|
||||||
|
this.send_message("certificate-accept-succeeded", {
|
||||||
|
|
||||||
|
} as CertificateAcceptSucceeded, message.sender);
|
||||||
|
return;
|
||||||
|
} else if(message.type == "certificate-accept-succeeded") {
|
||||||
|
if(!this._cert_accept_succeeded[message.sender]) {
|
||||||
|
log.warn(LogCategory.IPC, tr("Received certificate accept succeeded, but haven't a callback."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._cert_accept_succeeded[message.sender]();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _query_results: {[key: string]:ProcessQueryResponse[]} = {};
|
||||||
|
async query_processes(timeout?: number) : Promise<ProcessQueryResponse[]> {
|
||||||
|
const query_id = uuidv4();
|
||||||
|
this._query_results[query_id] = [];
|
||||||
|
|
||||||
|
this.send_message("process-query", {
|
||||||
|
query_id: query_id,
|
||||||
|
timestamp: Date.now()
|
||||||
|
} as ProcessQuery);
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, timeout || 250));
|
||||||
|
const result = this._query_results[query_id];
|
||||||
|
delete this._query_results[query_id];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _cert_accept_callbacks: {[key: string]:(() => any)} = {};
|
||||||
|
register_certificate_accept_callback(callback: () => any) : string {
|
||||||
|
const id = uuidv4();
|
||||||
|
this._cert_accept_callbacks[id] = callback;
|
||||||
|
return this.unique_id + ":" + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _cert_accept_succeeded: {[sender: string]:(() => any)} = {};
|
||||||
|
post_certificate_accpected(id: string, timeout?: number) : Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const data = id.split(":");
|
||||||
|
const timeout_id = setTimeout(() => {
|
||||||
|
delete this._cert_accept_succeeded[data[0]];
|
||||||
|
clearTimeout(timeout_id);
|
||||||
|
reject("timeout");
|
||||||
|
}, timeout || 250);
|
||||||
|
this._cert_accept_succeeded[data[0]] = () => {
|
||||||
|
delete this._cert_accept_succeeded[data[0]];
|
||||||
|
clearTimeout(timeout_id);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
this.send_message("certificate-accept-callback", {
|
||||||
|
request_id: data[1]
|
||||||
|
} as CertificateAcceptCallback, data[0]);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BroadcastChannelIPC extends BasicIPCHandler {
|
||||||
|
private static readonly CHANNEL_NAME = "TeaSpeak-Web";
|
||||||
|
|
||||||
|
private channel: BroadcastChannel;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
this.channel = new BroadcastChannel(BroadcastChannelIPC.CHANNEL_NAME);
|
||||||
|
this.channel.onmessage = this.on_message.bind(this);
|
||||||
|
this.channel.onmessageerror = this.on_error.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_message(event: MessageEvent) {
|
||||||
|
if(typeof(event.data) !== "string") {
|
||||||
|
log.warn(LogCategory.IPC, tr("Received message with an invalid type (%s): %o"), typeof(event.data), event.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let message: BroadcastMessage;
|
||||||
|
try {
|
||||||
|
message = JSON.parse(event.data);
|
||||||
|
} catch(error) {
|
||||||
|
log.error(LogCategory.IPC, tr("Received an invalid encoded message: %o"), event.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.handle_message(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_error(event: MessageEvent) {
|
||||||
|
log.warn(LogCategory.IPC, tr("Received error: %o"), event);
|
||||||
|
}
|
||||||
|
|
||||||
|
send_message(type: string, data: any, target?: string) {
|
||||||
|
const message: BroadcastMessage = {} as any;
|
||||||
|
|
||||||
|
message.sender = this.unique_id;
|
||||||
|
message.receiver = target ? target : BasicIPCHandler.BROADCAST_UNIQUE_ID;
|
||||||
|
message.timestamp = Date.now();
|
||||||
|
message.type = type;
|
||||||
|
message.data = data;
|
||||||
|
|
||||||
|
this.channel.postMessage(JSON.stringify(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let handler: BasicIPCHandler;
|
||||||
|
export function setup() {
|
||||||
|
if(!supported())
|
||||||
|
return;
|
||||||
|
/* TODO: test for support */
|
||||||
|
handler = new BroadcastChannelIPC();
|
||||||
|
handler.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get_handler() {
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function supported() {
|
||||||
|
/* ios does not support this */
|
||||||
|
return typeof(window.BroadcastChannel) !== "undefined";
|
||||||
|
}
|
||||||
|
}
|
596
shared/js/ConnectionHandler.ts
Normal file
596
shared/js/ConnectionHandler.ts
Normal file
|
@ -0,0 +1,596 @@
|
||||||
|
/// <reference path="log.ts" />
|
||||||
|
/// <reference path="voice/VoiceClient.ts" />
|
||||||
|
/// <reference path="proto.ts" />
|
||||||
|
/// <reference path="ui/view.ts" />
|
||||||
|
/// <reference path="settings.ts" />
|
||||||
|
/// <reference path="ui/frames/SelectedItemInfo.ts" />
|
||||||
|
/// <reference path="FileManager.ts" />
|
||||||
|
/// <reference path="permission/PermissionManager.ts" />
|
||||||
|
/// <reference path="permission/GroupManager.ts" />
|
||||||
|
/// <reference path="ui/frames/ControlBar.ts" />
|
||||||
|
/// <reference path="connection/ConnectionBase.ts" />
|
||||||
|
|
||||||
|
enum DisconnectReason {
|
||||||
|
HANDLER_DESTROYED,
|
||||||
|
REQUESTED,
|
||||||
|
CONNECT_FAILURE,
|
||||||
|
CONNECTION_CLOSED,
|
||||||
|
CONNECTION_FATAL_ERROR,
|
||||||
|
CONNECTION_PING_TIMEOUT,
|
||||||
|
CLIENT_KICKED,
|
||||||
|
CLIENT_BANNED,
|
||||||
|
HANDSHAKE_FAILED,
|
||||||
|
SERVER_CLOSED,
|
||||||
|
SERVER_REQUIRES_PASSWORD,
|
||||||
|
UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ConnectionState {
|
||||||
|
UNCONNECTED,
|
||||||
|
CONNECTING,
|
||||||
|
INITIALISING,
|
||||||
|
CONNECTED,
|
||||||
|
DISCONNECTING
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ViewReasonId {
|
||||||
|
VREASON_USER_ACTION = 0,
|
||||||
|
VREASON_MOVED = 1,
|
||||||
|
VREASON_SYSTEM = 2,
|
||||||
|
VREASON_TIMEOUT = 3,
|
||||||
|
VREASON_CHANNEL_KICK = 4,
|
||||||
|
VREASON_SERVER_KICK = 5,
|
||||||
|
VREASON_BAN = 6,
|
||||||
|
VREASON_SERVER_STOPPED = 7,
|
||||||
|
VREASON_SERVER_LEFT = 8,
|
||||||
|
VREASON_CHANNEL_UPDATED = 9,
|
||||||
|
VREASON_EDITED = 10,
|
||||||
|
VREASON_SERVER_SHUTDOWN = 11
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VoiceStatus {
|
||||||
|
input_hardware: boolean;
|
||||||
|
input_muted: boolean;
|
||||||
|
output_muted: boolean;
|
||||||
|
|
||||||
|
channel_codec_encoding_supported: boolean;
|
||||||
|
channel_codec_decoding_supported: boolean;
|
||||||
|
sound_playback_supported: boolean;
|
||||||
|
|
||||||
|
sound_record_supported;
|
||||||
|
|
||||||
|
away: boolean | string;
|
||||||
|
|
||||||
|
channel_subscribe_all: boolean;
|
||||||
|
queries_visible: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConnectionHandler {
|
||||||
|
channelTree: ChannelTree;
|
||||||
|
|
||||||
|
serverConnection: connection.ServerConnection;
|
||||||
|
|
||||||
|
fileManager: FileManager;
|
||||||
|
|
||||||
|
permissions: PermissionManager;
|
||||||
|
groups: GroupManager;
|
||||||
|
|
||||||
|
select_info: InfoBar;
|
||||||
|
chat: ChatBox;
|
||||||
|
|
||||||
|
settings: ServerSettings;
|
||||||
|
sound: sound.SoundManager;
|
||||||
|
|
||||||
|
readonly tag_connection_handler: JQuery;
|
||||||
|
|
||||||
|
private _clientId: number = 0;
|
||||||
|
private _local_client: LocalClientEntry;
|
||||||
|
private _reconnect_timer: NodeJS.Timer;
|
||||||
|
private _reconnect_attempt: boolean = false;
|
||||||
|
|
||||||
|
client_status: VoiceStatus = {
|
||||||
|
input_hardware: false,
|
||||||
|
input_muted: false,
|
||||||
|
output_muted: false,
|
||||||
|
away: false,
|
||||||
|
channel_subscribe_all: true,
|
||||||
|
queries_visible: false,
|
||||||
|
|
||||||
|
sound_playback_supported: undefined,
|
||||||
|
sound_record_supported: undefined,
|
||||||
|
channel_codec_encoding_supported: undefined,
|
||||||
|
channel_codec_decoding_supported: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
invoke_resized_on_activate: boolean = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.settings = new ServerSettings();
|
||||||
|
|
||||||
|
this.select_info = new InfoBar(this);
|
||||||
|
this.channelTree = new ChannelTree(this);
|
||||||
|
this.chat = new ChatBox(this);
|
||||||
|
this.sound = new sound.SoundManager(this);
|
||||||
|
|
||||||
|
this.serverConnection = new connection.ServerConnection(this);
|
||||||
|
this.serverConnection.onconnectionstatechanged = this.on_connection_state_changed.bind(this);
|
||||||
|
|
||||||
|
this.fileManager = new FileManager(this);
|
||||||
|
this.permissions = new PermissionManager(this);
|
||||||
|
this.groups = new GroupManager(this);
|
||||||
|
this._local_client = new LocalClientEntry(this);
|
||||||
|
this.channelTree.registerClient(this._local_client);
|
||||||
|
|
||||||
|
//settings.static_global(Settings.KEY_DISABLE_VOICE, false)
|
||||||
|
this.chat.initialize();
|
||||||
|
this.tag_connection_handler = $.spawn("div").addClass("connection-container");
|
||||||
|
$.spawn("div").addClass("server-icon icon client-server_green").appendTo(this.tag_connection_handler);
|
||||||
|
$.spawn("div").addClass("server-name").text(tr("Not connected")).appendTo(this.tag_connection_handler);
|
||||||
|
$.spawn("div").addClass("button-close icon client-tab_close_button").appendTo(this.tag_connection_handler);
|
||||||
|
this.tag_connection_handler.on('click', event => {
|
||||||
|
if(event.isDefaultPrevented())
|
||||||
|
return;
|
||||||
|
|
||||||
|
server_connections.set_active_connection_handler(this);
|
||||||
|
});
|
||||||
|
this.tag_connection_handler.find(".button-close").on('click', event => {
|
||||||
|
server_connections.destroy_server_connection_handler(this);
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() { }
|
||||||
|
|
||||||
|
startConnection(addr: string, profile: profiles.ConnectionProfile, name?: string, password?: {password: string, hashed: boolean}) {
|
||||||
|
this.tag_connection_handler.find(".server-name").text(tr("Connecting"));
|
||||||
|
this.cancel_reconnect();
|
||||||
|
this._reconnect_attempt = false;
|
||||||
|
if(this.serverConnection)
|
||||||
|
this.handleDisconnect(DisconnectReason.REQUESTED);
|
||||||
|
|
||||||
|
let idx = addr.lastIndexOf(':');
|
||||||
|
|
||||||
|
let port: number;
|
||||||
|
let host: string;
|
||||||
|
if(idx != -1) {
|
||||||
|
port = parseInt(addr.substr(idx + 1));
|
||||||
|
host = addr.substr(0, idx);
|
||||||
|
} else {
|
||||||
|
host = addr;
|
||||||
|
port = 9987;
|
||||||
|
}
|
||||||
|
console.log(tr("Start connection to %s:%d"), host, port);
|
||||||
|
this.channelTree.initialiseHead(addr, {host, port});
|
||||||
|
|
||||||
|
if(password && !password.hashed) {
|
||||||
|
helpers.hashPassword(password.password).then(password => {
|
||||||
|
/* errors will be already handled via the handle disconnect thing */
|
||||||
|
this.serverConnection.connect({host, port}, new connection.HandshakeHandler(profile, name, password));
|
||||||
|
}).catch(error => {
|
||||||
|
createErrorModal(tr("Error while hashing password"), tr("Failed to hash server password!<br>") + error).open();
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
/* errors will be already handled via the handle disconnect thing */
|
||||||
|
this.serverConnection.connect({host, port}, new connection.HandshakeHandler(profile, name, password ? password.password : undefined));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getClient() : LocalClientEntry { return this._local_client; }
|
||||||
|
getClientId() { return this._clientId; }
|
||||||
|
|
||||||
|
set clientId(id: number) {
|
||||||
|
this._clientId = id;
|
||||||
|
this._local_client["_clientId"] = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get clientId() {
|
||||||
|
return this._clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
getServerConnection() : connection.ServerConnection { return this.serverConnection; }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LISTENER
|
||||||
|
*/
|
||||||
|
onConnected() {
|
||||||
|
console.log("Client connected!");
|
||||||
|
this.channelTree.registerClient(this._local_client);
|
||||||
|
this.permissions.requestPermissionList();
|
||||||
|
if(this.groups.serverGroups.length == 0)
|
||||||
|
this.groups.requestGroups();
|
||||||
|
|
||||||
|
this.initialize_server_settings();
|
||||||
|
|
||||||
|
/* apply the server settings */
|
||||||
|
if(this.client_status.channel_subscribe_all)
|
||||||
|
this.channelTree.subscribe_all_channels();
|
||||||
|
else
|
||||||
|
this.channelTree.unsubscribe_all_channels();
|
||||||
|
this.channelTree.toggle_server_queries(this.client_status.queries_visible);
|
||||||
|
|
||||||
|
this.sync_status_with_server();
|
||||||
|
/*
|
||||||
|
No need to update the voice stuff because as soon we see ourself we're doing it
|
||||||
|
this.update_voice_status();
|
||||||
|
if(control_bar.current_connection_handler() === this)
|
||||||
|
control_bar.apply_server_voice_state();
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private initialize_server_settings() {
|
||||||
|
let update_control = false;
|
||||||
|
this.settings.setServer(this.channelTree.server);
|
||||||
|
{
|
||||||
|
const flag_subscribe = this.settings.server(Settings.KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL, true);
|
||||||
|
if(this.client_status.channel_subscribe_all != flag_subscribe) {
|
||||||
|
this.client_status.channel_subscribe_all = flag_subscribe;
|
||||||
|
update_control = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const flag_query = this.settings.server(Settings.KEY_CONTROL_SHOW_QUERIES, false);
|
||||||
|
if(this.client_status.queries_visible != flag_query) {
|
||||||
|
this.client_status.queries_visible = flag_query;
|
||||||
|
update_control = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(update_control && server_connections.active_connection_handler() === this) {
|
||||||
|
control_bar.apply_server_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
get connected() : boolean {
|
||||||
|
return this.serverConnection && this.serverConnection.connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
private generate_ssl_certificate_accept() : JQuery {
|
||||||
|
const properties = {
|
||||||
|
connect_default: true,
|
||||||
|
connect_profile: this.serverConnection._handshakeHandler.profile.id,
|
||||||
|
connect_address: this.serverConnection._remote_address.host + (this.serverConnection._remote_address.port !== 9987 ? ":" + this.serverConnection._remote_address.port : "")
|
||||||
|
};
|
||||||
|
|
||||||
|
const build_url = props => {
|
||||||
|
const parameters: string[] = [];
|
||||||
|
for(const key of Object.keys(props))
|
||||||
|
parameters.push(key + "=" + encodeURIComponent(props[key]));
|
||||||
|
|
||||||
|
let callback = document.location.origin + document.location.pathname + document.location.search; /* don't use document.URL because it may contains a #! */
|
||||||
|
if(document.location.search.length == 0)
|
||||||
|
callback += "?" + parameters.join("&");
|
||||||
|
else
|
||||||
|
callback += "&" + parameters.join("&");
|
||||||
|
|
||||||
|
return "https://" + this.serverConnection._remote_address.host + ":" + this.serverConnection._remote_address.port + "/?forward_url=" + encodeURIComponent(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* generate the tag */
|
||||||
|
const tag = $.spawn("a").text(tr("here"));
|
||||||
|
|
||||||
|
if(bipc.supported()) {
|
||||||
|
tag.attr('href', "#");
|
||||||
|
let popup: Window;
|
||||||
|
tag.on('click', event => {
|
||||||
|
const features = {
|
||||||
|
status: "no",
|
||||||
|
location: "no",
|
||||||
|
toolbar: "no",
|
||||||
|
menubar: "no",
|
||||||
|
width: 600,
|
||||||
|
height: 400
|
||||||
|
};
|
||||||
|
|
||||||
|
if(popup)
|
||||||
|
popup.close();
|
||||||
|
|
||||||
|
properties["certificate_callback"] = bipc.get_handler().register_certificate_accept_callback(() => {
|
||||||
|
log.info(LogCategory.GENERAL, tr("Received notification that the certificate has been accepted! Attempting reconnect!"));
|
||||||
|
if(this._certificate_modal)
|
||||||
|
this._certificate_modal.close();
|
||||||
|
|
||||||
|
popup.close(); /* no need, but nicer */
|
||||||
|
this.startConnection(properties.connect_address, profiles.find_profile(properties.connect_profile) || profiles.default_profile());
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = build_url(properties);
|
||||||
|
const features_string = [...Object.keys(features)].map(e => e + "=" + features[e]).reduce((a, b) => a + "," + b);
|
||||||
|
popup = window.open(url, "TeaWeb certificate accept", features_string);
|
||||||
|
try {
|
||||||
|
popup.focus();
|
||||||
|
} catch(e) {
|
||||||
|
log.warn(LogCategory.GENERAL, tr("Certificate accept popup has been blocked. Trying a blank page and replacing href"));
|
||||||
|
|
||||||
|
window.open(url, "TeaWeb certificate accept"); /* trying without features */
|
||||||
|
tag.attr("target", "_blank");
|
||||||
|
tag.attr("href", url);
|
||||||
|
tag.unbind('click');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
tag.attr('href', build_url(properties));
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _certificate_modal: Modal;
|
||||||
|
handleDisconnect(type: DisconnectReason, data: any = {}) {
|
||||||
|
this.tag_connection_handler.find(".server-name").text(tr("Not connected"));
|
||||||
|
let auto_reconnect = false;
|
||||||
|
switch (type) {
|
||||||
|
case DisconnectReason.REQUESTED:
|
||||||
|
break;
|
||||||
|
case DisconnectReason.HANDLER_DESTROYED:
|
||||||
|
if(data)
|
||||||
|
this.sound.play(Sound.CONNECTION_DISCONNECTED);
|
||||||
|
break;
|
||||||
|
case DisconnectReason.CONNECT_FAILURE:
|
||||||
|
if(this._reconnect_attempt) {
|
||||||
|
auto_reconnect = true;
|
||||||
|
this.chat.serverChat().appendError(tr("Connect failed"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
console.error(tr("Could not connect to remote host! Exception: %o"), data);
|
||||||
|
|
||||||
|
if(native_client) {
|
||||||
|
createErrorModal(
|
||||||
|
tr("Could not connect"),
|
||||||
|
tr("Could not connect to remote host (Connection refused)")
|
||||||
|
).open();
|
||||||
|
} else {
|
||||||
|
const error_message_format =
|
||||||
|
"Could not connect to remote host (Connection refused)\n" +
|
||||||
|
"If you're sure that the remote host is up, than you may not allow unsigned certificates.\n" +
|
||||||
|
"Click {0} to accept the remote certificate";
|
||||||
|
|
||||||
|
this._certificate_modal = createErrorModal(
|
||||||
|
tr("Could not connect"),
|
||||||
|
MessageHelper.formatMessage(tr(error_message_format), this.generate_ssl_certificate_accept())
|
||||||
|
);
|
||||||
|
this._certificate_modal.close_listener.push(() => this._certificate_modal = undefined);
|
||||||
|
this._certificate_modal.open();
|
||||||
|
}
|
||||||
|
this.sound.play(Sound.CONNECTION_REFUSED);
|
||||||
|
break;
|
||||||
|
case DisconnectReason.HANDSHAKE_FAILED:
|
||||||
|
//TODO sound
|
||||||
|
console.error(tr("Failed to process handshake: %o"), data);
|
||||||
|
createErrorModal(
|
||||||
|
tr("Could not connect"),
|
||||||
|
tr("Failed to process handshake: ") + data as string
|
||||||
|
).open();
|
||||||
|
break;
|
||||||
|
case DisconnectReason.CONNECTION_CLOSED:
|
||||||
|
console.error(tr("Lost connection to remote server!"));
|
||||||
|
createErrorModal(
|
||||||
|
tr("Connection closed"),
|
||||||
|
tr("The connection was closed by remote host")
|
||||||
|
).open();
|
||||||
|
this.sound.play(Sound.CONNECTION_DISCONNECTED);
|
||||||
|
|
||||||
|
auto_reconnect = true;
|
||||||
|
break;
|
||||||
|
case DisconnectReason.CONNECTION_PING_TIMEOUT:
|
||||||
|
console.error(tr("Connection ping timeout"));
|
||||||
|
this.sound.play(Sound.CONNECTION_DISCONNECTED_TIMEOUT);
|
||||||
|
createErrorModal(
|
||||||
|
tr("Connection lost"),
|
||||||
|
tr("Lost connection to remote host (Ping timeout)<br>Even possible?")
|
||||||
|
).open();
|
||||||
|
|
||||||
|
break;
|
||||||
|
case DisconnectReason.SERVER_CLOSED:
|
||||||
|
this.chat.serverChat().appendError(tr("Server closed ({0})"), data.reasonmsg);
|
||||||
|
createErrorModal(
|
||||||
|
tr("Server closed"),
|
||||||
|
"The server is closed.<br>" + //TODO tr
|
||||||
|
"Reason: " + data.reasonmsg
|
||||||
|
).open();
|
||||||
|
this.sound.play(Sound.CONNECTION_DISCONNECTED);
|
||||||
|
|
||||||
|
auto_reconnect = true;
|
||||||
|
break;
|
||||||
|
case DisconnectReason.SERVER_REQUIRES_PASSWORD:
|
||||||
|
this.chat.serverChat().appendError(tr("Server requires password"));
|
||||||
|
createInputModal(tr("Server password"), tr("Enter server password:"), password => password.length != 0, password => {
|
||||||
|
if(!(typeof password === "string")) return;
|
||||||
|
this.startConnection(this.serverConnection._remote_address.host + ":" + this.serverConnection._remote_address.port,
|
||||||
|
this.serverConnection._handshakeHandler.profile,
|
||||||
|
this.serverConnection._handshakeHandler.name,
|
||||||
|
{password: password as string, hashed: false});
|
||||||
|
}).open();
|
||||||
|
break;
|
||||||
|
case DisconnectReason.CLIENT_KICKED:
|
||||||
|
this.chat.serverChat().appendError(tr("You got kicked from the server by {0}{1}"),
|
||||||
|
ClientEntry.chatTag(data["invokerid"], data["invokername"], data["invokeruid"]),
|
||||||
|
data["reasonmsg"] ? " (" + data["reasonmsg"] + ")" : "");
|
||||||
|
this.sound.play(Sound.SERVER_KICKED);
|
||||||
|
auto_reconnect = true;
|
||||||
|
break;
|
||||||
|
case DisconnectReason.CLIENT_BANNED:
|
||||||
|
this.chat.serverChat().appendError(tr("You got banned from the server by {0}{1}"),
|
||||||
|
ClientEntry.chatTag(data["invokerid"], data["invokername"], data["invokeruid"]),
|
||||||
|
data["reasonmsg"] ? " (" + data["reasonmsg"] + ")" : "");
|
||||||
|
this.sound.play(Sound.CONNECTION_BANNED); //TODO findout if it was a disconnect or a connect refuse
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(tr("Got uncaught disconnect!"));
|
||||||
|
console.error(tr("Type: %o Data:"), type);
|
||||||
|
console.error(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.channelTree.reset();
|
||||||
|
if(this.serverConnection)
|
||||||
|
this.serverConnection.disconnect();
|
||||||
|
|
||||||
|
if(control_bar.current_connection_handler() == this)
|
||||||
|
control_bar.update_connection_state();
|
||||||
|
this.select_info.setCurrentSelected(null);
|
||||||
|
this.select_info.update_banner();
|
||||||
|
|
||||||
|
if(auto_reconnect) {
|
||||||
|
if(!this.serverConnection) {
|
||||||
|
console.log(tr("Allowed to auto reconnect but cant reconnect because we dont have any information left..."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.chat.serverChat().appendMessage(tr("Reconnecting in 5 seconds"));
|
||||||
|
|
||||||
|
console.log(tr("Allowed to auto reconnect. Reconnecting in 5000ms"));
|
||||||
|
const server_address = this.serverConnection._remote_address;
|
||||||
|
const profile = this.serverConnection._handshakeHandler.profile;
|
||||||
|
const name = this.serverConnection._handshakeHandler.name;
|
||||||
|
const password = this.serverConnection._handshakeHandler.server_password;
|
||||||
|
|
||||||
|
this._reconnect_timer = setTimeout(() => {
|
||||||
|
this._reconnect_timer = undefined;
|
||||||
|
this.chat.serverChat().appendMessage(tr("Reconnecting..."));
|
||||||
|
log.info(LogCategory.NETWORKING, tr("Reconnecting..."))
|
||||||
|
this.startConnection(server_address.host + ":" + server_address.port, profile, name, password ? { password: password, hashed: true} : undefined);
|
||||||
|
this._reconnect_attempt = true;
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel_reconnect() {
|
||||||
|
if(this._reconnect_timer) {
|
||||||
|
this.chat.serverChat().appendMessage(tr("Reconnect canceled"));
|
||||||
|
clearTimeout(this._reconnect_timer);
|
||||||
|
this._reconnect_timer = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_connection_state_changed() {
|
||||||
|
if(control_bar.current_connection_handler() == this)
|
||||||
|
control_bar.update_connection_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
update_voice_status(targetChannel?: ChannelEntry) {
|
||||||
|
targetChannel = targetChannel || this.getClient().currentChannel();
|
||||||
|
|
||||||
|
const vconnection = this.serverConnection.voice_connection();
|
||||||
|
const basic_voice_support = this.serverConnection.support_voice() && vconnection.connected();
|
||||||
|
const support_record = basic_voice_support && (!targetChannel || vconnection.encoding_supported(targetChannel.properties.channel_codec));
|
||||||
|
const support_playback = basic_voice_support && (!targetChannel || vconnection.decoding_supported(targetChannel.properties.channel_codec));
|
||||||
|
|
||||||
|
const property_update = {
|
||||||
|
client_input_muted: this.client_status.input_muted,
|
||||||
|
client_output_muted: this.client_status.output_muted
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!this.serverConnection.support_voice() || !vconnection.connected()) {
|
||||||
|
property_update["client_input_hardware"] = false;
|
||||||
|
property_update["client_output_hardware"] = false;
|
||||||
|
this.client_status.input_hardware = true; /* IDK if we have input hardware or not, but it dosn't matter at all so */
|
||||||
|
} else {
|
||||||
|
const audio_source = vconnection.voice_recorder();
|
||||||
|
const recording_supported = typeof(audio_source) !== "undefined" && audio_source.is_recording_supported() && (!targetChannel || vconnection.encoding_supported(targetChannel.properties.channel_codec));
|
||||||
|
const playback_supported = !targetChannel || vconnection.decoding_supported(targetChannel.properties.channel_codec);
|
||||||
|
|
||||||
|
property_update["client_input_hardware"] = recording_supported;
|
||||||
|
property_update["client_output_hardware"] = playback_supported;
|
||||||
|
this.client_status.input_hardware = recording_supported;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.serverConnection && this.serverConnection.connected()) {
|
||||||
|
const client_properties = this.getClient().properties;
|
||||||
|
for(const key of Object.keys(property_update)) {
|
||||||
|
if(client_properties[key] === property_update[key])
|
||||||
|
delete property_update[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Object.keys(property_update).length > 0) {
|
||||||
|
this.serverConnection.send_command("clientupdate", property_update).catch(error => {
|
||||||
|
log.warn(LogCategory.GENERAL, tr("Failed to update client audio hardware properties. Error: %o"), error);
|
||||||
|
this.chat.serverChat().appendError(tr("Failed to update audio hardware properties."));
|
||||||
|
|
||||||
|
/* Update these properties anyways (for case the server fails to handle the command) */
|
||||||
|
const updates = [];
|
||||||
|
for(const key of Object.keys(property_update))
|
||||||
|
updates.push({key: key, value: (property_update[key]) + ""});
|
||||||
|
this.getClient().updateVariables(...updates);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else { /* no icons are shown so no update at all */ }
|
||||||
|
|
||||||
|
|
||||||
|
if(targetChannel && (!vconnection || vconnection.connected())) {
|
||||||
|
const encoding_supported = vconnection.encoding_supported(targetChannel.properties.channel_codec);
|
||||||
|
const decoding_supported = vconnection.decoding_supported(targetChannel.properties.channel_codec);
|
||||||
|
|
||||||
|
if(this.client_status.channel_codec_decoding_supported !== decoding_supported || this.client_status.channel_codec_encoding_supported !== encoding_supported) {
|
||||||
|
this.client_status.channel_codec_decoding_supported = decoding_supported;
|
||||||
|
this.client_status.channel_codec_encoding_supported = encoding_supported;
|
||||||
|
|
||||||
|
let message;
|
||||||
|
if(!encoding_supported && !decoding_supported)
|
||||||
|
message = tr("This channel has an unsupported codec.<br>You cant speak or listen to anybody within this channel!");
|
||||||
|
else if(!encoding_supported)
|
||||||
|
message = tr("This channel has an unsupported codec.<br>You cant speak within this channel!");
|
||||||
|
else if(!decoding_supported)
|
||||||
|
message = tr("This channel has an unsupported codec.<br>You listen to anybody within this channel!"); /* implies speaking does not work as well */
|
||||||
|
if(message)
|
||||||
|
createErrorModal(tr("Channel codec unsupported"), message).open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.client_status = this.client_status || {} as any;
|
||||||
|
this.client_status.sound_record_supported = support_record;
|
||||||
|
this.client_status.sound_playback_supported = support_playback;
|
||||||
|
|
||||||
|
if(vconnection && vconnection.voice_recorder() && vconnection.voice_recorder().is_recording_supported())
|
||||||
|
vconnection.voice_recorder().set_recording(!this.client_status.input_muted && !this.client_status.output_muted);
|
||||||
|
|
||||||
|
if(control_bar.current_connection_handler() === this)
|
||||||
|
control_bar.apply_server_voice_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_status_with_server() {
|
||||||
|
if(this.serverConnection.connected())
|
||||||
|
this.serverConnection.send_command("clientupdate", {
|
||||||
|
client_input_muted: this.client_status.input_muted,
|
||||||
|
client_output_muted: this.client_status.output_muted,
|
||||||
|
client_away: typeof(this.client_status.away) === "string" || this.client_status.away,
|
||||||
|
client_away_message: typeof(this.client_status.away) === "string" ? this.client_status.away : "",
|
||||||
|
client_input_hardware: this.client_status.sound_record_supported && this.client_status.input_hardware,
|
||||||
|
client_output_hardware: this.client_status.sound_playback_supported
|
||||||
|
}).catch(error => {
|
||||||
|
log.warn(LogCategory.GENERAL, tr("Failed to sync handler state with server. Error: %o"), error);
|
||||||
|
this.chat.serverChat().appendError(tr("Failed to sync handler state with server."));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
set_away_status(state: boolean | string) {
|
||||||
|
if(this.client_status.away === state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.client_status.away = state;
|
||||||
|
this.serverConnection.send_command("clientupdate", {
|
||||||
|
client_away: typeof(this.client_status.away) === "string" || this.client_status.away,
|
||||||
|
client_away_message: typeof(this.client_status.away) === "string" ? this.client_status.away : "",
|
||||||
|
}).catch(error => {
|
||||||
|
log.warn(LogCategory.GENERAL, tr("Failed to update away status. Error: %o"), error);
|
||||||
|
this.chat.serverChat().appendError(tr("Failed to update away status."));
|
||||||
|
});
|
||||||
|
|
||||||
|
control_bar.update_button_away();
|
||||||
|
}
|
||||||
|
|
||||||
|
resize_elements() {
|
||||||
|
this.channelTree.handle_resized();
|
||||||
|
this.select_info.handle_resize();
|
||||||
|
this.invoke_resized_on_activate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
acquire_recorder(voice_recoder: VoiceRecorder, update_control_bar: boolean) {
|
||||||
|
const vconnection = this.serverConnection.voice_connection();
|
||||||
|
if(vconnection)
|
||||||
|
vconnection.acquire_voice_recorder(voice_recoder);
|
||||||
|
if(voice_recoder)
|
||||||
|
voice_recoder.clean_recording_supported();
|
||||||
|
this.update_voice_status(undefined);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,6 @@
|
||||||
/// <reference path="client.ts" />
|
/// <reference path="connection/CommandHandler.ts" />
|
||||||
/// <reference path="connection/ConnectionBase.ts" />
|
/// <reference path="connection/ConnectionBase.ts" />
|
||||||
|
|
||||||
/*
|
|
||||||
FIXME: Dont use item storage with base64! Use the larger cache API and drop IE support!
|
|
||||||
https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage#Browser_compatibility
|
|
||||||
*/
|
|
||||||
|
|
||||||
class FileEntry {
|
class FileEntry {
|
||||||
name: string;
|
name: string;
|
||||||
datetime: number;
|
datetime: number;
|
||||||
|
@ -218,7 +213,7 @@ class RequestFileUpload {
|
||||||
}
|
}
|
||||||
|
|
||||||
class FileManager extends connection.AbstractCommandHandler {
|
class FileManager extends connection.AbstractCommandHandler {
|
||||||
handle: TSClient;
|
handle: ConnectionHandler;
|
||||||
icons: IconManager;
|
icons: IconManager;
|
||||||
avatars: AvatarManager;
|
avatars: AvatarManager;
|
||||||
|
|
||||||
|
@ -228,7 +223,7 @@ class FileManager extends connection.AbstractCommandHandler {
|
||||||
|
|
||||||
private transfer_counter : number = 0;
|
private transfer_counter : number = 0;
|
||||||
|
|
||||||
constructor(client: TSClient) {
|
constructor(client: ConnectionHandler) {
|
||||||
super(client.serverConnection);
|
super(client.serverConnection);
|
||||||
|
|
||||||
this.handle = client;
|
this.handle = client;
|
||||||
|
@ -533,14 +528,17 @@ class CacheManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
class IconManager {
|
class IconManager {
|
||||||
|
private static cache: CacheManager;
|
||||||
|
|
||||||
handle: FileManager;
|
handle: FileManager;
|
||||||
private cache: CacheManager;
|
|
||||||
private _id_urls: {[id:number]:string} = {};
|
private _id_urls: {[id:number]:string} = {};
|
||||||
private _loading_promises: {[id:number]:Promise<Icon>} = {};
|
private _loading_promises: {[id:number]:Promise<Icon>} = {};
|
||||||
|
|
||||||
constructor(handle: FileManager) {
|
constructor(handle: FileManager) {
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
this.cache = new CacheManager("icons");
|
|
||||||
|
if(!IconManager.cache)
|
||||||
|
IconManager.cache = new CacheManager("icons");
|
||||||
}
|
}
|
||||||
|
|
||||||
iconList() : Promise<FileEntry[]> {
|
iconList() : Promise<FileEntry[]> {
|
||||||
|
@ -572,10 +570,10 @@ class IconManager {
|
||||||
url: this._id_urls[id]
|
url: this._id_urls[id]
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!this.cache.setupped())
|
if(!IconManager.cache.setupped())
|
||||||
await this.cache.setup();
|
await IconManager.cache.setup();
|
||||||
|
|
||||||
const response = await this.cache.resolve_cached('icon_' + id); //TODO age!
|
const response = await IconManager.cache.resolve_cached('icon_' + id); //TODO age!
|
||||||
if(response)
|
if(response)
|
||||||
return {
|
return {
|
||||||
id: id,
|
id: id,
|
||||||
|
@ -606,7 +604,7 @@ class IconManager {
|
||||||
const type = image_type(response.headers.get('X-media-bytes'));
|
const type = image_type(response.headers.get('X-media-bytes'));
|
||||||
const media = media_image_type(type);
|
const media = media_image_type(type);
|
||||||
|
|
||||||
await this.cache.put_cache('icon_' + id, response.clone(), "image/" + media);
|
await IconManager.cache.put_cache('icon_' + id, response.clone(), "image/" + media);
|
||||||
const url = (this._id_urls[id] = await this._response_url(response.clone()));
|
const url = (this._id_urls[id] = await this._response_url(response.clone()));
|
||||||
|
|
||||||
this._loading_promises[id] = undefined;
|
this._loading_promises[id] = undefined;
|
||||||
|
@ -694,14 +692,15 @@ class Avatar {
|
||||||
class AvatarManager {
|
class AvatarManager {
|
||||||
handle: FileManager;
|
handle: FileManager;
|
||||||
|
|
||||||
private cache: CacheManager;
|
private static cache: CacheManager;
|
||||||
private _cached_avatars: {[response_avatar_id:number]:Avatar} = {};
|
private _cached_avatars: {[response_avatar_id:number]:Avatar} = {};
|
||||||
private _loading_promises: {[response_avatar_id:number]:Promise<Icon>} = {};
|
private _loading_promises: {[response_avatar_id:number]:Promise<Icon>} = {};
|
||||||
|
|
||||||
constructor(handle: FileManager) {
|
constructor(handle: FileManager) {
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
|
|
||||||
this.cache = new CacheManager("avatars");
|
if(!AvatarManager.cache)
|
||||||
|
AvatarManager.cache = new CacheManager("avatars");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _response_url(response: Response, type: ImageType) : Promise<string> {
|
private async _response_url(response: Response, type: ImageType) : Promise<string> {
|
||||||
|
@ -724,10 +723,10 @@ class AvatarManager {
|
||||||
this._cached_avatars[avatar_id] = (avatar = undefined);
|
this._cached_avatars[avatar_id] = (avatar = undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!this.cache.setupped())
|
if(!AvatarManager.cache.setupped())
|
||||||
await this.cache.setup();
|
await AvatarManager.cache.setup();
|
||||||
|
|
||||||
const response = await this.cache.resolve_cached('avatar_' + client_avatar_id); //TODO age!
|
const response = await AvatarManager.cache.resolve_cached('avatar_' + client_avatar_id); //TODO age!
|
||||||
if(!response)
|
if(!response)
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
||||||
|
@ -770,7 +769,7 @@ class AvatarManager {
|
||||||
const type = image_type(response.headers.get('X-media-bytes'));
|
const type = image_type(response.headers.get('X-media-bytes'));
|
||||||
const media = media_image_type(type);
|
const media = media_image_type(type);
|
||||||
|
|
||||||
await this.cache.put_cache('avatar_' + client_avatar_id, response.clone(), "image/" + media, {
|
await AvatarManager.cache.put_cache('avatar_' + client_avatar_id, response.clone(), "image/" + media, {
|
||||||
"X-avatar-id": avatar_id
|
"X-avatar-id": avatar_id
|
||||||
});
|
});
|
||||||
const url = await this._response_url(response.clone(), type);
|
const url = await this._response_url(response.clone(), type);
|
||||||
|
|
|
@ -1,315 +0,0 @@
|
||||||
/// <reference path="log.ts" />
|
|
||||||
/// <reference path="voice/AudioController.ts" />
|
|
||||||
/// <reference path="proto.ts" />
|
|
||||||
/// <reference path="ui/view.ts" />
|
|
||||||
/// <reference path="settings.ts" />
|
|
||||||
/// <reference path="ui/frames/SelectedItemInfo.ts" />
|
|
||||||
/// <reference path="FileManager.ts" />
|
|
||||||
/// <reference path="permission/PermissionManager.ts" />
|
|
||||||
/// <reference path="permission/GroupManager.ts" />
|
|
||||||
/// <reference path="ui/frames/ControlBar.ts" />
|
|
||||||
/// <reference path="connection/ConnectionBase.ts" />
|
|
||||||
|
|
||||||
enum DisconnectReason {
|
|
||||||
REQUESTED,
|
|
||||||
CONNECT_FAILURE,
|
|
||||||
CONNECTION_CLOSED,
|
|
||||||
CONNECTION_FATAL_ERROR,
|
|
||||||
CONNECTION_PING_TIMEOUT,
|
|
||||||
CLIENT_KICKED,
|
|
||||||
CLIENT_BANNED,
|
|
||||||
HANDSHAKE_FAILED,
|
|
||||||
SERVER_CLOSED,
|
|
||||||
SERVER_REQUIRES_PASSWORD,
|
|
||||||
UNKNOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ConnectionState {
|
|
||||||
UNCONNECTED,
|
|
||||||
CONNECTING,
|
|
||||||
INITIALISING,
|
|
||||||
CONNECTED,
|
|
||||||
DISCONNECTING
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ViewReasonId {
|
|
||||||
VREASON_USER_ACTION = 0,
|
|
||||||
VREASON_MOVED = 1,
|
|
||||||
VREASON_SYSTEM = 2,
|
|
||||||
VREASON_TIMEOUT = 3,
|
|
||||||
VREASON_CHANNEL_KICK = 4,
|
|
||||||
VREASON_SERVER_KICK = 5,
|
|
||||||
VREASON_BAN = 6,
|
|
||||||
VREASON_SERVER_STOPPED = 7,
|
|
||||||
VREASON_SERVER_LEFT = 8,
|
|
||||||
VREASON_CHANNEL_UPDATED = 9,
|
|
||||||
VREASON_EDITED = 10,
|
|
||||||
VREASON_SERVER_SHUTDOWN = 11
|
|
||||||
}
|
|
||||||
|
|
||||||
class TSClient {
|
|
||||||
channelTree: ChannelTree;
|
|
||||||
serverConnection: connection.ServerConnection;
|
|
||||||
voiceConnection: VoiceConnection | undefined;
|
|
||||||
fileManager: FileManager;
|
|
||||||
selectInfo: InfoBar;
|
|
||||||
permissions: PermissionManager;
|
|
||||||
groups: GroupManager;
|
|
||||||
controlBar: ControlBar;
|
|
||||||
|
|
||||||
private _clientId: number = 0;
|
|
||||||
private _ownEntry: LocalClientEntry;
|
|
||||||
private _reconnect_timer: NodeJS.Timer;
|
|
||||||
private _reconnect_attempt: boolean = false;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.selectInfo = new InfoBar(this, $("#select_info"));
|
|
||||||
this.channelTree = new ChannelTree(this, $("#channelTree"));
|
|
||||||
this.serverConnection = new connection.ServerConnection(this);
|
|
||||||
this.fileManager = new FileManager(this);
|
|
||||||
this.permissions = new PermissionManager(this);
|
|
||||||
this.groups = new GroupManager(this);
|
|
||||||
this._ownEntry = new LocalClientEntry(this);
|
|
||||||
this.controlBar = new ControlBar(this, $("#control_bar"));
|
|
||||||
this.channelTree.registerClient(this._ownEntry);
|
|
||||||
|
|
||||||
if(!settings.static_global(Settings.KEY_DISABLE_VOICE, false))
|
|
||||||
this.voiceConnection = new VoiceConnection(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
this.controlBar.initialise();
|
|
||||||
}
|
|
||||||
|
|
||||||
startConnection(addr: string, profile: profiles.ConnectionProfile, name?: string, password?: {password: string, hashed: boolean}) {
|
|
||||||
this.cancel_reconnect();
|
|
||||||
this._reconnect_attempt = false;
|
|
||||||
if(this.serverConnection)
|
|
||||||
this.handleDisconnect(DisconnectReason.REQUESTED);
|
|
||||||
|
|
||||||
let idx = addr.lastIndexOf(':');
|
|
||||||
|
|
||||||
let port: number;
|
|
||||||
let host: string;
|
|
||||||
if(idx != -1) {
|
|
||||||
port = parseInt(addr.substr(idx + 1));
|
|
||||||
host = addr.substr(0, idx);
|
|
||||||
} else {
|
|
||||||
host = addr;
|
|
||||||
port = 9987;
|
|
||||||
}
|
|
||||||
console.log(tr("Start connection to %s:%d"), host, port);
|
|
||||||
this.channelTree.initialiseHead(addr, {host, port});
|
|
||||||
|
|
||||||
if(password && !password.hashed) {
|
|
||||||
helpers.hashPassword(password.password).then(password => {
|
|
||||||
/* errors will be already handled via the handle disconnect thing */
|
|
||||||
this.serverConnection.connect({host, port}, new connection.HandshakeHandler(profile, name, password));
|
|
||||||
}).catch(error => {
|
|
||||||
createErrorModal(tr("Error while hashing password"), tr("Failed to hash server password!<br>") + error).open();
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
/* errors will be already handled via the handle disconnect thing */
|
|
||||||
this.serverConnection.connect({host, port}, new connection.HandshakeHandler(profile, name, password ? password.password : undefined));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getClient() : LocalClientEntry { return this._ownEntry; }
|
|
||||||
getClientId() { return this._clientId; }
|
|
||||||
|
|
||||||
set clientId(id: number) {
|
|
||||||
this._clientId = id;
|
|
||||||
this._ownEntry["_clientId"] = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
get clientId() {
|
|
||||||
return this._clientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
getServerConnection() : connection.ServerConnection { return this.serverConnection; }
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LISTENER
|
|
||||||
*/
|
|
||||||
onConnected() {
|
|
||||||
console.log("Client connected!");
|
|
||||||
this.channelTree.registerClient(this._ownEntry);
|
|
||||||
settings.setServer(this.channelTree.server);
|
|
||||||
this.permissions.requestPermissionList();
|
|
||||||
if(this.groups.serverGroups.length == 0)
|
|
||||||
this.groups.requestGroups();
|
|
||||||
this.controlBar.updateProperties();
|
|
||||||
if(this.controlBar.channel_subscribe_all)
|
|
||||||
this.channelTree.subscribe_all_channels();
|
|
||||||
else
|
|
||||||
this.channelTree.unsubscribe_all_channels();
|
|
||||||
if(this.voiceConnection && !this.voiceConnection.current_encoding_supported())
|
|
||||||
createErrorModal(tr("Codec encode type not supported!"), tr("Codec encode type " + VoiceConnectionType[this.voiceConnection.type] + " not supported by this browser!<br>Choose another one!")).open(); //TODO tr
|
|
||||||
}
|
|
||||||
|
|
||||||
get connected() : boolean {
|
|
||||||
return this.serverConnection && this.serverConnection.connected();
|
|
||||||
}
|
|
||||||
|
|
||||||
private certAcceptUrl() {
|
|
||||||
const properties = {
|
|
||||||
connect_default: true,
|
|
||||||
connect_profile: this.serverConnection._handshakeHandler.profile.id,
|
|
||||||
connect_address: this.serverConnection._remote_address.host + ":" + this.serverConnection._remote_address.port
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const parameters: string[] = [];
|
|
||||||
for(const key in properties)
|
|
||||||
parameters.push(key + "=" + encodeURIComponent(properties[key]));
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// document.URL
|
|
||||||
let callback = document.URL;
|
|
||||||
if(document.location.search.length == 0)
|
|
||||||
callback += "?" + parameters.join("&");
|
|
||||||
else
|
|
||||||
callback += "&" + parameters.join("&");
|
|
||||||
|
|
||||||
return "https://" + this.serverConnection._remote_address.host + ":" + this.serverConnection._remote_address.port + "/?forward_url=" + encodeURIComponent(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDisconnect(type: DisconnectReason, data: any = {}) {
|
|
||||||
let auto_reconnect = false;
|
|
||||||
switch (type) {
|
|
||||||
case DisconnectReason.REQUESTED:
|
|
||||||
break;
|
|
||||||
case DisconnectReason.CONNECT_FAILURE:
|
|
||||||
if(this._reconnect_attempt) {
|
|
||||||
auto_reconnect = true;
|
|
||||||
chat.serverChat().appendError(tr("Connect failed"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
console.error(tr("Could not connect to remote host! Exception: %o"), data);
|
|
||||||
|
|
||||||
if(native_client) {
|
|
||||||
createErrorModal(
|
|
||||||
tr("Could not connect"),
|
|
||||||
tr("Could not connect to remote host (Connection refused)")
|
|
||||||
).open();
|
|
||||||
} else {
|
|
||||||
//TODO tr
|
|
||||||
createErrorModal(
|
|
||||||
tr("Could not connect"),
|
|
||||||
"Could not connect to remote host (Connection refused)<br>" +
|
|
||||||
"If you're sure that the remote host is up, than you may not allow unsigned certificates.<br>" +
|
|
||||||
"Click <a href='" + this.certAcceptUrl() + "'>here</a> to accept the remote certificate"
|
|
||||||
).open();
|
|
||||||
}
|
|
||||||
sound.play(Sound.CONNECTION_REFUSED);
|
|
||||||
break;
|
|
||||||
case DisconnectReason.HANDSHAKE_FAILED:
|
|
||||||
//TODO sound
|
|
||||||
console.error(tr("Failed to process handshake: %o"), data);
|
|
||||||
createErrorModal(
|
|
||||||
tr("Could not connect"),
|
|
||||||
tr("Failed to process handshake: ") + data as string
|
|
||||||
).open();
|
|
||||||
break;
|
|
||||||
case DisconnectReason.CONNECTION_CLOSED:
|
|
||||||
console.error(tr("Lost connection to remote server!"));
|
|
||||||
createErrorModal(
|
|
||||||
tr("Connection closed"),
|
|
||||||
tr("The connection was closed by remote host")
|
|
||||||
).open();
|
|
||||||
sound.play(Sound.CONNECTION_DISCONNECTED);
|
|
||||||
|
|
||||||
auto_reconnect = true;
|
|
||||||
break;
|
|
||||||
case DisconnectReason.CONNECTION_PING_TIMEOUT:
|
|
||||||
console.error(tr("Connection ping timeout"));
|
|
||||||
sound.play(Sound.CONNECTION_DISCONNECTED_TIMEOUT);
|
|
||||||
createErrorModal(
|
|
||||||
tr("Connection lost"),
|
|
||||||
tr("Lost connection to remote host (Ping timeout)<br>Even possible?")
|
|
||||||
).open();
|
|
||||||
|
|
||||||
break;
|
|
||||||
case DisconnectReason.SERVER_CLOSED:
|
|
||||||
chat.serverChat().appendError(tr("Server closed ({0})"), data.reasonmsg);
|
|
||||||
createErrorModal(
|
|
||||||
tr("Server closed"),
|
|
||||||
"The server is closed.<br>" + //TODO tr
|
|
||||||
"Reason: " + data.reasonmsg
|
|
||||||
).open();
|
|
||||||
sound.play(Sound.CONNECTION_DISCONNECTED);
|
|
||||||
|
|
||||||
auto_reconnect = true;
|
|
||||||
break;
|
|
||||||
case DisconnectReason.SERVER_REQUIRES_PASSWORD:
|
|
||||||
chat.serverChat().appendError(tr("Server requires password"));
|
|
||||||
createInputModal(tr("Server password"), tr("Enter server password:"), password => password.length != 0, password => {
|
|
||||||
if(!(typeof password === "string")) return;
|
|
||||||
this.startConnection(this.serverConnection._remote_address.host + ":" + this.serverConnection._remote_address.port,
|
|
||||||
this.serverConnection._handshakeHandler.profile,
|
|
||||||
this.serverConnection._handshakeHandler.name,
|
|
||||||
{password: password as string, hashed: false});
|
|
||||||
}).open();
|
|
||||||
break;
|
|
||||||
case DisconnectReason.CLIENT_KICKED:
|
|
||||||
chat.serverChat().appendError(tr("You got kicked from the server by {0}{1}"),
|
|
||||||
ClientEntry.chatTag(data["invokerid"], data["invokername"], data["invokeruid"]),
|
|
||||||
data["reasonmsg"] ? " (" + data["reasonmsg"] + ")" : "");
|
|
||||||
sound.play(Sound.SERVER_KICKED);
|
|
||||||
auto_reconnect = true;
|
|
||||||
break;
|
|
||||||
case DisconnectReason.CLIENT_BANNED:
|
|
||||||
chat.serverChat().appendError(tr("You got banned from the server by {0}{1}"),
|
|
||||||
ClientEntry.chatTag(data["invokerid"], data["invokername"], data["invokeruid"]),
|
|
||||||
data["reasonmsg"] ? " (" + data["reasonmsg"] + ")" : "");
|
|
||||||
sound.play(Sound.CONNECTION_BANNED); //TODO findout if it was a disconnect or a connect refuse
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error(tr("Got uncaught disconnect!"));
|
|
||||||
console.error(tr("Type: %o Data:"), type);
|
|
||||||
console.error(data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.channelTree.reset();
|
|
||||||
if(this.voiceConnection)
|
|
||||||
this.voiceConnection.dropSession();
|
|
||||||
if(this.serverConnection) this.serverConnection.disconnect();
|
|
||||||
this.controlBar.update_connection_state();
|
|
||||||
this.selectInfo.setCurrentSelected(null);
|
|
||||||
this.selectInfo.update_banner();
|
|
||||||
|
|
||||||
if(auto_reconnect) {
|
|
||||||
if(!this.serverConnection) {
|
|
||||||
console.log(tr("Allowed to auto reconnect but cant reconnect because we dont have any information left..."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
chat.serverChat().appendMessage(tr("Reconnecting in 5 seconds"));
|
|
||||||
|
|
||||||
console.log(tr("Allowed to auto reconnect. Reconnecting in 5000ms"));
|
|
||||||
const server_address = this.serverConnection._remote_address;
|
|
||||||
const profile = this.serverConnection._handshakeHandler.profile;
|
|
||||||
const name = this.serverConnection._handshakeHandler.name;
|
|
||||||
const password = this.serverConnection._handshakeHandler.server_password;
|
|
||||||
|
|
||||||
this._reconnect_timer = setTimeout(() => {
|
|
||||||
this._reconnect_timer = undefined;
|
|
||||||
chat.serverChat().appendMessage(tr("Reconnecting..."));
|
|
||||||
log.info(LogCategory.NETWORKING, tr("Reconnecting..."))
|
|
||||||
this.startConnection(server_address.host + ":" + server_address.port, profile, name, password ? { password: password, hashed: true} : undefined);
|
|
||||||
this._reconnect_attempt = true;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel_reconnect() {
|
|
||||||
if(this._reconnect_timer) {
|
|
||||||
chat.serverChat().appendMessage(tr("Reconnect canceled"));
|
|
||||||
clearTimeout(this._reconnect_timer);
|
|
||||||
this._reconnect_timer = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -35,6 +35,7 @@ class BufferChunk {
|
||||||
}
|
}
|
||||||
|
|
||||||
class CodecClientCache {
|
class CodecClientCache {
|
||||||
|
_last_access: number;
|
||||||
_chunks: BufferChunk[] = [];
|
_chunks: BufferChunk[] = [];
|
||||||
|
|
||||||
bufferedSamples(max: number = 0) : number {
|
bufferedSamples(max: number = 0) : number {
|
||||||
|
|
|
@ -173,7 +173,7 @@ class CodecWrapperWorker extends BasicCodec {
|
||||||
this._workerCallbackResolve = undefined;
|
this._workerCallbackResolve = undefined;
|
||||||
return;
|
return;
|
||||||
} else if(message["type"] == "chatmessage_server") {
|
} else if(message["type"] == "chatmessage_server") {
|
||||||
chat.serverChat().appendMessage(message["message"]);
|
//FIXME?
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(tr("Costume callback! (%o)"), message);
|
console.log(tr("Costume callback! (%o)"), message);
|
||||||
|
|
|
@ -8,9 +8,11 @@ namespace connection {
|
||||||
|
|
||||||
export class ConnectionCommandHandler extends AbstractCommandHandler {
|
export class ConnectionCommandHandler extends AbstractCommandHandler {
|
||||||
readonly connection: ServerConnection;
|
readonly connection: ServerConnection;
|
||||||
|
readonly connection_handler: ConnectionHandler;
|
||||||
|
|
||||||
constructor(connection: ServerConnection) {
|
constructor(connection: ServerConnection) {
|
||||||
super(connection);
|
super(connection);
|
||||||
|
this.connection_handler = connection.client;
|
||||||
|
|
||||||
this["error"] = this.handleCommandResult;
|
this["error"] = this.handleCommandResult;
|
||||||
this["channellist"] = this.handleCommandChannelList;
|
this["channellist"] = this.handleCommandChannelList;
|
||||||
|
@ -86,9 +88,14 @@ namespace connection {
|
||||||
|
|
||||||
handleCommandServerInit(json){
|
handleCommandServerInit(json){
|
||||||
//We could setup the voice channel
|
//We could setup the voice channel
|
||||||
if( this.connection.client.voiceConnection) {
|
if(this.connection.support_voice()) {
|
||||||
console.log(tr("Setting up voice"));
|
console.log(tr("Setting up voice"));
|
||||||
this.connection.client.voiceConnection.createSession();
|
|
||||||
|
const connection = this.connection.voice_connection();
|
||||||
|
if(connection instanceof audio.js.VoiceConnection)
|
||||||
|
connection.createSession();
|
||||||
|
else
|
||||||
|
throw "unknown voice connection";
|
||||||
} else {
|
} else {
|
||||||
console.log(tr("Skipping voice setup (No voice bridge available)"));
|
console.log(tr("Skipping voice setup (No voice bridge available)"));
|
||||||
}
|
}
|
||||||
|
@ -112,10 +119,10 @@ namespace connection {
|
||||||
this.connection.client.channelTree.server.updateVariables(false, ...updates);
|
this.connection.client.channelTree.server.updateVariables(false, ...updates);
|
||||||
|
|
||||||
|
|
||||||
chat.serverChat().name = this.connection.client.channelTree.server.properties["virtualserver_name"];
|
this.connection_handler.chat.serverChat().name = this.connection.client.channelTree.server.properties["virtualserver_name"];
|
||||||
chat.serverChat().appendMessage(tr("Connected as {0}"), true, this.connection.client.getClient().createChatTag(true));
|
this.connection_handler.chat.serverChat().appendMessage(tr("Connected as {0}"), true, this.connection.client.getClient().createChatTag(true));
|
||||||
sound.play(Sound.CONNECTION_CONNECTED);
|
this.connection_handler.sound.play(Sound.CONNECTION_CONNECTED);
|
||||||
globalClient.onConnected();
|
this.connection.client.onConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
private createChannelFromJson(json, ignoreOrder: boolean = false) {
|
private createChannelFromJson(json, ignoreOrder: boolean = false) {
|
||||||
|
@ -233,28 +240,28 @@ namespace connection {
|
||||||
client = tree.insertClient(client, channel);
|
client = tree.insertClient(client, channel);
|
||||||
} else {
|
} else {
|
||||||
if(client == this.connection.client.getClient())
|
if(client == this.connection.client.getClient())
|
||||||
chat.channelChat().name = channel.channelName();
|
this.connection_handler.chat.channelChat().name = channel.channelName();
|
||||||
tree.moveClient(client, channel);
|
tree.moveClient(client, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.connection.client.controlBar.query_visible || client.properties.client_type != ClientType.CLIENT_QUERY) {
|
if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) {
|
||||||
const own_channel = this.connection.client.getClient().currentChannel();
|
const own_channel = this.connection.client.getClient().currentChannel();
|
||||||
if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) {
|
if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) {
|
||||||
if(own_channel == channel)
|
if(own_channel == channel)
|
||||||
if(old_channel)
|
if(old_channel)
|
||||||
sound.play(Sound.USER_ENTERED);
|
this.connection_handler.sound.play(Sound.USER_ENTERED);
|
||||||
else
|
else
|
||||||
sound.play(Sound.USER_ENTERED_CONNECT);
|
this.connection_handler.sound.play(Sound.USER_ENTERED_CONNECT);
|
||||||
if(old_channel) {
|
if(old_channel) {
|
||||||
chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}"), true, client.createChatTag(true), old_channel.generate_tag(true), channel.generate_tag(true));
|
this.connection_handler.chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}"), true, client.createChatTag(true), old_channel.generate_tag(true), channel.generate_tag(true));
|
||||||
} else {
|
} else {
|
||||||
chat.serverChat().appendMessage(tr("{0} connected to channel {1}"), true, client.createChatTag(true), channel.generate_tag(true));
|
this.connection_handler.chat.serverChat().appendMessage(tr("{0} connected to channel {1}"), true, client.createChatTag(true), channel.generate_tag(true));
|
||||||
}
|
}
|
||||||
} else if(json["reasonid"] == ViewReasonId.VREASON_MOVED) {
|
} else if(json["reasonid"] == ViewReasonId.VREASON_MOVED) {
|
||||||
if(own_channel == channel)
|
if(own_channel == channel)
|
||||||
sound.play(Sound.USER_ENTERED_MOVED);
|
this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED);
|
||||||
|
|
||||||
chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}, moved by {3}"), true,
|
this.connection_handler.chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}, moved by {3}"), true,
|
||||||
client.createChatTag(true),
|
client.createChatTag(true),
|
||||||
old_channel ? old_channel.generate_tag(true) : undefined,
|
old_channel ? old_channel.generate_tag(true) : undefined,
|
||||||
channel.generate_tag(true),
|
channel.generate_tag(true),
|
||||||
|
@ -262,9 +269,9 @@ namespace connection {
|
||||||
);
|
);
|
||||||
} else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) {
|
} else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) {
|
||||||
if(own_channel == channel)
|
if(own_channel == channel)
|
||||||
sound.play(Sound.USER_ENTERED_KICKED);
|
this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED);
|
||||||
|
|
||||||
chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}, kicked by {3}{4}"), true,
|
this.connection_handler.chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}, kicked by {3}{4}"), true,
|
||||||
client.createChatTag(true),
|
client.createChatTag(true),
|
||||||
old_channel ? old_channel.generate_tag(true) : undefined,
|
old_channel ? old_channel.generate_tag(true) : undefined,
|
||||||
channel.generate_tag(true),
|
channel.generate_tag(true),
|
||||||
|
@ -297,7 +304,7 @@ namespace connection {
|
||||||
{
|
{
|
||||||
let client_chat = client.chat(false);
|
let client_chat = client.chat(false);
|
||||||
if(!client_chat) {
|
if(!client_chat) {
|
||||||
for(const c of chat.open_chats()) {
|
for(const c of this.connection_handler.chat.open_chats()) {
|
||||||
if(c.owner_unique_id == client.properties.client_unique_identifier && c.flag_offline) {
|
if(c.owner_unique_id == client.properties.client_unique_identifier && c.flag_offline) {
|
||||||
client_chat = c;
|
client_chat = c;
|
||||||
break;
|
break;
|
||||||
|
@ -314,8 +321,10 @@ namespace connection {
|
||||||
client_chat.flag_offline = false;
|
client_chat.flag_offline = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(client instanceof LocalClientEntry)
|
|
||||||
this.connection.client.controlBar.updateVoice();
|
if(client instanceof LocalClientEntry) {
|
||||||
|
this.connection_handler.update_voice_status();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommandClientLeftView(json) {
|
handleCommandClientLeftView(json) {
|
||||||
|
@ -340,48 +349,49 @@ namespace connection {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.connection.client.controlBar.query_visible || client.properties.client_type != ClientType.CLIENT_QUERY) {
|
|
||||||
|
if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) {
|
||||||
const own_channel = this.connection.client.getClient().currentChannel();
|
const own_channel = this.connection.client.getClient().currentChannel();
|
||||||
let channel_from = tree.findChannel(json["cfid"]);
|
let channel_from = tree.findChannel(json["cfid"]);
|
||||||
let channel_to = tree.findChannel(json["ctid"]);
|
let channel_to = tree.findChannel(json["ctid"]);
|
||||||
|
|
||||||
if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) {
|
if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) {
|
||||||
chat.serverChat().appendMessage(tr("{0} disappeared from {1} to {2}"), true, client.createChatTag(true), channel_from.generate_tag(true), channel_to.generate_tag(true));
|
this.connection_handler.chat.serverChat().appendMessage(tr("{0} disappeared from {1} to {2}"), true, client.createChatTag(true), channel_from.generate_tag(true), channel_to.generate_tag(true));
|
||||||
|
|
||||||
if(channel_from == own_channel)
|
if(channel_from == own_channel)
|
||||||
sound.play(Sound.USER_LEFT);
|
this.connection_handler.sound.play(Sound.USER_LEFT);
|
||||||
} else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_LEFT) {
|
} else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_LEFT) {
|
||||||
chat.serverChat().appendMessage(tr("{0} left the server{1}"), true,
|
this.connection_handler.chat.serverChat().appendMessage(tr("{0} left the server{1}"), true,
|
||||||
client.createChatTag(true),
|
client.createChatTag(true),
|
||||||
json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : ""
|
json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : ""
|
||||||
);
|
);
|
||||||
|
|
||||||
if(channel_from == own_channel)
|
if(channel_from == own_channel)
|
||||||
sound.play(Sound.USER_LEFT_DISCONNECT);
|
this.connection_handler.sound.play(Sound.USER_LEFT_DISCONNECT);
|
||||||
} else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_KICK) {
|
} else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_KICK) {
|
||||||
chat.serverChat().appendError(tr("{0} was kicked from the server by {1}.{2}"),
|
this.connection_handler.chat.serverChat().appendError(tr("{0} was kicked from the server by {1}.{2}"),
|
||||||
client.createChatTag(true),
|
client.createChatTag(true),
|
||||||
ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]),
|
ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]),
|
||||||
json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : ""
|
json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : ""
|
||||||
);
|
);
|
||||||
if(channel_from == own_channel)
|
if(channel_from == own_channel)
|
||||||
sound.play(Sound.USER_LEFT_KICKED_SERVER);
|
this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_SERVER);
|
||||||
} else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) {
|
} else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) {
|
||||||
chat.serverChat().appendError(tr("{0} was kicked from your channel by {1}.{2}"),
|
this.connection_handler.chat.serverChat().appendError(tr("{0} was kicked from your channel by {1}.{2}"),
|
||||||
client.createChatTag(true),
|
client.createChatTag(true),
|
||||||
ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]),
|
ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]),
|
||||||
json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : ""
|
json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : ""
|
||||||
);
|
);
|
||||||
|
|
||||||
if(channel_from == own_channel)
|
if(channel_from == own_channel)
|
||||||
sound.play(Sound.USER_LEFT_KICKED_CHANNEL);
|
this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL);
|
||||||
} else if(json["reasonid"] == ViewReasonId.VREASON_BAN) {
|
} else if(json["reasonid"] == ViewReasonId.VREASON_BAN) {
|
||||||
//"Mulus" was banned for 1 second from the server by "WolverinDEV" (Sry brauchte kurz ein opfer :P <3 (Nohomo))
|
//"Mulus" was banned for 1 second from the server by "WolverinDEV" (Sry brauchte kurz ein opfer :P <3 (Nohomo))
|
||||||
let duration = "permanently";
|
let duration = "permanently";
|
||||||
if(json["bantime"])
|
if(json["bantime"])
|
||||||
duration = "for " + formatDate(Number.parseInt(json["bantime"]));
|
duration = "for " + formatDate(Number.parseInt(json["bantime"]));
|
||||||
|
|
||||||
chat.serverChat().appendError(tr("{0} was banned {1} by {2}.{3}"),
|
this.connection_handler.chat.serverChat().appendError(tr("{0} was banned {1} by {2}.{3}"),
|
||||||
client.createChatTag(true),
|
client.createChatTag(true),
|
||||||
duration,
|
duration,
|
||||||
ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]),
|
ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]),
|
||||||
|
@ -389,7 +399,7 @@ namespace connection {
|
||||||
);
|
);
|
||||||
|
|
||||||
if(channel_from == own_channel)
|
if(channel_from == own_channel)
|
||||||
sound.play(Sound.USER_LEFT_BANNED);
|
this.connection_handler.sound.play(Sound.USER_LEFT_BANNED);
|
||||||
} else {
|
} else {
|
||||||
console.error(tr("Unknown client left reason!"));
|
console.error(tr("Unknown client left reason!"));
|
||||||
}
|
}
|
||||||
|
@ -431,44 +441,45 @@ namespace connection {
|
||||||
console.error(tr("Unknown client move (Channel from)!"));
|
console.error(tr("Unknown client move (Channel from)!"));
|
||||||
|
|
||||||
let self = client instanceof LocalClientEntry;
|
let self = client instanceof LocalClientEntry;
|
||||||
let current_clients;
|
let current_clients: ClientEntry[];
|
||||||
if(self) {
|
if(self) {
|
||||||
chat.channelChat().name = channel_to.channelName();
|
this.connection_handler.chat.channelChat().name = channel_to.channelName();
|
||||||
current_clients = client.channelTree.clientsByChannel(client.currentChannel())
|
current_clients = client.channelTree.clientsByChannel(client.currentChannel());
|
||||||
this.connection.client.controlBar.updateVoice(channel_to);
|
this.connection_handler.update_voice_status(channel_to);
|
||||||
}
|
}
|
||||||
|
|
||||||
tree.moveClient(client, channel_to);
|
tree.moveClient(client, channel_to);
|
||||||
for(const entry of current_clients || [])
|
for(const entry of current_clients || [])
|
||||||
if(entry !== client) entry.getAudioController().stopAudio(true);
|
if(entry !== client)
|
||||||
|
entry.get_audio_handle().abort_replay();
|
||||||
|
|
||||||
const own_channel = this.connection.client.getClient().currentChannel();
|
const own_channel = this.connection.client.getClient().currentChannel();
|
||||||
if(json["reasonid"] == ViewReasonId.VREASON_MOVED) {
|
if(json["reasonid"] == ViewReasonId.VREASON_MOVED) {
|
||||||
chat.serverChat().appendMessage(self ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"), true,
|
this.connection_handler.chat.serverChat().appendMessage(self ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"), true,
|
||||||
client.createChatTag(true),
|
client.createChatTag(true),
|
||||||
channel_from ? channel_from.generate_tag(true) : undefined,
|
channel_from ? channel_from.generate_tag(true) : undefined,
|
||||||
channel_to.generate_tag(true),
|
channel_to.generate_tag(true),
|
||||||
ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"])
|
ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"])
|
||||||
);
|
);
|
||||||
if(self)
|
if(self)
|
||||||
sound.play(Sound.USER_MOVED_SELF);
|
this.connection_handler.sound.play(Sound.USER_MOVED_SELF);
|
||||||
else if(own_channel == channel_to)
|
else if(own_channel == channel_to)
|
||||||
sound.play(Sound.USER_ENTERED_MOVED);
|
this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED);
|
||||||
else if(own_channel == channel_from)
|
else if(own_channel == channel_from)
|
||||||
sound.play(Sound.USER_LEFT_MOVED);
|
this.connection_handler.sound.play(Sound.USER_LEFT_MOVED);
|
||||||
} else if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) {
|
} else if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) {
|
||||||
chat.serverChat().appendMessage(self ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"), true,
|
this.connection_handler.chat.serverChat().appendMessage(self ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"), true,
|
||||||
client.createChatTag(true),
|
client.createChatTag(true),
|
||||||
channel_from ? channel_from.generate_tag(true) : undefined,
|
channel_from ? channel_from.generate_tag(true) : undefined,
|
||||||
channel_to.generate_tag(true)
|
channel_to.generate_tag(true)
|
||||||
);
|
);
|
||||||
if(self) {} //If we do an action we wait for the error response
|
if(self) {} //If we do an action we wait for the error response
|
||||||
else if(own_channel == channel_to)
|
else if(own_channel == channel_to)
|
||||||
sound.play(Sound.USER_ENTERED);
|
this.connection_handler.sound.play(Sound.USER_ENTERED);
|
||||||
else if(own_channel == channel_from)
|
else if(own_channel == channel_from)
|
||||||
sound.play(Sound.USER_LEFT);
|
this.connection_handler.sound.play(Sound.USER_LEFT);
|
||||||
} else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) {
|
} else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) {
|
||||||
chat.serverChat().appendMessage(self ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"), true,
|
this.connection_handler.chat.serverChat().appendMessage(self ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"), true,
|
||||||
client.createChatTag(true),
|
client.createChatTag(true),
|
||||||
channel_from ? channel_from.generate_tag(true) : undefined,
|
channel_from ? channel_from.generate_tag(true) : undefined,
|
||||||
channel_to.generate_tag(true),
|
channel_to.generate_tag(true),
|
||||||
|
@ -476,11 +487,11 @@ namespace connection {
|
||||||
json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : ""
|
json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : ""
|
||||||
);
|
);
|
||||||
if(self) {
|
if(self) {
|
||||||
sound.play(Sound.CHANNEL_KICKED);
|
this.connection_handler.sound.play(Sound.CHANNEL_KICKED);
|
||||||
} else if(own_channel == channel_to)
|
} else if(own_channel == channel_to)
|
||||||
sound.play(Sound.USER_ENTERED_KICKED);
|
this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED);
|
||||||
else if(own_channel == channel_from)
|
else if(own_channel == channel_from)
|
||||||
sound.play(Sound.USER_LEFT_KICKED_CHANNEL);
|
this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL);
|
||||||
} else {
|
} else {
|
||||||
console.warn(tr("Unknown reason id %o"), json["reasonid"]);
|
console.warn(tr("Unknown reason id %o"), json["reasonid"]);
|
||||||
}
|
}
|
||||||
|
@ -554,20 +565,20 @@ namespace connection {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(invoker == this.connection.client.getClient()) {
|
if(invoker == this.connection.client.getClient()) {
|
||||||
sound.play(Sound.MESSAGE_SEND, {default_volume: .5});
|
this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5});
|
||||||
target.chat(true).appendMessage("{0}: {1}", true, this.connection.client.getClient().createChatTag(true), MessageHelper.bbcode_chat(json["msg"]));
|
target.chat(true).appendMessage("{0}: {1}", true, this.connection.client.getClient().createChatTag(true), MessageHelper.bbcode_chat(json["msg"]));
|
||||||
} else {
|
} else {
|
||||||
sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5});
|
this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5});
|
||||||
invoker.chat(true).appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"]));
|
invoker.chat(true).appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"]));
|
||||||
}
|
}
|
||||||
} else if(mode == 2) {
|
} else if(mode == 2) {
|
||||||
if(json["invokerid"] == this.connection.client.clientId)
|
if(json["invokerid"] == this.connection.client.clientId)
|
||||||
sound.play(Sound.MESSAGE_SEND, {default_volume: .5});
|
this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5});
|
||||||
else
|
else
|
||||||
sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5});
|
this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5});
|
||||||
chat.channelChat().appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"]))
|
this.connection_handler.chat.channelChat().appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"]))
|
||||||
} else if(mode == 3) {
|
} else if(mode == 3) {
|
||||||
chat.serverChat().appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"]));
|
this.connection_handler.chat.serverChat().appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,8 +632,8 @@ namespace connection {
|
||||||
updates.push({key: key, value: json[key]});
|
updates.push({key: key, value: json[key]});
|
||||||
}
|
}
|
||||||
client.updateVariables(...updates);
|
client.updateVariables(...updates);
|
||||||
if(this.connection.client.selectInfo.currentSelected == client)
|
if(this.connection.client.select_info.currentSelected == client)
|
||||||
this.connection.client.selectInfo.update();
|
this.connection.client.select_info.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNotifyServerEdited(json) {
|
handleNotifyServerEdited(json) {
|
||||||
|
@ -641,8 +652,8 @@ namespace connection {
|
||||||
updates.push({key: key, value: json[key]});
|
updates.push({key: key, value: json[key]});
|
||||||
}
|
}
|
||||||
this.connection.client.channelTree.server.updateVariables(false, ...updates);
|
this.connection.client.channelTree.server.updateVariables(false, ...updates);
|
||||||
if(this.connection.client.selectInfo.currentSelected == this.connection.client.channelTree.server)
|
if(this.connection.client.select_info.currentSelected == this.connection.client.channelTree.server)
|
||||||
this.connection.client.selectInfo.update();
|
this.connection.client.select_info.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNotifyServerUpdated(json) {
|
handleNotifyServerUpdated(json) {
|
||||||
|
@ -661,7 +672,7 @@ namespace connection {
|
||||||
updates.push({key: key, value: json[key]});
|
updates.push({key: key, value: json[key]});
|
||||||
}
|
}
|
||||||
this.connection.client.channelTree.server.updateVariables(true, ...updates);
|
this.connection.client.channelTree.server.updateVariables(true, ...updates);
|
||||||
let info = this.connection.client.selectInfo;
|
let info = this.connection.client.select_info;
|
||||||
if(info.currentSelected instanceof ServerEntry)
|
if(info.currentSelected instanceof ServerEntry)
|
||||||
info.update();
|
info.update();
|
||||||
}
|
}
|
||||||
|
@ -686,7 +697,7 @@ namespace connection {
|
||||||
unique_id: json["invokeruid"]
|
unique_id: json["invokeruid"]
|
||||||
}, json["msg"]);
|
}, json["msg"]);
|
||||||
|
|
||||||
sound.play(Sound.USER_POKED_SELF);
|
this.connection_handler.sound.play(Sound.USER_POKED_SELF);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO server chat message
|
//TODO server chat message
|
||||||
|
@ -695,7 +706,7 @@ namespace connection {
|
||||||
|
|
||||||
const self = this.connection.client.getClient();
|
const self = this.connection.client.getClient();
|
||||||
if(json["clid"] == self.clientId())
|
if(json["clid"] == self.clientId())
|
||||||
sound.play(Sound.GROUP_SERVER_ASSIGNED_SELF);
|
this.connection_handler.sound.play(Sound.GROUP_SERVER_ASSIGNED_SELF);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO server chat message
|
//TODO server chat message
|
||||||
|
@ -704,7 +715,7 @@ namespace connection {
|
||||||
|
|
||||||
const self = this.connection.client.getClient();
|
const self = this.connection.client.getClient();
|
||||||
if(json["clid"] == self.clientId()) {
|
if(json["clid"] == self.clientId()) {
|
||||||
sound.play(Sound.GROUP_SERVER_REVOKED_SELF);
|
this.connection_handler.sound.play(Sound.GROUP_SERVER_REVOKED_SELF);
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -715,7 +726,7 @@ namespace connection {
|
||||||
|
|
||||||
const self = this.connection.client.getClient();
|
const self = this.connection.client.getClient();
|
||||||
if(json["clid"] == self.clientId()) {
|
if(json["clid"] == self.clientId()) {
|
||||||
sound.play(Sound.GROUP_CHANNEL_CHANGED_SELF);
|
this.connection_handler.sound.play(Sound.GROUP_CHANNEL_CHANGED_SELF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,12 @@ namespace connection {
|
||||||
timeout: 1000
|
timeout: 1000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ConnectionStateListener = (old_state: ConnectionState, new_state: ConnectionState) => any;
|
||||||
export abstract class AbstractServerConnection {
|
export abstract class AbstractServerConnection {
|
||||||
readonly client: TSClient;
|
readonly client: ConnectionHandler;
|
||||||
readonly command_helper: CommandHelper;
|
readonly command_helper: CommandHelper;
|
||||||
|
|
||||||
protected constructor(client: TSClient) {
|
protected constructor(client: ConnectionHandler) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
|
||||||
this.command_helper = new CommandHelper(this);
|
this.command_helper = new CommandHelper(this);
|
||||||
|
@ -32,18 +33,34 @@ namespace connection {
|
||||||
|
|
||||||
abstract command_handler_boss() : AbstractCommandHandlerBoss;
|
abstract command_handler_boss() : AbstractCommandHandlerBoss;
|
||||||
abstract send_command(command: string, data?: any | any[], options?: CommandOptions) : Promise<CommandResult>;
|
abstract send_command(command: string, data?: any | any[], options?: CommandOptions) : Promise<CommandResult>;
|
||||||
|
|
||||||
|
abstract get onconnectionstatechanged() : ConnectionStateListener;
|
||||||
|
abstract set onconnectionstatechanged(listener: ConnectionStateListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace voice {
|
export namespace voice {
|
||||||
|
export enum PlayerState {
|
||||||
|
PREBUFFERING,
|
||||||
|
PLAYING,
|
||||||
|
BUFFERING,
|
||||||
|
STOPPING,
|
||||||
|
STOPPED
|
||||||
|
}
|
||||||
|
|
||||||
export interface VoiceClient {
|
export interface VoiceClient {
|
||||||
client_id: number;
|
client_id: number;
|
||||||
|
|
||||||
callback_playback: () => any;
|
callback_playback: () => any;
|
||||||
callback_timeout: () => any;
|
|
||||||
callback_stopped: () => any;
|
callback_stopped: () => any;
|
||||||
|
|
||||||
|
callback_state_changed: (new_state: PlayerState) => any;
|
||||||
|
|
||||||
|
get_state() : PlayerState;
|
||||||
|
|
||||||
get_volume() : number;
|
get_volume() : number;
|
||||||
set_volume(volume: number) : Promise<void>;
|
set_volume(volume: number) : void;
|
||||||
|
|
||||||
|
abort_replay();
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class AbstractVoiceConnection {
|
export abstract class AbstractVoiceConnection {
|
||||||
|
@ -54,10 +71,15 @@ namespace connection {
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract connected() : boolean;
|
abstract connected() : boolean;
|
||||||
|
abstract encoding_supported(codec: number) : boolean;
|
||||||
|
abstract decoding_supported(codec: number) : boolean;
|
||||||
|
|
||||||
abstract register_client(client_id: number) : VoiceClient;
|
abstract register_client(client_id: number) : VoiceClient;
|
||||||
abstract availible_clients() : VoiceClient[];
|
abstract available_clients() : VoiceClient[];
|
||||||
abstract unregister_client(client: VoiceClient) : Promise<void>;
|
abstract unregister_client(client: VoiceClient) : Promise<void>;
|
||||||
|
|
||||||
|
abstract voice_recorder() : VoiceRecorder;
|
||||||
|
abstract acquire_voice_recorder(recorder: VoiceRecorder | undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,12 @@ namespace connection {
|
||||||
client_version: "TeaWeb " + git_version + " (" + navigator.userAgent + ")",
|
client_version: "TeaWeb " + git_version + " (" + navigator.userAgent + ")",
|
||||||
|
|
||||||
client_server_password: this.server_password,
|
client_server_password: this.server_password,
|
||||||
client_browser_engine: navigator.product
|
client_browser_engine: navigator.product,
|
||||||
|
|
||||||
|
client_input_hardware: this.connection.client.client_status.input_hardware,
|
||||||
|
client_output_hardware: false,
|
||||||
|
client_input_muted: this.connection.client.client_status.input_muted,
|
||||||
|
client_output_muted: this.connection.client.client_status.output_muted,
|
||||||
};
|
};
|
||||||
|
|
||||||
if(version) {
|
if(version) {
|
||||||
|
|
|
@ -48,7 +48,10 @@ namespace connection {
|
||||||
private _retCodeIdx: number;
|
private _retCodeIdx: number;
|
||||||
private _retListener: ReturnListener<CommandResult>[];
|
private _retListener: ReturnListener<CommandResult>[];
|
||||||
|
|
||||||
constructor(client : TSClient) {
|
private _connection_state_listener: connection.ConnectionStateListener;
|
||||||
|
private _voice_connection: audio.js.VoiceConnection;
|
||||||
|
|
||||||
|
constructor(client : ConnectionHandler) {
|
||||||
super(client);
|
super(client);
|
||||||
|
|
||||||
this._socket = null;
|
this._socket = null;
|
||||||
|
@ -60,11 +63,14 @@ namespace connection {
|
||||||
|
|
||||||
this._command_boss.register_handler(this._command_handler_default);
|
this._command_boss.register_handler(this._command_handler_default);
|
||||||
this.command_helper.initialize();
|
this.command_helper.initialize();
|
||||||
|
|
||||||
|
if(!settings.static_global(Settings.KEY_DISABLE_VOICE, false))
|
||||||
|
this._voice_connection = new audio.js.VoiceConnection(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
on_connect: () => void = () => {
|
on_connect: () => void = () => {
|
||||||
console.log(tr("Socket connected"));
|
console.log(tr("Socket connected"));
|
||||||
chat.serverChat().appendMessage(tr("Logging in..."));
|
this.client.chat.serverChat().appendMessage(tr("Logging in..."));
|
||||||
this._handshakeHandler.startHandshake();
|
this._handshakeHandler.startHandshake();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -90,12 +96,12 @@ namespace connection {
|
||||||
this._handshakeHandler = handshake;
|
this._handshakeHandler = handshake;
|
||||||
this._handshakeHandler.setConnection(this);
|
this._handshakeHandler.setConnection(this);
|
||||||
this._connected = false;
|
this._connected = false;
|
||||||
chat.serverChat().appendMessage(tr("Connecting to {0}:{1}"), true, address.host, address.port);
|
this.client.chat.serverChat().appendMessage(tr("Connecting to {0}:{1}"), true, address.host, address.port);
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
|
let local_socket: WebSocket;
|
||||||
|
let local_timeout_timer: NodeJS.Timer;
|
||||||
try {
|
try {
|
||||||
let local_socket: WebSocket;
|
|
||||||
let local_timeout_timer: NodeJS.Timer;
|
|
||||||
|
|
||||||
local_timeout_timer = setTimeout(async () => {
|
local_timeout_timer = setTimeout(async () => {
|
||||||
console.log(tr("Connect timeout triggered!"));
|
console.log(tr("Connect timeout triggered!"));
|
||||||
|
@ -144,6 +150,7 @@ namespace connection {
|
||||||
};
|
};
|
||||||
this.updateConnectionState(ConnectionState.INITIALISING);
|
this.updateConnectionState(ConnectionState.INITIALISING);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
clearTimeout(local_timeout_timer);
|
||||||
try {
|
try {
|
||||||
await this.disconnect();
|
await this.disconnect();
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
|
@ -154,8 +161,10 @@ namespace connection {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateConnectionState(state: ConnectionState) {
|
updateConnectionState(state: ConnectionState) {
|
||||||
|
const old_state = this._connectionState;
|
||||||
this._connectionState = state;
|
this._connectionState = state;
|
||||||
this.client.controlBar.update_connection_state();
|
if(this._connection_state_listener)
|
||||||
|
this._connection_state_listener(old_state, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
async disconnect(reason?: string) : Promise<void> {
|
async disconnect(reason?: string) : Promise<void> {
|
||||||
|
@ -176,6 +185,9 @@ namespace connection {
|
||||||
this._retListener = [];
|
this._retListener = [];
|
||||||
this._retCodeIdx = 0;
|
this._retCodeIdx = 0;
|
||||||
this._connected = false;
|
this._connected = false;
|
||||||
|
|
||||||
|
if(this._voice_connection)
|
||||||
|
this._voice_connection.dropSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
private handle_socket_message(data) {
|
private handle_socket_message(data) {
|
||||||
|
@ -203,10 +215,10 @@ namespace connection {
|
||||||
});
|
});
|
||||||
group.end();
|
group.end();
|
||||||
} else if(json["type"] === "WebRTC") {
|
} else if(json["type"] === "WebRTC") {
|
||||||
if(this.client.voiceConnection)
|
if(this._voice_connection)
|
||||||
this.client.voiceConnection.handleControlPacket(json);
|
this._voice_connection.handleControlPacket(json);
|
||||||
else
|
else
|
||||||
console.log(tr("Dropping WebRTC command packet, because we havent a bridge."))
|
console.log(tr("Dropping WebRTC command packet, because we haven't a bridge."))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log(tr("Unknown command type %o"), json["type"]);
|
console.log(tr("Unknown command type %o"), json["type"]);
|
||||||
|
@ -280,14 +292,14 @@ namespace connection {
|
||||||
if(!res.success) {
|
if(!res.success) {
|
||||||
if(res.id == 2568) { //Permission error
|
if(res.id == 2568) { //Permission error
|
||||||
res.message = tr("Insufficient client permissions. Failed on permission ") + this.client.permissions.resolveInfo(res.json["failed_permid"] as number).name;
|
res.message = tr("Insufficient client permissions. Failed on permission ") + this.client.permissions.resolveInfo(res.json["failed_permid"] as number).name;
|
||||||
chat.serverChat().appendError(tr("Insufficient client permissions. Failed on permission {}"), this.client.permissions.resolveInfo(res.json["failed_permid"] as number).name);
|
this.client.chat.serverChat().appendError(tr("Insufficient client permissions. Failed on permission {}"), this.client.permissions.resolveInfo(res.json["failed_permid"] as number).name);
|
||||||
sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
|
this.client.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
|
||||||
} else {
|
} else {
|
||||||
chat.serverChat().appendError(res.extra_message.length == 0 ? res.message : res.extra_message);
|
this.client.chat.serverChat().appendError(res.extra_message.length == 0 ? res.message : res.extra_message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(typeof(ex) === "string") {
|
} else if(typeof(ex) === "string") {
|
||||||
chat.serverChat().appendError(tr("Command execution results in ") + ex);
|
this.client.chat.serverChat().appendError(tr("Command execution results in ") + ex);
|
||||||
} else {
|
} else {
|
||||||
console.error(tr("Invalid promise result type: %o. Result:"), typeof (ex));
|
console.error(tr("Invalid promise result type: %o. Result:"), typeof (ex));
|
||||||
console.error(ex);
|
console.error(ex);
|
||||||
|
@ -303,16 +315,24 @@ namespace connection {
|
||||||
}
|
}
|
||||||
|
|
||||||
support_voice(): boolean {
|
support_voice(): boolean {
|
||||||
return false;
|
return this._voice_connection !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
voice_connection(): connection.voice.AbstractVoiceConnection | undefined {
|
voice_connection(): connection.voice.AbstractVoiceConnection | undefined {
|
||||||
return undefined;
|
return this._voice_connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
command_handler_boss(): connection.AbstractCommandHandlerBoss {
|
command_handler_boss(): connection.AbstractCommandHandlerBoss {
|
||||||
return this._command_boss;
|
return this._command_boss;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get onconnectionstatechanged() : connection.ConnectionStateListener {
|
||||||
|
return this._connection_state_listener;
|
||||||
|
}
|
||||||
|
set onconnectionstatechanged(listener: connection.ConnectionStateListener) {
|
||||||
|
this._connection_state_listener = listener;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -295,4 +295,5 @@ namespace i18n {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
const tr: typeof i18n.tr = i18n.tr;
|
const tr: typeof i18n.tr = i18n.tr;
|
|
@ -7,10 +7,14 @@ namespace app {
|
||||||
WEB_RELEASE
|
WEB_RELEASE
|
||||||
}
|
}
|
||||||
export let type: Type = Type.UNKNOWN;
|
export let type: Type = Type.UNKNOWN;
|
||||||
|
|
||||||
|
export function is_web() {
|
||||||
|
return type == Type.WEB_RELEASE || type == Type.WEB_DEBUG;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace loader {
|
namespace loader {
|
||||||
type Task = {
|
export type Task = {
|
||||||
name: string,
|
name: string,
|
||||||
priority: number, /* tasks with the same priority will be executed in sync */
|
priority: number, /* tasks with the same priority will be executed in sync */
|
||||||
function: () => Promise<void>
|
function: () => Promise<void>
|
||||||
|
@ -518,8 +522,6 @@ const loader_javascript = {
|
||||||
|
|
||||||
"js/sound/Sounds.js",
|
"js/sound/Sounds.js",
|
||||||
|
|
||||||
"js/utils/modal.js",
|
|
||||||
"js/utils/tab.js",
|
|
||||||
"js/utils/helpers.js",
|
"js/utils/helpers.js",
|
||||||
|
|
||||||
"js/crypto/sha.js",
|
"js/crypto/sha.js",
|
||||||
|
@ -530,6 +532,12 @@ const loader_javascript = {
|
||||||
"js/profiles/ConnectionProfile.js",
|
"js/profiles/ConnectionProfile.js",
|
||||||
"js/profiles/Identity.js",
|
"js/profiles/Identity.js",
|
||||||
|
|
||||||
|
//Basic UI elements
|
||||||
|
"js/ui/elements/context_divider.js",
|
||||||
|
"js/ui/elements/context_menu.js",
|
||||||
|
"js/ui/elements/modal.js",
|
||||||
|
"js/ui/elements/tab.js",
|
||||||
|
|
||||||
//Load UI
|
//Load UI
|
||||||
"js/ui/modal/ModalAvatarList.js",
|
"js/ui/modal/ModalAvatarList.js",
|
||||||
"js/ui/modal/ModalQuery.js",
|
"js/ui/modal/ModalQuery.js",
|
||||||
|
@ -556,11 +564,12 @@ const loader_javascript = {
|
||||||
"js/ui/server.js",
|
"js/ui/server.js",
|
||||||
"js/ui/view.js",
|
"js/ui/view.js",
|
||||||
"js/ui/client_move.js",
|
"js/ui/client_move.js",
|
||||||
"js/ui/context_divider.js",
|
|
||||||
"js/ui/htmltags.js",
|
"js/ui/htmltags.js",
|
||||||
|
|
||||||
"js/ui/frames/SelectedItemInfo.js",
|
"js/ui/frames/SelectedItemInfo.js",
|
||||||
"js/ui/frames/ControlBar.js",
|
"js/ui/frames/ControlBar.js",
|
||||||
|
"js/ui/frames/chat.js",
|
||||||
|
"js/ui/frames/connection_handlers.js",
|
||||||
|
|
||||||
//Load permissions
|
//Load permissions
|
||||||
"js/permission/PermissionManager.js",
|
"js/permission/PermissionManager.js",
|
||||||
|
@ -570,7 +579,7 @@ const loader_javascript = {
|
||||||
"js/voice/VoiceHandler.js",
|
"js/voice/VoiceHandler.js",
|
||||||
"js/voice/VoiceRecorder.js",
|
"js/voice/VoiceRecorder.js",
|
||||||
"js/voice/AudioResampler.js",
|
"js/voice/AudioResampler.js",
|
||||||
"js/voice/AudioController.js",
|
"js/voice/VoiceClient.js",
|
||||||
|
|
||||||
//Load codec
|
//Load codec
|
||||||
"js/codec/Codec.js",
|
"js/codec/Codec.js",
|
||||||
|
@ -579,10 +588,9 @@ const loader_javascript = {
|
||||||
//Load general stuff
|
//Load general stuff
|
||||||
"js/settings.js",
|
"js/settings.js",
|
||||||
"js/bookmarks.js",
|
"js/bookmarks.js",
|
||||||
"js/contextMenu.js",
|
|
||||||
"js/FileManager.js",
|
"js/FileManager.js",
|
||||||
"js/client.js",
|
"js/ConnectionHandler.js",
|
||||||
"js/chat.js",
|
"js/BrowserIPC.js",
|
||||||
|
|
||||||
//Connection
|
//Connection
|
||||||
"js/connection/CommandHandler.js",
|
"js/connection/CommandHandler.js",
|
||||||
|
@ -591,7 +599,6 @@ const loader_javascript = {
|
||||||
"js/connection/ServerConnection.js",
|
"js/connection/ServerConnection.js",
|
||||||
|
|
||||||
"js/stats.js",
|
"js/stats.js",
|
||||||
|
|
||||||
"js/PPTListener.js",
|
"js/PPTListener.js",
|
||||||
|
|
||||||
|
|
||||||
|
@ -687,6 +694,7 @@ const loader_style = {
|
||||||
"css/static/frame/SelectInfo.css",
|
"css/static/frame/SelectInfo.css",
|
||||||
"css/static/control_bar.css",
|
"css/static/control_bar.css",
|
||||||
"css/static/context_menu.css",
|
"css/static/context_menu.css",
|
||||||
|
"css/static/connection_handlers.css",
|
||||||
"css/static/htmltags.css"
|
"css/static/htmltags.css"
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,6 +8,7 @@ enum LogCategory {
|
||||||
NETWORKING,
|
NETWORKING,
|
||||||
VOICE,
|
VOICE,
|
||||||
I18N,
|
I18N,
|
||||||
|
IPC,
|
||||||
IDENTITIES
|
IDENTITIES
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +31,8 @@ namespace log {
|
||||||
[LogCategory.NETWORKING, "Network "],
|
[LogCategory.NETWORKING, "Network "],
|
||||||
[LogCategory.VOICE, "Voice "],
|
[LogCategory.VOICE, "Voice "],
|
||||||
[LogCategory.I18N, "I18N "],
|
[LogCategory.I18N, "I18N "],
|
||||||
[LogCategory.IDENTITIES, "IDENTITIES "]
|
[LogCategory.IDENTITIES, "IDENTITIES "],
|
||||||
|
[LogCategory.IPC, "IPC "]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export let enabled_mapping = new Map<number, boolean>([
|
export let enabled_mapping = new Map<number, boolean>([
|
||||||
|
@ -43,7 +45,8 @@ namespace log {
|
||||||
[LogCategory.NETWORKING, true],
|
[LogCategory.NETWORKING, true],
|
||||||
[LogCategory.VOICE, true],
|
[LogCategory.VOICE, true],
|
||||||
[LogCategory.I18N, false],
|
[LogCategory.I18N, false],
|
||||||
[LogCategory.IDENTITIES, true]
|
[LogCategory.IDENTITIES, true],
|
||||||
|
[LogCategory.IPC, true]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
enum GroupMode {
|
enum GroupMode {
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
/// <reference path="chat.ts" />
|
/// <reference path="ui/frames/chat.ts" />
|
||||||
/// <reference path="client.ts" />
|
|
||||||
/// <reference path="utils/modal.ts" />
|
|
||||||
/// <reference path="ui/modal/ModalConnect.ts" />
|
/// <reference path="ui/modal/ModalConnect.ts" />
|
||||||
/// <reference path="ui/modal/ModalCreateChannel.ts" />
|
/// <reference path="ui/modal/ModalCreateChannel.ts" />
|
||||||
/// <reference path="ui/modal/ModalBanCreate.ts" />
|
/// <reference path="ui/modal/ModalBanCreate.ts" />
|
||||||
|
@ -11,8 +9,6 @@
|
||||||
/// <reference path="log.ts" />
|
/// <reference path="log.ts" />
|
||||||
|
|
||||||
let settings: Settings;
|
let settings: Settings;
|
||||||
let globalClient: TSClient;
|
|
||||||
let chat: ChatBox;
|
|
||||||
|
|
||||||
const js_render = window.jsrender || $;
|
const js_render = window.jsrender || $;
|
||||||
const native_client = window.require !== undefined;
|
const native_client = window.require !== undefined;
|
||||||
|
@ -34,7 +30,8 @@ function setup_close() {
|
||||||
profiles.save();
|
profiles.save();
|
||||||
|
|
||||||
if(!settings.static(Settings.KEY_DISABLE_UNLOAD_DIALOG, false)) {
|
if(!settings.static(Settings.KEY_DISABLE_UNLOAD_DIALOG, false)) {
|
||||||
if(!globalClient.serverConnection || !globalClient.serverConnection.connected) return;
|
const active_connections = server_connections.server_connection_handlers().filter(e => e.connected);
|
||||||
|
if(active_connections.length == 0) return;
|
||||||
|
|
||||||
if(!native_client) {
|
if(!native_client) {
|
||||||
event.returnValue = "Are you really sure?<br>You're still connected!";
|
event.returnValue = "Are you really sure?<br>You're still connected!";
|
||||||
|
@ -92,13 +89,6 @@ function setup_jsrender() : boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initialize() {
|
async function initialize() {
|
||||||
const display_load_error = message => {
|
|
||||||
if(typeof(display_critical_load) !== "undefined")
|
|
||||||
display_critical_load(message);
|
|
||||||
else
|
|
||||||
displayCriticalError(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
settings = new Settings();
|
settings = new Settings();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -109,6 +99,18 @@ async function initialize() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bipc.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function initialize_app() {
|
||||||
|
const display_load_error = message => {
|
||||||
|
if(typeof(display_critical_load) !== "undefined")
|
||||||
|
display_critical_load(message);
|
||||||
|
else
|
||||||
|
displayCriticalError(message);
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(!setup_jsrender())
|
if(!setup_jsrender())
|
||||||
throw "invalid load";
|
throw "invalid load";
|
||||||
|
@ -128,7 +130,15 @@ async function initialize() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioController.initializeAudioController();
|
control_bar = new ControlBar($("#control_bar")); /* setup the control bar */
|
||||||
|
|
||||||
|
if(!audio.player.initialize())
|
||||||
|
console.warn(tr("Failed to initialize audio controller!"));
|
||||||
|
|
||||||
|
sound.initialize().then(() => {
|
||||||
|
console.log(tr("Sounds initialitzed"));
|
||||||
|
});
|
||||||
|
|
||||||
await profiles.load();
|
await profiles.load();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -230,47 +240,31 @@ function Base64DecodeUrl(str: string, pad?: boolean){
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
//http://localhost:63343/Web-Client/index.php?_ijt=omcpmt8b9hnjlfguh8ajgrgolr&default_connect_url=true&default_connect_type=teamspeak&default_connect_url=localhost%3A9987&disableUnloadDialog=1&loader_ignore_age=1
|
//http://localhost:63343/Web-Client/index.php?_ijt=omcpmt8b9hnjlfguh8ajgrgolr&default_connect_url=true&default_connect_type=teamspeak&default_connect_url=localhost%3A9987&disableUnloadDialog=1&loader_ignore_age=1
|
||||||
|
voice_recoder = new VoiceRecorder();
|
||||||
|
voice_recoder.reinitialiseVAD();
|
||||||
|
|
||||||
globalClient = new TSClient();
|
server_connections = new ServerConnectionManager($("#connection-handlers"));
|
||||||
|
control_bar.initialise(); /* before connection handler to allow property apply */
|
||||||
|
|
||||||
|
const initial_handler = server_connections.spawn_server_connection_handler();
|
||||||
|
initial_handler.acquire_recorder(voice_recoder, false);
|
||||||
|
control_bar.set_connection_handler(initial_handler);
|
||||||
/** Setup the XF forum identity **/
|
/** Setup the XF forum identity **/
|
||||||
profiles.identities.setup_forum();
|
profiles.identities.setup_forum();
|
||||||
|
|
||||||
chat = new ChatBox($("#chat"));
|
|
||||||
globalClient.setup();
|
|
||||||
|
|
||||||
if(settings.static(Settings.KEY_FLAG_CONNECT_DEFAULT, false) && settings.static(Settings.KEY_CONNECT_ADDRESS, "")) {
|
|
||||||
const profile_uuid = settings.static(Settings.KEY_CONNECT_PROFILE, (profiles.default_profile() || {id: 'default'}).id);
|
|
||||||
console.log("UUID: %s", profile_uuid);
|
|
||||||
const profile = profiles.find_profile(profile_uuid) || profiles.default_profile();
|
|
||||||
const address = settings.static(Settings.KEY_CONNECT_ADDRESS, "");
|
|
||||||
const username = settings.static(Settings.KEY_CONNECT_USERNAME, "Another TeaSpeak user");
|
|
||||||
|
|
||||||
const password = settings.static(Settings.KEY_CONNECT_PASSWORD, "");
|
|
||||||
const password_hashed = settings.static(Settings.KEY_FLAG_CONNECT_PASSWORD, false);
|
|
||||||
|
|
||||||
if(profile && profile.valid()) {
|
|
||||||
globalClient.startConnection(address, profile, username, password.length > 0 ? {
|
|
||||||
password: password,
|
|
||||||
hashed: password_hashed
|
|
||||||
} : undefined);
|
|
||||||
} else {
|
|
||||||
Modals.spawnConnectModal({
|
|
||||||
url: address,
|
|
||||||
enforce: true
|
|
||||||
}, {
|
|
||||||
profile: profile,
|
|
||||||
enforce: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let _resize_timeout: NodeJS.Timer;
|
let _resize_timeout: NodeJS.Timer;
|
||||||
$(window).on('resize', () => {
|
$(window).on('resize', event => {
|
||||||
|
if(event.target !== window)
|
||||||
|
return;
|
||||||
|
|
||||||
if(_resize_timeout)
|
if(_resize_timeout)
|
||||||
clearTimeout(_resize_timeout);
|
clearTimeout(_resize_timeout);
|
||||||
_resize_timeout = setTimeout(() => {
|
_resize_timeout = setTimeout(() => {
|
||||||
globalClient.channelTree.handle_resized();
|
for(const connection of server_connections.server_connection_handlers())
|
||||||
globalClient.selectInfo.handle_resize();
|
connection.invoke_resized_on_activate = true;
|
||||||
|
const active_connection = server_connections.active_connection_handler();
|
||||||
|
if(active_connection)
|
||||||
|
active_connection.resize_elements();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -288,22 +282,24 @@ function main() {
|
||||||
Modals.spawnAvatarList(globalClient);
|
Modals.spawnAvatarList(globalClient);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
*/
|
*/
|
||||||
(<any>window).test_upload = () => {
|
(<any>window).test_upload = (message?: string) => {
|
||||||
const data = "Hello World";
|
message = message || "Hello World";
|
||||||
globalClient.fileManager.upload_file({
|
|
||||||
size: data.length,
|
const connection = server_connections.active_connection_handler();
|
||||||
|
connection.fileManager.upload_file({
|
||||||
|
size: message.length,
|
||||||
overwrite: true,
|
overwrite: true,
|
||||||
channel: globalClient.getClient().currentChannel(),
|
channel: connection.getClient().currentChannel(),
|
||||||
name: '/HelloWorld.txt',
|
name: '/HelloWorld.txt',
|
||||||
path: ''
|
path: ''
|
||||||
}).then(key => {
|
}).then(key => {
|
||||||
console.log("Got key: %o", key);
|
console.log("Got key: %o", key);
|
||||||
const upload = new RequestFileUpload(key);
|
const upload = new RequestFileUpload(key);
|
||||||
|
|
||||||
const buffer = new Uint8Array(data.length);
|
const buffer = new Uint8Array(message.length);
|
||||||
{
|
{
|
||||||
for(let index = 0; index < data.length; index++)
|
for(let index = 0; index < message.length; index++)
|
||||||
buffer[index] = data.charCodeAt(index);
|
buffer[index] = message.charCodeAt(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
upload.put_data(buffer).catch(error => {
|
upload.put_data(buffer).catch(error => {
|
||||||
|
@ -311,13 +307,42 @@ function main() {
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
server_connections.set_active_connection_handler(server_connections.server_connection_handlers()[0]);
|
||||||
|
|
||||||
|
if(settings.static(Settings.KEY_FLAG_CONNECT_DEFAULT, false) && settings.static(Settings.KEY_CONNECT_ADDRESS, "")) {
|
||||||
|
const profile_uuid = settings.static(Settings.KEY_CONNECT_PROFILE, (profiles.default_profile() || {id: 'default'}).id);
|
||||||
|
console.log("UUID: %s", profile_uuid);
|
||||||
|
const profile = profiles.find_profile(profile_uuid) || profiles.default_profile();
|
||||||
|
const address = settings.static(Settings.KEY_CONNECT_ADDRESS, "");
|
||||||
|
const username = settings.static(Settings.KEY_CONNECT_USERNAME, "Another TeaSpeak user");
|
||||||
|
|
||||||
|
const password = settings.static(Settings.KEY_CONNECT_PASSWORD, "");
|
||||||
|
const password_hashed = settings.static(Settings.KEY_FLAG_CONNECT_PASSWORD, false);
|
||||||
|
|
||||||
|
if(profile && profile.valid()) {
|
||||||
|
const connection = server_connections.active_connection_handler() || server_connections.spawn_server_connection_handler();
|
||||||
|
connection.startConnection(address, profile, username, password.length > 0 ? {
|
||||||
|
password: password,
|
||||||
|
hashed: password_hashed
|
||||||
|
} : undefined);
|
||||||
|
} else {
|
||||||
|
Modals.spawnConnectModal({
|
||||||
|
url: address,
|
||||||
|
enforce: true
|
||||||
|
}, {
|
||||||
|
profile: profile,
|
||||||
|
enforce: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loader.register_task(loader.Stage.LOADED, {
|
const task_teaweb_starter: loader.Task = {
|
||||||
name: "async main invoke",
|
name: "voice app starter",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
try {
|
try {
|
||||||
await initialize();
|
await initialize_app();
|
||||||
main();
|
main();
|
||||||
if(!audio.player.initialized()) {
|
if(!audio.player.initialized()) {
|
||||||
log.info(LogCategory.VOICE, tr("Initialize audio controller later!"));
|
log.info(LogCategory.VOICE, tr("Initialize audio controller later!"));
|
||||||
|
@ -334,5 +359,77 @@ loader.register_task(loader.Stage.LOADED, {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
priority: 10
|
priority: 10
|
||||||
|
};
|
||||||
|
|
||||||
|
const task_certificate_callback: loader.Task = {
|
||||||
|
name: "certificate accept tester",
|
||||||
|
function: async () => {
|
||||||
|
const certificate_accept = settings.static_global(Settings.KEY_CERTIFICATE_CALLBACK, undefined);
|
||||||
|
if(certificate_accept) {
|
||||||
|
log.info(LogCategory.IPC, tr("Using this instance as certificate callback. ID: %s"), certificate_accept);
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
await bipc.get_handler().post_certificate_accpected(certificate_accept);
|
||||||
|
} catch(e) {} //FIXME remove!
|
||||||
|
log.info(LogCategory.IPC, tr("Other instance has acknowledged out work. Closing this window."));
|
||||||
|
|
||||||
|
const seconds_tag = $.spawn("a");
|
||||||
|
|
||||||
|
let seconds = 5;
|
||||||
|
let interval_id;
|
||||||
|
interval_id = setInterval(() => {
|
||||||
|
seconds--;
|
||||||
|
seconds_tag.text(seconds.toString());
|
||||||
|
|
||||||
|
if(seconds <= 0) {
|
||||||
|
clearTimeout(interval_id);
|
||||||
|
log.info(LogCategory.GENERAL, tr("Closing window"));
|
||||||
|
window.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
const message =
|
||||||
|
"You've successfully accepted the certificate.{:br:}" +
|
||||||
|
"This page will close in {0} seconds.";
|
||||||
|
createInfoModal(
|
||||||
|
tr("Certificate acccepted successfully"),
|
||||||
|
MessageHelper.formatMessage(tr(message), seconds_tag),
|
||||||
|
{
|
||||||
|
closeable: false,
|
||||||
|
footer: undefined
|
||||||
|
}
|
||||||
|
).open();
|
||||||
|
return;
|
||||||
|
} catch(error) {
|
||||||
|
log.warn(LogCategory.IPC, tr("Failed to successfully post certificate accept status: %o"), error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info(LogCategory.IPC, tr("We're not used to accept certificated. Booting app."));
|
||||||
|
}
|
||||||
|
|
||||||
|
loader.register_task(loader.Stage.LOADED, task_teaweb_starter);
|
||||||
|
},
|
||||||
|
priority: 10
|
||||||
|
};
|
||||||
|
|
||||||
|
loader.register_task(loader.Stage.LOADED, {
|
||||||
|
name: "app starter",
|
||||||
|
function: async () => {
|
||||||
|
try {
|
||||||
|
await initialize();
|
||||||
|
|
||||||
|
if(app.is_web()) {
|
||||||
|
loader.register_task(loader.Stage.LOADED, task_certificate_callback);
|
||||||
|
} else
|
||||||
|
loader.register_task(loader.Stage.LOADED, task_teaweb_starter);
|
||||||
|
} catch (ex) {
|
||||||
|
console.error(ex.stack);
|
||||||
|
if(ex instanceof ReferenceError || ex instanceof TypeError)
|
||||||
|
ex = ex.name + ": " + ex.message;
|
||||||
|
displayCriticalError("Failed to boot app function:<br>" + ex);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
priority: 10
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -60,13 +60,13 @@ class Group {
|
||||||
}
|
}
|
||||||
|
|
||||||
class GroupManager extends connection.AbstractCommandHandler {
|
class GroupManager extends connection.AbstractCommandHandler {
|
||||||
readonly handle: TSClient;
|
readonly handle: ConnectionHandler;
|
||||||
|
|
||||||
serverGroups: Group[] = [];
|
serverGroups: Group[] = [];
|
||||||
channelGroups: Group[] = [];
|
channelGroups: Group[] = [];
|
||||||
|
|
||||||
private requests_group_permissions: GroupPermissionRequest[] = [];
|
private requests_group_permissions: GroupPermissionRequest[] = [];
|
||||||
constructor(client: TSClient) {
|
constructor(client: ConnectionHandler) {
|
||||||
super(client.serverConnection);
|
super(client.serverConnection);
|
||||||
|
|
||||||
client.serverConnection.command_handler_boss().register_handler(this);
|
client.serverConnection.command_handler_boss().register_handler(this);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/// <reference path="../client.ts" />
|
/// <reference path="../ConnectionHandler.ts" />
|
||||||
/// <reference path="../connection/ConnectionBase.ts" />
|
/// <reference path="../connection/ConnectionBase.ts" />
|
||||||
|
|
||||||
enum PermissionType {
|
enum PermissionType {
|
||||||
|
@ -418,7 +418,7 @@ class TeaPermissionRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
class PermissionManager extends connection.AbstractCommandHandler {
|
class PermissionManager extends connection.AbstractCommandHandler {
|
||||||
readonly handle: TSClient;
|
readonly handle: ConnectionHandler;
|
||||||
|
|
||||||
permissionList: PermissionInfo[] = [];
|
permissionList: PermissionInfo[] = [];
|
||||||
permissionGroups: PermissionGroup[] = [];
|
permissionGroups: PermissionGroup[] = [];
|
||||||
|
@ -505,7 +505,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
||||||
return permissions;
|
return permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(client: TSClient) {
|
constructor(client: ConnectionHandler) {
|
||||||
super(client.serverConnection);
|
super(client.serverConnection);
|
||||||
|
|
||||||
//FIXME? Dont register the handler like this?
|
//FIXME? Dont register the handler like this?
|
||||||
|
|
|
@ -10,10 +10,11 @@ interface JSON {
|
||||||
map_field_to<T>(object: T, value: any, field: string) : T;
|
map_field_to<T>(object: T, value: any, field: string) : T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JQueryScrollType = "height" | "width";
|
||||||
interface JQuery<TElement = HTMLElement> {
|
interface JQuery<TElement = HTMLElement> {
|
||||||
render(values?: any) : string;
|
render(values?: any) : string;
|
||||||
renderTag(values?: any) : JQuery<TElement>;
|
renderTag(values?: any) : JQuery<TElement>;
|
||||||
hasScrollBar() : boolean;
|
hasScrollBar(direction?: JQueryScrollType) : boolean;
|
||||||
|
|
||||||
|
|
||||||
visible_height() : number;
|
visible_height() : number;
|
||||||
|
@ -137,10 +138,21 @@ if(typeof ($) !== "undefined") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!$.fn.hasScrollBar)
|
if(!$.fn.hasScrollBar)
|
||||||
$.fn.hasScrollBar = function() {
|
$.fn.hasScrollBar = function(direction?: "height" | "width") {
|
||||||
if(this.length <= 0) return false;
|
if(this.length <= 0)
|
||||||
return this.get(0).scrollHeight > this.height();
|
return false;
|
||||||
}
|
|
||||||
|
const scroll_height = this.get(0).scrollHeight > this.height();
|
||||||
|
const scroll_width = this.get(0).scrollWidth > this.width();
|
||||||
|
|
||||||
|
if(typeof(direction) === "string") {
|
||||||
|
if(direction === "height")
|
||||||
|
return scroll_height;
|
||||||
|
if(direction === "width")
|
||||||
|
return scroll_width;
|
||||||
|
}
|
||||||
|
return scroll_width || scroll_height;
|
||||||
|
};
|
||||||
|
|
||||||
if(!$.fn.visible_height)
|
if(!$.fn.visible_height)
|
||||||
$.fn.visible_height = function (this: JQuery<HTMLElement>) {
|
$.fn.visible_height = function (this: JQuery<HTMLElement>) {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/// <reference path="client.ts" />
|
|
||||||
|
|
||||||
if(typeof(customElements) !== "undefined") {
|
if(typeof(customElements) !== "undefined") {
|
||||||
try {
|
try {
|
||||||
class X_Properties extends HTMLElement {}
|
class X_Properties extends HTMLElement {}
|
||||||
|
@ -19,15 +17,11 @@ interface SettingsKey<T> {
|
||||||
fallback_keys?: string | string[];
|
fallback_keys?: string | string[];
|
||||||
fallback_imports?: {[key: string]:(value: string) => T};
|
fallback_imports?: {[key: string]:(value: string) => T};
|
||||||
description?: string;
|
description?: string;
|
||||||
|
default_value?: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
class StaticSettings {
|
class SettingsBase {
|
||||||
private static _instance: StaticSettings;
|
protected static readonly UPDATE_DIRECT: boolean = true;
|
||||||
static get instance() : StaticSettings {
|
|
||||||
if(!this._instance)
|
|
||||||
this._instance = new StaticSettings(true);
|
|
||||||
return this._instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static transformStO?<T>(input?: string, _default?: T, default_type?: string) : T {
|
protected static transformStO?<T>(input?: string, _default?: T, default_type?: string) : T {
|
||||||
default_type = default_type || typeof _default;
|
default_type = default_type || typeof _default;
|
||||||
|
@ -66,7 +60,7 @@ class StaticSettings {
|
||||||
if(typeof(value) !== 'string')
|
if(typeof(value) !== 'string')
|
||||||
return _default;
|
return _default;
|
||||||
|
|
||||||
return StaticSettings.transformStO(value as string, _default, default_type);
|
return SettingsBase.transformStO(value as string, _default, default_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static keyify<T>(key: string | SettingsKey<T>) : SettingsKey<T> {
|
protected static keyify<T>(key: string | SettingsKey<T>) : SettingsKey<T> {
|
||||||
|
@ -76,11 +70,21 @@ class StaticSettings {
|
||||||
return key;
|
return key;
|
||||||
throw "key is not a key";
|
throw "key is not a key";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StaticSettings extends SettingsBase {
|
||||||
|
private static _instance: StaticSettings;
|
||||||
|
static get instance() : StaticSettings {
|
||||||
|
if(!this._instance)
|
||||||
|
this._instance = new StaticSettings(true);
|
||||||
|
return this._instance;
|
||||||
|
}
|
||||||
|
|
||||||
protected _handle: StaticSettings;
|
protected _handle: StaticSettings;
|
||||||
protected _staticPropsTag: JQuery;
|
protected _staticPropsTag: JQuery;
|
||||||
|
|
||||||
protected constructor(_reserved = undefined) {
|
protected constructor(_reserved = undefined) {
|
||||||
|
super();
|
||||||
if(_reserved && !StaticSettings._instance) {
|
if(_reserved && !StaticSettings._instance) {
|
||||||
this._staticPropsTag = $("#properties");
|
this._staticPropsTag = $("#properties");
|
||||||
this.initializeStatic();
|
this.initializeStatic();
|
||||||
|
@ -90,7 +94,14 @@ class StaticSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeStatic() {
|
private initializeStatic() {
|
||||||
location.search.substr(1).split("&").forEach(part => {
|
let search;
|
||||||
|
if(window.opener && window.opener !== window) {
|
||||||
|
search = new URL(window.location.href).search;
|
||||||
|
} else {
|
||||||
|
search = location.search;
|
||||||
|
}
|
||||||
|
|
||||||
|
search.substr(1).split("&").forEach(part => {
|
||||||
let item = part.split("=");
|
let item = part.split("=");
|
||||||
$("<x-property></x-property>")
|
$("<x-property></x-property>")
|
||||||
.attr("key", item[0])
|
.attr("key", item[0])
|
||||||
|
@ -136,6 +147,9 @@ class Settings extends StaticSettings {
|
||||||
key: 'disableVoice',
|
key: 'disableVoice',
|
||||||
description: 'Disables the voice bridge. If disabled, the audio and codec workers aren\'t required anymore'
|
description: 'Disables the voice bridge. If disabled, the audio and codec workers aren\'t required anymore'
|
||||||
};
|
};
|
||||||
|
static readonly KEY_DISABLE_MULTI_SESSION: SettingsKey<boolean> = {
|
||||||
|
key: 'disableMultiSession',
|
||||||
|
};
|
||||||
|
|
||||||
static readonly KEY_LOAD_DUMMY_ERROR: SettingsKey<boolean> = {
|
static readonly KEY_LOAD_DUMMY_ERROR: SettingsKey<boolean> = {
|
||||||
key: 'dummy_load_error',
|
key: 'dummy_load_error',
|
||||||
|
@ -176,6 +190,10 @@ class Settings extends StaticSettings {
|
||||||
key: 'connect_password_hashed'
|
key: 'connect_password_hashed'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static readonly KEY_CERTIFICATE_CALLBACK: SettingsKey<string> = {
|
||||||
|
key: 'certificate_callback'
|
||||||
|
};
|
||||||
|
|
||||||
static readonly FN_SERVER_CHANNEL_SUBSCRIBE_MODE: (channel: ChannelEntry) => SettingsKey<ChannelSubscribeMode> = channel => {
|
static readonly FN_SERVER_CHANNEL_SUBSCRIBE_MODE: (channel: ChannelEntry) => SettingsKey<ChannelSubscribeMode> = channel => {
|
||||||
return {
|
return {
|
||||||
key: 'channel_subscribe_mode_' + channel.getChannelId()
|
key: 'channel_subscribe_mode_' + channel.getChannelId()
|
||||||
|
@ -197,10 +215,7 @@ class Settings extends StaticSettings {
|
||||||
return result;
|
return result;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
private static readonly UPDATE_DIRECT: boolean = true;
|
|
||||||
private cacheGlobal = {};
|
private cacheGlobal = {};
|
||||||
private cacheServer = {};
|
|
||||||
private currentServer: ServerEntry;
|
|
||||||
private saveWorker: NodeJS.Timer;
|
private saveWorker: NodeJS.Timer;
|
||||||
private updated: boolean = false;
|
private updated: boolean = false;
|
||||||
|
|
||||||
|
@ -225,10 +240,6 @@ class Settings extends StaticSettings {
|
||||||
return StaticSettings.resolveKey(Settings.keyify(key), _default, key => this.cacheGlobal[key]);
|
return StaticSettings.resolveKey(Settings.keyify(key), _default, key => this.cacheGlobal[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
server?<T>(key: string | SettingsKey<T>, _default?: T) : T {
|
|
||||||
return StaticSettings.resolveKey(Settings.keyify(key), _default, key => this.cacheServer[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
changeGlobal<T>(key: string | SettingsKey<T>, value?: T){
|
changeGlobal<T>(key: string | SettingsKey<T>, value?: T){
|
||||||
key = Settings.keyify(key);
|
key = Settings.keyify(key);
|
||||||
|
|
||||||
|
@ -242,12 +253,37 @@ class Settings extends StaticSettings {
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
this.updated = false;
|
||||||
|
let global = JSON.stringify(this.cacheGlobal);
|
||||||
|
localStorage.setItem("settings.global", global);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerSettings extends SettingsBase {
|
||||||
|
private cacheServer = {};
|
||||||
|
private currentServer: ServerEntry;
|
||||||
|
private _server_save_worker: NodeJS.Timer;
|
||||||
|
private _server_settings_updated: boolean = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._server_save_worker = setInterval(() => {
|
||||||
|
if(this._server_settings_updated)
|
||||||
|
this.save();
|
||||||
|
}, 5 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
server?<T>(key: string | SettingsKey<T>, _default?: T) : T {
|
||||||
|
return StaticSettings.resolveKey(Settings.keyify(key), _default, key => this.cacheServer[key]);
|
||||||
|
}
|
||||||
|
|
||||||
changeServer<T>(key: string | SettingsKey<T>, value?: T) {
|
changeServer<T>(key: string | SettingsKey<T>, value?: T) {
|
||||||
key = Settings.keyify(key);
|
key = Settings.keyify(key);
|
||||||
|
|
||||||
if(this.cacheServer[key.key] == value) return;
|
if(this.cacheServer[key.key] == value) return;
|
||||||
|
|
||||||
this.updated = true;
|
this._server_settings_updated = true;
|
||||||
this.cacheServer[key.key] = StaticSettings.transformOtS(value);
|
this.cacheServer[key.key] = StaticSettings.transformOtS(value);
|
||||||
|
|
||||||
if(Settings.UPDATE_DIRECT)
|
if(Settings.UPDATE_DIRECT)
|
||||||
|
@ -271,15 +307,12 @@ class Settings extends StaticSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
this.updated = false;
|
this._server_settings_updated = false;
|
||||||
|
|
||||||
if(this.currentServer) {
|
if(this.currentServer) {
|
||||||
let serverId = this.currentServer.properties.virtualserver_unique_identifier;
|
let serverId = this.currentServer.properties.virtualserver_unique_identifier;
|
||||||
let server = JSON.stringify(this.cacheServer);
|
let server = JSON.stringify(this.cacheServer);
|
||||||
localStorage.setItem("settings.server_" + serverId, server);
|
localStorage.setItem("settings.server_" + serverId, server);
|
||||||
}
|
}
|
||||||
|
|
||||||
let global = JSON.stringify(this.cacheGlobal);
|
|
||||||
localStorage.setItem("settings.global", global);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -55,7 +55,7 @@ enum Sound {
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace sound {
|
namespace sound {
|
||||||
interface SpeechFile {
|
export interface SoundHandle {
|
||||||
key: string;
|
key: string;
|
||||||
filename: string;
|
filename: string;
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ namespace sound {
|
||||||
}
|
}
|
||||||
|
|
||||||
let warned = false;
|
let warned = false;
|
||||||
let speech_mapping: {[key: string]:SpeechFile} = {};
|
let speech_mapping: {[key: string]:SoundHandle} = {};
|
||||||
|
|
||||||
let volume_require_save = false;
|
let volume_require_save = false;
|
||||||
let speech_volume: {[key: string]:number} = {};
|
let speech_volume: {[key: string]:number} = {};
|
||||||
|
@ -80,7 +80,7 @@ namespace sound {
|
||||||
let master_mixed: GainNode;
|
let master_mixed: GainNode;
|
||||||
|
|
||||||
function register_sound(key: string, file: string) {
|
function register_sound(key: string, file: string) {
|
||||||
speech_mapping[key] = {key: key, filename: file} as SpeechFile;
|
speech_mapping[key] = {key: key, filename: file} as SoundHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get_sound_volume(sound: Sound, default_volume?: number) : number {
|
export function get_sound_volume(sound: Sound, default_volume?: number) : number {
|
||||||
|
@ -192,6 +192,7 @@ namespace sound {
|
||||||
register_sound("message.received", "effects/message_received.wav");
|
register_sound("message.received", "effects/message_received.wav");
|
||||||
register_sound("message.send", "effects/message_send.wav");
|
register_sound("message.send", "effects/message_send.wav");
|
||||||
|
|
||||||
|
manager = new SoundManager(undefined);
|
||||||
audio.player.on_ready(reinitialisize_audio);
|
audio.player.on_ready(reinitialisize_audio);
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
@ -211,7 +212,7 @@ namespace sound {
|
||||||
async: true,
|
async: true,
|
||||||
type: 'GET'
|
type: 'GET'
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PlaybackOptions {
|
export interface PlaybackOptions {
|
||||||
|
@ -221,84 +222,44 @@ namespace sound {
|
||||||
default_volume?: number;
|
default_volume?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function play(sound: Sound, options?: PlaybackOptions) {
|
export async function resolve_sound(sound: Sound) : Promise<SoundHandle> {
|
||||||
if(!options) {
|
const file: SoundHandle = speech_mapping[sound];
|
||||||
options = {};
|
if(!file)
|
||||||
}
|
throw tr("Missing sound handle");
|
||||||
|
|
||||||
|
|
||||||
const file: SpeechFile = speech_mapping[sound];
|
|
||||||
if(!file) {
|
|
||||||
console.warn(tr("Missing sound %o"), sound);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(file.not_supported) {
|
if(file.not_supported) {
|
||||||
if(!file.not_supported_timeout || Date.now() < file.not_supported_timeout) //Test if the not supported isn't may timeouted
|
if(!file.not_supported_timeout || Date.now() < file.not_supported_timeout) //Test if the not supported flag has been expired
|
||||||
return;
|
return file;
|
||||||
|
|
||||||
file.not_supported = false;
|
file.not_supported = false;
|
||||||
file.not_supported_timeout = undefined;
|
file.not_supported_timeout = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = "audio/" + file.filename;
|
|
||||||
const context = audio.player.context();
|
const context = audio.player.context();
|
||||||
if(!context) {
|
if(!context)
|
||||||
console.warn(tr("Tried to replay a sound without an audio context (Sound: %o). Dropping playback"), sound);
|
return file;
|
||||||
return;
|
|
||||||
}
|
|
||||||
const volume = get_sound_volume(sound, options.default_volume);
|
|
||||||
|
|
||||||
console.log(tr("Replaying sound %s (Sound volume: %o | Master volume %o)"), sound, volume, master_volume);
|
|
||||||
if(volume == 0) return;
|
|
||||||
if(master_volume == 0) return;
|
|
||||||
if(!options.ignore_muted && !ignore_muted && globalClient.controlBar.muteOutput) return;
|
|
||||||
|
|
||||||
|
const path = "audio/" + file.filename;
|
||||||
if(context.decodeAudioData) {
|
if(context.decodeAudioData) {
|
||||||
if(file.cached) {
|
if(!file.cached) {
|
||||||
if(!options.ignore_overlap && file.replaying && !overlap_sounds) {
|
|
||||||
console.log(tr("Dropping requested playback for sound %s because it would overlap."), sound);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(tr("Using cached buffer: %o"), file.cached);
|
|
||||||
const player = context.createBufferSource();
|
|
||||||
player.buffer = file.cached;
|
|
||||||
player.start(0);
|
|
||||||
|
|
||||||
file.replaying = true;
|
|
||||||
player.onended = event => {
|
|
||||||
file.replaying = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if(volume != 1 && context.createGain) {
|
|
||||||
const gain = context.createGain();
|
|
||||||
if(gain.gain.setValueAtTime)
|
|
||||||
gain.gain.setValueAtTime(volume, 0);
|
|
||||||
else
|
|
||||||
gain.gain.value = volume;
|
|
||||||
|
|
||||||
player.connect(gain);
|
|
||||||
gain.connect(master_mixed);
|
|
||||||
} else {
|
|
||||||
player.connect(master_mixed);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const decode_data = buffer => {
|
const decode_data = buffer => {
|
||||||
console.log(buffer);
|
console.log(buffer);
|
||||||
try {
|
try {
|
||||||
console.log(tr("Decoding data"));
|
console.log(tr("Decoding data"));
|
||||||
context.decodeAudioData(buffer, result => {
|
context.decodeAudioData(buffer, result => {
|
||||||
log.info(LogCategory.VOICE, tr("Got decoded data"));
|
|
||||||
file.cached = result;
|
file.cached = result;
|
||||||
play(sound, options);
|
|
||||||
}, error => {
|
}, error => {
|
||||||
console.error(tr("Failed to decode audio data for %o"), sound);
|
console.error(tr("Failed to decode audio data for %o"), sound);
|
||||||
console.error(error);
|
console.error(error);
|
||||||
file.not_supported = true;
|
file.not_supported = true;
|
||||||
file.not_supported_timeout = Date.now() + 1000 * 60 * 60; //Try in 2min again!
|
file.not_supported_timeout = Date.now() + 1000 * 60 * 2; //Try in 2min again!
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
file.not_supported = true;
|
file.not_supported = true;
|
||||||
file.not_supported_timeout = Date.now() + 1000 * 60 * 60; //Try in 2min again!
|
file.not_supported_timeout = Date.now() + 1000 * 60 * 2; //Try in 2min again!
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -306,31 +267,33 @@ namespace sound {
|
||||||
xhr.open('GET', path, true);
|
xhr.open('GET', path, true);
|
||||||
xhr.responseType = 'arraybuffer';
|
xhr.responseType = 'arraybuffer';
|
||||||
|
|
||||||
xhr.onload = function(e) {
|
try {
|
||||||
if (this.status == 200) {
|
const result = new Promise((resolve, reject) => {
|
||||||
decode_data(this.response);
|
xhr.onload = resolve;
|
||||||
} else {
|
xhr.onerror = reject;
|
||||||
console.error(tr("Failed to load audio file. (Response code %o)"), this.status);
|
});
|
||||||
file.not_supported = true;
|
|
||||||
file.not_supported_timeout = Date.now() + 1000 * 60 * 60; //Try in 2min again!
|
xhr.send();
|
||||||
|
await result;
|
||||||
|
|
||||||
|
if (xhr.status != 200)
|
||||||
|
throw "invalid response code (" + xhr.status + ")";
|
||||||
|
|
||||||
|
console.log(tr("Decoding data"));
|
||||||
|
try {
|
||||||
|
file.cached = await context.decodeAudioData(xhr.response);
|
||||||
|
} catch(error) {
|
||||||
|
console.error(error);
|
||||||
|
throw "failed to decode audio data";
|
||||||
}
|
}
|
||||||
};
|
} catch(error) {
|
||||||
|
console.error(tr("Failed to load audio file %s. Error: %o"), sound, error);
|
||||||
xhr.onerror = error => {
|
|
||||||
console.error(tr("Failed to load audio file "), sound);
|
|
||||||
console.error(error);
|
|
||||||
file.not_supported = true;
|
file.not_supported = true;
|
||||||
file.not_supported_timeout = Date.now() + 1000 * 60 * 60; //Try in 2min again!
|
file.not_supported_timeout = Date.now() + 1000 * 60 * 2; //Try in 2min again!
|
||||||
};
|
}
|
||||||
|
|
||||||
xhr.send();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(tr("Replaying %s"), path);
|
if(!file.node) {
|
||||||
if(file.node) {
|
|
||||||
file.node.currentTime = 0;
|
|
||||||
file.node.play();
|
|
||||||
} else {
|
|
||||||
if(!warned) {
|
if(!warned) {
|
||||||
warned = true;
|
warned = true;
|
||||||
console.warn(tr("Your browser does not support decodeAudioData! Using a node to playback! This bypasses the audio output and volume regulation!"));
|
console.warn(tr("Your browser does not support decodeAudioData! Using a node to playback! This bypasses the audio output and volume regulation!"));
|
||||||
|
@ -340,8 +303,81 @@ namespace sound {
|
||||||
node.appendTo(container);
|
node.appendTo(container);
|
||||||
|
|
||||||
file.node = node[0];
|
file.node = node[0];
|
||||||
file.node.play();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
export let manager: SoundManager;
|
||||||
|
|
||||||
|
export class SoundManager {
|
||||||
|
private _handle: ConnectionHandler;
|
||||||
|
private _playing_sounds: {[key: string]:number} = {};
|
||||||
|
|
||||||
|
constructor(handle: ConnectionHandler) {
|
||||||
|
this._handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
play(_sound: Sound, options?: PlaybackOptions) {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
const volume = get_sound_volume(_sound, options.default_volume);
|
||||||
|
console.log(tr("Replaying sound %s (Sound volume: %o | Master volume %o)"), _sound, volume, master_volume);
|
||||||
|
|
||||||
|
if(volume == 0 || master_volume == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(this._handle && !options.ignore_muted && !sound.ignore_output_muted() && this._handle.client_status.output_muted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const context = audio.player.context();
|
||||||
|
if(!context) {
|
||||||
|
console.warn(tr("Tried to replay a sound without an audio context (Sound: %o). Dropping playback"), _sound);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sound.resolve_sound(_sound).then(handle => {
|
||||||
|
if(!handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!options.ignore_overlap && (this._playing_sounds[_sound] > 0) && !sound.overlap_activated()) {
|
||||||
|
console.log(tr("Dropping requested playback for sound %s because it would overlap."), _sound);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(handle.cached) {
|
||||||
|
this._playing_sounds[_sound] = Date.now();
|
||||||
|
|
||||||
|
const player = context.createBufferSource();
|
||||||
|
player.buffer = handle.cached;
|
||||||
|
player.start(0);
|
||||||
|
|
||||||
|
handle.replaying = true;
|
||||||
|
player.onended = event => {
|
||||||
|
delete this._playing_sounds[_sound];
|
||||||
|
};
|
||||||
|
|
||||||
|
if(volume != 1 && context.createGain) {
|
||||||
|
const gain = context.createGain();
|
||||||
|
if(gain.gain.setValueAtTime)
|
||||||
|
gain.gain.setValueAtTime(volume, 0);
|
||||||
|
else
|
||||||
|
gain.gain.value = volume;
|
||||||
|
|
||||||
|
player.connect(gain);
|
||||||
|
gain.connect(master_mixed);
|
||||||
|
} else {
|
||||||
|
player.connect(master_mixed);
|
||||||
|
}
|
||||||
|
} else if(handle.node) {
|
||||||
|
handle.node.currentTime = 0;
|
||||||
|
handle.node.play();
|
||||||
|
} else {
|
||||||
|
console.warn(tr("Failed to replay sound because of missing handles."), sound);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -457,13 +457,13 @@ class ChannelEntry {
|
||||||
name: tr("Show channel info"),
|
name: tr("Show channel info"),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
trigger_close = false;
|
trigger_close = false;
|
||||||
this.channelTree.client.selectInfo.open_popover()
|
this.channelTree.client.select_info.open_popover()
|
||||||
},
|
},
|
||||||
icon: "client-about",
|
icon: "client-about",
|
||||||
visible: this.channelTree.client.selectInfo.is_popover()
|
visible: this.channelTree.client.select_info.is_popover()
|
||||||
}, {
|
}, {
|
||||||
type: MenuEntryType.HR,
|
type: MenuEntryType.HR,
|
||||||
visible: this.channelTree.client.selectInfo.is_popover(),
|
visible: this.channelTree.client.select_info.is_popover(),
|
||||||
name: ''
|
name: ''
|
||||||
}, {
|
}, {
|
||||||
type: MenuEntryType.ENTRY,
|
type: MenuEntryType.ENTRY,
|
||||||
|
@ -500,7 +500,7 @@ class ChannelEntry {
|
||||||
name: tr("Edit channel"),
|
name: tr("Edit channel"),
|
||||||
invalidPermission: !channelModify,
|
invalidPermission: !channelModify,
|
||||||
callback: () => {
|
callback: () => {
|
||||||
Modals.createChannelModal(this, undefined, this.channelTree.client.permissions, (changes?, permissions?) => {
|
Modals.createChannelModal(this.channelTree.client, this, undefined, this.channelTree.client.permissions, (changes?, permissions?) => {
|
||||||
if(changes) {
|
if(changes) {
|
||||||
changes["cid"] = this.channelId;
|
changes["cid"] = this.channelId;
|
||||||
this.channelTree.client.serverConnection.send_command("channeledit", changes);
|
this.channelTree.client.serverConnection.send_command("channeledit", changes);
|
||||||
|
@ -522,7 +522,7 @@ class ChannelEntry {
|
||||||
this.channelTree.client.serverConnection.send_command("channeladdperm", perms, {
|
this.channelTree.client.serverConnection.send_command("channeladdperm", perms, {
|
||||||
flagset: ["continueonerror"]
|
flagset: ["continueonerror"]
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
sound.play(Sound.CHANNEL_EDITED_SELF);
|
this.channelTree.client.sound.play(Sound.CHANNEL_EDITED_SELF);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -535,7 +535,7 @@ class ChannelEntry {
|
||||||
invalidPermission: !flagDelete,
|
invalidPermission: !flagDelete,
|
||||||
callback: () => {
|
callback: () => {
|
||||||
this.channelTree.client.serverConnection.send_command("channeldelete", {cid: this.channelId}).then(() => {
|
this.channelTree.client.serverConnection.send_command("channeldelete", {cid: this.channelId}).then(() => {
|
||||||
sound.play(Sound.CHANNEL_DELETED);
|
this.channelTree.client.sound.play(Sound.CHANNEL_DELETED);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -694,8 +694,8 @@ class ChannelEntry {
|
||||||
} else if(key == "channel_codec") {
|
} else if(key == "channel_codec") {
|
||||||
(this.properties.channel_codec == 5 || this.properties.channel_codec == 3 ? $.fn.show : $.fn.hide).apply(this.channelTag().find(".icons .icon_music"));
|
(this.properties.channel_codec == 5 || this.properties.channel_codec == 3 ? $.fn.show : $.fn.hide).apply(this.channelTag().find(".icons .icon_music"));
|
||||||
this.channelTag().find(".icons .icon_no_sound").toggle(!(
|
this.channelTag().find(".icons .icon_no_sound").toggle(!(
|
||||||
this.channelTree.client.voiceConnection &&
|
this.channelTree.client.serverConnection.support_voice() &&
|
||||||
this.channelTree.client.voiceConnection.codecSupported(this.properties.channel_codec)
|
this.channelTree.client.serverConnection.voice_connection().decoding_supported(this.properties.channel_codec)
|
||||||
));
|
));
|
||||||
} else if(key == "channel_flag_default") {
|
} else if(key == "channel_flag_default") {
|
||||||
(this.properties.channel_flag_default ? $.fn.show : $.fn.hide).apply(this.channelTag().find(".icons .icon_default"));
|
(this.properties.channel_flag_default ? $.fn.show : $.fn.hide).apply(this.channelTag().find(".icons .icon_default"));
|
||||||
|
@ -771,7 +771,7 @@ class ChannelEntry {
|
||||||
}).open();
|
}).open();
|
||||||
} else if(this.channelTree.client.getClient().currentChannel() != this)
|
} else if(this.channelTree.client.getClient().currentChannel() != this)
|
||||||
this.channelTree.client.getServerConnection().command_helper.joinChannel(this, this._cachedPassword).then(() => {
|
this.channelTree.client.getServerConnection().command_helper.joinChannel(this, this._cachedPassword).then(() => {
|
||||||
sound.play(Sound.CHANNEL_JOINED);
|
this.channelTree.client.sound.play(Sound.CHANNEL_JOINED);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if(error instanceof CommandResult) {
|
if(error instanceof CommandResult) {
|
||||||
if(error.id == 781) { //Invalid password
|
if(error.id == 781) { //Invalid password
|
||||||
|
@ -803,7 +803,7 @@ class ChannelEntry {
|
||||||
|
|
||||||
if(inherited_subscription_mode) {
|
if(inherited_subscription_mode) {
|
||||||
this.subscribe_mode = ChannelSubscribeMode.INHERITED;
|
this.subscribe_mode = ChannelSubscribeMode.INHERITED;
|
||||||
unsubscribe = this.flag_subscribed && !this.channelTree.client.controlBar.channel_subscribe_all;
|
unsubscribe = this.flag_subscribed && !this.channelTree.client.client_status.channel_subscribe_all;
|
||||||
} else {
|
} else {
|
||||||
this.subscribe_mode = ChannelSubscribeMode.UNSUBSCRIBED;
|
this.subscribe_mode = ChannelSubscribeMode.UNSUBSCRIBED;
|
||||||
unsubscribe = this.flag_subscribed;
|
unsubscribe = this.flag_subscribed;
|
||||||
|
@ -831,7 +831,7 @@ class ChannelEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
get subscribe_mode() : ChannelSubscribeMode {
|
get subscribe_mode() : ChannelSubscribeMode {
|
||||||
return typeof(this._subscribe_mode) !== 'undefined' ? this._subscribe_mode : (this._subscribe_mode = settings.server(Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE(this), ChannelSubscribeMode.INHERITED));
|
return typeof(this._subscribe_mode) !== 'undefined' ? this._subscribe_mode : (this._subscribe_mode = this.channelTree.client.settings.server(Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE(this), ChannelSubscribeMode.INHERITED));
|
||||||
}
|
}
|
||||||
|
|
||||||
set subscribe_mode(mode: ChannelSubscribeMode) {
|
set subscribe_mode(mode: ChannelSubscribeMode) {
|
||||||
|
@ -839,21 +839,6 @@ class ChannelEntry {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._subscribe_mode = mode;
|
this._subscribe_mode = mode;
|
||||||
settings.changeServer(Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE(this), mode);
|
this.channelTree.client.settings.changeServer(Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE(this), mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Global functions
|
|
||||||
function chat_channel_contextmenu(_element: any, event: any) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
let element = $(_element);
|
|
||||||
let chid : number = Number.parseInt(element.attr("channelId"));
|
|
||||||
let channel = globalClient.channelTree.findChannel(chid);
|
|
||||||
if(!channel) {
|
|
||||||
//TODO
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.showContextMenu(event.pageX, event.pageY);
|
|
||||||
}
|
|
|
@ -54,28 +54,36 @@ class ClientEntry {
|
||||||
protected _properties: ClientProperties;
|
protected _properties: ClientProperties;
|
||||||
protected lastVariableUpdate: number = 0;
|
protected lastVariableUpdate: number = 0;
|
||||||
protected _speaking: boolean = false;
|
protected _speaking: boolean = false;
|
||||||
private _listener_initialized: boolean;
|
protected _listener_initialized: boolean;
|
||||||
|
protected _audio_handle: connection.voice.VoiceClient;
|
||||||
|
|
||||||
channelTree: ChannelTree;
|
channelTree: ChannelTree;
|
||||||
audioController: AudioController;
|
|
||||||
|
|
||||||
constructor(clientId, clientName, properties: ClientProperties = new ClientProperties()) {
|
constructor(clientId: number, clientName, properties: ClientProperties = new ClientProperties()) {
|
||||||
this._properties = properties;
|
this._properties = properties;
|
||||||
this._properties.client_nickname = clientName;
|
this._properties.client_nickname = clientName;
|
||||||
this._clientId = clientId;
|
this._clientId = clientId;
|
||||||
this.channelTree = null;
|
this.channelTree = null;
|
||||||
this._channel = null;
|
this._channel = null;
|
||||||
this.audioController = new AudioController();
|
}
|
||||||
|
|
||||||
const _this = this;
|
set_audio_handle(handle: connection.voice.VoiceClient) {
|
||||||
this.audioController.onSpeaking = function () {
|
if(this._audio_handle === handle)
|
||||||
_this.speaking = true;
|
return;
|
||||||
};
|
|
||||||
|
|
||||||
this.audioController.onSilence = function () {
|
//TODO may ensure that the id is the same?
|
||||||
_this.speaking = false;
|
this._audio_handle = handle;
|
||||||
};
|
if(!handle) {
|
||||||
this.audioController.initialize();
|
this.speaking = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.callback_playback = () => this.speaking = true;
|
||||||
|
handle.callback_stopped = () => this.speaking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_audio_handle() : connection.voice.VoiceClient {
|
||||||
|
return this._audio_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
get properties() : ClientProperties {
|
get properties() : ClientProperties {
|
||||||
|
@ -87,10 +95,6 @@ class ClientEntry {
|
||||||
clientUid(){ return this.properties.client_unique_identifier; }
|
clientUid(){ return this.properties.client_unique_identifier; }
|
||||||
clientId(){ return this._clientId; }
|
clientId(){ return this._clientId; }
|
||||||
|
|
||||||
getAudioController() : AudioController {
|
|
||||||
return this.audioController;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected initializeListener(){
|
protected initializeListener(){
|
||||||
if(this._listener_initialized) return;
|
if(this._listener_initialized) return;
|
||||||
this._listener_initialized = true;
|
this._listener_initialized = true;
|
||||||
|
@ -100,8 +104,9 @@ class ClientEntry {
|
||||||
this.channelTree.onSelect(this);
|
this.channelTree.onSelect(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.tag.click(event => {
|
|
||||||
console.log("Clicked!");
|
this.tag.on('click', event => {
|
||||||
|
console.log("I've been clicked!");
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!(this instanceof LocalClientEntry) && !(this instanceof MusicClientEntry))
|
if(!(this instanceof LocalClientEntry) && !(this instanceof MusicClientEntry))
|
||||||
|
@ -126,7 +131,7 @@ class ClientEntry {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tag.mousedown(event => {
|
this.tag.on('mousedown', event => {
|
||||||
if(event.which != 1) return; //Only the left button
|
if(event.which != 1) return; //Only the left button
|
||||||
|
|
||||||
let clients = this.channelTree.currently_selected as (ClientEntry | ClientEntry[]);
|
let clients = this.channelTree.currently_selected as (ClientEntry | ClientEntry[]);
|
||||||
|
@ -151,9 +156,9 @@ class ClientEntry {
|
||||||
cid: target.getChannelId()
|
cid: target.getChannelId()
|
||||||
}).then(event => {
|
}).then(event => {
|
||||||
if(client.clientId() == this.channelTree.client.clientId)
|
if(client.clientId() == this.channelTree.client.clientId)
|
||||||
sound.play(Sound.CHANNEL_JOINED);
|
this.channelTree.client.sound.play(Sound.CHANNEL_JOINED);
|
||||||
else if(target !== source && target != self.currentChannel())
|
else if(target !== source && target != self.currentChannel())
|
||||||
sound.play(Sound.USER_MOVED);
|
this.channelTree.client.sound.play(Sound.USER_MOVED);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,21 +278,21 @@ class ClientEntry {
|
||||||
name: tr("Show client info"),
|
name: tr("Show client info"),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
trigger_close = false;
|
trigger_close = false;
|
||||||
this.channelTree.client.selectInfo.open_popover()
|
this.channelTree.client.select_info.open_popover()
|
||||||
},
|
},
|
||||||
icon: "client-about",
|
icon: "client-about",
|
||||||
visible: this.channelTree.client.selectInfo.is_popover()
|
visible: this.channelTree.client.select_info.is_popover()
|
||||||
}, {
|
}, {
|
||||||
type: MenuEntryType.HR,
|
type: MenuEntryType.HR,
|
||||||
visible: this.channelTree.client.selectInfo.is_popover(),
|
visible: this.channelTree.client.select_info.is_popover(),
|
||||||
name: ''
|
name: ''
|
||||||
}, {
|
}, {
|
||||||
type: MenuEntryType.ENTRY,
|
type: MenuEntryType.ENTRY,
|
||||||
icon: "client-change_nickname",
|
icon: "client-change_nickname",
|
||||||
name: tr("<b>Open text chat</b>"),
|
name: tr("<b>Open text chat</b>"),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
chat.activeChat = this.chat(true);
|
this.channelTree.client.chat.activeChat = this.chat(true);
|
||||||
chat.focus();
|
this.channelTree.client.chat.focus();
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
type: MenuEntryType.ENTRY,
|
type: MenuEntryType.ENTRY,
|
||||||
|
@ -386,7 +391,7 @@ class ClientEntry {
|
||||||
}, {
|
}, {
|
||||||
flagset: [data.no_ip ? "no-ip" : "", data.no_hwid ? "no-hardware-id" : "", data.no_name ? "no-nickname" : ""]
|
flagset: [data.no_ip ? "no-ip" : "", data.no_hwid ? "no-hardware-id" : "", data.no_name ? "no-nickname" : ""]
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
sound.play(Sound.USER_BANNED);
|
this.channelTree.client.sound.play(Sound.USER_BANNED);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -415,11 +420,11 @@ class ClientEntry {
|
||||||
icon: "client-volume",
|
icon: "client-volume",
|
||||||
name: tr("Change Volume"),
|
name: tr("Change Volume"),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
Modals.spawnChangeVolume(this.audioController.volume, volume => {
|
Modals.spawnChangeVolume(this._audio_handle.get_volume(), volume => {
|
||||||
settings.changeServer("volume_client_" + this.clientUid(), volume);
|
this.channelTree.client.settings.changeServer("volume_client_" + this.clientUid(), volume);
|
||||||
this.audioController.volume = volume;
|
this._audio_handle.set_volume(volume);
|
||||||
if(globalClient.selectInfo.currentSelected == this)
|
if(this.channelTree.client.select_info.currentSelected == this)
|
||||||
globalClient.selectInfo.update();
|
this.channelTree.client.select_info.update();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -592,7 +597,7 @@ class ClientEntry {
|
||||||
log.table("Client update properties", entries);
|
log.table("Client update properties", entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(let variable of variables) {
|
for(const variable of variables) {
|
||||||
JSON.map_field_to(this._properties, variable.value, variable.key);
|
JSON.map_field_to(this._properties, variable.value, variable.key);
|
||||||
|
|
||||||
if(variable.key == "client_nickname") {
|
if(variable.key == "client_nickname") {
|
||||||
|
@ -602,17 +607,26 @@ class ClientEntry {
|
||||||
|
|
||||||
reorder_channel = true;
|
reorder_channel = true;
|
||||||
}
|
}
|
||||||
if(variable.key == "client_away" || variable.key == "client_output_muted" || variable.key == "client_input_hardware" || variable.key == "client_input_muted" || variable.key == "client_is_channel_commander"){
|
if(
|
||||||
|
variable.key == "client_away" ||
|
||||||
|
variable.key == "client_input_hardware" ||
|
||||||
|
variable.key == "client_output_hardware" ||
|
||||||
|
variable.key == "client_output_muted" ||
|
||||||
|
variable.key == "client_input_muted" ||
|
||||||
|
variable.key == "client_is_channel_commander"){
|
||||||
update_icon_speech = true;
|
update_icon_speech = true;
|
||||||
}
|
}
|
||||||
if(variable.key == "client_away_message" || variable.key == "client_away") {
|
if(variable.key == "client_away_message" || variable.key == "client_away") {
|
||||||
update_away = true;
|
update_away = true;
|
||||||
}
|
}
|
||||||
if(variable.key == "client_unique_identifier") {
|
if(variable.key == "client_unique_identifier") {
|
||||||
this.audioController.volume = parseFloat(settings.server("volume_client_" + this.clientUid(), "1"));
|
if(this._audio_handle) {
|
||||||
//TODO tr
|
const volume = parseFloat(this.channelTree.client.settings.server("volume_client_" + this.clientUid(), "1"));
|
||||||
console.error("Updated volume from config " + this.audioController.volume + " - " + "volume_client_" + this.clientUid() + " - " + settings.server("volume_client_" + this.clientUid(), "1"));
|
this._audio_handle.set_volume(volume);
|
||||||
console.log(this.avatarId());
|
log.debug(LogCategory.CLIENT, tr("Loaded client volume %d for client %s from config."), volume, this.clientUid());
|
||||||
|
} else {
|
||||||
|
log.warn(LogCategory.CLIENT, tr("Visible client got unique id assigned, but hasn't yet an audio handle. Ignoring volume assignment."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(variable.key == "client_talk_power") {
|
if(variable.key == "client_talk_power") {
|
||||||
reorder_channel = true;
|
reorder_channel = true;
|
||||||
|
@ -681,26 +695,26 @@ class ClientEntry {
|
||||||
|
|
||||||
chat(create: boolean = false) : ChatEntry {
|
chat(create: boolean = false) : ChatEntry {
|
||||||
let chatName = "client_" + this.clientUid() + ":" + this.clientId();
|
let chatName = "client_" + this.clientUid() + ":" + this.clientId();
|
||||||
let c = chat.findChat(chatName);
|
let chat = this.channelTree.client.chat.findChat(chatName);
|
||||||
if(!c && create) {
|
if(!chat && create) {
|
||||||
c = chat.createChat(chatName);
|
chat = this.channelTree.client.chat.createChat(chatName);
|
||||||
c.flag_closeable = true;
|
chat.flag_closeable = true;
|
||||||
c.name = this.clientNickName();
|
chat.name = this.clientNickName();
|
||||||
c.owner_unique_id = this.properties.client_unique_identifier;
|
chat.owner_unique_id = this.properties.client_unique_identifier;
|
||||||
|
|
||||||
c.onMessageSend = text => {
|
chat.onMessageSend = text => {
|
||||||
this.channelTree.client.serverConnection.command_helper.sendMessage(text, ChatType.CLIENT, this);
|
this.channelTree.client.serverConnection.command_helper.sendMessage(text, ChatType.CLIENT, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
c.onClose = () => {
|
chat.onClose = () => {
|
||||||
if(!c.flag_offline)
|
if(!chat.flag_offline)
|
||||||
this.channelTree.client.serverConnection.send_command("clientchatclosed", {"clid": this.clientId()}, {process_result: false}).catch(error => {
|
this.channelTree.client.serverConnection.send_command("clientchatclosed", {"clid": this.clientId()}, {process_result: false}).catch(error => {
|
||||||
log.warn(LogCategory.GENERAL, tr("Failed to notify chat participant (%o) that the chat has been closed. Error: %o"), this, error);
|
log.warn(LogCategory.GENERAL, tr("Failed to notify chat participant (%o) that the chat has been closed. Error: %o"), this, error);
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c;
|
return chat;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateClientIcon() {
|
updateClientIcon() {
|
||||||
|
@ -746,10 +760,7 @@ class ClientEntry {
|
||||||
} else return group.id == this.assignedChannelGroup();
|
} else return group.id == this.assignedChannelGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
onDelete() {
|
onDelete() { }
|
||||||
this.audioController.close();
|
|
||||||
this.audioController = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateOnlineTime() : number {
|
calculateOnlineTime() : number {
|
||||||
return Date.now() / 1000 - this.properties.client_lastconnected;
|
return Date.now() / 1000 - this.properties.client_lastconnected;
|
||||||
|
@ -796,11 +807,11 @@ class ClientEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalClientEntry extends ClientEntry {
|
class LocalClientEntry extends ClientEntry {
|
||||||
handle: TSClient;
|
handle: ConnectionHandler;
|
||||||
|
|
||||||
private renaming: boolean;
|
private renaming: boolean;
|
||||||
|
|
||||||
constructor(handle: TSClient) {
|
constructor(handle: ConnectionHandler) {
|
||||||
super(0, "local client");
|
super(0, "local client");
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
}
|
}
|
||||||
|
@ -866,7 +877,7 @@ class LocalClientEntry extends ClientEntry {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
elm.focusout(function (e) {
|
elm.focusout(e => {
|
||||||
if(!_self.renaming) return;
|
if(!_self.renaming) return;
|
||||||
_self.renaming = false;
|
_self.renaming = false;
|
||||||
|
|
||||||
|
@ -878,9 +889,9 @@ class LocalClientEntry extends ClientEntry {
|
||||||
|
|
||||||
elm.text(_self.clientNickName());
|
elm.text(_self.clientNickName());
|
||||||
_self.handle.serverConnection.command_helper.updateClient("client_nickname", text).then((e) => {
|
_self.handle.serverConnection.command_helper.updateClient("client_nickname", text).then((e) => {
|
||||||
chat.serverChat().appendMessage(tr("Nickname successfully changed"));
|
this.channelTree.client.chat.serverChat().appendMessage(tr("Nickname successfully changed"));
|
||||||
}).catch((e: CommandResult) => {
|
}).catch((e: CommandResult) => {
|
||||||
chat.serverChat().appendError(tr("Could not change nickname ({})"), e.extra_message);
|
this.channelTree.client.chat.serverChat().appendError(tr("Could not change nickname ({})"), e.extra_message);
|
||||||
_self.openRename();
|
_self.openRename();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -938,13 +949,13 @@ class MusicClientEntry extends ClientEntry {
|
||||||
name: tr("Show bot info"),
|
name: tr("Show bot info"),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
trigger_close = false;
|
trigger_close = false;
|
||||||
this.channelTree.client.selectInfo.open_popover()
|
this.channelTree.client.select_info.open_popover()
|
||||||
},
|
},
|
||||||
icon: "client-about",
|
icon: "client-about",
|
||||||
visible: this.channelTree.client.selectInfo.is_popover()
|
visible: this.channelTree.client.select_info.is_popover()
|
||||||
}, {
|
}, {
|
||||||
type: MenuEntryType.HR,
|
type: MenuEntryType.HR,
|
||||||
visible: this.channelTree.client.selectInfo.is_popover(),
|
visible: this.channelTree.client.select_info.is_popover(),
|
||||||
name: ''
|
name: ''
|
||||||
}, {
|
}, {
|
||||||
name: tr("<b>Change bot name</b>"),
|
name: tr("<b>Change bot name</b>"),
|
||||||
|
@ -1065,11 +1076,11 @@ class MusicClientEntry extends ClientEntry {
|
||||||
icon: "client-volume",
|
icon: "client-volume",
|
||||||
name: tr("Change local volume"),
|
name: tr("Change local volume"),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
Modals.spawnChangeVolume(this.audioController.volume, volume => {
|
Modals.spawnChangeVolume(this._audio_handle.get_volume(), volume => {
|
||||||
settings.changeServer("volume_client_" + this.clientUid(), volume);
|
this.channelTree.client.settings.changeServer("volume_client_" + this.clientUid(), volume);
|
||||||
this.audioController.volume = volume;
|
this._audio_handle.set_volume(volume);
|
||||||
if(globalClient.selectInfo.currentSelected == this)
|
if(this.channelTree.client.select_info.currentSelected == this)
|
||||||
(<MusicInfoManager>globalClient.selectInfo.current_manager()).update_local_volume(volume);
|
(<MusicInfoManager>this.channelTree.client.select_info.current_manager()).update_local_volume(volume);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1087,8 +1098,8 @@ class MusicClientEntry extends ClientEntry {
|
||||||
clid: this.clientId(),
|
clid: this.clientId(),
|
||||||
player_volume: value,
|
player_volume: value,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
if(globalClient.selectInfo.currentSelected == this)
|
if(this.channelTree.client.select_info.currentSelected == this)
|
||||||
(<MusicInfoManager>globalClient.selectInfo.current_manager()).update_remote_volume(value);
|
(<MusicInfoManager>this.channelTree.client.select_info.current_manager()).update_remote_volume(value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,9 @@ if(!$.fn.dividerfy) {
|
||||||
$(document.documentElement).css("user-select", "");
|
$(document.documentElement).css("user-select", "");
|
||||||
|
|
||||||
element.removeClass("seperator-selected");
|
element.removeClass("seperator-selected");
|
||||||
|
|
||||||
|
next_element.find("[x-divider-require-resize]").trigger('resize');
|
||||||
|
previous_element.find("[x-divider-require-resize]").trigger('resize');
|
||||||
};
|
};
|
||||||
|
|
||||||
element.on('mousedown', () => {
|
element.on('mousedown', () => {
|
|
@ -1,12 +1,13 @@
|
||||||
let context_menu: JQuery;
|
let context_menu: JQuery;
|
||||||
|
|
||||||
$(document).bind("mousedown", function (e) {
|
$(document).bind("click", function (e) {
|
||||||
let menu = context_menu || (context_menu = $(".context-menu"));
|
let menu = context_menu || (context_menu = $(".context-menu"));
|
||||||
|
|
||||||
if(!menu.is(":visible")) return;
|
if(!menu.is(":visible")) return;
|
||||||
|
|
||||||
if ($(e.target).parents(".context-menu").length == 0) {
|
if ($(e.target).parents(".context-menu").length == 0) {
|
||||||
despawn_context_menu();
|
despawn_context_menu();
|
||||||
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/// <reference path="../i18n/localize.ts" />
|
/// <reference path="../../i18n/localize.ts" />
|
||||||
|
|
||||||
interface JQuery<TElement = HTMLElement> {
|
interface JQuery<TElement = HTMLElement> {
|
||||||
asTabWidget(copy?: boolean) : JQuery<TElement>;
|
asTabWidget(copy?: boolean) : JQuery<TElement>;
|
||||||
|
@ -110,7 +110,7 @@ var TabFunctions = {
|
||||||
tag.on('tab.resize', update_height);
|
tag.on('tab.resize', update_height);
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if(!$.fn.asTabWidget) {
|
if(!$.fn.asTabWidget) {
|
||||||
$.fn.asTabWidget = function (copy?: boolean) : JQuery {
|
$.fn.asTabWidget = function (copy?: boolean) : JQuery {
|
|
@ -1,4 +1,4 @@
|
||||||
/// <reference path="../../client.ts" />
|
/// <reference path="../../ConnectionHandler.ts" />
|
||||||
/// <reference path="../modal/ModalSettings.ts" />
|
/// <reference path="../modal/ModalSettings.ts" />
|
||||||
/// <reference path="../modal/ModalBanList.ts" />
|
/// <reference path="../modal/ModalBanList.ts" />
|
||||||
/*
|
/*
|
||||||
|
@ -13,37 +13,72 @@
|
||||||
client_away_message Value: ''
|
client_away_message Value: ''
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
let control_bar: ControlBar; /* global variable to access the control bar */
|
||||||
|
|
||||||
|
type MicrophoneState = "disabled" | "muted" | "enabled";
|
||||||
|
type HeadphoneState = "muted" | "enabled";
|
||||||
|
type AwayState = "away-global" | "away" | "online";
|
||||||
class ControlBar {
|
class ControlBar {
|
||||||
private _muteInput: boolean;
|
private _button_away_active: AwayState;
|
||||||
private _muteOutput: boolean;
|
private _button_microphone: MicrophoneState;
|
||||||
private _away: boolean;
|
private _button_speakers: HeadphoneState;
|
||||||
private _query_visible: boolean;
|
private _button_subscribe_all: boolean;
|
||||||
private _awayMessage: string;
|
private _button_query_visible: boolean;
|
||||||
private _channel_subscribe_all: boolean;
|
|
||||||
|
|
||||||
private codec_supported: boolean = false;
|
private connection_handler: ConnectionHandler | undefined;
|
||||||
private support_playback: boolean = false;
|
|
||||||
private support_record: boolean = false;
|
|
||||||
|
|
||||||
readonly handle: TSClient;
|
|
||||||
htmlTag: JQuery;
|
htmlTag: JQuery;
|
||||||
|
constructor(htmlTag: JQuery) {
|
||||||
constructor(handle: TSClient, htmlTag: JQuery) {
|
|
||||||
this.handle = handle;
|
|
||||||
this.htmlTag = htmlTag;
|
this.htmlTag = htmlTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
initialise() {
|
initialize_connection_handler_state(handler?: ConnectionHandler) {
|
||||||
this.htmlTag.find(".btn_connect").on('click', this.onConnect.bind(this));
|
/* setup the state like the last displayed one */
|
||||||
this.htmlTag.find(".btn_disconnect").on('click', this.onDisconnect.bind(this));
|
handler.client_status.output_muted = this._button_speakers === "muted";
|
||||||
this.htmlTag.find(".btn_mute_input").on('click', this.onInputMute.bind(this));
|
handler.client_status.input_muted = this._button_microphone === "muted";
|
||||||
this.htmlTag.find(".btn_mute_output").on('click', this.onOutputMute.bind(this));
|
|
||||||
this.htmlTag.find(".btn_open_settings").on('click', this.onOpenSettings.bind(this));
|
|
||||||
this.htmlTag.find(".btn_permissions").on('click', this.onPermission.bind(this));
|
|
||||||
this.htmlTag.find(".btn_banlist").on('click', this.onBanlist.bind(this));
|
|
||||||
this.htmlTag.find(".button-subscribe-mode").on('click', this.on_toggle_channel_subscribe_all.bind(this));
|
|
||||||
this.htmlTag.find(".button-playlist-manage").on('click', this.on_playlist_manage.bind(this));
|
|
||||||
|
|
||||||
|
handler.client_status.channel_subscribe_all = this._button_subscribe_all;
|
||||||
|
handler.client_status.queries_visible = this._button_query_visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_connection_handler(handler?: ConnectionHandler) {
|
||||||
|
if(this.connection_handler == handler)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.connection_handler = handler;
|
||||||
|
this.apply_server_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_server_state() {
|
||||||
|
if(!this.connection_handler)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
const flag_away = typeof(this.connection_handler.client_status.away) === "string" || this.connection_handler.client_status.away;
|
||||||
|
if(!flag_away)
|
||||||
|
this.button_away_active = "online";
|
||||||
|
else if(flag_away && this._button_away_active === "online")
|
||||||
|
this.button_away_active = "away";
|
||||||
|
|
||||||
|
this.button_query_visible = this.connection_handler.client_status.queries_visible;
|
||||||
|
this.button_subscribe_all = this.connection_handler.client_status.channel_subscribe_all;
|
||||||
|
|
||||||
|
this.apply_server_voice_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_server_voice_state() {
|
||||||
|
if(!this.connection_handler)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.button_microphone = !this.connection_handler.client_status.input_hardware ? "disabled" : this.connection_handler.client_status.input_muted ? "muted" : "enabled";
|
||||||
|
this.button_speaker = this.connection_handler.client_status.output_muted ? "muted" : "enabled";
|
||||||
|
}
|
||||||
|
|
||||||
|
current_connection_handler() {
|
||||||
|
return this.connection_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialise() {
|
||||||
let dropdownify = (tag: JQuery) => {
|
let dropdownify = (tag: JQuery) => {
|
||||||
tag.find(".button-dropdown").on('click', () => {
|
tag.find(".button-dropdown").on('click', () => {
|
||||||
tag.addClass("displayed");
|
tag.addClass("displayed");
|
||||||
|
@ -58,201 +93,293 @@ class ControlBar {
|
||||||
tag.removeClass("displayed");
|
tag.removeClass("displayed");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
{
|
|
||||||
let tokens = this.htmlTag.find(".btn_token");
|
|
||||||
dropdownify(tokens);
|
|
||||||
|
|
||||||
tokens.find(".btn_token_use").on('click', this.on_token_use.bind(this));
|
this.htmlTag.find(".btn_connect").on('click', this.on_open_connect.bind(this));
|
||||||
tokens.find(".btn_token_list").on('click', this.on_token_list.bind(this));
|
this.htmlTag.find(".btn_disconnect").on('click', this.on_execute_disconnect.bind(this));
|
||||||
|
|
||||||
|
this.htmlTag.find(".btn_mute_input").on('click', this.on_toggle_microphone.bind(this));
|
||||||
|
this.htmlTag.find(".btn_mute_output").on('click', this.on_toggle_sound.bind(this));
|
||||||
|
this.htmlTag.find(".button-subscribe-mode").on('click', this.on_toggle_channel_subscribe.bind(this));
|
||||||
|
this.htmlTag.find(".btn_query_toggle").on('click', this.on_toggle_query_view.bind(this));
|
||||||
|
|
||||||
|
this.htmlTag.find(".btn_open_settings").on('click', this.on_open_settings.bind(this));
|
||||||
|
this.htmlTag.find(".btn_permissions").on('click', this.on_open_permissions.bind(this));
|
||||||
|
this.htmlTag.find(".btn_banlist").on('click', this.on_open_banslist.bind(this));
|
||||||
|
this.htmlTag.find(".button-playlist-manage").on('click', this.on_open_playlist_manage.bind(this));
|
||||||
|
|
||||||
|
this.htmlTag.find(".btn_token_use").on('click', this.on_token_use.bind(this));
|
||||||
|
this.htmlTag.find(".btn_token_list").on('click', this.on_token_list.bind(this));
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
this.htmlTag.find(".btn_away_disable").on('click', this.on_away_disable.bind(this));
|
||||||
|
this.htmlTag.find(".btn_away_disable_global").on('click', this.on_away_disable_global.bind(this));
|
||||||
|
|
||||||
|
this.htmlTag.find(".btn_away_enable").on('click', this.on_away_enable.bind(this));
|
||||||
|
this.htmlTag.find(".btn_away_enable_global").on('click', this.on_away_enable_global.bind(this));
|
||||||
|
|
||||||
|
this.htmlTag.find(".btn_away_message").on('click', this.on_away_set_message.bind(this));
|
||||||
|
this.htmlTag.find(".btn_away_message_global").on('click', this.on_away_set_message_global.bind(this));
|
||||||
|
|
||||||
|
this.htmlTag.find(".btn_away_toggle").on('click', this.on_away_toggle.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
dropdownify(this.htmlTag.find(".container-disconnect"));
|
||||||
|
dropdownify(this.htmlTag.find(".btn_token"));
|
||||||
|
dropdownify(this.htmlTag.find(".btn_away"));
|
||||||
|
dropdownify(this.htmlTag.find(".btn_bookmark"));
|
||||||
|
dropdownify(this.htmlTag.find(".btn_query"));
|
||||||
|
dropdownify(this.htmlTag.find(".dropdown-audio"));
|
||||||
|
dropdownify(this.htmlTag.find(".dropdown-servertools"));
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let away = this.htmlTag.find(".btn_away");
|
|
||||||
dropdownify(away);
|
|
||||||
|
|
||||||
away.find(".btn_away_toggle").on('click', this.on_away_toggle.bind(this));
|
this.htmlTag.find(".btn_bookmark_list").on('click', this.on_bookmark_manage.bind(this));
|
||||||
away.find(".btn_away_message").on('click', this.on_away_set_message.bind(this));
|
this.htmlTag.find(".btn_bookmark_add").on('click', this.on_bookmark_server_add.bind(this));
|
||||||
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let bookmark = this.htmlTag.find(".btn_bookmark");
|
|
||||||
dropdownify(bookmark);
|
|
||||||
|
|
||||||
bookmark.find(".btn_bookmark_list").on('click', this.on_bookmark_manage.bind(this));
|
|
||||||
bookmark.find(".btn_bookmark_add").on('click', this.on_bookmark_server_add.bind(this));
|
|
||||||
|
|
||||||
this.update_bookmarks();
|
|
||||||
this.update_bookmark_status();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let query = this.htmlTag.find(".btn_query");
|
|
||||||
dropdownify(query);
|
|
||||||
|
|
||||||
/* search for query buttons not only on the large device button */
|
/* search for query buttons not only on the large device button */
|
||||||
this.htmlTag.find(".btn_query_toggle").on('click', this.on_query_visibility_toggle.bind(this));
|
this.htmlTag.find(".btn_query_create").on('click', this.on_open_query_create.bind(this));
|
||||||
this.htmlTag.find(".btn_query_create").on('click', this.on_query_create.bind(this));
|
this.htmlTag.find(".btn_query_manage").on('click', this.on_open_query_manage.bind(this));
|
||||||
this.htmlTag.find(".btn_query_manage").on('click', this.on_query_manage.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mobile dropdowns */
|
|
||||||
{
|
this.update_bookmarks();
|
||||||
const dropdown = this.htmlTag.find(".dropdown-audio");
|
this.update_bookmark_status();
|
||||||
dropdownify(dropdown);
|
|
||||||
dropdown.find(".button-display").on('click', () => dropdown.addClass("displayed"));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const dropdown = this.htmlTag.find(".dropdown-servertools");
|
|
||||||
dropdownify(dropdown);
|
|
||||||
dropdown.find(".button-display").on('click', () => dropdown.addClass("displayed"));
|
|
||||||
}
|
|
||||||
|
|
||||||
//Need an initialise
|
//Need an initialise
|
||||||
this.muteInput = settings.static_global(Settings.KEY_CONTROL_MUTE_INPUT, false);
|
this.button_speaker = settings.static_global(Settings.KEY_CONTROL_MUTE_OUTPUT, false) ? "muted" : "enabled";
|
||||||
this.muteOutput = settings.static_global(Settings.KEY_CONTROL_MUTE_OUTPUT, false);
|
this.button_microphone = settings.static_global(Settings.KEY_CONTROL_MUTE_INPUT, false) ? "muted" : "enabled";
|
||||||
this.query_visible = settings.static_global(Settings.KEY_CONTROL_SHOW_QUERIES, false);
|
this.button_subscribe_all = true;
|
||||||
this.channel_subscribe_all = settings.static_global(Settings.KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL, true);
|
this.button_query_visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
on_away_toggle() {
|
|
||||||
this._awayMessage = "";
|
/* Update the UI */
|
||||||
this.away = !this._away;
|
set button_away_active(flag: AwayState) {
|
||||||
|
if(this._button_away_active === flag)
|
||||||
|
return;
|
||||||
|
this._button_away_active = flag;
|
||||||
|
this.update_button_away();
|
||||||
|
}
|
||||||
|
update_button_away() {
|
||||||
|
const button_away_enable = this.htmlTag.find(".btn_away_enable");
|
||||||
|
const button_away_disable = this.htmlTag.find(".btn_away_disable");
|
||||||
|
const button_away_toggle = this.htmlTag.find(".btn_away_toggle");
|
||||||
|
|
||||||
|
const button_away_disable_global = this.htmlTag.find(".btn_away_disable_global");
|
||||||
|
const button_away_enable_global = this.htmlTag.find(".btn_away_enable_global");
|
||||||
|
const button_away_message_global = this.htmlTag.find(".btn_away_message_global");
|
||||||
|
|
||||||
|
button_away_toggle.toggleClass("activated", this._button_away_active !== "online");
|
||||||
|
button_away_enable.toggle(this._button_away_active === "online");
|
||||||
|
button_away_disable.toggle(this._button_away_active !== "online");
|
||||||
|
|
||||||
|
const connections = server_connections.server_connection_handlers();
|
||||||
|
if(connections.length <= 1) {
|
||||||
|
button_away_disable_global.hide();
|
||||||
|
button_away_enable_global.hide();
|
||||||
|
button_away_message_global.hide();
|
||||||
|
} else {
|
||||||
|
button_away_message_global.show();
|
||||||
|
button_away_enable_global.toggle(server_connections.server_connection_handlers().filter(e => !e.client_status.away).length > 0);
|
||||||
|
button_away_disable_global.toggle(
|
||||||
|
this._button_away_active === "away-global" ||
|
||||||
|
server_connections.server_connection_handlers().filter(e => typeof(e.client_status.away) === "string" || e.client_status.away).length > 0
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
on_away_set_message() {
|
set button_microphone(state: MicrophoneState) {
|
||||||
createInputModal(tr("Set away message"), tr("Please enter the away message"), message => true, message => {
|
if(this._button_microphone === state)
|
||||||
if(message)
|
return;
|
||||||
this.away = message;
|
this._button_microphone = state;
|
||||||
}).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
onInputMute() {
|
|
||||||
this.muteInput = !this._muteInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
onOutputMute() {
|
|
||||||
this.muteOutput = !this._muteOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
set muteInput(flag: boolean) {
|
|
||||||
if(this._muteInput == flag) return;
|
|
||||||
this._muteInput = flag;
|
|
||||||
|
|
||||||
let tag = this.htmlTag.find(".btn_mute_input");
|
let tag = this.htmlTag.find(".btn_mute_input");
|
||||||
const tag_icon = tag.find(".icon_x32, .icon");
|
const tag_icon = tag.find(".icon_x32, .icon");
|
||||||
|
tag.toggleClass('activated', state === "muted");
|
||||||
tag.toggleClass('activated', flag)
|
|
||||||
|
|
||||||
tag_icon
|
tag_icon
|
||||||
.toggleClass('client-input_muted', flag)
|
.toggleClass('client-input_muted', state === "muted")
|
||||||
.toggleClass('client-capture', !flag);
|
.toggleClass('client-capture', state === "enabled")
|
||||||
|
.toggleClass('client-activate_microphone', state === "disabled");
|
||||||
|
|
||||||
|
if(state === "disabled")
|
||||||
if(this.handle.serverConnection.connected())
|
tag_icon.attr('title', tr("Enable your microphone on this server"));
|
||||||
this.handle.serverConnection.send_command("clientupdate", {
|
else if(state === "enabled")
|
||||||
client_input_muted: this._muteInput
|
tag_icon.attr('title', tr("Mute microphone"));
|
||||||
});
|
else
|
||||||
settings.changeGlobal(Settings.KEY_CONTROL_MUTE_INPUT, this._muteInput);
|
tag_icon.attr('title', tr("Unmute microphone"));
|
||||||
this.updateMicrophoneRecordState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get muteOutput() : boolean { return this._muteOutput; }
|
set button_speaker(state: HeadphoneState) {
|
||||||
|
if(this._button_speakers === state)
|
||||||
set muteOutput(flag: boolean) {
|
return;
|
||||||
if(this._muteOutput == flag) return;
|
this._button_speakers = state;
|
||||||
this._muteOutput = flag;
|
|
||||||
|
|
||||||
|
|
||||||
let tag = this.htmlTag.find(".btn_mute_output");
|
let tag = this.htmlTag.find(".btn_mute_output");
|
||||||
const tag_icon = tag.find(".icon_x32, .icon");
|
const tag_icon = tag.find(".icon_x32, .icon");
|
||||||
|
|
||||||
tag.toggleClass('activated', flag)
|
tag.toggleClass('activated', state === "muted");
|
||||||
|
|
||||||
tag_icon
|
tag_icon
|
||||||
.toggleClass('client-output_muted', flag)
|
.toggleClass('client-output_muted', state !== "enabled")
|
||||||
.toggleClass('client-volume', !flag);
|
.toggleClass('client-volume', state === "enabled");
|
||||||
|
|
||||||
if(this.handle.serverConnection.connected())
|
if(state === "enabled")
|
||||||
this.handle.serverConnection.send_command("clientupdate", {
|
tag_icon.attr('title', tr("Mute sound"));
|
||||||
client_output_muted: this._muteOutput
|
else
|
||||||
});
|
tag_icon.attr('title', tr("Unmute sound"));
|
||||||
settings.changeGlobal(Settings.KEY_CONTROL_MUTE_OUTPUT, this._muteOutput);
|
|
||||||
this.updateMicrophoneRecordState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set away(value: boolean | string) {
|
set button_subscribe_all(state: boolean) {
|
||||||
if(typeof(value) == "boolean") {
|
if(this._button_subscribe_all === state)
|
||||||
if(this._away == value) return;
|
return;
|
||||||
this._away = value;
|
this._button_subscribe_all = state;
|
||||||
this._awayMessage = "";
|
|
||||||
} else {
|
this.htmlTag
|
||||||
this._awayMessage = value;
|
.find(".button-subscribe-mode")
|
||||||
this._away = true;
|
.toggleClass('activated', this._button_subscribe_all)
|
||||||
|
.find('.icon_x32')
|
||||||
|
.toggleClass('client-unsubscribe_from_all_channels', !this._button_subscribe_all)
|
||||||
|
.toggleClass('client-subscribe_to_all_channels', this._button_subscribe_all);
|
||||||
|
}
|
||||||
|
|
||||||
|
set button_query_visible(state: boolean) {
|
||||||
|
if(this._button_query_visible === state)
|
||||||
|
return;
|
||||||
|
this._button_query_visible = state;
|
||||||
|
|
||||||
|
const button = this.htmlTag.find(".btn_query_toggle");
|
||||||
|
button.toggleClass('activated', this._button_query_visible);
|
||||||
|
if(this._button_query_visible)
|
||||||
|
button.find(".query-text").text(tr("Hide server queries"));
|
||||||
|
else
|
||||||
|
button.find(".query-text").text(tr("Show server queries"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* UI listener */
|
||||||
|
private on_away_toggle() {
|
||||||
|
if(this._button_away_active === "away" || this._button_away_active === "away-global")
|
||||||
|
this.button_away_active = "online";
|
||||||
|
else
|
||||||
|
this.button_away_active = "away";
|
||||||
|
if(this.connection_handler)
|
||||||
|
this.connection_handler.set_away_status(this._button_away_active !== "online");
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_away_enable() {
|
||||||
|
this.button_away_active = "away";
|
||||||
|
if(this.connection_handler)
|
||||||
|
this.connection_handler.set_away_status(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_away_disable() {
|
||||||
|
this.button_away_active = "online";
|
||||||
|
if(this.connection_handler)
|
||||||
|
this.connection_handler.set_away_status(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_away_set_message() {
|
||||||
|
createInputModal(tr("Set away message"), tr("Please enter your away message"), message => true, message => {
|
||||||
|
if(typeof(message) === "string") {
|
||||||
|
this.button_away_active = "away";
|
||||||
|
if(this.connection_handler)
|
||||||
|
this.connection_handler.set_away_status(message);
|
||||||
|
}
|
||||||
|
}).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_away_enable_global() {
|
||||||
|
this.button_away_active = "away-global";
|
||||||
|
for(const connection of server_connections.server_connection_handlers())
|
||||||
|
connection.set_away_status(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_away_disable_global() {
|
||||||
|
this.button_away_active = "online";
|
||||||
|
for(const connection of server_connections.server_connection_handlers())
|
||||||
|
connection.set_away_status(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_away_set_message_global() {
|
||||||
|
createInputModal(tr("Set global away message"), tr("Please enter your global away message"), message => true, message => {
|
||||||
|
if(typeof(message) === "string") {
|
||||||
|
this.button_away_active = "away";
|
||||||
|
for(const connection of server_connections.server_connection_handlers())
|
||||||
|
connection.set_away_status(message);
|
||||||
|
}
|
||||||
|
}).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private on_toggle_microphone() {
|
||||||
|
if(this._button_microphone === "disabled" || this._button_microphone === "muted")
|
||||||
|
this.button_microphone = "enabled";
|
||||||
|
else
|
||||||
|
this.button_microphone = "muted";
|
||||||
|
|
||||||
|
if(this.connection_handler) {
|
||||||
|
this.connection_handler.client_status.input_muted = this._button_microphone !== "enabled";
|
||||||
|
if(!this.connection_handler.client_status.input_hardware)
|
||||||
|
this.connection_handler.acquire_recorder(voice_recoder, true); /* acquire_recorder already updates the voice status */
|
||||||
|
else
|
||||||
|
this.connection_handler.update_voice_status(undefined);
|
||||||
|
|
||||||
|
/* just update the last changed value */
|
||||||
|
settings.changeGlobal(Settings.KEY_CONTROL_MUTE_INPUT, this.connection_handler.client_status.input_muted)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let tag = this.htmlTag.find(".btn_away_toggle");
|
private on_toggle_sound() {
|
||||||
if( this._away) {
|
if(this._button_speakers === "muted")
|
||||||
tag.addClass("activated");
|
this.button_speaker = "enabled";
|
||||||
} else {
|
else
|
||||||
tag.removeClass("activated");
|
this.button_speaker = "muted";
|
||||||
|
|
||||||
|
if(this.connection_handler) {
|
||||||
|
this.connection_handler.client_status.output_muted = this._button_speakers !== "enabled";
|
||||||
|
this.connection_handler.update_voice_status(undefined);
|
||||||
|
|
||||||
|
/* just update the last changed value */
|
||||||
|
settings.changeGlobal(Settings.KEY_CONTROL_MUTE_OUTPUT, this.connection_handler.client_status.output_muted)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.handle.serverConnection.connected)
|
|
||||||
this.handle.serverConnection.send_command("clientupdate", {
|
|
||||||
client_away: this._away,
|
|
||||||
client_away_message: this._awayMessage
|
|
||||||
});
|
|
||||||
this.updateMicrophoneRecordState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateMicrophoneRecordState() {
|
private on_toggle_channel_subscribe() {
|
||||||
let enabled = !this._muteInput && !this._muteOutput && !this._away;
|
this.button_subscribe_all = !this._button_subscribe_all;
|
||||||
if(this.handle.voiceConnection)
|
if(this.connection_handler) {
|
||||||
this.handle.voiceConnection.voiceRecorder.update(enabled);
|
this.connection_handler.client_status.channel_subscribe_all = this._button_subscribe_all;
|
||||||
|
if(this._button_subscribe_all)
|
||||||
|
this.connection_handler.channelTree.subscribe_all_channels();
|
||||||
|
else
|
||||||
|
this.connection_handler.channelTree.unsubscribe_all_channels(true);
|
||||||
|
this.connection_handler.settings.changeServer(Settings.KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL, this._button_subscribe_all);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProperties() {
|
private on_toggle_query_view() {
|
||||||
if(this.handle.serverConnection.connected)
|
this.button_query_visible = !this._button_query_visible;
|
||||||
this.handle.serverConnection.send_command("clientupdate", {
|
if(this.connection_handler) {
|
||||||
client_input_muted: this._muteInput,
|
this.connection_handler.client_status.queries_visible = this._button_query_visible;
|
||||||
client_output_muted: this._muteOutput,
|
this.connection_handler.channelTree.toggle_server_queries(this._button_query_visible);
|
||||||
client_away: this._away,
|
this.connection_handler.settings.changeServer(Settings.KEY_CONTROL_SHOW_QUERIES, this._button_subscribe_all);
|
||||||
client_away_message: this._awayMessage,
|
}
|
||||||
client_input_hardware: this.codec_supported && this.support_record,
|
|
||||||
client_output_hardware: this.codec_supported && this.support_playback
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateVoice(targetChannel?: ChannelEntry) {
|
private on_open_settings() {
|
||||||
if(!targetChannel)
|
|
||||||
targetChannel = this.handle.getClient().currentChannel();
|
|
||||||
let client = this.handle.getClient();
|
|
||||||
|
|
||||||
this.codec_supported = targetChannel ? this.handle.voiceConnection && this.handle.voiceConnection.codecSupported(targetChannel.properties.channel_codec) : true;
|
|
||||||
this.support_record = this.handle.voiceConnection && this.handle.voiceConnection.voice_send_support();
|
|
||||||
this.support_playback = this.handle.voiceConnection && this.handle.voiceConnection.voice_playback_support();
|
|
||||||
|
|
||||||
this.htmlTag.find(".btn_mute_input").prop("disabled", !this.codec_supported|| !this.support_playback || !this.support_record);
|
|
||||||
this.htmlTag.find(".btn_mute_output").prop("disabled", !this.codec_supported || !this.support_playback);
|
|
||||||
this.handle.serverConnection.send_command("clientupdate", {
|
|
||||||
client_input_hardware: this.codec_supported && this.support_record,
|
|
||||||
client_output_hardware: this.codec_supported && this.support_playback
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!this.codec_supported)
|
|
||||||
createErrorModal(tr("Channel codec unsupported"), tr("This channel has an unsupported codec.<br>You cant speak or listen to anybody within this channel!")).open();
|
|
||||||
|
|
||||||
/* Update these properties anyways (for case the server fails to handle the command) */
|
|
||||||
client.updateVariables(
|
|
||||||
{key: "client_input_hardware", value: (this.codec_supported && this.support_record) + ""},
|
|
||||||
{key: "client_output_hardware", value: (this.codec_supported && this.support_playback) + ""}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onOpenSettings() {
|
|
||||||
Modals.spawnSettingsModal();
|
Modals.spawnSettingsModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onConnect() {
|
private on_open_connect() {
|
||||||
this.handle.cancel_reconnect();
|
if(this.connection_handler)
|
||||||
|
this.connection_handler.cancel_reconnect();
|
||||||
Modals.spawnConnectModal({
|
Modals.spawnConnectModal({
|
||||||
url: "ts.TeaSpeak.de",
|
url: "ts.TeaSpeak.de",
|
||||||
enforce: false
|
enforce: false
|
||||||
|
@ -260,31 +387,31 @@ class ControlBar {
|
||||||
}
|
}
|
||||||
|
|
||||||
update_connection_state() {
|
update_connection_state() {
|
||||||
switch (this.handle.serverConnection ? this.handle.serverConnection._connectionState : ConnectionState.UNCONNECTED) {
|
switch (this.connection_handler.serverConnection ? this.connection_handler.serverConnection._connectionState : ConnectionState.UNCONNECTED) {
|
||||||
case ConnectionState.CONNECTED:
|
case ConnectionState.CONNECTED:
|
||||||
case ConnectionState.CONNECTING:
|
case ConnectionState.CONNECTING:
|
||||||
case ConnectionState.INITIALISING:
|
case ConnectionState.INITIALISING:
|
||||||
this.htmlTag.find(".btn_disconnect").show();
|
this.htmlTag.find(".container-disconnect").show();
|
||||||
this.htmlTag.find(".btn_connect").hide();
|
this.htmlTag.find(".container-connect").hide();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.htmlTag.find(".btn_disconnect").hide();
|
this.htmlTag.find(".container-disconnect").hide();
|
||||||
this.htmlTag.find(".btn_connect").show();
|
this.htmlTag.find(".container-connect").show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onDisconnect() {
|
private on_execute_disconnect() {
|
||||||
this.handle.cancel_reconnect();
|
this.connection_handler.cancel_reconnect();
|
||||||
this.handle.handleDisconnect(DisconnectReason.REQUESTED); //TODO message?
|
this.connection_handler.handleDisconnect(DisconnectReason.REQUESTED); //TODO message?
|
||||||
this.update_connection_state();
|
this.update_connection_state();
|
||||||
sound.play(Sound.CONNECTION_DISCONNECTED);
|
this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
private on_token_use() {
|
private on_token_use() {
|
||||||
createInputModal(tr("Use token"), tr("Please enter your token/priviledge key"), message => message.length > 0, result => {
|
createInputModal(tr("Use token"), tr("Please enter your token/priviledge key"), message => message.length > 0, result => {
|
||||||
if(!result) return;
|
if(!result) return;
|
||||||
if(this.handle.serverConnection.connected)
|
if(this.connection_handler.serverConnection.connected)
|
||||||
this.handle.serverConnection.send_command("tokenuse", {
|
this.connection_handler.serverConnection.send_command("tokenuse", {
|
||||||
token: result
|
token: result
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
createInfoModal(tr("Use token"), tr("Toke successfully used!")).open();
|
createInfoModal(tr("Use token"), tr("Toke successfully used!")).open();
|
||||||
|
@ -299,37 +426,40 @@ class ControlBar {
|
||||||
createErrorModal(tr("Not implemented"), tr("Token list is not implemented yet!")).open();
|
createErrorModal(tr("Not implemented"), tr("Token list is not implemented yet!")).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onPermission() {
|
private on_open_permissions() {
|
||||||
let button = this.htmlTag.find(".btn_permissions");
|
let button = this.htmlTag.find(".btn_permissions");
|
||||||
button.addClass("activated");
|
button.addClass("activated");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
Modals.spawnPermissionEdit().open();
|
if(this.connection_handler)
|
||||||
|
Modals.spawnPermissionEdit(this.connection_handler).open();
|
||||||
|
else
|
||||||
|
createErrorModal(tr("You have to be connected"), tr("You have to be connected!")).open();
|
||||||
button.removeClass("activated");
|
button.removeClass("activated");
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onBanlist() {
|
private on_open_banslist() {
|
||||||
if(!this.handle.serverConnection) return;
|
if(!this.connection_handler.serverConnection) return;
|
||||||
|
|
||||||
if(this.handle.permissions.neededPermission(PermissionType.B_CLIENT_BAN_LIST).granted(1)) {
|
if(this.connection_handler.permissions.neededPermission(PermissionType.B_CLIENT_BAN_LIST).granted(1)) {
|
||||||
Modals.openBanList(this.handle);
|
Modals.openBanList(this.connection_handler);
|
||||||
} else {
|
} else {
|
||||||
createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to view the ban list")).open();
|
createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to view the ban list")).open();
|
||||||
sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
|
this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private on_bookmark_server_add() {
|
private on_bookmark_server_add() {
|
||||||
if(globalClient && globalClient.connected) {
|
if(this.connection_handler && this.connection_handler.connected) {
|
||||||
createInputModal(tr("Enter bookmarks name"), tr("Please enter the bookmarks name:<br>"), text => true, result => {
|
createInputModal(tr("Enter bookmarks name"), tr("Please enter the bookmarks name:<br>"), text => true, result => {
|
||||||
if(result) {
|
if(result) {
|
||||||
const bookmark = bookmarks.create_bookmark(result as string, bookmarks.bookmarks(), {
|
const bookmark = bookmarks.create_bookmark(result as string, bookmarks.bookmarks(), {
|
||||||
server_port: globalClient.serverConnection._remote_address.port,
|
server_port: this.connection_handler.serverConnection._remote_address.port,
|
||||||
server_address: globalClient.serverConnection._remote_address.host,
|
server_address: this.connection_handler.serverConnection._remote_address.host,
|
||||||
|
|
||||||
server_password: "",
|
server_password: "",
|
||||||
server_password_hash: ""
|
server_password_hash: ""
|
||||||
}, globalClient.getClient().clientNickName());
|
}, this.connection_handler.getClient().clientNickName());
|
||||||
bookmarks.save_bookmark(bookmark);
|
bookmarks.save_bookmark(bookmark);
|
||||||
this.update_bookmarks()
|
this.update_bookmarks()
|
||||||
}
|
}
|
||||||
|
@ -353,6 +483,34 @@ class ControlBar {
|
||||||
const build_entry = (bookmark: bookmarks.DirectoryBookmark | bookmarks.Bookmark) => {
|
const build_entry = (bookmark: bookmarks.DirectoryBookmark | bookmarks.Bookmark) => {
|
||||||
if(bookmark.type == bookmarks.BookmarkType.ENTRY) {
|
if(bookmark.type == bookmarks.BookmarkType.ENTRY) {
|
||||||
const mark = <bookmarks.Bookmark>bookmark;
|
const mark = <bookmarks.Bookmark>bookmark;
|
||||||
|
|
||||||
|
const bookmark_connect = (new_tab: boolean) => {
|
||||||
|
this.htmlTag.find(".btn_bookmark").find(".dropdown").removeClass("displayed"); //FIXME Not working
|
||||||
|
|
||||||
|
const profile = profiles.find_profile(mark.connect_profile) || profiles.default_profile();
|
||||||
|
if(profile.valid()) {
|
||||||
|
const connection = this.connection_handler && !new_tab ? this.connection_handler : server_connections.spawn_server_connection_handler();
|
||||||
|
server_connections.set_active_connection_handler(connection);
|
||||||
|
connection.startConnection(
|
||||||
|
mark.server_properties.server_address + ":" + mark.server_properties.server_port,
|
||||||
|
profile,
|
||||||
|
mark.nickname,
|
||||||
|
{
|
||||||
|
password: mark.server_properties.server_password_hash,
|
||||||
|
hashed: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Modals.spawnConnectModal({
|
||||||
|
url: mark.server_properties.server_address + ":" + mark.server_properties.server_port,
|
||||||
|
enforce: true
|
||||||
|
}, {
|
||||||
|
profile: profile,
|
||||||
|
enforce: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return $.spawn("div")
|
return $.spawn("div")
|
||||||
.addClass("bookmark")
|
.addClass("bookmark")
|
||||||
.append(
|
.append(
|
||||||
|
@ -363,27 +521,32 @@ class ControlBar {
|
||||||
.addClass("name")
|
.addClass("name")
|
||||||
.text(bookmark.display_name)
|
.text(bookmark.display_name)
|
||||||
.on('click', event => {
|
.on('click', event => {
|
||||||
this.htmlTag.find(".btn_bookmark").find(".dropdown").removeClass("displayed");
|
if(event.isDefaultPrevented())
|
||||||
const profile = profiles.find_profile(mark.connect_profile) || profiles.default_profile();
|
return;
|
||||||
if(profile.valid()) {
|
bookmark_connect(false);
|
||||||
this.handle.startConnection(
|
})
|
||||||
mark.server_properties.server_address + ":" + mark.server_properties.server_port,
|
.on('contextmenu', event => {
|
||||||
profile,
|
if(event.isDefaultPrevented())
|
||||||
mark.nickname,
|
return;
|
||||||
{
|
event.preventDefault();
|
||||||
password: mark.server_properties.server_password_hash,
|
|
||||||
hashed: true
|
spawn_context_menu(event.pageX, event.pageY, {
|
||||||
}
|
type: MenuEntryType.ENTRY,
|
||||||
);
|
name: tr("Connect"),
|
||||||
} else {
|
icon: 'client-connect',
|
||||||
Modals.spawnConnectModal({
|
callback: () => bookmark_connect(false)
|
||||||
url: mark.server_properties.server_address + ":" + mark.server_properties.server_port,
|
}, {
|
||||||
enforce: true
|
type: MenuEntryType.ENTRY,
|
||||||
}, {
|
name: tr("Connect in a new tab"),
|
||||||
profile: profile,
|
icon: 'client-connect',
|
||||||
enforce: true
|
callback: () => bookmark_connect(true)
|
||||||
})
|
}, MenuEntry.CLOSE(() => {
|
||||||
}
|
setTimeout(() => {
|
||||||
|
this.htmlTag.find(".btn_bookmark.button-dropdown").removeClass("force-show")
|
||||||
|
}, 250);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.htmlTag.find(".btn_bookmark.button-dropdown").addClass("force-show");
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -423,84 +586,28 @@ class ControlBar {
|
||||||
Modals.spawnBookmarkModal();
|
Modals.spawnBookmarkModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
get query_visible() {
|
private on_open_query_create() {
|
||||||
return this._query_visible;
|
if(this.connection_handler.permissions.neededPermission(PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN).granted(1)) {
|
||||||
}
|
Modals.spawnQueryCreate(this.connection_handler);
|
||||||
|
|
||||||
set query_visible(flag: boolean) {
|
|
||||||
if(this._query_visible == flag) return;
|
|
||||||
|
|
||||||
this._query_visible = flag;
|
|
||||||
settings.changeGlobal(Settings.KEY_CONTROL_SHOW_QUERIES, flag);
|
|
||||||
this.update_query_visibility_button();
|
|
||||||
this.handle.channelTree.toggle_server_queries(flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
private on_query_visibility_toggle() {
|
|
||||||
this.query_visible = !this._query_visible;
|
|
||||||
this.update_query_visibility_button();
|
|
||||||
}
|
|
||||||
|
|
||||||
private update_query_visibility_button() {
|
|
||||||
const button = this.htmlTag.find(".btn_query_toggle");
|
|
||||||
button.toggleClass('activated', this._query_visible);
|
|
||||||
if(this._query_visible)
|
|
||||||
button.find(".query-text").text(tr("Hide server queries"));
|
|
||||||
else
|
|
||||||
button.find(".query-text").text(tr("Show server queries"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private on_query_create() {
|
|
||||||
if(this.handle.permissions.neededPermission(PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN).granted(1)) {
|
|
||||||
Modals.spawnQueryCreate();
|
|
||||||
} else {
|
} else {
|
||||||
createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to create a server query login")).open();
|
createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to create a server query login")).open();
|
||||||
sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
|
this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private on_query_manage() {
|
private on_open_query_manage() {
|
||||||
if(globalClient && globalClient.connected) {
|
if(this.connection_handler && this.connection_handler.connected) {
|
||||||
Modals.spawnQueryManage(globalClient);
|
Modals.spawnQueryManage(this.connection_handler);
|
||||||
} else {
|
} else {
|
||||||
createErrorModal(tr("You have to be connected"), tr("You have to be connected!")).open();
|
createErrorModal(tr("You have to be connected"), tr("You have to be connected!")).open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private on_playlist_manage() {
|
private on_open_playlist_manage() {
|
||||||
if(this.handle && this.handle.connected) {
|
if(this.connection_handler && this.connection_handler.connected) {
|
||||||
Modals.spawnPlaylistManage(this.handle);
|
Modals.spawnPlaylistManage(this.connection_handler);
|
||||||
} else {
|
} else {
|
||||||
createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open();
|
createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get channel_subscribe_all() : boolean {
|
|
||||||
return this._channel_subscribe_all;
|
|
||||||
}
|
|
||||||
|
|
||||||
set channel_subscribe_all(flag: boolean) {
|
|
||||||
if(this._channel_subscribe_all == flag)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._channel_subscribe_all = flag;
|
|
||||||
|
|
||||||
this.htmlTag
|
|
||||||
.find(".button-subscribe-mode")
|
|
||||||
.toggleClass('activated', this._channel_subscribe_all)
|
|
||||||
.find('.icon_x32')
|
|
||||||
.toggleClass('client-unsubscribe_from_all_channels', !this._channel_subscribe_all)
|
|
||||||
.toggleClass('client-subscribe_to_all_channels', this._channel_subscribe_all);
|
|
||||||
|
|
||||||
settings.changeGlobal(Settings.KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL, flag);
|
|
||||||
|
|
||||||
if(flag)
|
|
||||||
this.handle.channelTree.subscribe_all_channels();
|
|
||||||
else
|
|
||||||
this.handle.channelTree.unsubscribe_all_channels();
|
|
||||||
}
|
|
||||||
|
|
||||||
private on_toggle_channel_subscribe_all() {
|
|
||||||
this.channel_subscribe_all = !this.channel_subscribe_all;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
/// <reference path="../../client.ts" />
|
/// <reference path="../../ConnectionHandler.ts" />
|
||||||
/// <reference path="../../../../vendor/bbcode/xbbcode.ts" />
|
/// <reference path="../../../../vendor/bbcode/xbbcode.ts" />
|
||||||
|
|
||||||
abstract class InfoManagerBase {
|
abstract class InfoManagerBase {
|
||||||
|
@ -50,24 +50,24 @@ abstract class InfoManager<T> extends InfoManagerBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
class InfoBar<AvailableTypes = ServerEntry | ChannelEntry | ClientEntry | undefined> {
|
class InfoBar<AvailableTypes = ServerEntry | ChannelEntry | ClientEntry | undefined> {
|
||||||
readonly handle: TSClient;
|
readonly handle: ConnectionHandler;
|
||||||
|
|
||||||
private current_selected?: AvailableTypes;
|
private current_selected?: AvailableTypes;
|
||||||
|
|
||||||
private _tag: JQuery<HTMLElement>;
|
private _tag: JQuery<HTMLElement>;
|
||||||
private _tag_content: JQuery<HTMLElement>;
|
private readonly _tag_info: JQuery<HTMLElement>;
|
||||||
private _tag_info: JQuery<HTMLElement>;
|
private readonly _tag_banner: JQuery<HTMLElement>;
|
||||||
private _tag_banner: JQuery<HTMLElement>;
|
|
||||||
|
|
||||||
private _current_manager: InfoManagerBase = undefined;
|
private _current_manager: InfoManagerBase = undefined;
|
||||||
private managers: InfoManagerBase[] = [];
|
private managers: InfoManagerBase[] = [];
|
||||||
private banner_manager: Hostbanner;
|
private banner_manager: Hostbanner;
|
||||||
|
|
||||||
constructor(client: TSClient, htmlTag: JQuery<HTMLElement>) {
|
constructor(client: ConnectionHandler) {
|
||||||
this.handle = client;
|
this.handle = client;
|
||||||
this._tag = htmlTag;
|
|
||||||
this._tag_content = htmlTag.find("> .select_info");
|
this._tag = $("#tmpl_select_info").renderTag();
|
||||||
this._tag_info = this._tag_content.find(".container-select-info");
|
this._tag_info = this._tag.find(".container-select-info");
|
||||||
this._tag_banner = this._tag_content.find(".container-banner");
|
this._tag_banner = this._tag.find(".container-banner");
|
||||||
|
|
||||||
this.managers.push(new MusicInfoManager());
|
this.managers.push(new MusicInfoManager());
|
||||||
this.managers.push(new ClientInfoManager());
|
this.managers.push(new ClientInfoManager());
|
||||||
|
@ -76,18 +76,22 @@ class InfoBar<AvailableTypes = ServerEntry | ChannelEntry | ClientEntry | undefi
|
||||||
|
|
||||||
this.banner_manager = new Hostbanner(client, this._tag_banner);
|
this.banner_manager = new Hostbanner(client, this._tag_banner);
|
||||||
|
|
||||||
this._tag.find("button.close").on('click', () => {
|
this._tag.find("button.close").on('click', () => this.close_popover());
|
||||||
this._tag.toggleClass('shown', false);
|
}
|
||||||
});
|
|
||||||
|
get_tag() : JQuery {
|
||||||
|
return this._tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_resize() {
|
handle_resize() {
|
||||||
/* test if the popover isn't a popover anymore */
|
/* test if the popover isn't a popover anymore */
|
||||||
if(this._tag.hasClass('shown')) {
|
if(this._tag.parent().hasClass('shown')) {
|
||||||
this._tag.removeClass('shown');
|
this._tag.parent().removeClass('shown');
|
||||||
if(this.is_popover())
|
if(this.is_popover())
|
||||||
this._tag.addClass('shown');
|
this._tag.parent().addClass('shown');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.banner_manager.handle_resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentSelected(entry: AvailableTypes) {
|
setCurrentSelected(entry: AvailableTypes) {
|
||||||
|
@ -128,14 +132,21 @@ class InfoBar<AvailableTypes = ServerEntry | ChannelEntry | ClientEntry | undefi
|
||||||
|
|
||||||
current_manager() { return this._current_manager; }
|
current_manager() { return this._current_manager; }
|
||||||
|
|
||||||
html_tag() { return this._tag_content; }
|
|
||||||
|
|
||||||
is_popover() : boolean {
|
is_popover() : boolean {
|
||||||
return !this._tag.is(':visible') || this._tag.hasClass('shown');
|
return !this._tag.parent().is(':visible') || this._tag.parent().hasClass('shown');
|
||||||
}
|
}
|
||||||
|
|
||||||
open_popover() {
|
open_popover() {
|
||||||
this._tag.toggleClass('shown', true);
|
this._tag.parent().toggleClass('shown', true);
|
||||||
|
this.banner_manager.handle_resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
close_popover() {
|
||||||
|
this._tag.parent().toggleClass('shown', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered_tag() {
|
||||||
|
return this._tag_info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,12 +157,12 @@ interface Window {
|
||||||
|
|
||||||
class Hostbanner {
|
class Hostbanner {
|
||||||
readonly html_tag: JQuery<HTMLElement>;
|
readonly html_tag: JQuery<HTMLElement>;
|
||||||
readonly client: TSClient;
|
readonly client: ConnectionHandler;
|
||||||
|
|
||||||
private updater: NodeJS.Timer;
|
private updater: NodeJS.Timer;
|
||||||
private _hostbanner_url: string;
|
private _hostbanner_url: string;
|
||||||
|
|
||||||
constructor(client: TSClient, htmlTag: JQuery<HTMLElement>) {
|
constructor(client: ConnectionHandler, htmlTag: JQuery<HTMLElement>) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.html_tag = htmlTag;
|
this.html_tag = htmlTag;
|
||||||
}
|
}
|
||||||
|
@ -189,6 +200,10 @@ class Hostbanner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handle_resize() {
|
||||||
|
this.html_tag.find("[x-divider-require-resize]").trigger('resize');
|
||||||
|
}
|
||||||
|
|
||||||
private generate_tag?() : Promise<JQuery<HTMLElement>> {
|
private generate_tag?() : Promise<JQuery<HTMLElement>> {
|
||||||
if(!this.client.connected) return undefined;
|
if(!this.client.connected) return undefined;
|
||||||
|
|
||||||
|
@ -220,6 +235,43 @@ class Hostbanner {
|
||||||
|
|
||||||
const rendered = $("#tmpl_selected_hostbanner").renderTag(properties);
|
const rendered = $("#tmpl_selected_hostbanner").renderTag(properties);
|
||||||
|
|
||||||
|
/* ration watcher */
|
||||||
|
if(server.properties.virtualserver_hostbanner_mode == 2) {
|
||||||
|
const jimage = rendered.find(".meta-image");
|
||||||
|
if(jimage.length == 0) {
|
||||||
|
log.warn(LogCategory.SERVER, tr("Missing hostbanner meta image tag"));
|
||||||
|
} else {
|
||||||
|
const image = jimage[0];
|
||||||
|
image.onload = event => {
|
||||||
|
const image: HTMLImageElement = jimage[0] as any;
|
||||||
|
rendered.on('resize', event => {
|
||||||
|
const container = rendered.parent();
|
||||||
|
container.css('height', null);
|
||||||
|
container.css('flex-grow', '1');
|
||||||
|
|
||||||
|
const max_height = rendered.visible_height();
|
||||||
|
const max_width = rendered.visible_width();
|
||||||
|
container.css('flex-grow', '0');
|
||||||
|
|
||||||
|
|
||||||
|
const original_height = image.naturalHeight;
|
||||||
|
const original_width = image.naturalWidth;
|
||||||
|
|
||||||
|
const ratio_height = max_height / original_height;
|
||||||
|
const ratio_width = max_width / original_width;
|
||||||
|
|
||||||
|
const ratio = Math.min(ratio_height, ratio_width);
|
||||||
|
|
||||||
|
if(ratio == 0)
|
||||||
|
return;
|
||||||
|
const hostbanner_height = ratio * original_height;
|
||||||
|
container.css('height', Math.ceil(hostbanner_height) + "px");
|
||||||
|
/* the width is ignorable*/
|
||||||
|
});
|
||||||
|
setTimeout(() => rendered.trigger('resize'), 100);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(window.fetch) {
|
if(window.fetch) {
|
||||||
return (async () => {
|
return (async () => {
|
||||||
|
@ -244,6 +296,7 @@ class Hostbanner {
|
||||||
}
|
}
|
||||||
const url = (this._hostbanner_url = URL.createObjectURL(await result.blob()));
|
const url = (this._hostbanner_url = URL.createObjectURL(await result.blob()));
|
||||||
tag_image.css('background-image', 'url(' + url + ')');
|
tag_image.css('background-image', 'url(' + url + ')');
|
||||||
|
tag_image.attr('src', url);
|
||||||
log.debug(LogCategory.SERVER, tr("Fetsched hostbanner successfully (%o, type: %o, url: %o)"), Date.now() - start, result.type, url);
|
log.debug(LogCategory.SERVER, tr("Fetsched hostbanner successfully (%o, type: %o, url: %o)"), Date.now() - start, result.type, url);
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
log.warn(LogCategory.SERVER, tr("Failed to fetch hostbanner image: %o"), error);
|
log.warn(LogCategory.SERVER, tr("Failed to fetch hostbanner image: %o"), error);
|
||||||
|
@ -288,7 +341,7 @@ class ClientInfoManager extends InfoManager<ClientEntry> {
|
||||||
|
|
||||||
properties["client_name"] = client.createChatTag()[0];
|
properties["client_name"] = client.createChatTag()[0];
|
||||||
properties["client_onlinetime"] = formatDate(client.calculateOnlineTime());
|
properties["client_onlinetime"] = formatDate(client.calculateOnlineTime());
|
||||||
properties["sound_volume"] = client.audioController.volume * 100;
|
properties["sound_volume"] = client.get_audio_handle() ? client.get_audio_handle().get_volume() * 100 : -1;
|
||||||
properties["client_is_query"] = client.properties.client_type == ClientType.CLIENT_QUERY;
|
properties["client_is_query"] = client.properties.client_type == ClientType.CLIENT_QUERY;
|
||||||
properties["client_is_web"] = client.properties.client_type_exact == ClientType.CLIENT_WEB;
|
properties["client_is_web"] = client.properties.client_type_exact == ClientType.CLIENT_WEB;
|
||||||
|
|
||||||
|
@ -777,11 +830,11 @@ class MusicInfoManager extends ClientInfoManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
update_local_volume(volume: number) {
|
update_local_volume(volume: number) {
|
||||||
this.handle.html_tag().find(".property-volume-local").text(Math.floor(volume * 100) + "%");
|
this.handle.rendered_tag().find(".property-volume-local").text(Math.floor(volume * 100) + "%");
|
||||||
}
|
}
|
||||||
|
|
||||||
update_remote_volume(volume: number) {
|
update_remote_volume(volume: number) {
|
||||||
this.handle.html_tag().find(".property-volume-remote").text(Math.floor(volume * 100) + "%")
|
this.handle.rendered_tag().find(".property-volume-remote").text(Math.floor(volume * 100) + "%")
|
||||||
}
|
}
|
||||||
|
|
||||||
available<V>(object: V): boolean {
|
available<V>(object: V): boolean {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import LogType = log.LogType;
|
|
||||||
|
|
||||||
enum ChatType {
|
enum ChatType {
|
||||||
GENERAL,
|
GENERAL,
|
||||||
SERVER,
|
SERVER,
|
||||||
|
@ -56,20 +54,34 @@ namespace MessageHelper {
|
||||||
|
|
||||||
result.push(...formatElement(pattern.substr(begin, found - begin))); //Append the text
|
result.push(...formatElement(pattern.substr(begin, found - begin))); //Append the text
|
||||||
|
|
||||||
let number;
|
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
while ("0123456789".includes(pattern[found + 1 + offset])) offset++;
|
if(pattern[found + 1] == ':') {
|
||||||
number = parseInt(offset > 0 ? pattern.substr(found + 1, offset) : "0");
|
offset++; /* the beginning : */
|
||||||
|
while (pattern[found + 1 + offset] != ':') offset++;
|
||||||
|
const tag = pattern.substr(found + 2, offset - 1);
|
||||||
|
|
||||||
if(pattern[found + offset + 1] != '}') {
|
offset++; /* the ending : */
|
||||||
found++;
|
if(pattern[found + offset + 1] != '}') {
|
||||||
continue;
|
found++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push($.spawn(tag as any));
|
||||||
|
} else {
|
||||||
|
let number;
|
||||||
|
while ("0123456789".includes(pattern[found + 1 + offset])) offset++;
|
||||||
|
number = parseInt(offset > 0 ? pattern.substr(found + 1, offset) : "0");
|
||||||
|
if(pattern[found + offset + 1] != '}') {
|
||||||
|
found++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(objects.length < number)
|
||||||
|
log.warn(LogCategory.GENERAL, tr("Message to format contains invalid index (%o)"), number);
|
||||||
|
|
||||||
|
result.push(...formatElement(objects[number]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(objects.length < number)
|
|
||||||
log.warn(LogCategory.GENERAL, tr("Message to format contains invalid index (%o)"), number);
|
|
||||||
|
|
||||||
result.push(...formatElement(objects[number]));
|
|
||||||
found = found + 1 + offset;
|
found = found + 1 + offset;
|
||||||
begin = found + 1;
|
begin = found + 1;
|
||||||
} while(found++);
|
} while(found++);
|
||||||
|
@ -251,9 +263,7 @@ class ChatEntry {
|
||||||
type: MenuEntryType.ENTRY,
|
type: MenuEntryType.ENTRY,
|
||||||
icon: "client-tab_close_button",
|
icon: "client-tab_close_button",
|
||||||
name: tr("Close"),
|
name: tr("Close"),
|
||||||
callback: () => {
|
callback: () => this.handle.deleteChat(this)
|
||||||
chat.deleteChat(this);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,6 +351,7 @@ class ChatBox {
|
||||||
//static readonly URL_REGEX = /^(?<hostname>([a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]{2,63})(?:\/(?<path>(?:[^\s?]+)?)(?:\?(?<query>\S+))?)?$/gm;
|
//static readonly URL_REGEX = /^(?<hostname>([a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]{2,63})(?:\/(?<path>(?:[^\s?]+)?)(?:\?(?<query>\S+))?)?$/gm;
|
||||||
static readonly URL_REGEX = /^(([a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]{2,63})(?:\/((?:[^\s?]+)?)(?:\?(\S+))?)?$/gm;
|
static readonly URL_REGEX = /^(([a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]{2,63})(?:\/((?:[^\s?]+)?)(?:\?(\S+))?)?$/gm;
|
||||||
|
|
||||||
|
readonly connection_handler: ConnectionHandler;
|
||||||
htmlTag: JQuery;
|
htmlTag: JQuery;
|
||||||
chats: ChatEntry[];
|
chats: ChatEntry[];
|
||||||
private _activeChat: ChatEntry;
|
private _activeChat: ChatEntry;
|
||||||
|
@ -348,9 +359,12 @@ class ChatBox {
|
||||||
private _button_send: JQuery;
|
private _button_send: JQuery;
|
||||||
private _input_message: JQuery;
|
private _input_message: JQuery;
|
||||||
|
|
||||||
constructor(htmlTag: JQuery) {
|
constructor(connection_handler: ConnectionHandler) {
|
||||||
this.htmlTag = htmlTag;
|
this.connection_handler = connection_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.htmlTag = $("#tmpl_frame_chat").renderTag();
|
||||||
this._button_send = this.htmlTag.find(".button-send");
|
this._button_send = this.htmlTag.find(".button-send");
|
||||||
this._input_message = this.htmlTag.find(".input-message");
|
this._input_message = this.htmlTag.find(".input-message");
|
||||||
|
|
||||||
|
@ -372,15 +386,15 @@ class ChatBox {
|
||||||
this._activeChat = undefined;
|
this._activeChat = undefined;
|
||||||
|
|
||||||
this.createChat("chat_server", ChatType.SERVER).onMessageSend = (text: string) => {
|
this.createChat("chat_server", ChatType.SERVER).onMessageSend = (text: string) => {
|
||||||
if(!globalClient.serverConnection) {
|
if(!this.connection_handler.serverConnection) {
|
||||||
chat.serverChat().appendError(tr("Could not send chant message (Not connected)"));
|
this.serverChat().appendError(tr("Could not send chant message (Not connected)"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
globalClient.serverConnection.command_helper.sendMessage(text, ChatType.SERVER).catch(error => {
|
this.connection_handler.serverConnection.command_helper.sendMessage(text, ChatType.SERVER).catch(error => {
|
||||||
if(error instanceof CommandResult)
|
if(error instanceof CommandResult)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
chat.serverChat().appendMessage(tr("Failed to send text message."));
|
this.serverChat().appendMessage(tr("Failed to send text message."));
|
||||||
log.error(LogCategory.GENERAL, tr("Failed to send server text message: %o"), error);
|
log.error(LogCategory.GENERAL, tr("Failed to send server text message: %o"), error);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -388,20 +402,20 @@ class ChatBox {
|
||||||
this.serverChat().flag_closeable = false;
|
this.serverChat().flag_closeable = false;
|
||||||
|
|
||||||
this.createChat("chat_channel", ChatType.CHANNEL).onMessageSend = (text: string) => {
|
this.createChat("chat_channel", ChatType.CHANNEL).onMessageSend = (text: string) => {
|
||||||
if(!globalClient.serverConnection) {
|
if(!this.connection_handler.serverConnection) {
|
||||||
chat.channelChat().appendError(tr("Could not send chant message (Not connected)"));
|
this.channelChat().appendError(tr("Could not send chant message (Not connected)"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
globalClient.serverConnection.command_helper.sendMessage(text, ChatType.CHANNEL, globalClient.getClient().currentChannel()).catch(error => {
|
this.connection_handler.serverConnection.command_helper.sendMessage(text, ChatType.CHANNEL, this.connection_handler.getClient().currentChannel()).catch(error => {
|
||||||
chat.channelChat().appendMessage(tr("Failed to send text message."));
|
this.channelChat().appendMessage(tr("Failed to send text message."));
|
||||||
log.error(LogCategory.GENERAL, tr("Failed to send channel text message: %o"), error);
|
log.error(LogCategory.GENERAL, tr("Failed to send channel text message: %o"), error);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
this.channelChat().name = tr("Channel chat");
|
this.channelChat().name = tr("Channel chat");
|
||||||
this.channelChat().flag_closeable = false;
|
this.channelChat().flag_closeable = false;
|
||||||
|
|
||||||
globalClient.permissions.initializedListener.push(flag => {
|
this.connection_handler.permissions.initializedListener.push(flag => {
|
||||||
if(flag) this.activeChat0(this._activeChat);
|
if(flag) this.activeChat0(this._activeChat);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -492,16 +506,16 @@ class ChatBox {
|
||||||
this._activeChat.html_tag.addClass("active");
|
this._activeChat.html_tag.addClass("active");
|
||||||
this._activeChat.displayHistory();
|
this._activeChat.displayHistory();
|
||||||
|
|
||||||
if(!disable_input && globalClient && globalClient.permissions && globalClient.permissions.initialized())
|
if(!disable_input && this.connection_handler && this.connection_handler.permissions && this.connection_handler.permissions.initialized())
|
||||||
switch (this._activeChat.type) {
|
switch (this._activeChat.type) {
|
||||||
case ChatType.CLIENT:
|
case ChatType.CLIENT:
|
||||||
disable_input = false;
|
disable_input = false;
|
||||||
break;
|
break;
|
||||||
case ChatType.SERVER:
|
case ChatType.SERVER:
|
||||||
disable_input = !globalClient.permissions.neededPermission(PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND).granted(1);
|
disable_input = !this.connection_handler.permissions.neededPermission(PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND).granted(1);
|
||||||
break;
|
break;
|
||||||
case ChatType.CHANNEL:
|
case ChatType.CHANNEL:
|
||||||
disable_input = !globalClient.permissions.neededPermission(PermissionType.B_CLIENT_CHANNEL_TEXTMESSAGE_SEND).granted(1);
|
disable_input = !this.connection_handler.permissions.neededPermission(PermissionType.B_CLIENT_CHANNEL_TEXTMESSAGE_SEND).granted(1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
129
shared/js/ui/frames/connection_handlers.ts
Normal file
129
shared/js/ui/frames/connection_handlers.ts
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
|
||||||
|
let server_connections: ServerConnectionManager;
|
||||||
|
|
||||||
|
class ServerConnectionManager {
|
||||||
|
private connection_handlers: ConnectionHandler[] = [];
|
||||||
|
private active_handler: ConnectionHandler | undefined;
|
||||||
|
|
||||||
|
private _container_channel_tree: JQuery;
|
||||||
|
private _container_select_info: JQuery;
|
||||||
|
private _container_chat_box: JQuery;
|
||||||
|
|
||||||
|
private _tag: JQuery;
|
||||||
|
private _tag_connection_entries: JQuery;
|
||||||
|
private _tag_buttons_scoll: JQuery;
|
||||||
|
private _tag_button_scoll_right: JQuery;
|
||||||
|
private _tag_button_scoll_left: JQuery;
|
||||||
|
|
||||||
|
constructor(tag: JQuery) {
|
||||||
|
this._tag = tag;
|
||||||
|
|
||||||
|
if(settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION, false))
|
||||||
|
this._tag.hide();
|
||||||
|
|
||||||
|
this._tag_connection_entries = this._tag.find(".connection-handlers");
|
||||||
|
this._tag_buttons_scoll = this._tag.find(".container-scroll");
|
||||||
|
this._tag_button_scoll_left = this._tag_buttons_scoll.find(".button-scroll-left");
|
||||||
|
this._tag_button_scoll_right = this._tag_buttons_scoll.find(".button-scroll-right");
|
||||||
|
|
||||||
|
this._tag_button_scoll_left.on('click', this._button_scroll_left_clicked.bind(this));
|
||||||
|
this._tag_button_scoll_right.on('click', this._button_scroll_right_clicked.bind(this));
|
||||||
|
|
||||||
|
this._container_channel_tree = $("#channelTree");
|
||||||
|
this._container_select_info = $("#select_info");
|
||||||
|
this._container_chat_box = $("#chat");
|
||||||
|
|
||||||
|
this.set_active_connection_handler(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
spawn_server_connection_handler() : ConnectionHandler {
|
||||||
|
const handler = new ConnectionHandler();
|
||||||
|
this.connection_handlers.push(handler);
|
||||||
|
control_bar.update_button_away();
|
||||||
|
control_bar.initialize_connection_handler_state(handler);
|
||||||
|
|
||||||
|
handler.tag_connection_handler.appendTo(this._tag_connection_entries);
|
||||||
|
this._update_scroll();
|
||||||
|
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_server_connection_handler(handler: ConnectionHandler) {
|
||||||
|
this.connection_handlers.remove(handler);
|
||||||
|
handler.tag_connection_handler.detach();
|
||||||
|
this._update_scroll();
|
||||||
|
|
||||||
|
if(handler.serverConnection) {
|
||||||
|
const connected = handler.connected;
|
||||||
|
handler.serverConnection.disconnect("handler destroyed");
|
||||||
|
handler.handleDisconnect(DisconnectReason.HANDLER_DESTROYED, connected);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(handler === this.active_handler)
|
||||||
|
this.set_active_connection_handler(this.connection_handlers[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_active_connection_handler(handler: ConnectionHandler) {
|
||||||
|
if(handler && this.connection_handlers.indexOf(handler) == -1)
|
||||||
|
throw "Handler hasn't been registrated or is already obsolete!";
|
||||||
|
|
||||||
|
if(this.active_handler)
|
||||||
|
this.active_handler.select_info.close_popover();
|
||||||
|
this._tag_connection_entries.find(".active").removeClass("active");
|
||||||
|
this._container_channel_tree.children().detach();
|
||||||
|
this._container_select_info.children().detach();
|
||||||
|
this._container_chat_box.children().detach();
|
||||||
|
|
||||||
|
control_bar.set_connection_handler(handler);
|
||||||
|
if(handler) {
|
||||||
|
handler.tag_connection_handler.addClass("active");
|
||||||
|
|
||||||
|
this._container_channel_tree.append(handler.channelTree.tag_tree());
|
||||||
|
this._container_select_info.append(handler.select_info.get_tag());
|
||||||
|
this._container_chat_box.append(handler.chat.htmlTag);
|
||||||
|
|
||||||
|
if(handler.invoke_resized_on_activate)
|
||||||
|
handler.resize_elements();
|
||||||
|
}
|
||||||
|
this.active_handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
active_connection_handler() : ConnectionHandler | undefined {
|
||||||
|
return this.active_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
server_connection_handlers() : ConnectionHandler[] {
|
||||||
|
return this.connection_handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_ui() {
|
||||||
|
this._update_scroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _update_scroll() {
|
||||||
|
const has_scroll = this._tag_connection_entries.hasScrollBar("width")
|
||||||
|
&& this._tag_connection_entries.width() + 10 >= this._tag_connection_entries.parent().width();
|
||||||
|
|
||||||
|
this._tag_buttons_scoll.toggleClass("enabled", has_scroll);
|
||||||
|
this._tag.toggleClass("scrollbar", has_scroll);
|
||||||
|
|
||||||
|
if(has_scroll)
|
||||||
|
this._update_scroll_buttons();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _button_scroll_right_clicked() {
|
||||||
|
this._tag_connection_entries.scrollLeft((this._tag_connection_entries.scrollLeft() || 0) + 50);
|
||||||
|
this._update_scroll_buttons();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _button_scroll_left_clicked() {
|
||||||
|
this._tag_connection_entries.scrollLeft((this._tag_connection_entries.scrollLeft() || 0) - 50);
|
||||||
|
this._update_scroll_buttons();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _update_scroll_buttons() {
|
||||||
|
const scroll = this._tag_connection_entries.scrollLeft() || 0;
|
||||||
|
this._tag_button_scoll_left.toggleClass("disabled", scroll <= 0);
|
||||||
|
this._tag_button_scoll_right.toggleClass("disabled", scroll + this._tag_connection_entries.width() + 2 >= this._tag_connection_entries[0].scrollWidth);
|
||||||
|
}
|
||||||
|
}
|
|
@ -111,15 +111,16 @@ namespace htmltags {
|
||||||
|
|
||||||
let client: ClientEntry;
|
let client: ClientEntry;
|
||||||
|
|
||||||
if(globalClient && globalClient.channelTree) {
|
const current_connection = server_connections.active_connection_handler();
|
||||||
|
if(current_connection && current_connection.channelTree) {
|
||||||
if(!client && client_id) {
|
if(!client && client_id) {
|
||||||
client = globalClient.channelTree.findClient(client_id);
|
client = current_connection.channelTree.findClient(client_id);
|
||||||
if(client && (client_unique_id && client.properties.client_unique_identifier != client_unique_id)) {
|
if(client && (client_unique_id && client.properties.client_unique_identifier != client_unique_id)) {
|
||||||
client = undefined; /* client id dosn't match anymore, lets search for the unique id */
|
client = undefined; /* client id dosn't match anymore, lets search for the unique id */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!client && client_unique_id)
|
if(!client && client_unique_id)
|
||||||
client = globalClient.channelTree.find_client_by_unique_id(client_unique_id);
|
client = current_connection.channelTree.find_client_by_unique_id(client_unique_id);
|
||||||
}
|
}
|
||||||
if(!client) {
|
if(!client) {
|
||||||
/* we may should open a "offline" menu? */
|
/* we may should open a "offline" menu? */
|
||||||
|
@ -138,9 +139,10 @@ namespace htmltags {
|
||||||
export function callback_context_channel(element: JQuery) {
|
export function callback_context_channel(element: JQuery) {
|
||||||
const channel_id = parseInt(element.attr("channel-id") || "0");
|
const channel_id = parseInt(element.attr("channel-id") || "0");
|
||||||
|
|
||||||
|
const current_connection = server_connections.active_connection_handler();
|
||||||
let channel: ChannelEntry;
|
let channel: ChannelEntry;
|
||||||
if(globalClient && globalClient.channelTree) {
|
if(current_connection && current_connection.channelTree) {
|
||||||
channel = globalClient.channelTree.findChannel(channel_id);
|
channel = current_connection.channelTree.findChannel(channel_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!channel)
|
if(!channel)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
/// <reference path="../../ConnectionHandler.ts" />
|
||||||
/// <reference path="../../proto.ts" />
|
/// <reference path="../../proto.ts" />
|
||||||
/// <reference path="../../client.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
const avatar_to_uid = (id: string) => {
|
const avatar_to_uid = (id: string) => {
|
||||||
|
@ -20,7 +20,7 @@ namespace Modals {
|
||||||
return (size / Math.pow(1024, exp)).toFixed(2) + 'KMGTPE'.charAt(exp - 1) + "iB";
|
return (size / Math.pow(1024, exp)).toFixed(2) + 'KMGTPE'.charAt(exp - 1) + "iB";
|
||||||
};
|
};
|
||||||
|
|
||||||
export function spawnAvatarList(client: TSClient) {
|
export function spawnAvatarList(client: ConnectionHandler) {
|
||||||
const modal = createModal({
|
const modal = createModal({
|
||||||
header: tr("Avatars"),
|
header: tr("Avatars"),
|
||||||
footer: undefined,
|
footer: undefined,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
/// <reference path="../../ConnectionHandler.ts" />
|
||||||
/// <reference path="../../proto.ts" />
|
/// <reference path="../../proto.ts" />
|
||||||
/// <reference path="../../client.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
export function spawnBanClient(name: string | string[], callback: (data: {
|
export function spawnBanClient(name: string | string[], callback: (data: {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
export function spawnBanCreate(base?: BanEntry, callback?: (entry?: BanEntry) => any) {
|
export function spawnBanCreate(connection: ConnectionHandler, base?: BanEntry, callback?: (entry?: BanEntry) => any) {
|
||||||
let result: BanEntry = {} as any;
|
let result: BanEntry = {} as any;
|
||||||
result.banid = base ? base.banid : 0;
|
result.banid = base ? base.banid : 0;
|
||||||
|
|
||||||
|
@ -98,8 +98,8 @@ namespace Modals {
|
||||||
input_global.prop("checked", base.server_id == 0);
|
input_global.prop("checked", base.server_id == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(globalClient && globalClient.permissions)
|
if(connection && connection.permissions)
|
||||||
input_global.prop("disabled", !globalClient.permissions.neededPermission(base ? PermissionType.B_CLIENT_BAN_EDIT_GLOBAL : PermissionType.B_CLIENT_BAN_CREATE_GLOBAL));
|
input_global.prop("disabled", !connection.permissions.neededPermission(base ? PermissionType.B_CLIENT_BAN_EDIT_GLOBAL : PermissionType.B_CLIENT_BAN_CREATE_GLOBAL));
|
||||||
|
|
||||||
return template;
|
return template;
|
||||||
},
|
},
|
||||||
|
|
|
@ -28,10 +28,10 @@ namespace Modals {
|
||||||
modal: Modal;
|
modal: Modal;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function openBanList(client: TSClient) {
|
export function openBanList(client: ConnectionHandler) {
|
||||||
let update;
|
let update;
|
||||||
const modal = spawnBanListModal(() => update(), () => {
|
const modal = spawnBanListModal(() => update(), () => {
|
||||||
spawnBanCreate(undefined, result => {
|
spawnBanCreate(client, undefined, result => {
|
||||||
if(result.server_id < 0) result.server_id = undefined;
|
if(result.server_id < 0) result.server_id = undefined;
|
||||||
console.log(tr("Adding ban %o"), result);
|
console.log(tr("Adding ban %o"), result);
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ namespace Modals {
|
||||||
});
|
});
|
||||||
}, ban => {
|
}, ban => {
|
||||||
console.log(tr("Editing ban %o"), ban);
|
console.log(tr("Editing ban %o"), ban);
|
||||||
spawnBanCreate(ban, result => {
|
spawnBanCreate(client, ban, result => {
|
||||||
console.log(tr("Apply edit changes %o"), result);
|
console.log(tr("Apply edit changes %o"), result);
|
||||||
if(result.server_id < 0) result.server_id = undefined;
|
if(result.server_id < 0) result.server_id = undefined;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
/// <reference path="../../ConnectionHandler.ts" />
|
||||||
/// <reference path="../../proto.ts" />
|
/// <reference path="../../proto.ts" />
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
|
@ -252,7 +253,7 @@ namespace Modals {
|
||||||
width: 750
|
width: 750
|
||||||
});
|
});
|
||||||
|
|
||||||
modal.close_listener.push(() => globalClient.controlBar.update_bookmarks());
|
modal.close_listener.push(() => control_bar.update_bookmarks());
|
||||||
modal.open();
|
modal.open();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
/// <reference path="../../ConnectionHandler.ts" />
|
||||||
/// <reference path="../../proto.ts" />
|
/// <reference path="../../proto.ts" />
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
export function spawnConnectModal(defaultHost: { url: string, enforce: boolean} = { url: "ts.TeaSpeak.de", enforce: false}, connect_profile?: { profile: profiles.ConnectionProfile, enforce: boolean}) {
|
export function spawnConnectModal(defaultHost: { url: string, enforce: boolean} = { url: "ts.TeaSpeak.de", enforce: false}, connect_profile?: { profile: profiles.ConnectionProfile, enforce: boolean}) {
|
||||||
|
@ -13,9 +13,11 @@ namespace Modals {
|
||||||
const connect_modal = $("#tmpl_connect").renderTag({
|
const connect_modal = $("#tmpl_connect").renderTag({
|
||||||
client: native_client,
|
client: native_client,
|
||||||
forum_path: settings.static("forum_path"),
|
forum_path: settings.static("forum_path"),
|
||||||
password_id: random_id
|
password_id: random_id,
|
||||||
|
multi_tab: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION, false)
|
||||||
}).modalize((header, body, footer) => {
|
}).modalize((header, body, footer) => {
|
||||||
const button_connect = footer.find(".button-connect");
|
const button_connect = footer.find(".button-connect");
|
||||||
|
const button_connect_tab = footer.find(".button-connect-new-tab");
|
||||||
const button_manage = body.find(".button-manage-profiles");
|
const button_manage = body.find(".button-manage-profiles");
|
||||||
|
|
||||||
const input_profile = body.find(".container-select-profile select");
|
const input_profile = body.find(".container-select-profile select");
|
||||||
|
@ -41,11 +43,9 @@ namespace Modals {
|
||||||
input_address.attr('pattern', flag_address ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_address);
|
input_address.attr('pattern', flag_address ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_address);
|
||||||
input_nickname.attr('pattern', flag_nickname ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_nickname);
|
input_nickname.attr('pattern', flag_nickname ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_nickname);
|
||||||
|
|
||||||
if(!flag_nickname || !flag_address || !selected_profile || !selected_profile.valid()) {
|
const flag_disabled = !flag_nickname || !flag_address || !selected_profile || !selected_profile.valid();
|
||||||
button_connect.prop("disabled", true);
|
button_connect.prop("disabled", flag_disabled);
|
||||||
} else {
|
button_connect_tab.prop("disabled", flag_disabled);
|
||||||
button_connect.prop("disabled", false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
input_nickname.val(settings.static_global(Settings.KEY_CONNECT_USERNAME, undefined));
|
input_nickname.val(settings.static_global(Settings.KEY_CONNECT_USERNAME, undefined));
|
||||||
|
@ -89,7 +89,24 @@ namespace Modals {
|
||||||
button_connect.on('click', event => {
|
button_connect.on('click', event => {
|
||||||
connect_modal.close();
|
connect_modal.close();
|
||||||
|
|
||||||
globalClient.startConnection(
|
const connection = server_connections.active_connection_handler();
|
||||||
|
if(connection) {
|
||||||
|
connection.startConnection(
|
||||||
|
input_address.val().toString(),
|
||||||
|
selected_profile,
|
||||||
|
input_nickname.val().toString() || selected_profile.default_username,
|
||||||
|
{password: input_password.val().toString(), hashed: false}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
button_connect_tab.trigger('click');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button_connect_tab.on('click', event => {
|
||||||
|
connect_modal.close();
|
||||||
|
|
||||||
|
const connection = server_connections.spawn_server_connection_handler();
|
||||||
|
server_connections.set_active_connection_handler(connection);
|
||||||
|
connection.startConnection(
|
||||||
input_address.val().toString(),
|
input_address.val().toString(),
|
||||||
selected_profile,
|
selected_profile,
|
||||||
input_nickname.val().toString() || selected_profile.default_username,
|
input_nickname.val().toString() || selected_profile.default_username,
|
||||||
|
@ -102,57 +119,6 @@ namespace Modals {
|
||||||
|
|
||||||
connect_modal.open();
|
connect_modal.open();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
const connectModal = createModal({
|
|
||||||
header: (tr("Create a new connection")),
|
|
||||||
body: function () {
|
|
||||||
const random_id = (() => {
|
|
||||||
const array = new Uint32Array(10);
|
|
||||||
window.crypto.getRandomValues(array);
|
|
||||||
return array.join("");
|
|
||||||
})();
|
|
||||||
|
|
||||||
let tag = $("#tmpl_connect").renderTag({
|
|
||||||
client: native_client,
|
|
||||||
forum_path: settings.static("forum_path"),
|
|
||||||
password_id: random_id
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//connect_address
|
|
||||||
return tag;
|
|
||||||
},
|
|
||||||
footer: function () {
|
|
||||||
let tag = $.spawn("div");
|
|
||||||
tag.css("text-align", "right");
|
|
||||||
tag.css("margin-top", "3px");
|
|
||||||
tag.css("margin-bottom", "6px");
|
|
||||||
tag.addClass("modal-button-group");
|
|
||||||
|
|
||||||
let button = $.spawn("button");
|
|
||||||
button.addClass("connect_connect_button");
|
|
||||||
button.text(tr("Connect"));
|
|
||||||
button.on("click", function () {
|
|
||||||
connectModal.close();
|
|
||||||
|
|
||||||
let field_address = tag.parents(".modal-content").find(".connect_address");
|
|
||||||
let address = field_address.val().toString();
|
|
||||||
globalClient.startConnection(
|
|
||||||
address,
|
|
||||||
selected_profile,
|
|
||||||
tag.parents(".modal-content").find(".connect_nickname").val().toString() || selected_profile.default_username,
|
|
||||||
{password: tag.parents(".modal-content").find(".connect_password").val().toString(), hashed: false}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
tag.append(button);
|
|
||||||
return tag;
|
|
||||||
},
|
|
||||||
|
|
||||||
width: '70%',
|
|
||||||
//flag_closeable: false
|
|
||||||
});
|
|
||||||
connectModal.open();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let Regex = {
|
let Regex = {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
export function createChannelModal(channel: ChannelEntry | undefined, parent: ChannelEntry | undefined, permissions: PermissionManager, callback: (properties?: ChannelProperties, permissions?: PermissionValue[]) => any) {
|
export function createChannelModal(connection: ConnectionHandler, channel: ChannelEntry | undefined, parent: ChannelEntry | undefined, permissions: PermissionManager, callback: (properties?: ChannelProperties, permissions?: PermissionValue[]) => any) {
|
||||||
let properties: ChannelProperties = { } as ChannelProperties; //The changes properties
|
let properties: ChannelProperties = { } as ChannelProperties; //The changes properties
|
||||||
const modal = createModal({
|
const modal = createModal({
|
||||||
header: channel ? tr("Edit channel") : tr("Create channel"),
|
header: channel ? tr("Edit channel") : tr("Create channel"),
|
||||||
|
@ -11,7 +11,7 @@ namespace Modals {
|
||||||
channel_flag_maxfamilyclients_unlimited: true,
|
channel_flag_maxfamilyclients_unlimited: true,
|
||||||
channel_flag_maxclients_unlimited: true,
|
channel_flag_maxclients_unlimited: true,
|
||||||
});
|
});
|
||||||
render_properties["channel_icon"] = globalClient.fileManager.icons.generateTag(channel ? channel.properties.channel_icon_id : 0);
|
render_properties["channel_icon"] = connection.fileManager.icons.generateTag(channel ? channel.properties.channel_icon_id : 0);
|
||||||
|
|
||||||
let template = $("#tmpl_channel_edit").renderTag(render_properties);
|
let template = $("#tmpl_channel_edit").renderTag(render_properties);
|
||||||
return template.tabify();
|
return template.tabify();
|
||||||
|
@ -36,11 +36,11 @@ namespace Modals {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
applyGeneralListener(properties, modal.htmlTag.find(".general_properties"), modal.htmlTag.find(".button_ok"), channel);
|
applyGeneralListener(connection, properties, modal.htmlTag.find(".general_properties"), modal.htmlTag.find(".button_ok"), channel);
|
||||||
applyStandardListener(properties, modal.htmlTag.find(".settings_standard"), modal.htmlTag.find(".button_ok"), parent, !channel);
|
applyStandardListener(connection, properties, modal.htmlTag.find(".settings_standard"), modal.htmlTag.find(".button_ok"), parent, !channel);
|
||||||
applyPermissionListener(properties, modal.htmlTag.find(".settings_permissions"), modal.htmlTag.find(".button_ok"), permissions, channel);
|
applyPermissionListener(connection, properties, modal.htmlTag.find(".settings_permissions"), modal.htmlTag.find(".button_ok"), permissions, channel);
|
||||||
applyAudioListener(properties, modal.htmlTag.find(".container-channel-settings-audio"), modal.htmlTag.find(".button_ok"), channel);
|
applyAudioListener(connection, properties, modal.htmlTag.find(".container-channel-settings-audio"), modal.htmlTag.find(".button_ok"), channel);
|
||||||
applyAdvancedListener(properties, modal.htmlTag.find(".settings_advanced"), modal.htmlTag.find(".button_ok"), channel);
|
applyAdvancedListener(connection, properties, modal.htmlTag.find(".settings_advanced"), modal.htmlTag.find(".button_ok"), channel);
|
||||||
|
|
||||||
let updated: PermissionValue[] = [];
|
let updated: PermissionValue[] = [];
|
||||||
modal.htmlTag.find(".button_ok").click(() => {
|
modal.htmlTag.find(".button_ok").click(() => {
|
||||||
|
@ -72,7 +72,7 @@ namespace Modals {
|
||||||
modal.htmlTag.find(".channel_name").focus();
|
modal.htmlTag.find(".channel_name").focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyGeneralListener(properties: ChannelProperties, tag: JQuery, button: JQuery, channel: ChannelEntry | undefined) {
|
function applyGeneralListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, button: JQuery, channel: ChannelEntry | undefined) {
|
||||||
let updateButton = () => {
|
let updateButton = () => {
|
||||||
if(tag.find(".input_error").length == 0)
|
if(tag.find(".input_error").length == 0)
|
||||||
button.removeAttr("disabled");
|
button.removeAttr("disabled");
|
||||||
|
@ -86,13 +86,13 @@ namespace Modals {
|
||||||
if(this.value.length < 1 || this.value.length > 40)
|
if(this.value.length < 1 || this.value.length > 40)
|
||||||
$(this).addClass("input_error");
|
$(this).addClass("input_error");
|
||||||
updateButton();
|
updateButton();
|
||||||
}).prop("disabled", channel && !globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_NAME).granted(1));
|
}).prop("disabled", channel && !connection.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_NAME).granted(1));
|
||||||
|
|
||||||
tag.find(".button-select-icon").on('click', event => {
|
tag.find(".button-select-icon").on('click', event => {
|
||||||
Modals.spawnIconSelect(globalClient, id => {
|
Modals.spawnIconSelect(connection, id => {
|
||||||
const icon_node = tag.find(".button-select-icon").find(".icon-node");
|
const icon_node = tag.find(".button-select-icon").find(".icon-node");
|
||||||
icon_node.empty();
|
icon_node.empty();
|
||||||
icon_node.append(globalClient.fileManager.icons.generateTag(id));
|
icon_node.append(connection.fileManager.icons.generateTag(id));
|
||||||
|
|
||||||
console.log("Selected icon ID: %d", id);
|
console.log("Selected icon ID: %d", id);
|
||||||
properties.channel_icon_id = id;
|
properties.channel_icon_id = id;
|
||||||
|
@ -106,18 +106,18 @@ namespace Modals {
|
||||||
|
|
||||||
$(this).removeClass("input_error");
|
$(this).removeClass("input_error");
|
||||||
if(!properties.channel_flag_password)
|
if(!properties.channel_flag_password)
|
||||||
if(globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD).granted(1))
|
if(connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD).granted(1))
|
||||||
$(this).addClass("input_error");
|
$(this).addClass("input_error");
|
||||||
updateButton();
|
updateButton();
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_PASSWORD : PermissionType.B_CHANNEL_MODIFY_PASSWORD).granted(1));
|
}).prop("disabled", !connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_PASSWORD : PermissionType.B_CHANNEL_MODIFY_PASSWORD).granted(1));
|
||||||
|
|
||||||
tag.find(".channel_topic").change(function (this: HTMLInputElement) {
|
tag.find(".channel_topic").change(function (this: HTMLInputElement) {
|
||||||
properties.channel_topic = this.value;
|
properties.channel_topic = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_TOPIC : PermissionType.B_CHANNEL_MODIFY_TOPIC).granted(1));
|
}).prop("disabled", !connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_TOPIC : PermissionType.B_CHANNEL_MODIFY_TOPIC).granted(1));
|
||||||
|
|
||||||
tag.find(".channel_description").change(function (this: HTMLInputElement) {
|
tag.find(".channel_description").change(function (this: HTMLInputElement) {
|
||||||
properties.channel_description = this.value;
|
properties.channel_description = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_DESCRIPTION : PermissionType.B_CHANNEL_MODIFY_DESCRIPTION).granted(1));
|
}).prop("disabled", !connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_DESCRIPTION : PermissionType.B_CHANNEL_MODIFY_DESCRIPTION).granted(1));
|
||||||
|
|
||||||
if(!channel) {
|
if(!channel) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -127,7 +127,7 @@ namespace Modals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyStandardListener(properties: ChannelProperties, tag: JQuery, button: JQuery, parent: ChannelEntry, create: boolean) {
|
function applyStandardListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, button: JQuery, parent: ChannelEntry, create: boolean) {
|
||||||
tag.find("input[name=\"channel_type\"]").change(function (this: HTMLInputElement) {
|
tag.find("input[name=\"channel_type\"]").change(function (this: HTMLInputElement) {
|
||||||
switch(this.value) {
|
switch(this.value) {
|
||||||
case "semi":
|
case "semi":
|
||||||
|
@ -145,11 +145,11 @@ namespace Modals {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
tag.find("input[name=\"channel_type\"][value=\"temp\"]")
|
tag.find("input[name=\"channel_type\"][value=\"temp\"]")
|
||||||
.prop("disabled", !globalClient.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_TEMPORARY : PermissionType.B_CHANNEL_MODIFY_MAKE_TEMPORARY).granted(1));
|
.prop("disabled", !connection.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_TEMPORARY : PermissionType.B_CHANNEL_MODIFY_MAKE_TEMPORARY).granted(1));
|
||||||
tag.find("input[name=\"channel_type\"][value=\"semi\"]")
|
tag.find("input[name=\"channel_type\"][value=\"semi\"]")
|
||||||
.prop("disabled", !globalClient.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT).granted(1));
|
.prop("disabled", !connection.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT).granted(1));
|
||||||
tag.find("input[name=\"channel_type\"][value=\"perm\"]")
|
tag.find("input[name=\"channel_type\"][value=\"perm\"]")
|
||||||
.prop("disabled", !globalClient.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT).granted(1));
|
.prop("disabled", !connection.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT).granted(1));
|
||||||
if(create)
|
if(create)
|
||||||
tag.find("input[name=\"channel_type\"]:not(:disabled)").last().prop("checked", true).trigger('change');
|
tag.find("input[name=\"channel_type\"]:not(:disabled)").last().prop("checked", true).trigger('change');
|
||||||
|
|
||||||
|
@ -164,26 +164,26 @@ namespace Modals {
|
||||||
tag.find("input[name=\"channel_type\"][value=\"perm\"]").prop("checked", true).trigger("change");
|
tag.find("input[name=\"channel_type\"][value=\"perm\"]").prop("checked", true).trigger("change");
|
||||||
}
|
}
|
||||||
}).prop("disabled",
|
}).prop("disabled",
|
||||||
!globalClient.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT).granted(1) ||
|
!connection.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT).granted(1) ||
|
||||||
!globalClient.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_WITH_DEFAULT : PermissionType.B_CHANNEL_MODIFY_MAKE_DEFAULT).granted(1));
|
!connection.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_WITH_DEFAULT : PermissionType.B_CHANNEL_MODIFY_MAKE_DEFAULT).granted(1));
|
||||||
|
|
||||||
tag.find("input[name=\"talk_power\"]").change(function (this: HTMLInputElement) {
|
tag.find("input[name=\"talk_power\"]").change(function (this: HTMLInputElement) {
|
||||||
properties.channel_needed_talk_power = parseInt(this.value);
|
properties.channel_needed_talk_power = parseInt(this.value);
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_WITH_NEEDED_TALK_POWER : PermissionType.B_CHANNEL_MODIFY_NEEDED_TALK_POWER).granted(1));
|
}).prop("disabled", !connection.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_WITH_NEEDED_TALK_POWER : PermissionType.B_CHANNEL_MODIFY_NEEDED_TALK_POWER).granted(1));
|
||||||
|
|
||||||
let orderTag = tag.find(".order_id");
|
let orderTag = tag.find(".order_id");
|
||||||
for(let channel of (parent ? parent.children() : globalClient.channelTree.rootChannel()))
|
for(let channel of (parent ? parent.children() : connection.channelTree.rootChannel()))
|
||||||
$.spawn("option").attr("channelId", channel.channelId.toString()).text(channel.channelName()).appendTo(orderTag);
|
$.spawn("option").attr("channelId", channel.channelId.toString()).text(channel.channelName()).appendTo(orderTag);
|
||||||
|
|
||||||
orderTag.change(function (this: HTMLSelectElement) {
|
orderTag.change(function (this: HTMLSelectElement) {
|
||||||
let selected = $(this.options.item(this.selectedIndex));
|
let selected = $(this.options.item(this.selectedIndex));
|
||||||
properties.channel_order = parseInt(selected.attr("channelId"));
|
properties.channel_order = parseInt(selected.attr("channelId"));
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_WITH_SORTORDER : PermissionType.B_CHANNEL_MODIFY_SORTORDER).granted(1));
|
}).prop("disabled", !connection.permissions.neededPermission(create ? PermissionType.B_CHANNEL_CREATE_WITH_SORTORDER : PermissionType.B_CHANNEL_MODIFY_SORTORDER).granted(1));
|
||||||
orderTag.find("option").last().prop("selected", true);
|
orderTag.find("option").last().prop("selected", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function applyPermissionListener(properties: ChannelProperties, tag: JQuery, button: JQuery, permissions: PermissionManager, channel?: ChannelEntry) {
|
function applyPermissionListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, button: JQuery, permissions: PermissionManager, channel?: ChannelEntry) {
|
||||||
let apply_permissions = (channel_permissions: PermissionValue[]) => {
|
let apply_permissions = (channel_permissions: PermissionValue[]) => {
|
||||||
console.log(tr("Got permissions: %o"), channel_permissions);
|
console.log(tr("Got permissions: %o"), channel_permissions);
|
||||||
let required_power = -2;
|
let required_power = -2;
|
||||||
|
@ -229,7 +229,7 @@ namespace Modals {
|
||||||
} else apply_permissions([]);
|
} else apply_permissions([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyAudioListener(properties: ChannelProperties, tag: JQuery, button: JQuery, channel?: ChannelEntry) {
|
function applyAudioListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, button: JQuery, channel?: ChannelEntry) {
|
||||||
let update_template = () => {
|
let update_template = () => {
|
||||||
let codec = properties.channel_codec;
|
let codec = properties.channel_codec;
|
||||||
if(!codec && channel)
|
if(!codec && channel)
|
||||||
|
@ -291,19 +291,19 @@ namespace Modals {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
tag.find("input[name=\"voice_template\"][value=\"voice_mobile\"]")
|
tag.find("input[name=\"voice_template\"][value=\"voice_mobile\"]")
|
||||||
.prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1));
|
.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1));
|
||||||
tag.find("input[name=\"voice_template\"][value=\"voice_desktop\"]")
|
tag.find("input[name=\"voice_template\"][value=\"voice_desktop\"]")
|
||||||
.prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1));
|
.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1));
|
||||||
tag.find("input[name=\"voice_template\"][value=\"music\"]")
|
tag.find("input[name=\"voice_template\"][value=\"music\"]")
|
||||||
.prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1));
|
.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1));
|
||||||
|
|
||||||
let codecs = tag.find(".voice_codec option");
|
let codecs = tag.find(".voice_codec option");
|
||||||
codecs.eq(0).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX8).granted(1));
|
codecs.eq(0).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX8).granted(1));
|
||||||
codecs.eq(1).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX16).granted(1));
|
codecs.eq(1).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX16).granted(1));
|
||||||
codecs.eq(2).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX32).granted(1));
|
codecs.eq(2).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX32).granted(1));
|
||||||
codecs.eq(3).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_CELTMONO48).granted(1));
|
codecs.eq(3).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_CELTMONO48).granted(1));
|
||||||
codecs.eq(4).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1));
|
codecs.eq(4).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1));
|
||||||
codecs.eq(5).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1));
|
codecs.eq(5).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1));
|
||||||
tag.find(".voice_codec").change(function (this: HTMLSelectElement) {
|
tag.find(".voice_codec").change(function (this: HTMLSelectElement) {
|
||||||
if($(this.item(this.selectedIndex)).prop("disabled")) return false;
|
if($(this.item(this.selectedIndex)).prop("disabled")) return false;
|
||||||
|
|
||||||
|
@ -322,25 +322,25 @@ namespace Modals {
|
||||||
quality_slider.on('input', event => change_quality(parseInt(quality_slider.val() as string)));
|
quality_slider.on('input', event => change_quality(parseInt(quality_slider.val() as string)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyAdvancedListener(properties: ChannelProperties, tag: JQuery, button: JQuery, channel?: ChannelEntry) {
|
function applyAdvancedListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, button: JQuery, channel?: ChannelEntry) {
|
||||||
tag.find(".channel_name_phonetic").change(function (this: HTMLInputElement) {
|
tag.find(".channel_name_phonetic").change(function (this: HTMLInputElement) {
|
||||||
properties.channel_topic = this.value;
|
properties.channel_topic = this.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
tag.find(".channel_delete_delay").change(function (this: HTMLInputElement) {
|
tag.find(".channel_delete_delay").change(function (this: HTMLInputElement) {
|
||||||
properties.channel_delete_delay = parseInt(this.value);
|
properties.channel_delete_delay = parseInt(this.value);
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_TEMP_DELETE_DELAY).granted(1));
|
}).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_TEMP_DELETE_DELAY).granted(1));
|
||||||
|
|
||||||
tag.find(".channel_codec_is_unencrypted").change(function (this: HTMLInputElement) {
|
tag.find(".channel_codec_is_unencrypted").change(function (this: HTMLInputElement) {
|
||||||
properties.channel_codec_is_unencrypted = parseInt(this.value) == 0;
|
properties.channel_codec_is_unencrypted = parseInt(this.value) == 0;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED).granted(1));
|
}).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED).granted(1));
|
||||||
|
|
||||||
{
|
{
|
||||||
let tag_infinity = tag.find("input[name=\"max_users\"][value=\"infinity\"]");
|
let tag_infinity = tag.find("input[name=\"max_users\"][value=\"infinity\"]");
|
||||||
let tag_limited = tag.find("input[name=\"max_users\"][value=\"limited\"]");
|
let tag_limited = tag.find("input[name=\"max_users\"][value=\"limited\"]");
|
||||||
let tag_limited_value = tag.find(".channel_maxclients");
|
let tag_limited_value = tag.find(".channel_maxclients");
|
||||||
|
|
||||||
if(!globalClient.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_MAXCLIENTS : PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS).granted(1)) {
|
if(!connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_MAXCLIENTS : PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS).granted(1)) {
|
||||||
tag_infinity.prop("disabled", true);
|
tag_infinity.prop("disabled", true);
|
||||||
tag_limited.prop("disabled", true);
|
tag_limited.prop("disabled", true);
|
||||||
tag_limited_value.prop("disabled", true);
|
tag_limited_value.prop("disabled", true);
|
||||||
|
@ -363,7 +363,7 @@ namespace Modals {
|
||||||
let tag_limited = tag.find("input[name=\"max_users_family\"][value=\"limited\"]");
|
let tag_limited = tag.find("input[name=\"max_users_family\"][value=\"limited\"]");
|
||||||
let tag_limited_value = tag.find(".channel_maxfamilyclients");
|
let tag_limited_value = tag.find(".channel_maxfamilyclients");
|
||||||
|
|
||||||
if(!globalClient.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_MAXCLIENTS : PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS).granted(1)) {
|
if(!connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_MAXCLIENTS : PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS).granted(1)) {
|
||||||
tag_inherited.prop("disabled", true);
|
tag_inherited.prop("disabled", true);
|
||||||
tag_infinity.prop("disabled", true);
|
tag_infinity.prop("disabled", true);
|
||||||
tag_limited.prop("disabled", true);
|
tag_limited.prop("disabled", true);
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
/// <reference path="../../ConnectionHandler.ts" />
|
||||||
/// <reference path="../../proto.ts" />
|
/// <reference path="../../proto.ts" />
|
||||||
/// <reference path="../../client.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
//TODO Upload/delete button
|
//TODO Upload/delete button
|
||||||
export function spawnIconSelect(client: TSClient, callback_icon?: (id: number) => any, selected_icon?: number) {
|
export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id: number) => any, selected_icon?: number) {
|
||||||
callback_icon = callback_icon || (() => {});
|
callback_icon = callback_icon || (() => {});
|
||||||
selected_icon = selected_icon || 0;
|
selected_icon = selected_icon || 0;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
/// <reference path="../../ConnectionHandler.ts" />
|
||||||
/// <reference path="../../proto.ts" />
|
/// <reference path="../../proto.ts" />
|
||||||
/// <reference path="../../client.ts" />
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: Check needed permissions and may not even try to request, because we dont have the permission. Permissions:
|
TODO: Check needed permissions and may not even try to request, because we dont have the permission. Permissions:
|
||||||
|
@ -542,7 +542,7 @@ namespace Modals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function spawnPermissionEdit() : Modal {
|
export function spawnPermissionEdit(connection: ConnectionHandler) : Modal {
|
||||||
const connectModal = createModal({
|
const connectModal = createModal({
|
||||||
header: function() {
|
header: function() {
|
||||||
return tr("Server Permissions");
|
return tr("Server Permissions");
|
||||||
|
@ -551,7 +551,7 @@ namespace Modals {
|
||||||
let properties: any = {};
|
let properties: any = {};
|
||||||
|
|
||||||
let tag = $("#tmpl_server_permissions").renderTag(properties);
|
let tag = $("#tmpl_server_permissions").renderTag(properties);
|
||||||
const pe = new PermissionEditor(globalClient.permissions.groupedPermissions());
|
const pe = new PermissionEditor(connection.permissions.groupedPermissions());
|
||||||
pe.build_tag();
|
pe.build_tag();
|
||||||
/* initialisation */
|
/* initialisation */
|
||||||
{
|
{
|
||||||
|
@ -560,11 +560,11 @@ namespace Modals {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
apply_server_groups(pe, tag.find(".tab-group-server"));
|
apply_server_groups(connection, pe, tag.find(".tab-group-server"));
|
||||||
apply_channel_groups(pe, tag.find(".tab-group-channel"));
|
apply_channel_groups(connection, pe, tag.find(".tab-group-channel"));
|
||||||
apply_channel_permission(pe, tag.find(".tab-channel"));
|
apply_channel_permission(connection, pe, tag.find(".tab-channel"));
|
||||||
apply_client_permission(pe, tag.find(".tab-client"));
|
apply_client_permission(connection, pe, tag.find(".tab-client"));
|
||||||
apply_client_channel_permission(pe, tag.find(".tab-client-channel"));
|
apply_client_channel_permission(connection, pe, tag.find(".tab-client-channel"));
|
||||||
return tag.tabify(false);
|
return tag.tabify(false);
|
||||||
},
|
},
|
||||||
footer: undefined,
|
footer: undefined,
|
||||||
|
@ -583,16 +583,16 @@ namespace Modals {
|
||||||
return connectModal;
|
return connectModal;
|
||||||
}
|
}
|
||||||
|
|
||||||
function build_channel_tree(channel_list: JQuery, select_callback: (channel: ChannelEntry) => any) {
|
function build_channel_tree(connection: ConnectionHandler, channel_list: JQuery, select_callback: (channel: ChannelEntry) => any) {
|
||||||
const root = globalClient.channelTree.get_first_channel();
|
const root = connection.channelTree.get_first_channel();
|
||||||
if(!root) return;
|
if(!root) return;
|
||||||
|
|
||||||
const build_channel = (channel: ChannelEntry) => {
|
const build_channel = (channel: ChannelEntry) => {
|
||||||
let tag = $.spawn("div").addClass("channel").attr("channel-id", channel.channelId);
|
let tag = $.spawn("div").addClass("channel").attr("channel-id", channel.channelId);
|
||||||
globalClient.fileManager.icons.generateTag(channel.properties.channel_icon_id).appendTo(tag);
|
connection.fileManager.icons.generateTag(channel.properties.channel_icon_id).appendTo(tag);
|
||||||
{
|
{
|
||||||
let name = $.spawn("a").text(channel.channelName() + " (" + channel.channelId + ")").addClass("name");
|
let name = $.spawn("a").text(channel.channelName() + " (" + channel.channelId + ")").addClass("name");
|
||||||
//if(globalClient.channelTree.server.properties. == group.id)
|
//if(connection.channelTree.server.properties. == group.id)
|
||||||
// name.addClass("default");
|
// name.addClass("default");
|
||||||
name.appendTo(tag);
|
name.appendTo(tag);
|
||||||
}
|
}
|
||||||
|
@ -619,7 +619,7 @@ namespace Modals {
|
||||||
setTimeout(() => channel_list.find('.channel').first().trigger('click'), 0);
|
setTimeout(() => channel_list.find('.channel').first().trigger('click'), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function apply_client_channel_permission(editor: PermissionEditor, tab_tag: JQuery) {
|
function apply_client_channel_permission(connection: ConnectionHandler, editor: PermissionEditor, tab_tag: JQuery) {
|
||||||
let current_cldbid: number = 0;
|
let current_cldbid: number = 0;
|
||||||
let current_channel: ChannelEntry;
|
let current_channel: ChannelEntry;
|
||||||
|
|
||||||
|
@ -629,7 +629,7 @@ namespace Modals {
|
||||||
tab_tag.on('show', event => {
|
tab_tag.on('show', event => {
|
||||||
console.error("Channel tab show");
|
console.error("Channel tab show");
|
||||||
pe_client.append(editor.container);
|
pe_client.append(editor.container);
|
||||||
if(globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST).granted(1)) {
|
if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST).granted(1)) {
|
||||||
if(current_cldbid && current_channel)
|
if(current_cldbid && current_channel)
|
||||||
editor.set_mode(PermissionEditorMode.VISIBLE);
|
editor.set_mode(PermissionEditorMode.VISIBLE);
|
||||||
else
|
else
|
||||||
|
@ -643,7 +643,7 @@ namespace Modals {
|
||||||
editor.set_listener_update(() => {
|
editor.set_listener_update(() => {
|
||||||
if(!current_cldbid || !current_channel) return;
|
if(!current_cldbid || !current_channel) return;
|
||||||
|
|
||||||
globalClient.permissions.requestClientChannelPermissions(current_cldbid, current_channel.channelId).then(result => {
|
connection.permissions.requestClientChannelPermissions(current_cldbid, current_channel.channelId).then(result => {
|
||||||
editor.set_permissions(result);
|
editor.set_permissions(result);
|
||||||
editor.set_mode(PermissionEditorMode.VISIBLE);
|
editor.set_mode(PermissionEditorMode.VISIBLE);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
@ -665,7 +665,7 @@ namespace Modals {
|
||||||
permission.id,
|
permission.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("channelclientdelperm", {
|
return connection.serverConnection.send_command("channelclientdelperm", {
|
||||||
cldbid: current_cldbid,
|
cldbid: current_cldbid,
|
||||||
cid: current_channel.channelId,
|
cid: current_channel.channelId,
|
||||||
permid: permission.id,
|
permid: permission.id,
|
||||||
|
@ -677,7 +677,7 @@ namespace Modals {
|
||||||
value.granted,
|
value.granted,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("channelclientdelperm", {
|
return connection.serverConnection.send_command("channelclientdelperm", {
|
||||||
cldbid: current_cldbid,
|
cldbid: current_cldbid,
|
||||||
cid: current_channel.channelId,
|
cid: current_channel.channelId,
|
||||||
permid: permission.id_grant(),
|
permid: permission.id_grant(),
|
||||||
|
@ -694,7 +694,7 @@ namespace Modals {
|
||||||
value.flag_negate
|
value.flag_negate
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("channelclientaddperm", {
|
return connection.serverConnection.send_command("channelclientaddperm", {
|
||||||
cldbid: current_cldbid,
|
cldbid: current_cldbid,
|
||||||
cid: current_channel.channelId,
|
cid: current_channel.channelId,
|
||||||
permid: permission.id,
|
permid: permission.id,
|
||||||
|
@ -709,7 +709,7 @@ namespace Modals {
|
||||||
value.granted,
|
value.granted,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("channelclientaddperm", {
|
return connection.serverConnection.send_command("channelclientaddperm", {
|
||||||
cldbid: current_cldbid,
|
cldbid: current_cldbid,
|
||||||
cid: current_channel.channelId,
|
cid: current_channel.channelId,
|
||||||
permid: permission.id_grant(),
|
permid: permission.id_grant(),
|
||||||
|
@ -726,7 +726,7 @@ namespace Modals {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
build_channel_tree(tab_tag.find(".list-channel .entries"), channel => {
|
build_channel_tree(connection, tab_tag.find(".list-channel .entries"), channel => {
|
||||||
if(current_channel == channel) return;
|
if(current_channel == channel) return;
|
||||||
|
|
||||||
current_channel = channel;
|
current_channel = channel;
|
||||||
|
@ -745,7 +745,7 @@ namespace Modals {
|
||||||
|
|
||||||
const resolve_client = () => {
|
const resolve_client = () => {
|
||||||
let client_uid = tag_select_uid.val() as string;
|
let client_uid = tag_select_uid.val() as string;
|
||||||
globalClient.serverConnection.command_helper.info_from_uid(client_uid).then(result => {
|
connection.serverConnection.command_helper.info_from_uid(client_uid).then(result => {
|
||||||
if(!result || result.length == 0) return Promise.reject("invalid data");
|
if(!result || result.length == 0) return Promise.reject("invalid data");
|
||||||
tag_select_uid.attr('pattern', null).removeClass('is-invalid');
|
tag_select_uid.attr('pattern', null).removeClass('is-invalid');
|
||||||
|
|
||||||
|
@ -778,7 +778,7 @@ namespace Modals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function apply_client_permission(editor: PermissionEditor, tab_tag: JQuery) {
|
function apply_client_permission(connection: ConnectionHandler, editor: PermissionEditor, tab_tag: JQuery) {
|
||||||
let current_cldbid: number = 0;
|
let current_cldbid: number = 0;
|
||||||
|
|
||||||
/* the editor */
|
/* the editor */
|
||||||
|
@ -788,7 +788,7 @@ namespace Modals {
|
||||||
console.error("Channel tab show");
|
console.error("Channel tab show");
|
||||||
|
|
||||||
pe_client.append(editor.container);
|
pe_client.append(editor.container);
|
||||||
if(globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST).granted(1)) {
|
if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST).granted(1)) {
|
||||||
if(current_cldbid)
|
if(current_cldbid)
|
||||||
editor.set_mode(PermissionEditorMode.VISIBLE);
|
editor.set_mode(PermissionEditorMode.VISIBLE);
|
||||||
else
|
else
|
||||||
|
@ -801,7 +801,7 @@ namespace Modals {
|
||||||
editor.set_listener_update(() => {
|
editor.set_listener_update(() => {
|
||||||
if(!current_cldbid) return;
|
if(!current_cldbid) return;
|
||||||
|
|
||||||
globalClient.permissions.requestClientPermissions(current_cldbid).then(result => {
|
connection.permissions.requestClientPermissions(current_cldbid).then(result => {
|
||||||
editor.set_permissions(result);
|
editor.set_permissions(result);
|
||||||
editor.set_mode(PermissionEditorMode.VISIBLE);
|
editor.set_mode(PermissionEditorMode.VISIBLE);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
@ -821,7 +821,7 @@ namespace Modals {
|
||||||
permission.id,
|
permission.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("clientaddperm", {
|
return connection.serverConnection.send_command("clientaddperm", {
|
||||||
cldbid: current_cldbid,
|
cldbid: current_cldbid,
|
||||||
permid: permission.id,
|
permid: permission.id,
|
||||||
});
|
});
|
||||||
|
@ -832,7 +832,7 @@ namespace Modals {
|
||||||
value.granted,
|
value.granted,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("clientaddperm", {
|
return connection.serverConnection.send_command("clientaddperm", {
|
||||||
cldbid: current_cldbid,
|
cldbid: current_cldbid,
|
||||||
permid: permission.id_grant(),
|
permid: permission.id_grant(),
|
||||||
});
|
});
|
||||||
|
@ -848,7 +848,7 @@ namespace Modals {
|
||||||
value.flag_negate
|
value.flag_negate
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("clientaddperm", {
|
return connection.serverConnection.send_command("clientaddperm", {
|
||||||
cldbid: current_cldbid,
|
cldbid: current_cldbid,
|
||||||
permid: permission.id,
|
permid: permission.id,
|
||||||
permvalue: value.value,
|
permvalue: value.value,
|
||||||
|
@ -862,7 +862,7 @@ namespace Modals {
|
||||||
value.granted,
|
value.granted,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("clientaddperm", {
|
return connection.serverConnection.send_command("clientaddperm", {
|
||||||
cldbid: current_cldbid,
|
cldbid: current_cldbid,
|
||||||
permid: permission.id_grant(),
|
permid: permission.id_grant(),
|
||||||
permvalue: value.granted,
|
permvalue: value.granted,
|
||||||
|
@ -888,7 +888,7 @@ namespace Modals {
|
||||||
|
|
||||||
const resolve_client = () => {
|
const resolve_client = () => {
|
||||||
let client_uid = tag_select_uid.val() as string;
|
let client_uid = tag_select_uid.val() as string;
|
||||||
globalClient.serverConnection.command_helper.info_from_uid(client_uid).then(result => {
|
connection.serverConnection.command_helper.info_from_uid(client_uid).then(result => {
|
||||||
if(!result || result.length == 0) return Promise.reject("invalid data");
|
if(!result || result.length == 0) return Promise.reject("invalid data");
|
||||||
tag_select_uid.attr('pattern', null).removeClass('is-invalid');
|
tag_select_uid.attr('pattern', null).removeClass('is-invalid');
|
||||||
|
|
||||||
|
@ -920,7 +920,7 @@ namespace Modals {
|
||||||
tab_tag.find(".client-select-uid").on('change', event => resolve_client());
|
tab_tag.find(".client-select-uid").on('change', event => resolve_client());
|
||||||
}
|
}
|
||||||
|
|
||||||
function apply_channel_permission(editor: PermissionEditor, tab_tag: JQuery) {
|
function apply_channel_permission(connection: ConnectionHandler, editor: PermissionEditor, tab_tag: JQuery) {
|
||||||
let current_channel: ChannelEntry | undefined;
|
let current_channel: ChannelEntry | undefined;
|
||||||
|
|
||||||
/* the editor */
|
/* the editor */
|
||||||
|
@ -929,7 +929,7 @@ namespace Modals {
|
||||||
tab_tag.on('show', event => {
|
tab_tag.on('show', event => {
|
||||||
console.error("Channel tab show");
|
console.error("Channel tab show");
|
||||||
pe_channel.append(editor.container);
|
pe_channel.append(editor.container);
|
||||||
if(globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST).granted(1))
|
if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST).granted(1))
|
||||||
editor.set_mode(PermissionEditorMode.VISIBLE);
|
editor.set_mode(PermissionEditorMode.VISIBLE);
|
||||||
else {
|
else {
|
||||||
editor.set_mode(PermissionEditorMode.NO_PERMISSION);
|
editor.set_mode(PermissionEditorMode.NO_PERMISSION);
|
||||||
|
@ -939,7 +939,7 @@ namespace Modals {
|
||||||
editor.set_listener_update(() => {
|
editor.set_listener_update(() => {
|
||||||
if(!current_channel) return;
|
if(!current_channel) return;
|
||||||
|
|
||||||
globalClient.permissions.requestChannelPermissions(current_channel.channelId).then(result => editor.set_permissions(result)).catch(error => {
|
connection.permissions.requestChannelPermissions(current_channel.channelId).then(result => editor.set_permissions(result)).catch(error => {
|
||||||
console.log(error); //TODO handling?
|
console.log(error); //TODO handling?
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -956,7 +956,7 @@ namespace Modals {
|
||||||
permission.id,
|
permission.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("channeldelperm", {
|
return connection.serverConnection.send_command("channeldelperm", {
|
||||||
cid: current_channel.channelId,
|
cid: current_channel.channelId,
|
||||||
permid: permission.id,
|
permid: permission.id,
|
||||||
});
|
});
|
||||||
|
@ -968,7 +968,7 @@ namespace Modals {
|
||||||
value.granted,
|
value.granted,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("channeldelperm", {
|
return connection.serverConnection.send_command("channeldelperm", {
|
||||||
cid: current_channel.channelId,
|
cid: current_channel.channelId,
|
||||||
permid: permission.id_grant(),
|
permid: permission.id_grant(),
|
||||||
});
|
});
|
||||||
|
@ -984,7 +984,7 @@ namespace Modals {
|
||||||
value.flag_negate
|
value.flag_negate
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("channeladdperm", {
|
return connection.serverConnection.send_command("channeladdperm", {
|
||||||
cid: current_channel.channelId,
|
cid: current_channel.channelId,
|
||||||
permid: permission.id,
|
permid: permission.id,
|
||||||
permvalue: value.value,
|
permvalue: value.value,
|
||||||
|
@ -999,7 +999,7 @@ namespace Modals {
|
||||||
value.granted,
|
value.granted,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("channeladdperm", {
|
return connection.serverConnection.send_command("channeladdperm", {
|
||||||
cid: current_channel.channelId,
|
cid: current_channel.channelId,
|
||||||
permid: permission.id_grant(),
|
permid: permission.id_grant(),
|
||||||
permvalue: value.granted,
|
permvalue: value.granted,
|
||||||
|
@ -1016,13 +1016,13 @@ namespace Modals {
|
||||||
}
|
}
|
||||||
|
|
||||||
let channel_list = tab_tag.find(".list-channel .entries");
|
let channel_list = tab_tag.find(".list-channel .entries");
|
||||||
build_channel_tree(channel_list, channel => {
|
build_channel_tree(connection, channel_list, channel => {
|
||||||
current_channel = channel;
|
current_channel = channel;
|
||||||
editor.trigger_update();
|
editor.trigger_update();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function apply_channel_groups(editor: PermissionEditor, tab_tag: JQuery) {
|
function apply_channel_groups(connection: ConnectionHandler, editor: PermissionEditor, tab_tag: JQuery) {
|
||||||
let current_group;
|
let current_group;
|
||||||
|
|
||||||
/* the editor */
|
/* the editor */
|
||||||
|
@ -1031,7 +1031,7 @@ namespace Modals {
|
||||||
tab_tag.on('show', event => {
|
tab_tag.on('show', event => {
|
||||||
console.error("Channel group tab show");
|
console.error("Channel group tab show");
|
||||||
pe_server.append(editor.container);
|
pe_server.append(editor.container);
|
||||||
if(globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST).granted(1))
|
if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST).granted(1))
|
||||||
editor.set_mode(PermissionEditorMode.VISIBLE);
|
editor.set_mode(PermissionEditorMode.VISIBLE);
|
||||||
else {
|
else {
|
||||||
editor.set_mode(PermissionEditorMode.NO_PERMISSION);
|
editor.set_mode(PermissionEditorMode.NO_PERMISSION);
|
||||||
|
@ -1041,7 +1041,7 @@ namespace Modals {
|
||||||
editor.set_listener_update(() => {
|
editor.set_listener_update(() => {
|
||||||
if(!current_group) return;
|
if(!current_group) return;
|
||||||
|
|
||||||
globalClient.groups.request_permissions(current_group).then(result => editor.set_permissions(result)).catch(error => {
|
connection.groups.request_permissions(current_group).then(result => editor.set_permissions(result)).catch(error => {
|
||||||
console.log(error); //TODO handling?
|
console.log(error); //TODO handling?
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1058,7 +1058,7 @@ namespace Modals {
|
||||||
permission.id,
|
permission.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("channelgroupdelperm", {
|
return connection.serverConnection.send_command("channelgroupdelperm", {
|
||||||
cgid: current_group.id,
|
cgid: current_group.id,
|
||||||
permid: permission.id,
|
permid: permission.id,
|
||||||
});
|
});
|
||||||
|
@ -1069,7 +1069,7 @@ namespace Modals {
|
||||||
value.granted,
|
value.granted,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("channelgroupdelperm", {
|
return connection.serverConnection.send_command("channelgroupdelperm", {
|
||||||
cgid: current_group.id,
|
cgid: current_group.id,
|
||||||
permid: permission.id_grant(),
|
permid: permission.id_grant(),
|
||||||
});
|
});
|
||||||
|
@ -1085,7 +1085,7 @@ namespace Modals {
|
||||||
value.flag_negate
|
value.flag_negate
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("channelgroupaddperm", {
|
return connection.serverConnection.send_command("channelgroupaddperm", {
|
||||||
cgid: current_group.id,
|
cgid: current_group.id,
|
||||||
permid: permission.id,
|
permid: permission.id,
|
||||||
permvalue: value.value,
|
permvalue: value.value,
|
||||||
|
@ -1099,7 +1099,7 @@ namespace Modals {
|
||||||
value.granted,
|
value.granted,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("channelgroupaddperm", {
|
return connection.serverConnection.send_command("channelgroupaddperm", {
|
||||||
cgid: current_group.id,
|
cgid: current_group.id,
|
||||||
permid: permission.id_grant(),
|
permid: permission.id_grant(),
|
||||||
permvalue: value.granted,
|
permvalue: value.granted,
|
||||||
|
@ -1120,14 +1120,24 @@ namespace Modals {
|
||||||
{
|
{
|
||||||
let group_list = tab_tag.find(".list-group-channel .entries");
|
let group_list = tab_tag.find(".list-group-channel .entries");
|
||||||
|
|
||||||
for(let group of globalClient.groups.channelGroups.sort(GroupManager.sorter())) {
|
const allow_query_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1);
|
||||||
|
const allow_template_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1);
|
||||||
|
for(let group of connection.groups.channelGroups.sort(GroupManager.sorter())) {
|
||||||
|
if(group.type == GroupType.QUERY) {
|
||||||
|
if(!allow_query_groups)
|
||||||
|
continue;
|
||||||
|
} else if(group.type == GroupType.TEMPLATE) {
|
||||||
|
if(!allow_template_groups)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let tag = $.spawn("div").addClass("group").attr("group-id", group.id);
|
let tag = $.spawn("div").addClass("group").attr("group-id", group.id);
|
||||||
globalClient.fileManager.icons.generateTag(group.properties.iconid).appendTo(tag);
|
connection.fileManager.icons.generateTag(group.properties.iconid).appendTo(tag);
|
||||||
{
|
{
|
||||||
let name = $.spawn("a").text(group.name + " (" + group.id + ")").addClass("name");
|
let name = $.spawn("a").text(group.name + " (" + group.id + ")").addClass("name");
|
||||||
if(group.properties.savedb)
|
if(group.properties.savedb)
|
||||||
name.addClass("savedb");
|
name.addClass("savedb");
|
||||||
if(globalClient.channelTree.server.properties.virtualserver_default_channel_group == group.id)
|
if(connection.channelTree.server.properties.virtualserver_default_channel_group == group.id)
|
||||||
name.addClass("default");
|
name.addClass("default");
|
||||||
name.appendTo(tag);
|
name.appendTo(tag);
|
||||||
}
|
}
|
||||||
|
@ -1156,21 +1166,30 @@ namespace Modals {
|
||||||
b_virtualserver_channelclient_permission_list
|
b_virtualserver_channelclient_permission_list
|
||||||
b_virtualserver_playlist_permission_list
|
b_virtualserver_playlist_permission_list
|
||||||
*/
|
*/
|
||||||
function apply_server_groups(editor: PermissionEditor, tab_tag: JQuery) {
|
function apply_server_groups(connection: ConnectionHandler, editor: PermissionEditor, tab_tag: JQuery) {
|
||||||
let current_group;
|
let current_group;
|
||||||
|
|
||||||
/* list all groups */
|
/* list all groups */
|
||||||
{
|
{
|
||||||
let group_list = tab_tag.find(".list-group-server .entries");
|
let group_list = tab_tag.find(".list-group-server .entries");
|
||||||
|
|
||||||
for(let group of globalClient.groups.serverGroups.sort(GroupManager.sorter())) {
|
const allow_query_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1);
|
||||||
|
const allow_template_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1);
|
||||||
|
for(const group of connection.groups.serverGroups.sort(GroupManager.sorter())) {
|
||||||
|
if(group.type == GroupType.QUERY) {
|
||||||
|
if(!allow_query_groups)
|
||||||
|
continue;
|
||||||
|
} else if(group.type == GroupType.TEMPLATE) {
|
||||||
|
if(!allow_template_groups)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let tag = $.spawn("div").addClass("group").attr("group-id", group.id);
|
let tag = $.spawn("div").addClass("group").attr("group-id", group.id);
|
||||||
globalClient.fileManager.icons.generateTag(group.properties.iconid).appendTo(tag);
|
connection.fileManager.icons.generateTag(group.properties.iconid).appendTo(tag);
|
||||||
{
|
{
|
||||||
let name = $.spawn("a").text(group.name + " (" + group.id + ")").addClass("name");
|
let name = $.spawn("a").text(group.name + " (" + group.id + ")").addClass("name");
|
||||||
if(group.properties.savedb)
|
if(group.properties.savedb)
|
||||||
name.addClass("savedb");
|
name.addClass("savedb");
|
||||||
if(globalClient.channelTree.server.properties.virtualserver_default_server_group == group.id)
|
if(connection.channelTree.server.properties.virtualserver_default_server_group == group.id)
|
||||||
name.addClass("default");
|
name.addClass("default");
|
||||||
name.appendTo(tag);
|
name.appendTo(tag);
|
||||||
}
|
}
|
||||||
|
@ -1194,14 +1213,14 @@ namespace Modals {
|
||||||
tab_tag.on('show', event => {
|
tab_tag.on('show', event => {
|
||||||
console.error("Server tab show");
|
console.error("Server tab show");
|
||||||
pe_server.append(editor.container);
|
pe_server.append(editor.container);
|
||||||
if(globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST).granted(1))
|
if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST).granted(1))
|
||||||
editor.set_mode(PermissionEditorMode.VISIBLE);
|
editor.set_mode(PermissionEditorMode.VISIBLE);
|
||||||
else {
|
else {
|
||||||
editor.set_mode(PermissionEditorMode.NO_PERMISSION);
|
editor.set_mode(PermissionEditorMode.NO_PERMISSION);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
editor.set_listener_update(() => {
|
editor.set_listener_update(() => {
|
||||||
globalClient.groups.request_permissions(current_group).then(result => editor.set_permissions(result)).catch(error => {
|
connection.groups.request_permissions(current_group).then(result => editor.set_permissions(result)).catch(error => {
|
||||||
console.log(error); //TODO handling?
|
console.log(error); //TODO handling?
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1218,7 +1237,7 @@ namespace Modals {
|
||||||
permission.id,
|
permission.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("servergroupdelperm", {
|
return connection.serverConnection.send_command("servergroupdelperm", {
|
||||||
sgid: current_group.id,
|
sgid: current_group.id,
|
||||||
permid: permission.id,
|
permid: permission.id,
|
||||||
});
|
});
|
||||||
|
@ -1229,7 +1248,7 @@ namespace Modals {
|
||||||
value.granted,
|
value.granted,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("servergroupdelperm", {
|
return connection.serverConnection.send_command("servergroupdelperm", {
|
||||||
sgid: current_group.id,
|
sgid: current_group.id,
|
||||||
permid: permission.id_grant(),
|
permid: permission.id_grant(),
|
||||||
});
|
});
|
||||||
|
@ -1245,7 +1264,7 @@ namespace Modals {
|
||||||
value.flag_negate
|
value.flag_negate
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("servergroupaddperm", {
|
return connection.serverConnection.send_command("servergroupaddperm", {
|
||||||
sgid: current_group.id,
|
sgid: current_group.id,
|
||||||
permid: permission.id,
|
permid: permission.id,
|
||||||
permvalue: value.value,
|
permvalue: value.value,
|
||||||
|
@ -1259,7 +1278,7 @@ namespace Modals {
|
||||||
value.granted,
|
value.granted,
|
||||||
);
|
);
|
||||||
|
|
||||||
return globalClient.serverConnection.send_command("servergroupaddperm", {
|
return connection.serverConnection.send_command("servergroupaddperm", {
|
||||||
sgid: current_group.id,
|
sgid: current_group.id,
|
||||||
permid: permission.id_grant(),
|
permid: permission.id_grant(),
|
||||||
permvalue: value.granted,
|
permvalue: value.granted,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
/// <reference path="../../ConnectionHandler.ts" />
|
||||||
/// <reference path="../../proto.ts" />
|
/// <reference path="../../proto.ts" />
|
||||||
/// <reference path="../../client.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
export function spawnPlaylistSongInfo(song: PlaylistSong) {
|
export function spawnPlaylistSongInfo(song: PlaylistSong) {
|
||||||
|
@ -68,7 +68,7 @@ namespace Modals {
|
||||||
modal.open();
|
modal.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function spawnPlaylistEdit(client: TSClient, playlist: Playlist) {
|
export function spawnPlaylistEdit(client: ConnectionHandler, playlist: Playlist) {
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
let changed_properties = {};
|
let changed_properties = {};
|
||||||
let changed_permissions = {};
|
let changed_permissions = {};
|
||||||
|
@ -153,7 +153,7 @@ namespace Modals {
|
||||||
return modal;
|
return modal;
|
||||||
}
|
}
|
||||||
|
|
||||||
function apply_songs(tag: JQuery, client: TSClient, playlist: Playlist) {
|
function apply_songs(tag: JQuery, client: ConnectionHandler, playlist: Playlist) {
|
||||||
const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id;
|
const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id;
|
||||||
const song_tag = tag.find(".container-songs");
|
const song_tag = tag.find(".container-songs");
|
||||||
|
|
||||||
|
@ -254,7 +254,7 @@ namespace Modals {
|
||||||
return set_current_song;
|
return set_current_song;
|
||||||
}
|
}
|
||||||
|
|
||||||
function apply_permissions(tag: JQuery, client: TSClient, playlist: Playlist, change_permission: (key: string, value: number) => any) {
|
function apply_permissions(tag: JQuery, client: ConnectionHandler, playlist: Playlist, change_permission: (key: string, value: number) => any) {
|
||||||
const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id;
|
const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id;
|
||||||
const permission_tag = tag.find(".container-permissions");
|
const permission_tag = tag.find(".container-permissions");
|
||||||
const nopermission_tag = tag.find(".container-no-permissions");
|
const nopermission_tag = tag.find(".container-no-permissions");
|
||||||
|
@ -294,7 +294,7 @@ namespace Modals {
|
||||||
return update_permissions;
|
return update_permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function apply_properties(tag: JQuery, client: TSClient, playlist: Playlist, change_property: (key: string, value: string) => any, callback_current_song: (id: number) => any) {
|
function apply_properties(tag: JQuery, client: ConnectionHandler, playlist: Playlist, change_property: (key: string, value: string) => any, callback_current_song: (id: number) => any) {
|
||||||
const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id;
|
const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id;
|
||||||
|
|
||||||
client.serverConnection.command_helper.request_playlist_info(playlist.playlist_id).then(info => {
|
client.serverConnection.command_helper.request_playlist_info(playlist.playlist_id).then(info => {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
/// <reference path="../../ConnectionHandler.ts" />
|
||||||
/// <reference path="../../proto.ts" />
|
/// <reference path="../../proto.ts" />
|
||||||
/// <reference path="../../client.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
export function spawnPlaylistManage(client: TSClient) {
|
export function spawnPlaylistManage(client: ConnectionHandler) {
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
let selected_playlist: Playlist;
|
let selected_playlist: Playlist;
|
||||||
let available_playlists: Playlist[];
|
let available_playlists: Playlist[];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
/// <reference path="../../ConnectionHandler.ts" />
|
||||||
/// <reference path="../../proto.ts" />
|
/// <reference path="../../proto.ts" />
|
||||||
/// <reference path="../../client.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
export function spawnPoke(invoker: {
|
export function spawnPoke(invoker: {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
/// <reference path="../../ConnectionHandler.ts" />
|
||||||
/// <reference path="../../proto.ts" />
|
/// <reference path="../../proto.ts" />
|
||||||
/// <reference path="../../client.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
export function spawnQueryCreate(callback_created?: (user, pass) => any) {
|
export function spawnQueryCreate(connection: ConnectionHandler, callback_created?: (user, pass) => any) {
|
||||||
let modal;
|
let modal;
|
||||||
modal = createModal({
|
modal = createModal({
|
||||||
header: tr("Create a server query login"),
|
header: tr("Create a server query login"),
|
||||||
|
@ -34,11 +34,11 @@ namespace Modals {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
globalClient.serverConnection.command_handler_boss().register_single_handler(single_handler);
|
connection.serverConnection.command_handler_boss().register_single_handler(single_handler);
|
||||||
globalClient.serverConnection.send_command("querycreate", {
|
connection.serverConnection.send_command("querycreate", {
|
||||||
client_login_name: name
|
client_login_name: name
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
globalClient.serverConnection.command_handler_boss().remove_single_handler(single_handler);
|
connection.serverConnection.command_handler_boss().remove_single_handler(single_handler);
|
||||||
|
|
||||||
if(error instanceof CommandResult)
|
if(error instanceof CommandResult)
|
||||||
error = error.extra_message || error.message;
|
error = error.extra_message || error.message;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
/// <reference path="../../ConnectionHandler.ts" />
|
||||||
/// <reference path="../../proto.ts" />
|
/// <reference path="../../proto.ts" />
|
||||||
/// <reference path="../../client.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
export function spawnQueryManage(client: TSClient) {
|
export function spawnQueryManage(client: ConnectionHandler) {
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
let selected_query: QueryListEntry;
|
let selected_query: QueryListEntry;
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ namespace Modals {
|
||||||
|
|
||||||
template.find(".footer .buttons .button-refresh").on('click', update_list);
|
template.find(".footer .buttons .button-refresh").on('click', update_list);
|
||||||
template.find(".button-query-create").on('click', () => {
|
template.find(".button-query-create").on('click', () => {
|
||||||
Modals.spawnQueryCreate((user, pass) => update_list());
|
Modals.spawnQueryCreate(client, (user, pass) => update_list());
|
||||||
});
|
});
|
||||||
template.find(".button-query-rename").on('click', () => {
|
template.find(".button-query-rename").on('click', () => {
|
||||||
if(!selected_query) return;
|
if(!selected_query) return;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
export function createServerModal(server: ServerEntry, callback: (properties?: ServerProperties) => any) {
|
export function createServerModal(server: ServerEntry, callback: (properties?: ServerProperties) => any) {
|
||||||
|
@ -37,6 +37,8 @@ namespace Modals {
|
||||||
}
|
}
|
||||||
|
|
||||||
function server_applyGeneralListener(properties: ServerProperties, server: ServerEntry, tag: JQuery, button: JQuery) {
|
function server_applyGeneralListener(properties: ServerProperties, server: ServerEntry, tag: JQuery, button: JQuery) {
|
||||||
|
const connection_handler = server.channelTree.client;
|
||||||
|
|
||||||
let updateButton = () => {
|
let updateButton = () => {
|
||||||
if(tag.find(".input_error").length == 0)
|
if(tag.find(".input_error").length == 0)
|
||||||
button.removeAttr("disabled");
|
button.removeAttr("disabled");
|
||||||
|
@ -50,11 +52,11 @@ namespace Modals {
|
||||||
if(this.value.length < 1 || this.value.length > 70)
|
if(this.value.length < 1 || this.value.length > 70)
|
||||||
$(this).addClass("input_error");
|
$(this).addClass("input_error");
|
||||||
updateButton();
|
updateButton();
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_NAME).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_NAME).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_name_phonetic").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_name_phonetic").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_name_phonetic = this.value;
|
properties.virtualserver_name_phonetic = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_NAME).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_NAME).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_password").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_password").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_flag_password = this.value.length != 0;
|
properties.virtualserver_flag_password = this.value.length != 0;
|
||||||
|
@ -63,16 +65,16 @@ namespace Modals {
|
||||||
|
|
||||||
$(this).removeClass("input_error");
|
$(this).removeClass("input_error");
|
||||||
if(!properties.virtualserver_flag_password)
|
if(!properties.virtualserver_flag_password)
|
||||||
if(globalClient.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD).granted(1))
|
if(connection_handler.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD).granted(1))
|
||||||
$(this).addClass("input_error");
|
$(this).addClass("input_error");
|
||||||
updateButton();
|
updateButton();
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_PASSWORD).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_PASSWORD).granted(1));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
tag.find(".virtualserver_maxclients").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_maxclients").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_maxclients = this.valueAsNumber;
|
properties.virtualserver_maxclients = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_MAXCLIENTS).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_MAXCLIENTS).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_reserved_slots").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_reserved_slots").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_reserved_slots = this.valueAsNumber;
|
properties.virtualserver_reserved_slots = this.valueAsNumber;
|
||||||
|
@ -80,17 +82,17 @@ namespace Modals {
|
||||||
if(this.valueAsNumber > properties.virtualserver_maxclients)
|
if(this.valueAsNumber > properties.virtualserver_maxclients)
|
||||||
$(this).addClass("input_error");
|
$(this).addClass("input_error");
|
||||||
updateButton();
|
updateButton();
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_RESERVED_SLOTS).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_RESERVED_SLOTS).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_welcomemessage").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_welcomemessage").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_welcomemessage = this.value;
|
properties.virtualserver_welcomemessage = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_WELCOMEMESSAGE).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_WELCOMEMESSAGE).granted(1));
|
||||||
|
|
||||||
tag.find(".button-select-icon").on('click', event => {
|
tag.find(".button-select-icon").on('click', event => {
|
||||||
Modals.spawnIconSelect(globalClient, id => {
|
Modals.spawnIconSelect(connection_handler, id => {
|
||||||
const icon_node = tag.find(".button-select-icon").find(".icon-node");
|
const icon_node = tag.find(".button-select-icon").find(".icon-node");
|
||||||
icon_node.empty();
|
icon_node.empty();
|
||||||
icon_node.append(globalClient.fileManager.icons.generateTag(id));
|
icon_node.append(connection_handler.fileManager.icons.generateTag(id));
|
||||||
|
|
||||||
console.log("Selected icon ID: %d", id);
|
console.log("Selected icon ID: %d", id);
|
||||||
properties.virtualserver_icon_id = id;
|
properties.virtualserver_icon_id = id;
|
||||||
|
@ -100,54 +102,56 @@ namespace Modals {
|
||||||
|
|
||||||
|
|
||||||
function server_applyHostListener(server: ServerEntry, properties: ServerProperties, original_properties: ServerProperties, tag: JQuery, button: JQuery) {
|
function server_applyHostListener(server: ServerEntry, properties: ServerProperties, original_properties: ServerProperties, tag: JQuery, button: JQuery) {
|
||||||
|
const connection_handler = server.channelTree.client;
|
||||||
|
|
||||||
tag.find(".virtualserver_host").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_host").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_host = this.value;
|
properties.virtualserver_host = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOST).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOST).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_port").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_port").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_port = this.valueAsNumber;
|
properties.virtualserver_port = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_PORT).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_PORT).granted(1));
|
||||||
|
|
||||||
|
|
||||||
tag.find(".virtualserver_hostmessage").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_hostmessage").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_hostmessage = this.value;
|
properties.virtualserver_hostmessage = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTMESSAGE).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTMESSAGE).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_hostmessage_mode").change(function (this: HTMLSelectElement) {
|
tag.find(".virtualserver_hostmessage_mode").change(function (this: HTMLSelectElement) {
|
||||||
properties.virtualserver_hostmessage_mode = this.selectedIndex;
|
properties.virtualserver_hostmessage_mode = this.selectedIndex;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTMESSAGE).granted(1))
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTMESSAGE).granted(1))
|
||||||
.find("option").eq(original_properties.virtualserver_hostmessage_mode).prop('selected', true);
|
.find("option").eq(original_properties.virtualserver_hostmessage_mode).prop('selected', true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
tag.find(".virtualserver_hostbanner_url").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_hostbanner_url").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_hostbanner_url = this.value;
|
properties.virtualserver_hostbanner_url = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBANNER).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBANNER).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_hostbanner_gfx_url").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_hostbanner_gfx_url").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_hostbanner_gfx_url = this.value;
|
properties.virtualserver_hostbanner_gfx_url = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBANNER).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBANNER).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_hostbanner_gfx_interval").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_hostbanner_gfx_interval").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_hostbanner_gfx_interval = this.valueAsNumber;
|
properties.virtualserver_hostbanner_gfx_interval = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBANNER).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBANNER).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_hostbanner_mode").change(function (this: HTMLSelectElement) {
|
tag.find(".virtualserver_hostbanner_mode").change(function (this: HTMLSelectElement) {
|
||||||
properties.virtualserver_hostbanner_mode = this.selectedIndex;
|
properties.virtualserver_hostbanner_mode = this.selectedIndex;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTMESSAGE).granted(1))
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTMESSAGE).granted(1))
|
||||||
.find("option").eq(original_properties.virtualserver_hostbanner_mode).prop('selected', true);
|
.find("option").eq(original_properties.virtualserver_hostbanner_mode).prop('selected', true);
|
||||||
|
|
||||||
tag.find(".virtualserver_hostbutton_tooltip").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_hostbutton_tooltip").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_hostbutton_tooltip = this.value;
|
properties.virtualserver_hostbutton_tooltip = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBUTTON).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBUTTON).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_hostbutton_url").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_hostbutton_url").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_hostbutton_url = this.value;
|
properties.virtualserver_hostbutton_url = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBUTTON).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBUTTON).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_hostbutton_gfx_url").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_hostbutton_gfx_url").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_hostbutton_gfx_url = this.value;
|
properties.virtualserver_hostbutton_gfx_url = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBUTTON).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBUTTON).granted(1));
|
||||||
|
|
||||||
server.updateProperties().then(() => {
|
server.updateProperties().then(() => {
|
||||||
tag.find(".virtualserver_host").val(server.properties.virtualserver_host);
|
tag.find(".virtualserver_host").val(server.properties.virtualserver_host);
|
||||||
|
@ -156,6 +160,8 @@ namespace Modals {
|
||||||
}
|
}
|
||||||
|
|
||||||
function server_applyMessages(properties: ServerProperties, server: ServerEntry, tag: JQuery) {
|
function server_applyMessages(properties: ServerProperties, server: ServerEntry, tag: JQuery) {
|
||||||
|
const connection_handler = server.channelTree.client;
|
||||||
|
|
||||||
server.updateProperties().then(() => {
|
server.updateProperties().then(() => {
|
||||||
tag.find(".virtualserver_default_client_description").val(server.properties.virtualserver_default_client_description);
|
tag.find(".virtualserver_default_client_description").val(server.properties.virtualserver_default_client_description);
|
||||||
tag.find(".virtualserver_default_channel_description").val(server.properties.virtualserver_default_channel_description);
|
tag.find(".virtualserver_default_channel_description").val(server.properties.virtualserver_default_channel_description);
|
||||||
|
@ -164,18 +170,20 @@ namespace Modals {
|
||||||
|
|
||||||
tag.find(".virtualserver_default_client_description").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_default_client_description").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_default_client_description = this.value;
|
properties.virtualserver_default_client_description = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_default_channel_description").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_default_channel_description").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_default_channel_description = this.value;
|
properties.virtualserver_default_channel_description = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_default_channel_topic").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_default_channel_topic").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_default_channel_topic = this.value;
|
properties.virtualserver_default_channel_topic = this.value;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES).granted(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
function server_applyFlood(properties: ServerProperties, server: ServerEntry, tag: JQuery) {
|
function server_applyFlood(properties: ServerProperties, server: ServerEntry, tag: JQuery) {
|
||||||
|
const connection_handler = server.channelTree.client;
|
||||||
|
|
||||||
server.updateProperties().then(() => {
|
server.updateProperties().then(() => {
|
||||||
tag.find(".virtualserver_antiflood_points_tick_reduce").val(server.properties.virtualserver_antiflood_points_tick_reduce);
|
tag.find(".virtualserver_antiflood_points_tick_reduce").val(server.properties.virtualserver_antiflood_points_tick_reduce);
|
||||||
tag.find(".virtualserver_antiflood_points_needed_command_block").val(server.properties.virtualserver_antiflood_points_needed_command_block);
|
tag.find(".virtualserver_antiflood_points_needed_command_block").val(server.properties.virtualserver_antiflood_points_needed_command_block);
|
||||||
|
@ -184,34 +192,38 @@ namespace Modals {
|
||||||
|
|
||||||
tag.find(".virtualserver_antiflood_points_tick_reduce").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_antiflood_points_tick_reduce").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_antiflood_points_tick_reduce = this.valueAsNumber;
|
properties.virtualserver_antiflood_points_tick_reduce = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_antiflood_points_needed_command_block").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_antiflood_points_needed_command_block").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_antiflood_points_needed_command_block = this.valueAsNumber;
|
properties.virtualserver_antiflood_points_needed_command_block = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_antiflood_points_needed_ip_block").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_antiflood_points_needed_ip_block").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_antiflood_points_needed_ip_block = this.valueAsNumber;
|
properties.virtualserver_antiflood_points_needed_ip_block = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function server_applySecurity(properties: ServerProperties, server: ServerEntry, tag: JQuery) {
|
function server_applySecurity(properties: ServerProperties, server: ServerEntry, tag: JQuery) {
|
||||||
|
const connection_handler = server.channelTree.client;
|
||||||
|
|
||||||
server.updateProperties().then(() => {
|
server.updateProperties().then(() => {
|
||||||
tag.find(".virtualserver_needed_identity_security_level").val(server.properties.virtualserver_needed_identity_security_level);
|
tag.find(".virtualserver_needed_identity_security_level").val(server.properties.virtualserver_needed_identity_security_level);
|
||||||
});
|
});
|
||||||
|
|
||||||
tag.find(".virtualserver_needed_identity_security_level").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_needed_identity_security_level").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_needed_identity_security_level = this.valueAsNumber;
|
properties.virtualserver_needed_identity_security_level = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_NEEDED_IDENTITY_SECURITY_LEVEL).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_NEEDED_IDENTITY_SECURITY_LEVEL).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_codec_encryption_mode").change(function (this: HTMLSelectElement) {
|
tag.find(".virtualserver_codec_encryption_mode").change(function (this: HTMLSelectElement) {
|
||||||
properties.virtualserver_codec_encryption_mode = this.selectedIndex;
|
properties.virtualserver_codec_encryption_mode = this.selectedIndex;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1))
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1))
|
||||||
.find("option").eq(server.properties.virtualserver_codec_encryption_mode).prop('selected', true);
|
.find("option").eq(server.properties.virtualserver_codec_encryption_mode).prop('selected', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function server_applyMisc(properties: ServerProperties, server: ServerEntry, tag: JQuery) {
|
function server_applyMisc(properties: ServerProperties, server: ServerEntry, tag: JQuery) {
|
||||||
|
const connection_handler = server.channelTree.client;
|
||||||
|
|
||||||
{ //TODO notify on tmp channeladmin group and vice versa
|
{ //TODO notify on tmp channeladmin group and vice versa
|
||||||
{
|
{
|
||||||
let groups_tag = tag.find(".default_server_group");
|
let groups_tag = tag.find(".default_server_group");
|
||||||
|
@ -293,37 +305,38 @@ namespace Modals {
|
||||||
|
|
||||||
tag.find(".virtualserver_antiflood_points_needed_ip_block").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_antiflood_points_needed_ip_block").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_antiflood_points_needed_ip_block = this.valueAsNumber;
|
properties.virtualserver_antiflood_points_needed_ip_block = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_antiflood_points_needed_command_block").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_antiflood_points_needed_command_block").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_antiflood_points_needed_command_block = this.valueAsNumber;
|
properties.virtualserver_antiflood_points_needed_command_block = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_antiflood_points_tick_reduce").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_antiflood_points_tick_reduce").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_antiflood_points_tick_reduce = this.valueAsNumber;
|
properties.virtualserver_antiflood_points_tick_reduce = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1));
|
||||||
|
|
||||||
|
|
||||||
tag.find(".virtualserver_complain_autoban_count").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_complain_autoban_count").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_complain_autoban_count = this.valueAsNumber;
|
properties.virtualserver_complain_autoban_count = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_COMPLAIN).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_COMPLAIN).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_complain_autoban_time").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_complain_autoban_time").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_complain_autoban_time = this.valueAsNumber;
|
properties.virtualserver_complain_autoban_time = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_COMPLAIN).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_COMPLAIN).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_complain_remove_time").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_complain_remove_time").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_complain_remove_time = this.valueAsNumber;
|
properties.virtualserver_complain_remove_time = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_COMPLAIN).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_COMPLAIN).granted(1));
|
||||||
|
|
||||||
|
|
||||||
tag.find(".virtualserver_weblist_enabled").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_weblist_enabled").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_weblist_enabled = $(this).prop("checked");
|
properties.virtualserver_weblist_enabled = $(this).prop("checked");
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_WEBLIST).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_WEBLIST).granted(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function server_applyTransferListener(properties: ServerProperties, server: ServerEntry, tag: JQuery) {
|
function server_applyTransferListener(properties: ServerProperties, server: ServerEntry, tag: JQuery) {
|
||||||
|
const connection_handler = server.channelTree.client;
|
||||||
server.updateProperties().then(() => {
|
server.updateProperties().then(() => {
|
||||||
//virtualserver_max_upload_total_bandwidth
|
//virtualserver_max_upload_total_bandwidth
|
||||||
//virtualserver_upload_quota
|
//virtualserver_upload_quota
|
||||||
|
@ -339,16 +352,16 @@ namespace Modals {
|
||||||
|
|
||||||
tag.find(".virtualserver_max_upload_total_bandwidth").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_max_upload_total_bandwidth").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_max_upload_total_bandwidth = this.valueAsNumber;
|
properties.virtualserver_max_upload_total_bandwidth = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_SETTINGS).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_SETTINGS).granted(1));
|
||||||
tag.find(".virtualserver_max_download_total_bandwidth").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_max_download_total_bandwidth").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_max_download_total_bandwidth = this.valueAsNumber;
|
properties.virtualserver_max_download_total_bandwidth = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_SETTINGS).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_SETTINGS).granted(1));
|
||||||
|
|
||||||
tag.find(".virtualserver_upload_quota").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_upload_quota").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_upload_quota = this.valueAsNumber;
|
properties.virtualserver_upload_quota = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_QUOTAS).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_QUOTAS).granted(1));
|
||||||
tag.find(".virtualserver_download_quota").change(function (this: HTMLInputElement) {
|
tag.find(".virtualserver_download_quota").change(function (this: HTMLInputElement) {
|
||||||
properties.virtualserver_download_quota = this.valueAsNumber;
|
properties.virtualserver_download_quota = this.valueAsNumber;
|
||||||
}).prop("disabled", !globalClient.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_QUOTAS).granted(1));
|
}).prop("disabled", !connection_handler.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_QUOTAS).granted(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
/// <reference path="../../../declarations/imports_client.d.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
/// <reference path="../../utils/modal.ts" />
|
|
||||||
/// <reference path="../../utils/tab.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
/// <reference path="../../proto.ts" />
|
||||||
/// <reference path="../../voice/AudioController.ts" />
|
/// <reference path="../../voice/VoiceClient.ts" />
|
||||||
/// <reference path="../../profiles/Identity.ts" />
|
/// <reference path="../../profiles/Identity.ts" />
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
|
@ -207,7 +205,7 @@ namespace Modals {
|
||||||
client: native_client,
|
client: native_client,
|
||||||
valid_forum_identity: profiles.identities.valid_static_forum_identity(),
|
valid_forum_identity: profiles.identities.valid_static_forum_identity(),
|
||||||
forum_path: settings.static("forum_path"),
|
forum_path: settings.static("forum_path"),
|
||||||
voice_available: !!globalClient.voiceConnection
|
voice_available: !settings.static_global(Settings.KEY_DISABLE_VOICE, false)
|
||||||
});
|
});
|
||||||
|
|
||||||
initialiseVoiceListeners(modal, (template = template.tabify()).find(".settings_audio"));
|
initialiseVoiceListeners(modal, (template = template.tabify()).find(".settings_audio"));
|
||||||
|
@ -262,7 +260,7 @@ namespace Modals {
|
||||||
.text(message);
|
.text(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (globalClient.voiceConnection) {
|
if (!settings.static_global(Settings.KEY_DISABLE_VOICE, false)) {
|
||||||
{ //Initialized voice activation detection
|
{ //Initialized voice activation detection
|
||||||
const vad_tag = tag.find(".settings-vad-container");
|
const vad_tag = tag.find(".settings-vad-container");
|
||||||
|
|
||||||
|
@ -274,7 +272,7 @@ namespace Modals {
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
settings.changeGlobal("vad_type", select.value);
|
settings.changeGlobal("vad_type", select.value);
|
||||||
globalClient.voiceConnection.voiceRecorder.reinitialiseVAD();
|
voice_recoder.reinitialiseVAD();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (select.value) {
|
switch (select.value) {
|
||||||
|
@ -288,10 +286,10 @@ namespace Modals {
|
||||||
break;
|
break;
|
||||||
case "vad":
|
case "vad":
|
||||||
let slider = vad_tag.find(".vad_vad_slider");
|
let slider = vad_tag.find(".vad_vad_slider");
|
||||||
let vad: VoiceActivityDetectorVAD = globalClient.voiceConnection.voiceRecorder.getVADHandler() as VoiceActivityDetectorVAD;
|
let vad: VoiceActivityDetectorVAD = voice_recoder.getVADHandler() as VoiceActivityDetectorVAD;
|
||||||
slider.val(vad.percentageThreshold);
|
slider.val(vad.percentageThreshold);
|
||||||
slider.trigger("change");
|
slider.trigger("change");
|
||||||
globalClient.voiceConnection.voiceRecorder.update(true);
|
voice_recoder.set_recording(true);
|
||||||
vad.percentage_listener = per => {
|
vad.percentage_listener = per => {
|
||||||
vad_tag.find(".vad_vad_bar_filler")
|
vad_tag.find(".vad_vad_bar_filler")
|
||||||
.css("width", (100 - per) + "%");
|
.css("width", (100 - per) + "%");
|
||||||
|
@ -323,7 +321,7 @@ namespace Modals {
|
||||||
Object.assign(ppt_settings, event);
|
Object.assign(ppt_settings, event);
|
||||||
settings.changeGlobal('vad_ppt_settings', ppt_settings);
|
settings.changeGlobal('vad_ppt_settings', ppt_settings);
|
||||||
|
|
||||||
globalClient.voiceConnection.voiceRecorder.reinitialiseVAD();
|
voice_recoder.reinitialiseVAD();
|
||||||
|
|
||||||
ppt.unregister_key_listener(listener);
|
ppt.unregister_key_listener(listener);
|
||||||
modal.close();
|
modal.close();
|
||||||
|
@ -340,7 +338,7 @@ namespace Modals {
|
||||||
ppt_settings.delay = (<HTMLInputElement>event.target).valueAsNumber;
|
ppt_settings.delay = (<HTMLInputElement>event.target).valueAsNumber;
|
||||||
settings.changeGlobal('vad_ppt_settings', ppt_settings);
|
settings.changeGlobal('vad_ppt_settings', ppt_settings);
|
||||||
|
|
||||||
globalClient.voiceConnection.voiceRecorder.reinitialiseVAD();
|
voice_recoder.reinitialiseVAD();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,13 +346,13 @@ namespace Modals {
|
||||||
let slider = vad_tag.find(".vad_vad_slider");
|
let slider = vad_tag.find(".vad_vad_slider");
|
||||||
slider.on("input change", () => {
|
slider.on("input change", () => {
|
||||||
settings.changeGlobal("vad_threshold", slider.val().toString());
|
settings.changeGlobal("vad_threshold", slider.val().toString());
|
||||||
let vad = globalClient.voiceConnection.voiceRecorder.getVADHandler();
|
let vad = voice_recoder.getVADHandler();
|
||||||
if (vad instanceof VoiceActivityDetectorVAD)
|
if (vad instanceof VoiceActivityDetectorVAD)
|
||||||
vad.percentageThreshold = slider.val() as number;
|
vad.percentageThreshold = slider.val() as number;
|
||||||
vad_tag.find(".vad_vad_slider_value").text(slider.val().toString());
|
vad_tag.find(".vad_vad_slider_value").text(slider.val().toString());
|
||||||
});
|
});
|
||||||
modal.properties.registerCloseListener(() => {
|
modal.properties.registerCloseListener(() => {
|
||||||
let vad = globalClient.voiceConnection.voiceRecorder.getVADHandler();
|
let vad = voice_recoder.getVADHandler();
|
||||||
if (vad instanceof VoiceActivityDetectorVAD)
|
if (vad instanceof VoiceActivityDetectorVAD)
|
||||||
vad.percentage_listener = undefined;
|
vad.percentage_listener = undefined;
|
||||||
|
|
||||||
|
@ -386,7 +384,7 @@ namespace Modals {
|
||||||
.appendTo(tag_select);
|
.appendTo(tag_select);
|
||||||
|
|
||||||
navigator.mediaDevices.enumerateDevices().then(devices => {
|
navigator.mediaDevices.enumerateDevices().then(devices => {
|
||||||
const active_device = globalClient.voiceConnection.voiceRecorder.device_id();
|
const active_device = voice_recoder.device_id();
|
||||||
|
|
||||||
for (const device of devices) {
|
for (const device of devices) {
|
||||||
console.debug(tr("Got device %s (%s): %s (%o)"), device.deviceId, device.kind, device.label);
|
console.debug(tr("Got device %s (%s): %s (%o)"), device.deviceId, device.kind, device.label);
|
||||||
|
@ -416,7 +414,7 @@ namespace Modals {
|
||||||
let deviceId = selected_tag.attr("device-id");
|
let deviceId = selected_tag.attr("device-id");
|
||||||
let groupId = selected_tag.attr("device-group");
|
let groupId = selected_tag.attr("device-group");
|
||||||
console.log(tr("Selected microphone device: id: %o group: %o"), deviceId, groupId);
|
console.log(tr("Selected microphone device: id: %o group: %o"), deviceId, groupId);
|
||||||
globalClient.voiceConnection.voiceRecorder.change_device(deviceId, groupId);
|
voice_recoder.change_device(deviceId, groupId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,7 +517,7 @@ namespace Modals {
|
||||||
});
|
});
|
||||||
|
|
||||||
entry.find(".button-playback").on('click', event => {
|
entry.find(".button-playback").on('click', event => {
|
||||||
sound.play(sound_name as Sound);
|
sound.manager.play(sound_name as Sound);
|
||||||
});
|
});
|
||||||
|
|
||||||
entry_tag.append(entry);
|
entry_tag.append(entry);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/// <reference path="../../utils/modal.ts" />
|
/// <reference path="../../ui/elements/modal.ts" />
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
export function spawnYesNo(header: BodyCreator, body: BodyCreator, callback: (_: boolean) => any, properties?: {
|
export function spawnYesNo(header: BodyCreator, body: BodyCreator, callback: (_: boolean) => any, properties?: {
|
||||||
|
|
|
@ -141,13 +141,13 @@ class ServerEntry {
|
||||||
name: tr("Show server info"),
|
name: tr("Show server info"),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
trigger_close = false;
|
trigger_close = false;
|
||||||
this.channelTree.client.selectInfo.open_popover()
|
this.channelTree.client.select_info.open_popover()
|
||||||
},
|
},
|
||||||
icon: "client-about",
|
icon: "client-about",
|
||||||
visible: this.channelTree.client.selectInfo.is_popover()
|
visible: this.channelTree.client.select_info.is_popover()
|
||||||
}, {
|
}, {
|
||||||
type: MenuEntryType.HR,
|
type: MenuEntryType.HR,
|
||||||
visible: this.channelTree.client.selectInfo.is_popover(),
|
visible: this.channelTree.client.select_info.is_popover(),
|
||||||
name: ''
|
name: ''
|
||||||
}, {
|
}, {
|
||||||
type: MenuEntryType.ENTRY,
|
type: MenuEntryType.ENTRY,
|
||||||
|
@ -159,7 +159,7 @@ class ServerEntry {
|
||||||
console.log(tr("Changed properties: %o"), properties);
|
console.log(tr("Changed properties: %o"), properties);
|
||||||
if (properties)
|
if (properties)
|
||||||
this.channelTree.client.serverConnection.send_command("serveredit", properties).then(() => {
|
this.channelTree.client.serverConnection.send_command("serveredit", properties).then(() => {
|
||||||
sound.play(Sound.SERVER_EDITED_SELF);
|
this.channelTree.client.sound.play(Sound.SERVER_EDITED_SELF);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -210,6 +210,8 @@ class ServerEntry {
|
||||||
|
|
||||||
if(variable.key == "virtualserver_name") {
|
if(variable.key == "virtualserver_name") {
|
||||||
this.htmlTag.find(".name").text(variable.value);
|
this.htmlTag.find(".name").text(variable.value);
|
||||||
|
this.channelTree.client.tag_connection_handler.find(".server-name").text(variable.value);
|
||||||
|
server_connections.update_ui();
|
||||||
} else if(variable.key == "virtualserver_icon_id") {
|
} else if(variable.key == "virtualserver_icon_id") {
|
||||||
if(this.channelTree.client.fileManager && this.channelTree.client.fileManager.icons)
|
if(this.channelTree.client.fileManager && this.channelTree.client.fileManager.icons)
|
||||||
this.htmlTag.find(".icon_property").replaceWith(this.channelTree.client.fileManager.icons.generateTag(this.properties.virtualserver_icon_id).addClass("icon_property"));
|
this.htmlTag.find(".icon_property").replaceWith(this.channelTree.client.fileManager.icons.generateTag(this.properties.virtualserver_icon_id).addClass("icon_property"));
|
||||||
|
@ -218,7 +220,7 @@ class ServerEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(update_bannner)
|
if(update_bannner)
|
||||||
this.channelTree.client.selectInfo.update_banner();
|
this.channelTree.client.select_info.update_banner();
|
||||||
|
|
||||||
group.end();
|
group.end();
|
||||||
if(is_self_notify && this.info_request_promise_resolve) {
|
if(is_self_notify && this.info_request_promise_resolve) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/// <reference path="../voice/VoiceHandler.ts" />
|
/// <reference path="../voice/VoiceHandler.ts" />
|
||||||
/// <reference path="../client.ts" />
|
/// <reference path="../ConnectionHandler.ts" />
|
||||||
/// <reference path="../contextMenu.ts" />
|
|
||||||
/// <reference path="../proto.ts" />
|
/// <reference path="../proto.ts" />
|
||||||
/// <reference path="channel.ts" />
|
/// <reference path="channel.ts" />
|
||||||
/// <reference path="client.ts" />
|
/// <reference path="client.ts" />
|
||||||
|
@ -8,10 +7,9 @@
|
||||||
|
|
||||||
|
|
||||||
class ChannelTree {
|
class ChannelTree {
|
||||||
client: TSClient;
|
client: ConnectionHandler;
|
||||||
htmlTree: JQuery;
|
|
||||||
htmlTree_parent: JQuery;
|
|
||||||
server: ServerEntry;
|
server: ServerEntry;
|
||||||
|
|
||||||
channels: ChannelEntry[];
|
channels: ChannelEntry[];
|
||||||
clients: ClientEntry[];
|
clients: ClientEntry[];
|
||||||
|
|
||||||
|
@ -19,6 +17,9 @@ class ChannelTree {
|
||||||
currently_selected_context_callback: (event) => any = undefined;
|
currently_selected_context_callback: (event) => any = undefined;
|
||||||
readonly client_mover: ClientMover;
|
readonly client_mover: ClientMover;
|
||||||
|
|
||||||
|
private _tag_container: JQuery;
|
||||||
|
private _tag_entries: JQuery;
|
||||||
|
|
||||||
private _tree_detached: boolean = false;
|
private _tree_detached: boolean = false;
|
||||||
private _show_queries: boolean;
|
private _show_queries: boolean;
|
||||||
private channel_last?: ChannelEntry;
|
private channel_last?: ChannelEntry;
|
||||||
|
@ -26,18 +27,19 @@ class ChannelTree {
|
||||||
|
|
||||||
private selected_event?: Event;
|
private selected_event?: Event;
|
||||||
|
|
||||||
constructor(client, htmlTree) {
|
constructor(client) {
|
||||||
document.addEventListener("touchstart", function(){}, true);
|
document.addEventListener("touchstart", function(){}, true);
|
||||||
|
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.htmlTree = htmlTree;
|
|
||||||
this.htmlTree_parent = this.htmlTree.parent();
|
this._tag_container = $.spawn("div").addClass("channel-tree-container");
|
||||||
|
this._tag_entries = $.spawn("div").addClass("channel-tree");
|
||||||
|
|
||||||
this.client_mover = new ClientMover(this);
|
this.client_mover = new ClientMover(this);
|
||||||
this.reset();
|
this.reset();
|
||||||
|
|
||||||
if(!settings.static(Settings.KEY_DISABLE_CONTEXT_MENU, false)) {
|
if(!settings.static(Settings.KEY_DISABLE_CONTEXT_MENU, false)) {
|
||||||
this.htmlTree.parent().on("contextmenu", (event) => {
|
this._tag_container.on("contextmenu", (event) => {
|
||||||
if(event.isDefaultPrevented()) return;
|
if(event.isDefaultPrevented()) return;
|
||||||
|
|
||||||
for(const element of document.elementsFromPoint(event.pageX, event.pageY))
|
for(const element of document.elementsFromPoint(event.pageX, event.pageY))
|
||||||
|
@ -54,7 +56,7 @@ class ChannelTree {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.htmlTree.on('resize', this.handle_resized.bind(this));
|
this._tag_container.on('resize', this.handle_resized.bind(this));
|
||||||
|
|
||||||
/* TODO release these events again when ChannelTree get deinitialized */
|
/* TODO release these events again when ChannelTree get deinitialized */
|
||||||
$(document).on('click', event => {
|
$(document).on('click', event => {
|
||||||
|
@ -62,19 +64,23 @@ class ChannelTree {
|
||||||
this.selected_event = undefined;
|
this.selected_event = undefined;
|
||||||
});
|
});
|
||||||
$(document).on('keydown', this.handle_key_press.bind(this));
|
$(document).on('keydown', this.handle_key_press.bind(this));
|
||||||
this.htmlTree.on('click', event => {{
|
this._tag_container.on('click', event => {{
|
||||||
this.selected_event = event.originalEvent;
|
this.selected_event = event.originalEvent;
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tag_tree() : JQuery {
|
||||||
|
return this._tag_container;
|
||||||
|
}
|
||||||
|
|
||||||
hide_channel_tree() {
|
hide_channel_tree() {
|
||||||
this.htmlTree.detach();
|
this._tag_entries.detach();
|
||||||
this._tree_detached = true;
|
this._tree_detached = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
show_channel_tree() {
|
show_channel_tree() {
|
||||||
this._tree_detached = false;
|
this._tree_detached = false;
|
||||||
this.htmlTree.appendTo(this.htmlTree_parent);
|
this._tag_entries.appendTo(this._tag_container);
|
||||||
|
|
||||||
this.channels.forEach(e => e.recalculate_repetitive_name());
|
this.channels.forEach(e => e.recalculate_repetitive_name());
|
||||||
}
|
}
|
||||||
|
@ -99,14 +105,14 @@ class ChannelTree {
|
||||||
|
|
||||||
initialiseHead(serverName: string, address: ServerAddress) {
|
initialiseHead(serverName: string, address: ServerAddress) {
|
||||||
this.server = new ServerEntry(this, serverName, address);
|
this.server = new ServerEntry(this, serverName, address);
|
||||||
this.server.htmlTag.appendTo(this.htmlTree);
|
this.server.htmlTag.appendTo(this._tag_entries);
|
||||||
this.server.initializeListener();
|
this.server.initializeListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
private __deleteAnimation(element: ChannelEntry | ClientEntry) {
|
private __deleteAnimation(element: ChannelEntry | ClientEntry) {
|
||||||
let tag = element instanceof ChannelEntry ? element.rootTag() : element.tag;
|
let tag = element instanceof ChannelEntry ? element.rootTag() : element.tag;
|
||||||
this.htmlTree.find(tag).fadeOut("slow", () => {
|
tag.fadeOut("slow", () => {
|
||||||
tag.remove();
|
tag.detach();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +158,7 @@ class ChannelTree {
|
||||||
this.channels.push(channel);
|
this.channels.push(channel);
|
||||||
|
|
||||||
let elm = undefined;
|
let elm = undefined;
|
||||||
let tag = this.htmlTree;
|
let tag = this._tag_entries;
|
||||||
|
|
||||||
let previous_channel = null;
|
let previous_channel = null;
|
||||||
if(channel.hasParent()) {
|
if(channel.hasParent()) {
|
||||||
|
@ -266,7 +272,7 @@ class ChannelTree {
|
||||||
channel.channel_next.channel_previous = channel;
|
channel.channel_next.channel_previous = channel;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.htmlTree.find(".server").after(channel.rootTag());
|
this._tag_entries.find(".server").after(channel.rootTag());
|
||||||
|
|
||||||
channel.channel_next = this.channel_first;
|
channel.channel_next = this.channel_first;
|
||||||
if(this.channel_first)
|
if(this.channel_first)
|
||||||
|
@ -290,24 +296,45 @@ class ChannelTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteClient(client: ClientEntry) {
|
deleteClient(client: ClientEntry, animate_tag?: boolean) {
|
||||||
this.clients.remove(client);
|
this.clients.remove(client);
|
||||||
this.__deleteAnimation(client);
|
if(typeof(animate_tag) !== "boolean" || animate_tag)
|
||||||
|
this.__deleteAnimation(client);
|
||||||
|
else
|
||||||
|
client.tag.detach();
|
||||||
client.onDelete();
|
client.onDelete();
|
||||||
|
|
||||||
|
const voice_connection = this.client.serverConnection.voice_connection();
|
||||||
|
if(client.get_audio_handle()) {
|
||||||
|
if(!voice_connection) {
|
||||||
|
log.warn(LogCategory.VOICE, tr("Deleting client with a voice handle, but we haven't a voice connection!"));
|
||||||
|
} else {
|
||||||
|
voice_connection.unregister_client(client.get_audio_handle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client.set_audio_handle(undefined); /* just to be sure */
|
||||||
|
}
|
||||||
|
|
||||||
|
registerClient(client: ClientEntry) {
|
||||||
|
this.clients.push(client);
|
||||||
|
client.channelTree = this;
|
||||||
|
|
||||||
|
const voice_connection = this.client.serverConnection.voice_connection();
|
||||||
|
if(voice_connection)
|
||||||
|
client.set_audio_handle(voice_connection.register_client(client.clientId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
insertClient(client: ClientEntry, channel: ChannelEntry) : ClientEntry {
|
insertClient(client: ClientEntry, channel: ChannelEntry) : ClientEntry {
|
||||||
let newClient = this.findClient(client.clientId());
|
let newClient = this.findClient(client.clientId());
|
||||||
if(newClient) client = newClient; //Got new client :)
|
if(newClient)
|
||||||
else
|
client = newClient; //Got new client :)
|
||||||
this.clients.push(client);
|
else {
|
||||||
|
this.registerClient(client);
|
||||||
|
}
|
||||||
|
|
||||||
client.channelTree = this;
|
|
||||||
client["_channel"] = channel;
|
client["_channel"] = channel;
|
||||||
|
|
||||||
let tag = client.tag;
|
let tag = client.tag;
|
||||||
|
|
||||||
|
|
||||||
if(!this._show_queries && client.properties.client_type == ClientType.CLIENT_QUERY)
|
if(!this._show_queries && client.properties.client_type == ClientType.CLIENT_QUERY)
|
||||||
client.tag.hide();
|
client.tag.hide();
|
||||||
else if(!this._tree_detached)
|
else if(!this._tree_detached)
|
||||||
|
@ -321,11 +348,6 @@ class ChannelTree {
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerClient(client: ClientEntry) {
|
|
||||||
this.clients.push(client);
|
|
||||||
client.channelTree = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
moveClient(client: ClientEntry, channel: ChannelEntry) {
|
moveClient(client: ClientEntry, channel: ChannelEntry) {
|
||||||
let oldChannel = client.currentChannel();
|
let oldChannel = client.currentChannel();
|
||||||
client["_channel"] = channel;
|
client["_channel"] = channel;
|
||||||
|
@ -397,7 +419,7 @@ class ChannelTree {
|
||||||
|
|
||||||
if(!$.isArray(this.currently_selected) || enforce_single) {
|
if(!$.isArray(this.currently_selected) || enforce_single) {
|
||||||
this.currently_selected = entry;
|
this.currently_selected = entry;
|
||||||
this.htmlTree.find(".selected").each(function (idx, e) {
|
this._tag_entries.find(".selected").each(function (idx, e) {
|
||||||
$(e).removeClass("selected");
|
$(e).removeClass("selected");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -427,7 +449,7 @@ class ChannelTree {
|
||||||
else if(entry instanceof ServerEntry)
|
else if(entry instanceof ServerEntry)
|
||||||
(entry as ServerEntry).htmlTag.addClass("selected");
|
(entry as ServerEntry).htmlTag.addClass("selected");
|
||||||
|
|
||||||
this.client.selectInfo.setCurrentSelected($.isArray(this.currently_selected) ? undefined : entry);
|
this.client.select_info.setCurrentSelected($.isArray(this.currently_selected) ? undefined : entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
private callback_multiselect_channel(event) {
|
private callback_multiselect_channel(event) {
|
||||||
|
@ -527,7 +549,7 @@ class ChannelTree {
|
||||||
}, {
|
}, {
|
||||||
flagset: [data.no_ip ? "no-ip" : "", data.no_hwid ? "no-hardware-id" : "", data.no_name ? "no-nickname" : ""]
|
flagset: [data.no_ip ? "no-ip" : "", data.no_hwid ? "no-hardware-id" : "", data.no_name ? "no-nickname" : ""]
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
sound.play(Sound.USER_BANNED);
|
this.client.sound.play(Sound.USER_BANNED);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -586,14 +608,14 @@ class ChannelTree {
|
||||||
this.server = null;
|
this.server = null;
|
||||||
this.clients = [];
|
this.clients = [];
|
||||||
this.channels = [];
|
this.channels = [];
|
||||||
this.htmlTree.children().detach(); //Do not remove the listener!
|
this._tag_entries.children().detach(); //Do not remove the listener!
|
||||||
|
|
||||||
this.channel_first = undefined;
|
this.channel_first = undefined;
|
||||||
this.channel_last = undefined;
|
this.channel_last = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
spawnCreateChannel(parent?: ChannelEntry) {
|
spawnCreateChannel(parent?: ChannelEntry) {
|
||||||
Modals.createChannelModal(undefined, parent, this.client.permissions, (properties?, permissions?) => {
|
Modals.createChannelModal(this.client, undefined, parent, this.client.permissions, (properties?, permissions?) => {
|
||||||
if(!properties) return;
|
if(!properties) return;
|
||||||
properties["cpid"] = parent ? parent.channelId : 0;
|
properties["cpid"] = parent ? parent.channelId : 0;
|
||||||
log.debug(LogCategory.CHANNEL, tr("Creating a new channel.\nProperties: %o\nPermissions: %o"), properties);
|
log.debug(LogCategory.CHANNEL, tr("Creating a new channel.\nProperties: %o\nPermissions: %o"), properties);
|
||||||
|
@ -622,8 +644,8 @@ class ChannelTree {
|
||||||
|
|
||||||
return new Promise<ChannelEntry>(resolve => { resolve(channel); })
|
return new Promise<ChannelEntry>(resolve => { resolve(channel); })
|
||||||
}).then(channel => {
|
}).then(channel => {
|
||||||
chat.serverChat().appendMessage(tr("Channel {} successfully created!"), true, channel.generate_tag(true));
|
this.client.chat.serverChat().appendMessage(tr("Channel {} successfully created!"), true, channel.generate_tag(true));
|
||||||
sound.play(Sound.CHANNEL_CREATED);
|
this.client.sound.play(Sound.CHANNEL_CREATED);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,248 +0,0 @@
|
||||||
enum PlayerState {
|
|
||||||
PREBUFFERING,
|
|
||||||
PLAYING,
|
|
||||||
BUFFERING,
|
|
||||||
STOPPING,
|
|
||||||
STOPPED
|
|
||||||
}
|
|
||||||
|
|
||||||
class AudioController {
|
|
||||||
private static _audioInstances: AudioController[] = [];
|
|
||||||
private static _globalReplayScheduler: NodeJS.Timer;
|
|
||||||
private static _timeIndex: number = 0;
|
|
||||||
private static _audioDestinationStream: MediaStream;
|
|
||||||
|
|
||||||
static initializeAudioController() {
|
|
||||||
if(!audio.player.initialize())
|
|
||||||
console.warn(tr("Failed to initialize audio controller!"));
|
|
||||||
sound.initialize().then(() => {
|
|
||||||
console.log(tr("Sounds initialitzed"));
|
|
||||||
});
|
|
||||||
//this._globalReplayScheduler = setInterval(() => { AudioController.invokeNextReplay(); }, 20); //Fix me
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
private static joinTracks(tracks: AudioBuffer[]) : Promise<AudioBuffer> {
|
|
||||||
let length = Math.max.apply(Math, tracks.map(e => e.length));
|
|
||||||
if(length == 0 || tracks.length == 0) return new Promise<AudioBuffer>((resolve, reject) => {}); //Do nothink
|
|
||||||
|
|
||||||
let context = new OfflineAudioContext(2, length, 44100);
|
|
||||||
//let context = new OfflineAudioContext(tracks[0].numberOfChannels, tracks[0].length, tracks[0].sampleRate);
|
|
||||||
|
|
||||||
tracks.forEach(track => {
|
|
||||||
let player = context.createBufferSource();
|
|
||||||
player.buffer = track;
|
|
||||||
player.connect(context.destination);
|
|
||||||
player.start(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
return context.startRendering();
|
|
||||||
}
|
|
||||||
|
|
||||||
static invokeNextReplay() {
|
|
||||||
let replay: {controller: AudioController,buffer: AudioBuffer}[] = [];
|
|
||||||
|
|
||||||
for(let instance of this._audioInstances)
|
|
||||||
if(instance.playerState == PlayerState.PLAYING || instance.playerState == PlayerState.STOPPING) {
|
|
||||||
let entry = {controller: instance, buffer: instance.audioCache.pop_front() };
|
|
||||||
instance.flagPlaying = !!entry.buffer;
|
|
||||||
instance.testBufferQueue();
|
|
||||||
if(!!entry.buffer) replay.push(entry);
|
|
||||||
} else if(instance.flagPlaying) {
|
|
||||||
instance.flagPlaying = false;
|
|
||||||
instance.testBufferQueue();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.joinTracks(replay.map(e => e.buffer)).then(buffer => {
|
|
||||||
if(this._timeIndex < this._globalContext.currentTime) {
|
|
||||||
this._timeIndex = this._globalContext.currentTime;
|
|
||||||
console.log("Resetting time index!");
|
|
||||||
}
|
|
||||||
//console.log(buffer.length + "|" + buffer.duration);
|
|
||||||
//console.log(buffer);
|
|
||||||
|
|
||||||
let player = this._globalContext.createBufferSource();
|
|
||||||
player.buffer = buffer;
|
|
||||||
player.connect(this._globalContext.destination);
|
|
||||||
player.start(this._timeIndex);
|
|
||||||
|
|
||||||
this._timeIndex += buffer.duration;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
speakerContext: AudioContext;
|
|
||||||
private playerState: PlayerState = PlayerState.STOPPED;
|
|
||||||
private audioCache: AudioBuffer[] = [];
|
|
||||||
private playingAudioCache: AudioBufferSourceNode[] = [];
|
|
||||||
private _volume: number = 1;
|
|
||||||
private _codecCache: CodecClientCache[] = [];
|
|
||||||
private _timeIndex: number = 0;
|
|
||||||
private _latencyBufferLength: number = 3;
|
|
||||||
private _buffer_timeout: NodeJS.Timer;
|
|
||||||
|
|
||||||
allowBuffering: boolean = true;
|
|
||||||
|
|
||||||
//Events
|
|
||||||
onSpeaking: () => void;
|
|
||||||
onSilence: () => void;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
audio.player.on_ready(() => this.speakerContext = audio.player.context());
|
|
||||||
|
|
||||||
this.onSpeaking = function () { };
|
|
||||||
this.onSilence = function () { };
|
|
||||||
}
|
|
||||||
|
|
||||||
public initialize() {
|
|
||||||
AudioController._audioInstances.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public close(){
|
|
||||||
AudioController._audioInstances.remove(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
playBuffer(buffer: AudioBuffer) {
|
|
||||||
if(!buffer) {
|
|
||||||
console.warn(tr("[AudioController] Got empty or undefined buffer! Dropping it"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(!this.speakerContext) {
|
|
||||||
console.warn(tr("[AudioController] Failed to replay audio. Global audio context not initialized yet!"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (buffer.sampleRate != this.speakerContext.sampleRate)
|
|
||||||
console.warn(tr("[AudioController] Source sample rate isn't equal to playback sample rate! (%o | %o)"), buffer.sampleRate, this.speakerContext.sampleRate);
|
|
||||||
|
|
||||||
this.applyVolume(buffer);
|
|
||||||
this.audioCache.push(buffer);
|
|
||||||
if(this.playerState == PlayerState.STOPPED || this.playerState == PlayerState.STOPPING) {
|
|
||||||
console.log(tr("[Audio] Starting new playback"));
|
|
||||||
this.playerState = PlayerState.PREBUFFERING;
|
|
||||||
//New audio
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
switch (this.playerState) {
|
|
||||||
case PlayerState.PREBUFFERING:
|
|
||||||
case PlayerState.BUFFERING:
|
|
||||||
this.reset_buffer_timeout(true); //Reset timeout, we got a new buffer
|
|
||||||
if(this.audioCache.length <= this._latencyBufferLength) {
|
|
||||||
if(this.playerState == PlayerState.BUFFERING) {
|
|
||||||
if(this.allowBuffering) break;
|
|
||||||
} else break;
|
|
||||||
}
|
|
||||||
if(this.playerState == PlayerState.PREBUFFERING) {
|
|
||||||
console.log(tr("[Audio] Prebuffering succeeded (Replaying now)"));
|
|
||||||
this.onSpeaking();
|
|
||||||
} else {
|
|
||||||
if(this.allowBuffering)
|
|
||||||
console.log(tr("[Audio] Buffering succeeded (Replaying now)"));
|
|
||||||
}
|
|
||||||
this.playerState = PlayerState.PLAYING;
|
|
||||||
case PlayerState.PLAYING:
|
|
||||||
this.playQueue();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private playQueue() {
|
|
||||||
let buffer: AudioBuffer;
|
|
||||||
while((buffer = this.audioCache.pop_front())) {
|
|
||||||
if(this.playingAudioCache.length >= this._latencyBufferLength * 1.5 + 3) {
|
|
||||||
console.log(tr("Dropping buffer because playing queue grows to much"));
|
|
||||||
continue; /* drop the data (we're behind) */
|
|
||||||
}
|
|
||||||
if(this._timeIndex < this.speakerContext.currentTime) this._timeIndex = this.speakerContext.currentTime;
|
|
||||||
|
|
||||||
let player = this.speakerContext.createBufferSource();
|
|
||||||
player.buffer = buffer;
|
|
||||||
|
|
||||||
player.onended = () => this.removeNode(player);
|
|
||||||
this.playingAudioCache.push(player);
|
|
||||||
|
|
||||||
player.connect(audio.player.destination());
|
|
||||||
player.start(this._timeIndex);
|
|
||||||
this._timeIndex += buffer.duration;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private removeNode(node: AudioBufferSourceNode) {
|
|
||||||
this.playingAudioCache.remove(node);
|
|
||||||
this.testBufferQueue();
|
|
||||||
}
|
|
||||||
|
|
||||||
stopAudio(now: boolean = false) {
|
|
||||||
this.playerState = PlayerState.STOPPING;
|
|
||||||
if(now) {
|
|
||||||
this.playerState = PlayerState.STOPPED;
|
|
||||||
this.audioCache = [];
|
|
||||||
|
|
||||||
for(let entry of this.playingAudioCache)
|
|
||||||
entry.stop(0);
|
|
||||||
this.playingAudioCache = [];
|
|
||||||
}
|
|
||||||
this.testBufferQueue();
|
|
||||||
this.playQueue(); //Flush queue
|
|
||||||
}
|
|
||||||
|
|
||||||
private testBufferQueue() {
|
|
||||||
if(this.audioCache.length == 0 && this.playingAudioCache.length == 0) {
|
|
||||||
if(this.playerState != PlayerState.STOPPING && this.playerState != PlayerState.STOPPED) {
|
|
||||||
if(this.playerState == PlayerState.BUFFERING) return; //We're already buffering
|
|
||||||
|
|
||||||
this.playerState = PlayerState.BUFFERING;
|
|
||||||
if(!this.allowBuffering)
|
|
||||||
console.warn(tr("[Audio] Detected a buffer underflow!"));
|
|
||||||
this.reset_buffer_timeout(true);
|
|
||||||
} else {
|
|
||||||
this.playerState = PlayerState.STOPPED;
|
|
||||||
this.onSilence();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private reset_buffer_timeout(restart: boolean) {
|
|
||||||
if(this._buffer_timeout)
|
|
||||||
clearTimeout(this._buffer_timeout);
|
|
||||||
if(restart)
|
|
||||||
this._buffer_timeout = setTimeout(() => {
|
|
||||||
if(this.playerState == PlayerState.PREBUFFERING || this.playerState == PlayerState.BUFFERING) {
|
|
||||||
console.warn(tr("[Audio] Buffering exceeded timeout. Flushing and stopping replay"));
|
|
||||||
this.stopAudio();
|
|
||||||
}
|
|
||||||
this._buffer_timeout = undefined;
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
get volume() : number { return this._volume; }
|
|
||||||
|
|
||||||
set volume(val: number) {
|
|
||||||
if(this._volume == val) return;
|
|
||||||
this._volume = val;
|
|
||||||
for(let buffer of this.audioCache)
|
|
||||||
this.applyVolume(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private applyVolume(buffer: AudioBuffer) {
|
|
||||||
if(this._volume == 1) return;
|
|
||||||
|
|
||||||
for(let channel = 0; channel < buffer.numberOfChannels; channel++) {
|
|
||||||
let data = buffer.getChannelData(channel);
|
|
||||||
for(let sample = 0; sample < data.length; sample++) {
|
|
||||||
let lane = data[sample];
|
|
||||||
lane *= this._volume;
|
|
||||||
data[sample] = lane;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
codecCache(codec: number) : CodecClientCache {
|
|
||||||
while(this._codecCache.length <= codec)
|
|
||||||
this._codecCache.push(new CodecClientCache());
|
|
||||||
return this._codecCache[codec];
|
|
||||||
}
|
|
||||||
}
|
|
216
shared/js/voice/VoiceClient.ts
Normal file
216
shared/js/voice/VoiceClient.ts
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
/// <reference path="../connection/ConnectionBase.ts" />
|
||||||
|
|
||||||
|
namespace audio {
|
||||||
|
export namespace js {
|
||||||
|
export class VoiceClientController implements connection.voice.VoiceClient {
|
||||||
|
callback_playback: () => any;
|
||||||
|
callback_state_changed: (new_state: connection.voice.PlayerState) => any;
|
||||||
|
callback_stopped: () => any;
|
||||||
|
client_id: number;
|
||||||
|
|
||||||
|
speakerContext: AudioContext;
|
||||||
|
private _player_state: connection.voice.PlayerState = connection.voice.PlayerState.STOPPED;
|
||||||
|
private _codecCache: CodecClientCache[] = [];
|
||||||
|
|
||||||
|
private _time_index: number = 0;
|
||||||
|
private _latency_buffer_length: number = 3;
|
||||||
|
private _buffer_timeout: NodeJS.Timer;
|
||||||
|
|
||||||
|
private _buffered_samples: AudioBuffer[] = [];
|
||||||
|
private _playing_nodes: AudioBufferSourceNode[] = [];
|
||||||
|
|
||||||
|
private _volume: number = 1;
|
||||||
|
allowBuffering: boolean = true;
|
||||||
|
|
||||||
|
constructor(client_id: number) {
|
||||||
|
this.client_id = client_id;
|
||||||
|
|
||||||
|
audio.player.on_ready(() => this.speakerContext = audio.player.context());
|
||||||
|
}
|
||||||
|
|
||||||
|
public initialize() { }
|
||||||
|
|
||||||
|
public close(){ }
|
||||||
|
|
||||||
|
playback_buffer(buffer: AudioBuffer) {
|
||||||
|
if(!buffer) {
|
||||||
|
console.warn(tr("[AudioController] Got empty or undefined buffer! Dropping it"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this.speakerContext) {
|
||||||
|
console.warn(tr("[AudioController] Failed to replay audio. Global audio context not initialized yet!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.sampleRate != this.speakerContext.sampleRate)
|
||||||
|
console.warn(tr("[AudioController] Source sample rate isn't equal to playback sample rate! (%o | %o)"), buffer.sampleRate, this.speakerContext.sampleRate);
|
||||||
|
|
||||||
|
this.apply_volume_to_buffer(buffer);
|
||||||
|
|
||||||
|
this._buffered_samples.push(buffer);
|
||||||
|
if(this._player_state == connection.voice.PlayerState.STOPPED || this._player_state == connection.voice.PlayerState.STOPPING) {
|
||||||
|
console.log(tr("[Audio] Starting new playback"));
|
||||||
|
this.set_state(connection.voice.PlayerState.PREBUFFERING);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
switch (this._player_state) {
|
||||||
|
case connection.voice.PlayerState.PREBUFFERING:
|
||||||
|
case connection.voice.PlayerState.BUFFERING:
|
||||||
|
this.reset_buffer_timeout(true); //Reset timeout, we got a new buffer
|
||||||
|
if(this._buffered_samples.length <= this._latency_buffer_length) {
|
||||||
|
if(this._player_state == connection.voice.PlayerState.BUFFERING) {
|
||||||
|
if(this.allowBuffering)
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(this._player_state == connection.voice.PlayerState.PREBUFFERING) {
|
||||||
|
console.log(tr("[Audio] Prebuffering succeeded (Replaying now)"));
|
||||||
|
if(this.callback_playback)
|
||||||
|
this.callback_playback();
|
||||||
|
} else if(this.allowBuffering) {
|
||||||
|
console.log(tr("[Audio] Buffering succeeded (Replaying now)"));
|
||||||
|
}
|
||||||
|
this._player_state = connection.voice.PlayerState.PLAYING;
|
||||||
|
case connection.voice.PlayerState.PLAYING:
|
||||||
|
this.replay_queue();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private replay_queue() {
|
||||||
|
let buffer: AudioBuffer;
|
||||||
|
while((buffer = this._buffered_samples.pop_front())) {
|
||||||
|
if(this._playing_nodes.length >= this._latency_buffer_length * 1.5 + 3) {
|
||||||
|
console.log(tr("Dropping buffer because playing queue grows to much"));
|
||||||
|
continue; /* drop the data (we're behind) */
|
||||||
|
}
|
||||||
|
if(this._time_index < this.speakerContext.currentTime)
|
||||||
|
this._time_index = this.speakerContext.currentTime;
|
||||||
|
|
||||||
|
const player = this.speakerContext.createBufferSource();
|
||||||
|
player.buffer = buffer;
|
||||||
|
|
||||||
|
player.onended = () => this.on_buffer_replay_finished(player);
|
||||||
|
this._playing_nodes.push(player);
|
||||||
|
|
||||||
|
player.connect(audio.player.destination());
|
||||||
|
player.start(this._time_index);
|
||||||
|
this._time_index += buffer.duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_buffer_replay_finished(node: AudioBufferSourceNode) {
|
||||||
|
this._playing_nodes.remove(node);
|
||||||
|
this.test_buffer_queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
stopAudio(now: boolean = false) {
|
||||||
|
this._player_state = connection.voice.PlayerState.STOPPING;
|
||||||
|
if(now) {
|
||||||
|
this._player_state = connection.voice.PlayerState.STOPPED;
|
||||||
|
this._buffered_samples = [];
|
||||||
|
|
||||||
|
for(const entry of this._playing_nodes)
|
||||||
|
entry.stop(0);
|
||||||
|
this._playing_nodes = [];
|
||||||
|
|
||||||
|
if(this.callback_stopped)
|
||||||
|
this.callback_stopped();
|
||||||
|
} else {
|
||||||
|
this.test_buffer_queue(); /* test if we're not already done */
|
||||||
|
this.replay_queue(); /* flush the queue */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private test_buffer_queue() {
|
||||||
|
if(this._buffered_samples.length == 0 && this._playing_nodes.length == 0) {
|
||||||
|
if(this._player_state != connection.voice.PlayerState.STOPPING && this._player_state != connection.voice.PlayerState.STOPPED) {
|
||||||
|
if(this._player_state == connection.voice.PlayerState.BUFFERING)
|
||||||
|
return; //We're already buffering
|
||||||
|
|
||||||
|
this._player_state = connection.voice.PlayerState.BUFFERING;
|
||||||
|
if(!this.allowBuffering)
|
||||||
|
console.warn(tr("[Audio] Detected a buffer underflow!"));
|
||||||
|
this.reset_buffer_timeout(true);
|
||||||
|
} else {
|
||||||
|
this._player_state = connection.voice.PlayerState.STOPPED;
|
||||||
|
if(this.callback_stopped)
|
||||||
|
this.callback_stopped();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private reset_buffer_timeout(restart: boolean) {
|
||||||
|
if(this._buffer_timeout)
|
||||||
|
clearTimeout(this._buffer_timeout);
|
||||||
|
|
||||||
|
if(restart)
|
||||||
|
this._buffer_timeout = setTimeout(() => {
|
||||||
|
if(this._player_state == connection.voice.PlayerState.PREBUFFERING || this._player_state == connection.voice.PlayerState.BUFFERING) {
|
||||||
|
console.warn(tr("[Audio] Buffering exceeded timeout. Flushing and stopping replay"));
|
||||||
|
this.stopAudio();
|
||||||
|
}
|
||||||
|
this._buffer_timeout = undefined;
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private apply_volume_to_buffer(buffer: AudioBuffer) {
|
||||||
|
if(this._volume == 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(let channel = 0; channel < buffer.numberOfChannels; channel++) {
|
||||||
|
let data = buffer.getChannelData(channel);
|
||||||
|
for(let sample = 0; sample < data.length; sample++) {
|
||||||
|
let lane = data[sample];
|
||||||
|
lane *= this._volume;
|
||||||
|
data[sample] = lane;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private set_state(state: connection.voice.PlayerState) {
|
||||||
|
if(this._player_state == state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._player_state = state;
|
||||||
|
if(this.callback_state_changed)
|
||||||
|
this.callback_state_changed(this._player_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
get_codec_cache(codec: number) : CodecClientCache {
|
||||||
|
while(this._codecCache.length <= codec)
|
||||||
|
this._codecCache.push(new CodecClientCache());
|
||||||
|
|
||||||
|
return this._codecCache[codec];
|
||||||
|
}
|
||||||
|
|
||||||
|
get_state(): connection.voice.PlayerState {
|
||||||
|
return this._player_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_volume(): number {
|
||||||
|
return this._volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_volume(volume: number): void {
|
||||||
|
if(this._volume == volume)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._volume = volume;
|
||||||
|
|
||||||
|
/* apply the volume to all other buffers */
|
||||||
|
for(const buffer of this._buffered_samples)
|
||||||
|
this.apply_volume_to_buffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
abort_replay() {
|
||||||
|
this.stopAudio(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
||||||
/// <reference path="VoiceHandler.ts" />
|
/// <reference path="VoiceHandler.ts" />
|
||||||
/// <reference path="../utils/modal.ts" />
|
/// <reference path="../ui/elements/modal.ts" />
|
||||||
|
|
||||||
abstract class VoiceActivityDetector {
|
abstract class VoiceActivityDetector {
|
||||||
protected handle: VoiceRecorder;
|
protected handle: VoiceRecorder;
|
||||||
|
@ -34,17 +34,25 @@ if(!AudioBuffer.prototype.copyToChannel) { //Webkit does not implement this func
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let voice_recoder: VoiceRecorder;
|
||||||
class VoiceRecorder {
|
class VoiceRecorder {
|
||||||
private static readonly CHANNEL = 0;
|
private static readonly CHANNEL = 0;
|
||||||
private static readonly CHANNELS = 2;
|
private static readonly CHANNELS = 2;
|
||||||
private static readonly BUFFER_SIZE = 1024 * 4;
|
private static readonly BUFFER_SIZE = 1024 * 4;
|
||||||
|
|
||||||
handle: VoiceConnection;
|
on_support_state_change: () => any;
|
||||||
on_data: (data: AudioBuffer, head: boolean) => void = undefined;
|
on_data: (data: AudioBuffer, head: boolean) => void = undefined;
|
||||||
on_end: () => any;
|
on_end: () => any;
|
||||||
on_start: () => any;
|
on_start: () => any;
|
||||||
|
on_yield: () => any; /* called when owner looses ownership */
|
||||||
|
|
||||||
|
owner: connection.voice.AbstractVoiceConnection | undefined;
|
||||||
|
|
||||||
|
private on_ready_callbacks: (() => any)[] = [];
|
||||||
|
|
||||||
private _recording: boolean = false;
|
private _recording: boolean = false;
|
||||||
|
private _recording_supported: boolean = true; /* recording is supported until anything else had been set */
|
||||||
|
private _tag_favicon: JQuery;
|
||||||
|
|
||||||
private microphoneStream: MediaStreamAudioSourceNode = undefined;
|
private microphoneStream: MediaStreamAudioSourceNode = undefined;
|
||||||
private mediaStream: MediaStream = undefined;
|
private mediaStream: MediaStream = undefined;
|
||||||
|
@ -59,9 +67,9 @@ class VoiceRecorder {
|
||||||
private _deviceId: string;
|
private _deviceId: string;
|
||||||
private _deviceGroup: string;
|
private _deviceGroup: string;
|
||||||
|
|
||||||
constructor(handle: VoiceConnection) {
|
private current_handler: ConnectionHandler;
|
||||||
this.handle = handle;
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
this._deviceId = settings.global("microphone_device_id", "default");
|
this._deviceId = settings.global("microphone_device_id", "default");
|
||||||
this._deviceGroup = settings.global("microphone_device_group", "default");
|
this._deviceGroup = settings.global("microphone_device_group", "default");
|
||||||
|
|
||||||
|
@ -72,8 +80,8 @@ class VoiceRecorder {
|
||||||
const empty_buffer = this.audioContext.createBuffer(VoiceRecorder.CHANNELS, VoiceRecorder.BUFFER_SIZE, 48000);
|
const empty_buffer = this.audioContext.createBuffer(VoiceRecorder.CHANNELS, VoiceRecorder.BUFFER_SIZE, 48000);
|
||||||
this.processor.addEventListener('audioprocess', ev => {
|
this.processor.addEventListener('audioprocess', ev => {
|
||||||
if(this.microphoneStream && this.vadHandler.shouldRecord(ev.inputBuffer)) {
|
if(this.microphoneStream && this.vadHandler.shouldRecord(ev.inputBuffer)) {
|
||||||
if(this._chunkCount == 0 && this.on_start)
|
if(this._chunkCount == 0)
|
||||||
this.on_start();
|
this.on_voice_start();
|
||||||
|
|
||||||
if(this.on_data)
|
if(this.on_data)
|
||||||
this.on_data(ev.inputBuffer, this._chunkCount == 0);
|
this.on_data(ev.inputBuffer, this._chunkCount == 0);
|
||||||
|
@ -83,8 +91,8 @@ class VoiceRecorder {
|
||||||
}
|
}
|
||||||
this._chunkCount++;
|
this._chunkCount++;
|
||||||
} else {
|
} else {
|
||||||
if(this._chunkCount != 0 && this.on_end)
|
if(this._chunkCount != 0 )
|
||||||
this.on_end();
|
this.on_voice_end();
|
||||||
this._chunkCount = 0;
|
this._chunkCount = 0;
|
||||||
|
|
||||||
for(let channel = 0; channel < ev.inputBuffer.numberOfChannels; channel++)
|
for(let channel = 0; channel < ev.inputBuffer.numberOfChannels; channel++)
|
||||||
|
@ -96,17 +104,39 @@ class VoiceRecorder {
|
||||||
if(this.vadHandler)
|
if(this.vadHandler)
|
||||||
this.vadHandler.initialise();
|
this.vadHandler.initialise();
|
||||||
this.on_microphone(this.mediaStream);
|
this.on_microphone(this.mediaStream);
|
||||||
|
|
||||||
|
for(const callback of this.on_ready_callbacks)
|
||||||
|
callback();
|
||||||
|
this.on_ready_callbacks = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setVADHandler(new PassThroughVAD());
|
this.setVADHandler(new PassThroughVAD());
|
||||||
|
this._tag_favicon = $("head link[rel='icon']");
|
||||||
}
|
}
|
||||||
|
|
||||||
available() : boolean {
|
own_recoder(connection: connection.voice.AbstractVoiceConnection | undefined) {
|
||||||
return !!getUserMediaFunction() && !!getUserMediaFunction();
|
if(connection === this.owner)
|
||||||
|
return;
|
||||||
|
if(this.on_yield)
|
||||||
|
this.on_yield();
|
||||||
|
|
||||||
|
this.owner = connection;
|
||||||
|
|
||||||
|
this.on_end = undefined;
|
||||||
|
this.on_start = undefined;
|
||||||
|
this.on_data = undefined;
|
||||||
|
this.on_yield = undefined;
|
||||||
|
this.on_support_state_change = undefined;
|
||||||
|
this.on_ready_callbacks = [];
|
||||||
|
|
||||||
|
this._chunkCount = 0;
|
||||||
|
|
||||||
|
if(this.processor) /* processor stream might be null because of the late audio initialisation */
|
||||||
|
this.processor.connect(this.audioContext.destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
recording() : boolean {
|
input_available() : boolean {
|
||||||
return this._recording;
|
return !!getUserMediaFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
getMediaStream() : MediaStream {
|
getMediaStream() : MediaStream {
|
||||||
|
@ -182,12 +212,22 @@ class VoiceRecorder {
|
||||||
return this.vadHandler;
|
return this.vadHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
update(flag: boolean) {
|
set_recording(flag_enabled: boolean) {
|
||||||
if(this._recording == flag) return;
|
if(this._recording == flag_enabled)
|
||||||
if(flag) this.start(this._deviceId, this._deviceGroup);
|
return;
|
||||||
else this.stop();
|
|
||||||
|
if(flag_enabled)
|
||||||
|
this.start_recording(this._deviceId, this._deviceGroup);
|
||||||
|
else
|
||||||
|
this.stop_recording();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clean_recording_supported() { this._recording_supported = true; }
|
||||||
|
|
||||||
|
is_recording_supported() { return this._recording_supported; }
|
||||||
|
|
||||||
|
is_recording() { return this._recording; }
|
||||||
|
|
||||||
device_group_id() : string { return this._deviceGroup; }
|
device_group_id() : string { return this._deviceGroup; }
|
||||||
device_id() : string { return this._deviceId; }
|
device_id() : string { return this._deviceId; }
|
||||||
|
|
||||||
|
@ -199,12 +239,12 @@ class VoiceRecorder {
|
||||||
settings.changeGlobal("microphone_device_id", device);
|
settings.changeGlobal("microphone_device_id", device);
|
||||||
settings.changeGlobal("microphone_device_group", group);
|
settings.changeGlobal("microphone_device_group", group);
|
||||||
if(this._recording) {
|
if(this._recording) {
|
||||||
this.stop();
|
this.stop_recording();
|
||||||
this.start(device, group);
|
this.start_recording(device, group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
start(device: string, groupId: string){
|
start_recording(device: string, groupId: string){
|
||||||
this._deviceId = device;
|
this._deviceId = device;
|
||||||
this._deviceGroup = groupId;
|
this._deviceGroup = groupId;
|
||||||
|
|
||||||
|
@ -220,13 +260,20 @@ class VoiceRecorder {
|
||||||
echoCancellationType: 'browser'
|
echoCancellationType: 'browser'
|
||||||
}
|
}
|
||||||
}, this.on_microphone.bind(this), error => {
|
}, this.on_microphone.bind(this), error => {
|
||||||
|
this._recording = false;
|
||||||
|
if(this._recording_supported) {
|
||||||
|
this._recording_supported = false;
|
||||||
|
if(this.on_support_state_change)
|
||||||
|
this.on_support_state_change();
|
||||||
|
}
|
||||||
|
|
||||||
createErrorModal(tr("Could not resolve microphone!"), tr("Could not resolve microphone!<br>Message: ") + error).open();
|
createErrorModal(tr("Could not resolve microphone!"), tr("Could not resolve microphone!<br>Message: ") + error).open();
|
||||||
console.error(tr("Could not get microphone!"));
|
console.error(tr("Could not get microphone!"));
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(stop_media_stream: boolean = true){
|
stop_recording(stop_media_stream: boolean = true){
|
||||||
console.log(tr("Stop recording!"));
|
console.log(tr("Stop recording!"));
|
||||||
this._recording = false;
|
this._recording = false;
|
||||||
|
|
||||||
|
@ -244,13 +291,21 @@ class VoiceRecorder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
on_initialized(callback: () => any) {
|
||||||
|
if(this.processor)
|
||||||
|
callback();
|
||||||
|
else
|
||||||
|
this.on_ready_callbacks.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
private on_microphone(stream: MediaStream) {
|
private on_microphone(stream: MediaStream) {
|
||||||
const old_microphone_stream = this.microphoneStream;
|
const old_microphone_stream = this.microphoneStream;
|
||||||
if(old_microphone_stream)
|
if(old_microphone_stream)
|
||||||
this.stop(this.mediaStream != stream); //Disconnect old stream
|
this.stop_recording(this.mediaStream != stream); //Disconnect old stream
|
||||||
|
|
||||||
this.mediaStream = stream;
|
this.mediaStream = stream;
|
||||||
if(!this.mediaStream) return;
|
if(!this.mediaStream)
|
||||||
|
return;
|
||||||
|
|
||||||
if(!this.audioContext) {
|
if(!this.audioContext) {
|
||||||
console.log(tr("[VoiceRecorder] Got microphone stream, but havn't a audio context. Waiting until its initialized"));
|
console.log(tr("[VoiceRecorder] Got microphone stream, but havn't a audio context. Waiting until its initialized"));
|
||||||
|
@ -261,6 +316,24 @@ class VoiceRecorder {
|
||||||
this.microphoneStream.connect(this.processor);
|
this.microphoneStream.connect(this.processor);
|
||||||
if(this.vadHandler)
|
if(this.vadHandler)
|
||||||
this.vadHandler.initialiseNewStream(old_microphone_stream, this.microphoneStream);
|
this.vadHandler.initialiseNewStream(old_microphone_stream, this.microphoneStream);
|
||||||
|
|
||||||
|
if(!this._recording_supported) {
|
||||||
|
this._recording_supported = true;
|
||||||
|
if(this.on_support_state_change)
|
||||||
|
this.on_support_state_change();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_voice_start() {
|
||||||
|
this._tag_favicon.attr('href', "img/favicon/speaking.png");
|
||||||
|
if(this.on_start)
|
||||||
|
this.on_start();
|
||||||
|
|
||||||
|
}
|
||||||
|
private on_voice_end() {
|
||||||
|
this._tag_favicon.attr('href', "img/favicon/teacup.png");
|
||||||
|
if(this.on_end)
|
||||||
|
this.on_end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class MuteVAD extends VoiceActivityDetector {
|
class MuteVAD extends VoiceActivityDetector {
|
||||||
|
|
|
@ -322,6 +322,9 @@ generators[SyntaxKind.ClassDeclaration] = (settings, stack, node: ts.ClassDeclar
|
||||||
};
|
};
|
||||||
|
|
||||||
generators[SyntaxKind.PropertySignature] = (settings, stack, node: ts.PropertySignature) => {
|
generators[SyntaxKind.PropertySignature] = (settings, stack, node: ts.PropertySignature) => {
|
||||||
|
if(!node.type)
|
||||||
|
return node;
|
||||||
|
|
||||||
console.log(SyntaxKind[node.type.kind]);
|
console.log(SyntaxKind[node.type.kind]);
|
||||||
let type: ts.TypeNode = node.type;
|
let type: ts.TypeNode = node.type;
|
||||||
switch (node.type.kind) {
|
switch (node.type.kind) {
|
||||||
|
|
|
@ -5,13 +5,13 @@ html, body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
||||||
min-height: 250px;
|
//min-height: 250px;
|
||||||
min-width: 250px;
|
//min-width: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-container {
|
.app-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: stretch;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
right: 10px;
|
right: 10px;
|
||||||
|
|
Loading…
Add table
Reference in a new issue