Merged with develop and fixed build
commit
d75e1ca492
17
ChangeLog.md
17
ChangeLog.md
|
@ -1,4 +1,21 @@
|
||||||
# Changelog:
|
# Changelog:
|
||||||
|
* **23.12.18**
|
||||||
|
- Added query account management (since server 1.2.32b)
|
||||||
|
|
||||||
|
* **18.12.18**
|
||||||
|
- Added bookmarks and bookmarks management
|
||||||
|
- Added query user visibility button and creation (Query management will follow soon)
|
||||||
|
- Fixed overflow within the group assignment dialog
|
||||||
|
|
||||||
|
* **17.12.18**
|
||||||
|
- Implemented group prefix and suffix
|
||||||
|
|
||||||
|
* **15.12.18**
|
||||||
|
- Implemented a translation system with default translated language sets for
|
||||||
|
- German
|
||||||
|
- Turkish
|
||||||
|
- Russian
|
||||||
|
|
||||||
* **3.12.18**
|
* **3.12.18**
|
||||||
- Fixed url connect parameters
|
- Fixed url connect parameters
|
||||||
|
|
||||||
|
|
|
@ -103,9 +103,7 @@ $background:lightgray;
|
||||||
background-color: $background;
|
background-color: $background;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 2px solid rgba(0, 0, 0, 0);
|
border: 2px solid $border_color_activated;
|
||||||
|
|
||||||
border-color: $border_color_activated;
|
|
||||||
width: 230px;
|
width: 230px;
|
||||||
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
@ -134,6 +132,10 @@ $background:lightgray;
|
||||||
& > div:last-of-type {
|
& > div:last-of-type {
|
||||||
border-radius: 0 0 2px 2px;
|
border-radius: 0 0 2px 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.display_left {
|
||||||
|
margin-left: -165px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -142,4 +144,10 @@ $background:lightgray;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bookmark-dropdown {
|
||||||
|
hr:last-child {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
.query-create {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.row-name {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
input {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
margin-top: 5px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-created {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.property-row {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: stretch;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
margin-top: 2px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:first-of-type {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div:last-of-type {
|
||||||
|
margin-left: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
margin-top: 5px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-management {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
.header, .footer {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
margin-left: 5px;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-list {
|
||||||
|
margin-top: 5px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
.column {
|
||||||
|
&.column-username {
|
||||||
|
width: calc(50% - 75px)
|
||||||
|
}
|
||||||
|
|
||||||
|
&.column-unique-id {
|
||||||
|
width: calc(50% - 75px)
|
||||||
|
}
|
||||||
|
|
||||||
|
&.column-bound-server {
|
||||||
|
width: 150px;
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-list-header {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 20px;
|
||||||
|
|
||||||
|
.column {
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-list-entries-container {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: start;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-height: 250px;
|
||||||
|
|
||||||
|
.entry {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.column {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.scrollbar {
|
||||||
|
.column-username {
|
||||||
|
width: calc(50% - 75px + 30px)
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-unique-id {
|
||||||
|
width: calc(50% - 75px + 30px)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
margin-top: 5px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -520,6 +520,7 @@
|
||||||
.group-list {
|
.group-list {
|
||||||
border: lightgray solid 1px;
|
border: lightgray solid 1px;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
.group-entry {
|
.group-entry {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -53,11 +53,21 @@
|
||||||
.channelTree div * {vertical-align: middle;display:inline-block;height: 16px;padding: 0px;}
|
.channelTree div * {vertical-align: middle;display:inline-block;height: 16px;padding: 0px;}
|
||||||
.channelTree div img {border: 0;}
|
.channelTree div img {border: 0;}
|
||||||
.channelTree div > span {position: absolute; right: 0;}
|
.channelTree div > span {position: absolute; right: 0;}
|
||||||
.channelTree .name {
|
.channelTree {
|
||||||
vertical-align: middle;
|
.name, .group_prefix, .group_suffix, .away {
|
||||||
margin-top: 1px;
|
vertical-align: middle;
|
||||||
height: 14px;
|
margin-top: 1px;
|
||||||
display: inline;
|
height: 14px;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group_prefix {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group_suffix {
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.channelTree .own_name {
|
.channelTree .own_name {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
<link rel="stylesheet" href="css/ts/icons.css" type="text/css">
|
<link rel="stylesheet" href="css/ts/icons.css" type="text/css">
|
||||||
<link rel="stylesheet" href="css/general.css" type="text/css">
|
<link rel="stylesheet" href="css/general.css" type="text/css">
|
||||||
<link rel="stylesheet" href="css/modals.css" type="text/css">
|
<link rel="stylesheet" href="css/modals.css" type="text/css">
|
||||||
|
<link rel="stylesheet" href="css/modal-query.css" type="text/css">
|
||||||
<link rel="stylesheet" href="css/modal-banlist.css" type="text/css">
|
<link rel="stylesheet" href="css/modal-banlist.css" type="text/css">
|
||||||
<link rel="stylesheet" href="css/modal-bancreate.css" type="text/css">
|
<link rel="stylesheet" href="css/modal-bancreate.css" type="text/css">
|
||||||
<link rel="stylesheet" href="css/modal-settings.css" type="text/css">
|
<link rel="stylesheet" href="css/modal-settings.css" type="text/css">
|
||||||
|
|
|
@ -19,7 +19,24 @@
|
||||||
<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 btn_disconnect"><div class="icon_x32 client-disconnect"></div></div>-->
|
|
||||||
|
<!--
|
||||||
|
<div class="button-dropdown btn_bookmark" title="{{tr 'Bookmarks' /}}">
|
||||||
|
<div class="buttons">
|
||||||
|
<div class="button icon_x32 client-bookmark_manager btn_bookmark_list"></div>
|
||||||
|
<div class="button-dropdown">
|
||||||
|
<div class="arrow"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown bookmark-dropdown" style="width: 300px">
|
||||||
|
<div class="btn_bookmark_list"><div class="icon client-bookmark_manager"></div><a>{{tr "Manage bookmarks" /}}</a></div>
|
||||||
|
<div class="btn_bookmark_add"><div class="icon client-bookmark_add"></div><a>{{tr "Add current server to bookmarks" /}}</a></div>
|
||||||
|
<div class="btn_bookmark_remove"><div class="icon client-bookmark_remove"></div><a>{{tr "Remove current server to bookmarks" /}}</a></div>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="button-dropdown btn_away" title="{{tr 'Toggle away status' /}}">
|
<div class="button-dropdown btn_away" title="{{tr 'Toggle away status' /}}">
|
||||||
|
@ -62,6 +79,21 @@
|
||||||
<div class="button btn_permissions" title="{{tr 'View/edit permissions' /}}">
|
<div class="button btn_permissions" title="{{tr 'View/edit permissions' /}}">
|
||||||
<div class="icon_x32 client-permission_overview"></div>
|
<div class="icon_x32 client-permission_overview"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- the query button -->
|
||||||
|
<div class="button-dropdown btn_query" title="{{tr 'Show/hide server queries' /}}">
|
||||||
|
<div class="buttons">
|
||||||
|
<div class="button icon_x32 client-server_query btn_query_toggle"></div>
|
||||||
|
<div class="button-dropdown">
|
||||||
|
<div class="arrow"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown display_left">
|
||||||
|
<div class="btn_query_toggle"><div class="icon client-toggle_server_query_clients"></div><a>{{tr "Show/hide server queries" /}}</a></div>
|
||||||
|
<div class="btn_query_manage"><div class="icon client-server_query"></div><a>{{tr "Manage server queries" /}}</a></div>
|
||||||
|
<!-- <div class="btn_query_create"><div class="icon client-away"></div><a>{{tr "Create server query login" /}}</a></div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="button btn_open_settings" title="{{tr 'Edit global client settings' /}}">
|
<div class="button btn_open_settings" title="{{tr 'Edit global client settings' /}}">
|
||||||
<div class="icon_x32 client-settings"></div>
|
<div class="icon_x32 client-settings"></div>
|
||||||
|
@ -1074,7 +1106,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
9
|
||||||
<script class="jsrender-template" id="tmpl_client_ban" type="text/html">
|
<script class="jsrender-template" id="tmpl_client_ban" type="text/html">
|
||||||
<div class="align_column">
|
<div class="align_column">
|
||||||
<div class="align_column" style="margin: 5px">
|
<div class="align_column" style="margin: 5px">
|
||||||
|
@ -1656,5 +1688,84 @@
|
||||||
<button class="button-close">{{tr "Close" /}}</button>
|
<button class="button-close">{{tr "Close" /}}</button>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script class="jsrender-template" id="tmpl_query_create" type="text/html">
|
||||||
|
<div class="query-create">
|
||||||
|
<a>{{tr "Set the login name for your Server Query account." /}}</a>
|
||||||
|
<a>{{tr "You'll receive your password within the next step." /}}</a>
|
||||||
|
<div class="row-name">
|
||||||
|
<a>Name:</a>
|
||||||
|
<input type="text" maxlength="64" minlength="3" class="input-name">
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="button-close">{{tr "Close" /}}</button>
|
||||||
|
<button class="button-create">{{tr "Create" /}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
<script class="jsrender-template" id="tmpl_query_created" type="text/html">
|
||||||
|
<div class="query-created">
|
||||||
|
<a>{{tr "Your server query credentials:" /}}</a>
|
||||||
|
<div class="property-row">
|
||||||
|
<a>Name:</a>
|
||||||
|
<input class="query_name" type="text" maxlength="64" minlength="3" value="{{>username}}">
|
||||||
|
<div class="btn_copy_name icon client-copy" title="{{tr 'Copy username' /}}"></div>
|
||||||
|
</div>
|
||||||
|
<div class="property-row">
|
||||||
|
<a>Password:</a>
|
||||||
|
<input class="query_password" type="text" value="{{>password}}">
|
||||||
|
<div class="btn_copy_password icon client-copy" title="{{tr 'Copy password' /}}"></div>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="button-close">{{tr "Close" /}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script class="jsrender-template" id="tmpl_query_manager" type="text/html">
|
||||||
|
<div class="query-management">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="button button-query-create">{{tr "Create account" /}}</button>
|
||||||
|
<button class="button button-query-delete">{{tr "Delete account" /}}</button>
|
||||||
|
<button class="button button-query-rename">{{tr "Rename account" /}}</button>
|
||||||
|
<button class="button button-query-change-password">{{tr "Change password" /}}</button>
|
||||||
|
</div>
|
||||||
|
<div class="search">
|
||||||
|
<input class="input input-search" type="text" placeholder="search">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="query-list">
|
||||||
|
<div class="query-list-header">
|
||||||
|
<div class="column column-username">{{tr "Username" /}}</div>
|
||||||
|
<div class="column column-unique-id">{{tr "Unique ID" /}}</div>
|
||||||
|
<div class="column column-bound-server">{{tr "Bounded Server" /}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="query-list-entries-container">
|
||||||
|
<div class="query-list-entries">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<div class="info">
|
||||||
|
<a>loading...</a>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="button-refresh">{{tr "Refresh" /}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<script class="jsrender-template" id="tmpl_query_manager-list_entry" type="text/html">
|
||||||
|
<div class="entry">
|
||||||
|
<div class="column column-username">{{>username}}</div>
|
||||||
|
<div class="column column-unique-id">{{>unique_id}}</div>
|
||||||
|
<div class="column column-bound-server">{{>bounded_server}}</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -11,6 +11,10 @@
|
||||||
{
|
{
|
||||||
"key": "tr_gt",
|
"key": "tr_gt",
|
||||||
"path": "tr_google_translate.translation"
|
"path": "tr_google_translate.translation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "fr_gt",
|
||||||
|
"path": "fr_google_translate.translation"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"name": "Default TeaSpeak repository",
|
"name": "Default TeaSpeak repository",
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
namespace bookmarks {
|
||||||
|
function guid() {
|
||||||
|
function s4() {
|
||||||
|
return Math.floor((1 + Math.random()) * 0x10000)
|
||||||
|
.toString(16)
|
||||||
|
.substring(1);
|
||||||
|
}
|
||||||
|
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConnectIdentity {
|
||||||
|
identity_type: IdentitifyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ForumConnectIdentity extends ConnectIdentity { }
|
||||||
|
export interface NicknameConnectIdentity extends ConnectIdentity { }
|
||||||
|
export interface TeamSpeakConnectIdentity extends ConnectIdentity { }
|
||||||
|
|
||||||
|
export interface ServerProperties {
|
||||||
|
server_address: string;
|
||||||
|
server_port: number;
|
||||||
|
server_password_hash?: string;
|
||||||
|
server_password?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum BookmarkType {
|
||||||
|
ENTRY,
|
||||||
|
DIRECTORY
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Bookmark {
|
||||||
|
type: /* BookmarkType.ENTRY */ BookmarkType;
|
||||||
|
|
||||||
|
/* readonly directory: DirectoryBookmark; */
|
||||||
|
server_properties: ServerProperties;
|
||||||
|
display_name: string;
|
||||||
|
unique_id: string;
|
||||||
|
|
||||||
|
nickname: string;
|
||||||
|
default_channel?: number | string;
|
||||||
|
default_channel_password_hash?: string;
|
||||||
|
default_channel_password?: string;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectoryBookmark {
|
||||||
|
type: /* BookmarkType.DIRECTORY */ BookmarkType;
|
||||||
|
|
||||||
|
readonly content: (Bookmark | DirectoryBookmark)[];
|
||||||
|
unique_id: string;
|
||||||
|
display_name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BookmarkConfig {
|
||||||
|
root_bookmark?: DirectoryBookmark;
|
||||||
|
default_added?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _bookmark_config: BookmarkConfig;
|
||||||
|
|
||||||
|
function bookmark_config() : BookmarkConfig {
|
||||||
|
if(_bookmark_config)
|
||||||
|
return _bookmark_config;
|
||||||
|
|
||||||
|
let bookmark_json = localStorage.getItem("bookmarks");
|
||||||
|
let bookmarks = JSON.parse(bookmark_json) || {} as BookmarkConfig;
|
||||||
|
|
||||||
|
_bookmark_config = bookmarks;
|
||||||
|
_bookmark_config.root_bookmark = _bookmark_config.root_bookmark || { content: [], display_name: "root", type: BookmarkType.DIRECTORY} as DirectoryBookmark;
|
||||||
|
|
||||||
|
if(!_bookmark_config.default_added) {
|
||||||
|
_bookmark_config.default_added = true;
|
||||||
|
create_bookmark("TeaSpeak official Test-Server", _bookmark_config.root_bookmark, {
|
||||||
|
server_address: "ts.teaspeak.de",
|
||||||
|
server_port: 9987
|
||||||
|
}, "Another TeaSpeak user");
|
||||||
|
|
||||||
|
save_config();
|
||||||
|
}
|
||||||
|
return _bookmark_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
function save_config() {
|
||||||
|
localStorage.setItem("bookmarks", JSON.stringify(bookmark_config()));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bookmarks() : DirectoryBookmark {
|
||||||
|
return bookmark_config().root_bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
function find_bookmark_recursive(parent: DirectoryBookmark, uuid: string) : Bookmark | DirectoryBookmark {
|
||||||
|
for(const entry of parent.content) {
|
||||||
|
if(entry.unique_id == uuid)
|
||||||
|
return entry;
|
||||||
|
if(entry.type == BookmarkType.DIRECTORY) {
|
||||||
|
const result = find_bookmark_recursive(entry as DirectoryBookmark, uuid);
|
||||||
|
if(result) return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function find_bookmark(uuid: string) : Bookmark | DirectoryBookmark | undefined {
|
||||||
|
return find_bookmark_recursive(bookmarks(), uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function create_bookmark(display_name: string, directory: DirectoryBookmark, server_properties: ServerProperties, nickname: string) : Bookmark {
|
||||||
|
const bookmark = {
|
||||||
|
display_name: display_name,
|
||||||
|
server_properties: server_properties,
|
||||||
|
nickname: nickname,
|
||||||
|
type: BookmarkType.ENTRY,
|
||||||
|
unique_id: guid()
|
||||||
|
} as Bookmark;
|
||||||
|
|
||||||
|
directory.content.push(bookmark);
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function create_bookmark_directory(parent: DirectoryBookmark, name: string) : DirectoryBookmark {
|
||||||
|
const bookmark = {
|
||||||
|
type: BookmarkType.DIRECTORY,
|
||||||
|
|
||||||
|
display_name: name,
|
||||||
|
content: [],
|
||||||
|
unique_id: guid()
|
||||||
|
} as DirectoryBookmark;
|
||||||
|
|
||||||
|
parent.content.push(bookmark);
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO test if the new parent is within the old bookmark
|
||||||
|
export function change_directory(parent: DirectoryBookmark, bookmark: Bookmark | DirectoryBookmark) {
|
||||||
|
delete_bookmark(bookmark)
|
||||||
|
parent.content.push(bookmark)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function save_bookmark(bookmark?: Bookmark | DirectoryBookmark) {
|
||||||
|
save_config(); /* nvm we dont give a fuck... saving everything */
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete_bookmark_recursive(parent: DirectoryBookmark, bookmark: Bookmark | DirectoryBookmark) {
|
||||||
|
const index = parent.content.indexOf(bookmark);
|
||||||
|
if(index != -1)
|
||||||
|
parent.content.remove(bookmark);
|
||||||
|
else
|
||||||
|
for(const entry of parent.content)
|
||||||
|
if(entry.type == BookmarkType.DIRECTORY)
|
||||||
|
delete_bookmark_recursive(entry as DirectoryBookmark, bookmark)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function delete_bookmark(bookmark: Bookmark | DirectoryBookmark) {
|
||||||
|
delete_bookmark_recursive(bookmarks(), bookmark)
|
||||||
|
}
|
||||||
|
}
|
|
@ -402,10 +402,24 @@ interface ClientNameFromUid {
|
||||||
response: ClientNameInfo[]
|
response: ClientNameInfo[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface QueryListEntry {
|
||||||
|
username: string;
|
||||||
|
unique_id: string;
|
||||||
|
bounded_server: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface QueryList {
|
||||||
|
flag_own: boolean;
|
||||||
|
flag_all: boolean;
|
||||||
|
|
||||||
|
queries: QueryListEntry[];
|
||||||
|
}
|
||||||
|
|
||||||
class CommandHelper {
|
class CommandHelper {
|
||||||
readonly connection: ServerConnection;
|
readonly connection: ServerConnection;
|
||||||
|
|
||||||
private _callbacks_namefromuid: ClientNameFromUid[] = [];
|
private _callbacks_namefromuid: ClientNameFromUid[] = [];
|
||||||
|
private _who_am_i: any;
|
||||||
|
|
||||||
constructor(connection) {
|
constructor(connection) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
|
@ -432,6 +446,63 @@ class CommandHelper {
|
||||||
return req.promise;
|
return req.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request_query_list(server_id: number = undefined) : Promise<QueryList> {
|
||||||
|
return new Promise<QueryList>((resolve, reject) => {
|
||||||
|
this.connection.commandHandler["notifyquerylist"] = json => {
|
||||||
|
const result = {} as QueryList;
|
||||||
|
|
||||||
|
result.flag_all = json[0]["flag_all"];
|
||||||
|
result.flag_own = json[0]["flag_own"];
|
||||||
|
result.queries = [];
|
||||||
|
|
||||||
|
for(const entry of json) {
|
||||||
|
const rentry = {} as QueryListEntry;
|
||||||
|
rentry.bounded_server = entry["client_bounded_server"];
|
||||||
|
rentry.username = entry["client_login_name"];
|
||||||
|
rentry.unique_id = entry["client_unique_identifier"];
|
||||||
|
|
||||||
|
result.queries.push(rentry);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(result);
|
||||||
|
this.connection.commandHandler["notifyquerylist"] = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = {};
|
||||||
|
if(server_id !== undefined)
|
||||||
|
data["server_id"] = server_id;
|
||||||
|
|
||||||
|
this.connection.sendCommand("querylist", data).catch(error => {
|
||||||
|
if(error instanceof CommandResult) {
|
||||||
|
if(error.id == 0x0501) {
|
||||||
|
resolve(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reject(error);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* Its just a workaround for the query management.
|
||||||
|
* There is no garante that the whoami trick will work forever
|
||||||
|
*/
|
||||||
|
current_virtual_server_id() : Promise<number> {
|
||||||
|
if(this._who_am_i)
|
||||||
|
return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"]));
|
||||||
|
|
||||||
|
return new Promise<number>((resolve, reject) => {
|
||||||
|
this.connection.commandHandler[""] = json => {
|
||||||
|
this._who_am_i = json[0];
|
||||||
|
resolve(parseInt(this._who_am_i["virtualserver_id"]));
|
||||||
|
this.connection.commandHandler[""] = undefined;
|
||||||
|
};
|
||||||
|
this.connection.sendCommand("whoami");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private handle_notifyclientnamefromuid(json: any[]) {
|
private handle_notifyclientnamefromuid(json: any[]) {
|
||||||
for(let entry of json) {
|
for(let entry of json) {
|
||||||
let info: ClientNameInfo = {} as any;
|
let info: ClientNameInfo = {} as any;
|
||||||
|
|
|
@ -180,6 +180,8 @@ function loadDebug() {
|
||||||
"js/crypto/hex.js",
|
"js/crypto/hex.js",
|
||||||
|
|
||||||
//Load UI
|
//Load UI
|
||||||
|
"js/ui/modal/ModalQuery.js",
|
||||||
|
"js/ui/modal/ModalQueryManage.js",
|
||||||
"js/ui/modal/ModalConnect.js",
|
"js/ui/modal/ModalConnect.js",
|
||||||
"js/ui/modal/ModalSettings.js",
|
"js/ui/modal/ModalSettings.js",
|
||||||
"js/ui/modal/ModalCreateChannel.js",
|
"js/ui/modal/ModalCreateChannel.js",
|
||||||
|
@ -219,6 +221,7 @@ function loadDebug() {
|
||||||
|
|
||||||
//Load general stuff
|
//Load general stuff
|
||||||
"js/settings.js",
|
"js/settings.js",
|
||||||
|
"js/bookmarks.js",
|
||||||
"js/contextMenu.js",
|
"js/contextMenu.js",
|
||||||
"js/connection.js",
|
"js/connection.js",
|
||||||
"js/FileManager.js",
|
"js/FileManager.js",
|
||||||
|
|
|
@ -21,9 +21,9 @@ const js_render = window.jsrender || $;
|
||||||
const native_client = window.require !== undefined;
|
const native_client = window.require !== undefined;
|
||||||
|
|
||||||
function getUserMediaFunction() {
|
function getUserMediaFunction() {
|
||||||
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)
|
if((navigator as any).mediaDevices && (navigator as any).mediaDevices.getUserMedia)
|
||||||
return (settings, success, fail) => { navigator.mediaDevices.getUserMedia(settings).then(success).catch(fail); };
|
return (settings, success, fail) => { (navigator as any).mediaDevices.getUserMedia(settings).then(success).catch(fail); };
|
||||||
return navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
|
return (navigator as any).getUserMedia || (navigator as any).webkitGetUserMedia || (navigator as any).mozGetUserMedia;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup_close() {
|
function setup_close() {
|
||||||
|
|
|
@ -143,9 +143,9 @@ class GroupManager {
|
||||||
group.updateProperty(key, groupData[key]);
|
group.updateProperty(key, groupData[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
group.requiredMemberRemovePower = groupData["n_member_removep"];
|
group.requiredMemberRemovePower = parseInt(groupData["n_member_removep"]);
|
||||||
group.requiredMemberAddPower = groupData["n_member_addp"];
|
group.requiredMemberAddPower = parseInt(groupData["n_member_addp"]);
|
||||||
group.requiredModifyPower = groupData["n_modifyp"];
|
group.requiredModifyPower = parseInt(groupData["n_modifyp"]);
|
||||||
|
|
||||||
if(target == GroupTarget.SERVER)
|
if(target == GroupTarget.SERVER)
|
||||||
this.serverGroups.push(group);
|
this.serverGroups.push(group);
|
||||||
|
@ -154,6 +154,8 @@ class GroupManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Got " + json.length + " new " + target + " groups:");
|
console.log("Got " + json.length + " new " + target + " groups:");
|
||||||
|
for(const client of this.handle.channelTree.clients)
|
||||||
|
client.update_displayed_client_groups();
|
||||||
}
|
}
|
||||||
|
|
||||||
request_permissions(group: Group) : Promise<PermissionValue[]> { //database_empty_result
|
request_permissions(group: Group) : Promise<PermissionValue[]> { //database_empty_result
|
||||||
|
|
|
@ -644,11 +644,12 @@ class PermissionManager {
|
||||||
for(let perm of this.neededPermissions)
|
for(let perm of this.neededPermissions)
|
||||||
if(perm.type.id == key || perm.type.name == key || perm.type == key)
|
if(perm.type.id == key || perm.type.name == key || perm.type == key)
|
||||||
return perm;
|
return perm;
|
||||||
|
|
||||||
log.debug(LogCategory.PERMISSIONS, tr("Could not resolve grant permission %o. Creating a new one."), key);
|
log.debug(LogCategory.PERMISSIONS, tr("Could not resolve grant permission %o. Creating a new one."), key);
|
||||||
let info = key instanceof PermissionInfo ? key : this.resolveInfo(key);
|
let info = key instanceof PermissionInfo ? key : this.resolveInfo(key);
|
||||||
if(!info) {
|
if(!info) {
|
||||||
log.warn(LogCategory.PERMISSIONS, tr("Requested needed permission with invalid key! (%o)"), key);
|
log.warn(LogCategory.PERMISSIONS, tr("Requested needed permission with invalid key! (%o)"), key);
|
||||||
return undefined;
|
return new NeededPermissionValue(undefined, -2);
|
||||||
}
|
}
|
||||||
let result = new NeededPermissionValue(info, -2);
|
let result = new NeededPermissionValue(info, -2);
|
||||||
this.neededPermissions.push(result);
|
this.neededPermissions.push(result);
|
||||||
|
|
|
@ -13,6 +13,7 @@ interface JSON {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface JQueryStatic<TElement extends Node = HTMLElement> {
|
interface JQueryStatic<TElement extends Node = HTMLElement> {
|
||||||
|
@ -106,8 +107,8 @@ if(typeof ($) !== "undefined") {
|
||||||
return $(document.createElement(tagName) as any);
|
return $(document.createElement(tagName) as any);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!$.prototype.renderTag) {
|
if(!$.fn.renderTag) {
|
||||||
$.prototype.renderTag = function (values?: any) : JQuery {
|
$.fn.renderTag = function (values?: any) : JQuery {
|
||||||
let result;
|
let result;
|
||||||
if(this.render) {
|
if(this.render) {
|
||||||
result = $(this.render(values));
|
result = $(this.render(values));
|
||||||
|
@ -126,6 +127,11 @@ if(typeof ($) !== "undefined") {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(!$.fn.hasScrollBar)
|
||||||
|
$.fn.hasScrollBar = function() {
|
||||||
|
return this.get(0).scrollHeight > this.height();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!String.prototype.format) {
|
if (!String.prototype.format) {
|
||||||
|
|
|
@ -294,12 +294,14 @@ class ChannelEntry {
|
||||||
|
|
||||||
const sub = this.siblings(false);
|
const sub = this.siblings(false);
|
||||||
sub.forEach(function (e) {
|
sub.forEach(function (e) {
|
||||||
subSize += e.rootTag().outerHeight(true);
|
if(e.rootTag().is(":visible"))
|
||||||
|
subSize += e.rootTag().outerHeight(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
const clients = this.clients(false);
|
const clients = this.clients(false);
|
||||||
clients.forEach(function (e) {
|
clients.forEach(function (e) {
|
||||||
clientSize += e.tag.outerHeight(true);
|
if(e.tag.is(":visible"))
|
||||||
|
clientSize += e.tag.outerHeight(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
this._tag_root.css({height: size + subSize + clientSize});
|
this._tag_root.css({height: size + subSize + clientSize});
|
||||||
|
|
|
@ -408,7 +408,9 @@ class ClientEntry {
|
||||||
|
|
||||||
tag.append($.spawn("div").addClass("icon_client_state").attr("title", "Client state"));
|
tag.append($.spawn("div").addClass("icon_client_state").attr("title", "Client state"));
|
||||||
|
|
||||||
|
tag.append($.spawn("div").addClass("group_prefix").attr("title", "Server groups prefixes").hide());
|
||||||
tag.append($.spawn("div").addClass("name").text(this.clientNickName()));
|
tag.append($.spawn("div").addClass("name").text(this.clientNickName()));
|
||||||
|
tag.append($.spawn("div").addClass("group_suffix").attr("title", "Server groups suffix").hide());
|
||||||
tag.append($.spawn("div").addClass("away").text(this.clientNickName()));
|
tag.append($.spawn("div").addClass("away").text(this.clientNickName()));
|
||||||
|
|
||||||
let clientIcons = $.spawn("span");
|
let clientIcons = $.spawn("span");
|
||||||
|
@ -561,7 +563,7 @@ class ClientEntry {
|
||||||
if(variable.key == "client_icon_id")
|
if(variable.key == "client_icon_id")
|
||||||
this.updateClientIcon();
|
this.updateClientIcon();
|
||||||
if(variable.key =="client_channel_group_id" || variable.key == "client_servergroups")
|
if(variable.key =="client_channel_group_id" || variable.key == "client_servergroups")
|
||||||
this.updateGroupIcons();
|
this.update_displayed_client_groups();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* process updates after variables have been set */
|
/* process updates after variables have been set */
|
||||||
|
@ -577,11 +579,39 @@ class ClientEntry {
|
||||||
group.end();
|
group.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGroupIcons() {
|
update_displayed_client_groups() {
|
||||||
this.tag.find("span .group_icons").children().detach();
|
this.tag.find("span .group_icons").children().detach();
|
||||||
|
|
||||||
for(let id of this.assignedServerGroupIds())
|
for(let id of this.assignedServerGroupIds())
|
||||||
this.updateGroupIcon(this.channelTree.client.groups.serverGroup(id));
|
this.updateGroupIcon(this.channelTree.client.groups.serverGroup(id));
|
||||||
|
|
||||||
this.updateGroupIcon(this.channelTree.client.groups.channelGroup(this.properties.client_channel_group_id));
|
this.updateGroupIcon(this.channelTree.client.groups.channelGroup(this.properties.client_channel_group_id));
|
||||||
|
|
||||||
|
let prefix_groups: string[] = [];
|
||||||
|
let suffix_groups: string[] = [];
|
||||||
|
for(const group_id of this.assignedServerGroupIds()) {
|
||||||
|
const group = this.channelTree.client.groups.serverGroup(group_id);
|
||||||
|
if(!group) continue;
|
||||||
|
|
||||||
|
if(group.properties.namemode == 1)
|
||||||
|
prefix_groups.push(group.name);
|
||||||
|
else if(group.properties.namemode == 2)
|
||||||
|
suffix_groups.push(group.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tag_group_prefix = this.tag.find(".group_prefix");
|
||||||
|
const tag_group_suffix = this.tag.find(".group_suffix");
|
||||||
|
if(prefix_groups.length > 0) {
|
||||||
|
tag_group_prefix.text("[" + prefix_groups.join("][") + "]").show();
|
||||||
|
} else {
|
||||||
|
tag_group_prefix.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
if(suffix_groups.length > 0) {
|
||||||
|
tag_group_suffix.text("[" + suffix_groups.join("][") + "]").show();
|
||||||
|
} else {
|
||||||
|
tag_group_suffix.hide()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateClientVariables(){
|
updateClientVariables(){
|
||||||
|
|
|
@ -13,11 +13,13 @@
|
||||||
client_away_message Value: ''
|
client_away_message Value: ''
|
||||||
*/
|
*/
|
||||||
import openBanList = Modals.openBanList;
|
import openBanList = Modals.openBanList;
|
||||||
|
import spawnConnectModal = Modals.spawnConnectModal;
|
||||||
|
|
||||||
class ControlBar {
|
class ControlBar {
|
||||||
private _muteInput: boolean;
|
private _muteInput: boolean;
|
||||||
private _muteOutput: boolean;
|
private _muteOutput: boolean;
|
||||||
private _away: boolean;
|
private _away: boolean;
|
||||||
|
private _query_visible: boolean;
|
||||||
private _awayMessage: string;
|
private _awayMessage: string;
|
||||||
|
|
||||||
private codec_supported: boolean = false;
|
private codec_supported: boolean = false;
|
||||||
|
@ -64,10 +66,35 @@ class ControlBar {
|
||||||
away.find(".btn_away_toggle").on('click', this.on_away_toggle.bind(this));
|
away.find(".btn_away_toggle").on('click', this.on_away_toggle.bind(this));
|
||||||
away.find(".btn_away_message").on('click', this.on_away_set_message.bind(this));
|
away.find(".btn_away_message").on('click', this.on_away_set_message.bind(this));
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
let bookmark = this.htmlTag.find(".btn_bookmark");
|
||||||
|
bookmark.find(".button-dropdown").on('click', () => {
|
||||||
|
bookmark.find(".dropdown").addClass("displayed");
|
||||||
|
});
|
||||||
|
bookmark.on('mouseleave', () => {
|
||||||
|
bookmark.find(".dropdown").removeClass("displayed");
|
||||||
|
});
|
||||||
|
|
||||||
|
this.update_bookmarks()
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let query = this.htmlTag.find(".btn_query");
|
||||||
|
query.find(".button-dropdown").on('click', () => {
|
||||||
|
query.find(".dropdown").addClass("displayed");
|
||||||
|
});
|
||||||
|
query.on('mouseleave', () => {
|
||||||
|
query.find(".dropdown").removeClass("displayed");
|
||||||
|
});
|
||||||
|
|
||||||
|
query.find(".btn_query_toggle").on('click', this.on_query_visibility_toggle.bind(this));
|
||||||
|
query.find(".btn_query_create").on('click', this.on_query_create.bind(this));
|
||||||
|
query.find(".btn_query_manage").on('click', this.on_query_manage.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
//Need an initialise
|
//Need an initialise
|
||||||
this.muteInput = settings.global("mute_input") == "1";
|
this.muteInput = settings.global("mute_input") == "1";
|
||||||
this.muteOutput = settings.global("mute_output") == "1";
|
this.muteOutput = settings.global("mute_output") == "1";
|
||||||
|
this.query_visibility = settings.global("show_server_queries") == "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -269,6 +296,77 @@ class ControlBar {
|
||||||
private onBanlist() {
|
private onBanlist() {
|
||||||
if(!this.handle.serverConnection) return;
|
if(!this.handle.serverConnection) return;
|
||||||
|
|
||||||
openBanList(this.handle);
|
if(this.handle.permissions.neededPermission(PermissionType.B_CLIENT_BAN_LIST).granted(1)) {
|
||||||
|
openBanList(this.handle);
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_bookmarks() {
|
||||||
|
//<div class="btn_bookmark_connect" target="localhost"><a>Localhost</a></div>
|
||||||
|
let tag_bookmark = this.htmlTag.find(".btn_bookmark .dropdown");
|
||||||
|
tag_bookmark.find(".bookmark, .bookmark_directory").detach();
|
||||||
|
|
||||||
|
for(const bookmark of bookmarks.bookmarks().content) {
|
||||||
|
if(bookmark.type == bookmarks.BookmarkType.ENTRY) {
|
||||||
|
tag_bookmark.append(
|
||||||
|
$.spawn("div")
|
||||||
|
.addClass("bookmark")
|
||||||
|
/* /.attr("bookmark-uuid", bookmark.unique_id) */
|
||||||
|
.text(bookmark.display_name)
|
||||||
|
.on('click', event => {
|
||||||
|
spawnConnectModal()
|
||||||
|
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
//TODO add bookmark directories here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get query_visibility() {
|
||||||
|
return this._query_visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
set query_visibility(flag: boolean) {
|
||||||
|
if(this._query_visible == flag) return;
|
||||||
|
|
||||||
|
this._query_visible = flag;
|
||||||
|
settings.global("show_server_queries", flag);
|
||||||
|
this.update_query_visibility_button();
|
||||||
|
this.handle.channelTree.toggle_server_queries(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_query_visibility_toggle() {
|
||||||
|
this.query_visibility = !this._query_visible;
|
||||||
|
this.update_query_visibility_button();
|
||||||
|
}
|
||||||
|
|
||||||
|
private update_query_visibility_button() {
|
||||||
|
let tag = this.htmlTag.find(".btn_query_toggle");
|
||||||
|
if(this._query_visible) {
|
||||||
|
tag.addClass("activated");
|
||||||
|
} else {
|
||||||
|
tag.removeClass("activated");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_query_create() {
|
||||||
|
if(this.handle.permissions.neededPermission(PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN).granted(1)) {
|
||||||
|
Modals.spawnQueryCreate();
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private on_query_manage() {
|
||||||
|
if(globalClient && globalClient.connected) {
|
||||||
|
Modals.spawnQueryManage(globalClient);
|
||||||
|
} else {
|
||||||
|
createErrorModal(tr("You have to be connected"), tr("You have to be connected!")).open();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/// <reference path="../../utils/modal.ts" />
|
||||||
|
/// <reference path="../../proto.ts" />
|
||||||
|
/// <reference path="../../client.ts" />
|
||||||
|
|
||||||
|
namespace Modals {
|
||||||
|
export function spawnQueryCreate(callback_created?: (user, pass) => any) {
|
||||||
|
let modal;
|
||||||
|
modal = createModal({
|
||||||
|
header: tr("Create a server query login"),
|
||||||
|
body: () => {
|
||||||
|
let template = $("#tmpl_query_create").renderTag();
|
||||||
|
template = $.spawn("div").append(template);
|
||||||
|
|
||||||
|
template.find(".button-close").on('click', event => modal.close());
|
||||||
|
template.find(".button-create").on('click', event => {
|
||||||
|
const name = template.find(".input-name").val() as string;
|
||||||
|
if(name.length < 3 || name.length > 64) {
|
||||||
|
createErrorModal(tr("Invalid username"), tr("Please enter a valid name!")).open();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//client_login_password
|
||||||
|
globalClient.serverConnection.commandHandler["notifyquerycreated"] = json => {
|
||||||
|
json = json[0];
|
||||||
|
|
||||||
|
spawnQueryCreated({
|
||||||
|
username: name,
|
||||||
|
password: json.client_login_password
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
if(callback_created)
|
||||||
|
callback_created(name, json.client_login_password);
|
||||||
|
};
|
||||||
|
|
||||||
|
globalClient.serverConnection.sendCommand("querycreate", {
|
||||||
|
client_login_name: name
|
||||||
|
}).catch(error => {
|
||||||
|
if(error instanceof CommandResult)
|
||||||
|
error = error.extra_message || error.message;
|
||||||
|
createErrorModal(tr("Unable to create account"), tr("Failed to create account<br>Message: ") + error).open();
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.close();
|
||||||
|
//TODO create account
|
||||||
|
});
|
||||||
|
return template;
|
||||||
|
},
|
||||||
|
footer: undefined,
|
||||||
|
width: 750
|
||||||
|
});
|
||||||
|
modal.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function spawnQueryCreated(credentials: {
|
||||||
|
username: string,
|
||||||
|
password: string
|
||||||
|
}, yust_created: boolean) {
|
||||||
|
let modal;
|
||||||
|
modal = createModal({
|
||||||
|
header: yust_created ? tr("Server query credentials") : tr("New server query credentials"),
|
||||||
|
body: () => {
|
||||||
|
let template = $("#tmpl_query_created").renderTag(credentials);
|
||||||
|
template = $.spawn("div").append(template);
|
||||||
|
|
||||||
|
template.find(".button-close").on('click', event => modal.close());
|
||||||
|
template.find(".query_name").text(credentials.username);
|
||||||
|
template.find(".query_password").text(credentials.password);
|
||||||
|
|
||||||
|
template.find(".btn_copy_name").on('click', () => {
|
||||||
|
template.find(".query_name").select();
|
||||||
|
document.execCommand("copy");
|
||||||
|
});
|
||||||
|
template.find(".btn_copy_password").on('click', () => {
|
||||||
|
template.find(".query_password").select();
|
||||||
|
document.execCommand("copy");
|
||||||
|
});
|
||||||
|
return template;
|
||||||
|
},
|
||||||
|
footer: undefined,
|
||||||
|
width: 750
|
||||||
|
});
|
||||||
|
modal.open();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
/// <reference path="../../utils/modal.ts" />
|
||||||
|
/// <reference path="../../proto.ts" />
|
||||||
|
/// <reference path="../../client.ts" />
|
||||||
|
|
||||||
|
namespace Modals {
|
||||||
|
export function spawnQueryManage(client: TSClient) {
|
||||||
|
let modal: Modal;
|
||||||
|
let selected_query: QueryListEntry;
|
||||||
|
|
||||||
|
const update_selected = () => {
|
||||||
|
const buttons = modal.htmlTag.find(".header .buttons");
|
||||||
|
|
||||||
|
//TODO gray out if no permissions (Server needs to send that... :D)
|
||||||
|
buttons.find(".button-query-delete").prop("disabled", selected_query === undefined);
|
||||||
|
buttons.find(".button-query-rename").prop("disabled", selected_query === undefined);
|
||||||
|
buttons.find(".button-query-change-password").prop("disabled", selected_query === undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
const update_list = () => {
|
||||||
|
const info_tag = modal.htmlTag.find(".footer .info a");
|
||||||
|
info_tag.text("loading...");
|
||||||
|
client.serverConnection.helper.current_virtual_server_id().then(server_id => {
|
||||||
|
client.serverConnection.helper.request_query_list(server_id).then(result => {
|
||||||
|
selected_query = undefined;
|
||||||
|
|
||||||
|
const entries_tag = modal.htmlTag.find(".query-list-entries");
|
||||||
|
const entry_template = $("#tmpl_query_manager-list_entry");
|
||||||
|
entries_tag.empty();
|
||||||
|
|
||||||
|
for(const query of result.queries || []) {
|
||||||
|
entries_tag.append(entry_template.renderTag(query).on('click', event => {
|
||||||
|
entries_tag.find(".entry.selected").removeClass("selected");
|
||||||
|
$(event.target).parent(".entry").addClass("selected");
|
||||||
|
selected_query = query;
|
||||||
|
update_selected();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const entry_container = modal.htmlTag.find(".query-list-entries-container");
|
||||||
|
if(entry_container.hasScrollBar())
|
||||||
|
entry_container.addClass("scrollbar");
|
||||||
|
|
||||||
|
if(!result || result.flag_all) {
|
||||||
|
info_tag.text("Showing all server queries");
|
||||||
|
} else {
|
||||||
|
info_tag.text("Showing your server queries")
|
||||||
|
}
|
||||||
|
update_selected();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
//TODO error handling
|
||||||
|
};
|
||||||
|
|
||||||
|
modal = createModal({
|
||||||
|
header: tr("Manage query accounts"),
|
||||||
|
body: () => {
|
||||||
|
let template = $("#tmpl_query_manager").renderTag();
|
||||||
|
template = $.spawn("div").append(template);
|
||||||
|
|
||||||
|
/* first open the modal */
|
||||||
|
setTimeout(() => {
|
||||||
|
const entry_container = template.find(".query-list-entries-container");
|
||||||
|
if(entry_container.hasScrollBar())
|
||||||
|
entry_container.addClass("scrollbar");
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
template.find(".footer .buttons .button-refresh").on('click', update_list);
|
||||||
|
template.find(".button-query-create").on('click', () => {
|
||||||
|
Modals.spawnQueryCreate((user, pass) => update_list());
|
||||||
|
});
|
||||||
|
template.find(".button-query-rename").on('click', () => {
|
||||||
|
if(!selected_query) return;
|
||||||
|
|
||||||
|
createInputModal(tr("Change account name"), tr("Enter the new name for the login:<br>"), text => text.length >= 3, result => {
|
||||||
|
if(result) {
|
||||||
|
client.serverConnection.sendCommand("queryrename", {
|
||||||
|
client_login_name: selected_query.username,
|
||||||
|
client_new_login_name: result
|
||||||
|
}).catch(error => {
|
||||||
|
if(error instanceof CommandResult)
|
||||||
|
error = error.extra_message || error.message;
|
||||||
|
createErrorModal(tr("Unable to rename account"), tr("Failed to rename account<br>Message: ") + error).open();
|
||||||
|
}).then(() => {
|
||||||
|
createInfoModal(tr("Account successfully renamed"), tr("The query account has been renamed!")).open();
|
||||||
|
update_list();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).open();
|
||||||
|
});
|
||||||
|
template.find(".button-query-change-password").on('click', () => {
|
||||||
|
if(!selected_query) return;
|
||||||
|
|
||||||
|
createInputModal(tr("Change account's password"), tr("Enter a new password (leave blank for auto generation):<br>"), text => true, result => {
|
||||||
|
if(result !== false) {
|
||||||
|
client.serverConnection.sendCommand("querychangepassword", {
|
||||||
|
client_login_name: selected_query.username,
|
||||||
|
client_login_password: result
|
||||||
|
}).catch(error => {
|
||||||
|
if(error instanceof CommandResult)
|
||||||
|
error = error.extra_message || error.message;
|
||||||
|
createErrorModal(tr("Unable to change password"), tr("Failed to change password<br>Message: ") + error).open();
|
||||||
|
});
|
||||||
|
|
||||||
|
client.serverConnection.commandHandler["notifyquerypasswordchanges"] = json => {
|
||||||
|
Modals.spawnQueryCreated({
|
||||||
|
username: json[0]["client_login_name"],
|
||||||
|
password: json[0]["client_login_password"]
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
client.serverConnection.commandHandler["notifyquerypasswordchanges"] = undefined;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}).open();
|
||||||
|
});
|
||||||
|
template.find(".button-query-delete").on('click', () => {
|
||||||
|
if(!selected_query) return;
|
||||||
|
|
||||||
|
Modals.spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this account?"), result => {
|
||||||
|
if(result) {
|
||||||
|
client.serverConnection.sendCommand("querydelete", {
|
||||||
|
client_login_name: selected_query.username
|
||||||
|
}).catch(error => {
|
||||||
|
if(error instanceof CommandResult)
|
||||||
|
error = error.extra_message || error.message;
|
||||||
|
createErrorModal(tr("Unable to delete account"), tr("Failed to delete account<br>Message: ") + error).open();
|
||||||
|
}).then(() => {
|
||||||
|
createInfoModal(tr("Account successfully deleted"), tr("The query account has been successfully deleted!")).open();
|
||||||
|
update_list();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
template.find(".input-search").on('change keyup', () => {
|
||||||
|
const text = (template.find(".input-search").val() as string || "").toLowerCase();
|
||||||
|
if(text.length == 0) {
|
||||||
|
template.find(".query-list-entries .entry").show();
|
||||||
|
} else {
|
||||||
|
template.find(".query-list-entries .entry").each((_, e) => {
|
||||||
|
const element = $(e);
|
||||||
|
if(element.text().toLowerCase().indexOf(text) == -1)
|
||||||
|
element.hide();
|
||||||
|
else
|
||||||
|
element.show();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return template;
|
||||||
|
},
|
||||||
|
footer: undefined,
|
||||||
|
width: 750
|
||||||
|
});
|
||||||
|
|
||||||
|
update_list();
|
||||||
|
modal.open();
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ 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 _show_queries: boolean;
|
||||||
private channel_last?: ChannelEntry;
|
private channel_last?: ChannelEntry;
|
||||||
private channel_first?: ChannelEntry;
|
private channel_first?: ChannelEntry;
|
||||||
|
|
||||||
|
@ -290,6 +291,10 @@ class ChannelTree {
|
||||||
if(newClient) client = newClient; //Got new client :)
|
if(newClient) client = newClient; //Got new client :)
|
||||||
else
|
else
|
||||||
this.clients.push(client);
|
this.clients.push(client);
|
||||||
|
|
||||||
|
if(!this._show_queries && client.properties.client_type == ClientType.CLIENT_QUERY)
|
||||||
|
client.tag.hide();
|
||||||
|
|
||||||
client.channelTree = this;
|
client.channelTree = this;
|
||||||
client["_channel"] = channel;
|
client["_channel"] = channel;
|
||||||
|
|
||||||
|
@ -709,4 +714,23 @@ class ChannelTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggle_server_queries(flag: boolean) {
|
||||||
|
if(this._show_queries == flag) return;
|
||||||
|
this._show_queries = flag;
|
||||||
|
|
||||||
|
//FIXME resize channels
|
||||||
|
const channels: ChannelEntry[] = []
|
||||||
|
for(const client of this.clients)
|
||||||
|
if(client.properties.client_type == ClientType.CLIENT_QUERY) {
|
||||||
|
if(this._show_queries)
|
||||||
|
client.tag.show();
|
||||||
|
else
|
||||||
|
client.tag.hide();
|
||||||
|
if(channels.indexOf(client.currentChannel()) == -1)
|
||||||
|
channels.push(client.currentChannel());
|
||||||
|
}
|
||||||
|
for(const channel of channels)
|
||||||
|
channel.adjustSize();
|
||||||
|
}
|
||||||
}
|
}
|
3
todo.md
3
todo.md
|
@ -1 +1,2 @@
|
||||||
nan
|
Add connect profiles for bookmarks, like TeaSpeak-Forum or multiple TeamSpeak Identities
|
||||||
|
Fix server group management dialog for a lots of groups (May even add a search function?)
|
|
@ -321,11 +321,30 @@ generators[SyntaxKind.ClassDeclaration] = (settings, stack, node: ts.ClassDeclar
|
||||||
return ts.createClassDeclaration(node.decorators, append_export(append_declare(node.modifiers, !stack.flag_declare), stack.flag_namespace), node.name, node.typeParameters, node.heritageClauses, members as any);
|
return ts.createClassDeclaration(node.decorators, append_export(append_declare(node.modifiers, !stack.flag_declare), stack.flag_namespace), node.name, node.typeParameters, node.heritageClauses, members as any);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
generators[SyntaxKind.PropertySignature] = (settings, stack, node: ts.PropertySignature) => {
|
||||||
|
console.log(SyntaxKind[node.type.kind]);
|
||||||
|
let type: ts.TypeNode = node.type;
|
||||||
|
switch (node.type.kind) {
|
||||||
|
case SyntaxKind.LiteralType:
|
||||||
|
type = ts.createIdentifier("any") as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts.createPropertySignature(node.modifiers, node.name, node.questionToken, type, undefined);
|
||||||
|
};
|
||||||
|
|
||||||
generators[SyntaxKind.InterfaceDeclaration] = (settings, stack, node: ts.InterfaceDeclaration) => {
|
generators[SyntaxKind.InterfaceDeclaration] = (settings, stack, node: ts.InterfaceDeclaration) => {
|
||||||
if(settings.remove_private.field && has_private(node.modifiers)) return;
|
if(settings.remove_private.field && has_private(node.modifiers)) return;
|
||||||
if(stack.flag_namespace && !has_modifier(node.modifiers, SyntaxKind.ExportKeyword)) return;
|
if(stack.flag_namespace && !has_modifier(node.modifiers, SyntaxKind.ExportKeyword)) return;
|
||||||
|
|
||||||
return node;
|
const members: any[] = [];
|
||||||
|
for(const member of node.members) {
|
||||||
|
if(generators[member.kind])
|
||||||
|
members.push(generators[member.kind](settings, stack, member));
|
||||||
|
else
|
||||||
|
members.push(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts.createInterfaceDeclaration(undefined, append_export(append_declare(node.modifiers, !stack.flag_declare), stack.flag_namespace), node.name, node.typeParameters, node.heritageClauses, members);
|
||||||
};
|
};
|
||||||
|
|
||||||
generators[SyntaxKind.VariableDeclaration] = (settings, stack, node: ts.VariableDeclaration) => {
|
generators[SyntaxKind.VariableDeclaration] = (settings, stack, node: ts.VariableDeclaration) => {
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
enum YY {
|
||||||
|
H = "C",
|
||||||
|
B = "Y"
|
||||||
|
}
|
||||||
|
|
||||||
|
interface X {
|
||||||
|
type: "",
|
||||||
|
c: YY.B
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7b931ed61cf265937dc742579f9070e7c4e50775
|
Subproject commit 0221bd137ef5bbc846018ff86deda0aca38aed26
|
Loading…
Reference in New Issue