Starting with webpack
This commit is contained in:
parent
a6f0fcdda9
commit
13b65a1f35
114 changed files with 14384 additions and 3963 deletions
6186
dist/bundle.js
vendored
Normal file
6186
dist/bundle.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3889
package-lock.json
generated
3889
package-lock.json
generated
File diff suppressed because it is too large
Load diff
22
package.json
22
package.json
|
@ -17,29 +17,41 @@
|
||||||
"csso": "csso",
|
"csso": "csso",
|
||||||
"rebuild-structure-web-dev": "php files.php generate web dev",
|
"rebuild-structure-web-dev": "php files.php generate web dev",
|
||||||
"minify-web-rel-file": "terser --compress --mangle --ecma 6 --keep_classnames --keep_fnames --output",
|
"minify-web-rel-file": "terser --compress --mangle --ecma 6 --keep_classnames --keep_fnames --output",
|
||||||
"start": "npm run compile-file-helper && node file.js ndevelop"
|
"start": "npm run compile-file-helper && node file.js ndevelop",
|
||||||
|
"build": "webpack --config webpack.config.js",
|
||||||
|
"watch": "webpack --watch"
|
||||||
},
|
},
|
||||||
"author": "TeaSpeak (WolverinDEV)",
|
"author": "TeaSpeak (WolverinDEV)",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/emscripten": "^1.38.0",
|
"@types/emscripten": "^1.38.0",
|
||||||
"@types/jquery": "^3.3.31",
|
"@types/jquery": "^3.3.34",
|
||||||
|
"@types/lodash": "^4.14.149",
|
||||||
"@types/moment": "^2.13.0",
|
"@types/moment": "^2.13.0",
|
||||||
"@types/node": "^12.7.2",
|
"@types/node": "^12.7.2",
|
||||||
|
"@types/react-dom": "^16.9.5",
|
||||||
"@types/sha256": "^0.2.0",
|
"@types/sha256": "^0.2.0",
|
||||||
"@types/websocket": "0.0.40",
|
"@types/websocket": "0.0.40",
|
||||||
"clean-css": "^4.2.1",
|
"clean-css": "^4.2.1",
|
||||||
|
"css-loader": "^3.4.2",
|
||||||
"csso-cli": "^2.0.2",
|
"csso-cli": "^2.0.2",
|
||||||
|
"fs-extra": "latest",
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^4.0.2",
|
||||||
"mime-types": "^2.1.24",
|
"mime-types": "^2.1.24",
|
||||||
|
"mini-css-extract-plugin": "^0.9.0",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
|
"node-sass": "^4.13.1",
|
||||||
"sass": "1.22.10",
|
"sass": "1.22.10",
|
||||||
|
"sass-loader": "^8.0.2",
|
||||||
"sha256": "^0.2.0",
|
"sha256": "^0.2.0",
|
||||||
|
"style-loader": "^1.1.3",
|
||||||
"terser": "^4.2.1",
|
"terser": "^4.2.1",
|
||||||
|
"ts-loader": "^6.2.2",
|
||||||
"ttypescript": "^1.5.10",
|
"ttypescript": "^1.5.10",
|
||||||
"typescript": "3.6.5",
|
"typescript": "3.6.5",
|
||||||
"wat2wasm": "^1.0.2",
|
"wat2wasm": "^1.0.2",
|
||||||
"fs-extra": "latest"
|
"webpack": "^4.42.1",
|
||||||
|
"webpack-cli": "^3.3.11"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -50,6 +62,8 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://www.teaspeak.de",
|
"homepage": "https://www.teaspeak.de",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/fs-extra": "^8.0.1"
|
"@types/fs-extra": "^8.0.1",
|
||||||
|
"react": "^16.13.1",
|
||||||
|
"react-dom": "^16.13.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,96 +94,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.channel {
|
&.channel {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.container-channel {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: stretch;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
min-height: 16px;
|
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.channel-type {
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
margin-right: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-channel-name {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
|
|
||||||
justify-content: left;
|
|
||||||
|
|
||||||
max-width: 100%; /* important for the repetitive channel name! */
|
|
||||||
overflow-x: hidden;
|
|
||||||
height: 16px;
|
|
||||||
|
|
||||||
&.align-right {
|
|
||||||
justify-content: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.align-center, &.align-repetitive {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-name {
|
|
||||||
align-self: center;
|
|
||||||
color: $channel_tree_entry_text_color;
|
|
||||||
|
|
||||||
min-width: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.align-repetitive {
|
|
||||||
.channel-name {
|
|
||||||
text-overflow: clip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icons {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.move-selected {
|
|
||||||
border-bottom: 1px solid black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-channel-normal-only {
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
&.channel-normal {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon_no_sound {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-clients {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.client {
|
&.client {
|
||||||
|
@ -272,40 +183,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.channel .container-channel, &.client, &.server {
|
|
||||||
.marker-text-unread {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
width: 1px;
|
|
||||||
background-color: #a814147F;
|
|
||||||
|
|
||||||
opacity: 1;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
width: 24px;
|
|
||||||
|
|
||||||
background: -moz-linear-gradient(left, rgba(168,20,20,.18) 0%, rgba(168,20,20,0) 100%); /* FF3.6-15 */
|
|
||||||
background: -webkit-linear-gradient(left, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* Chrome10-25,Safari5.1-6 */
|
|
||||||
background: linear-gradient(to right, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
|
||||||
}
|
|
||||||
|
|
||||||
&.hidden {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include transition(opacity $button_hover_animation_time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
interface Window {
|
export interface Window {
|
||||||
BroadcastChannel: BroadcastChannel;
|
BroadcastChannel: BroadcastChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace bipc {
|
export namespace bipc {
|
||||||
export interface BroadcastMessage {
|
export interface BroadcastMessage {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
receiver: string;
|
receiver: string;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/// <reference path="log.ts" />
|
/// <reference path="log.ts" />
|
||||||
/// <reference path="proto.ts" />
|
/// <reference path="proto.ts" />
|
||||||
/// <reference path="ui/view.ts" />
|
/// <reference path="channel-tree/view.ts" />
|
||||||
/// <reference path="settings.ts" />
|
/// <reference path="settings.ts" />
|
||||||
/// <reference path="FileManager.ts" />
|
/// <reference path="FileManager.ts" />
|
||||||
/// <reference path="permission/PermissionManager.ts" />
|
/// <reference path="permission/PermissionManager.ts" />
|
||||||
|
@ -8,7 +8,17 @@
|
||||||
/// <reference path="ui/frames/ControlBar.ts" />
|
/// <reference path="ui/frames/ControlBar.ts" />
|
||||||
/// <reference path="connection/ConnectionBase.ts" />
|
/// <reference path="connection/ConnectionBase.ts" />
|
||||||
|
|
||||||
enum DisconnectReason {
|
import {ChannelTree} from "./channel-tree/view";
|
||||||
|
import {LocalClientEntry} from "./channel-tree/client";
|
||||||
|
import {ServerAddress} from "./channel-tree/server";
|
||||||
|
import {ChannelEntry} from "./channel-tree/channel";
|
||||||
|
import {AbstractServerConnection} from "./connection/ConnectionBase";
|
||||||
|
import {PermissionManager} from "./permission/PermissionManager";
|
||||||
|
import {GroupManager} from "./permission/GroupManager";
|
||||||
|
import {ServerSettings} from "./settings";
|
||||||
|
import {Hostbanner} from "./ui/frames/hostbanner";
|
||||||
|
|
||||||
|
export enum DisconnectReason {
|
||||||
HANDLER_DESTROYED,
|
HANDLER_DESTROYED,
|
||||||
REQUESTED,
|
REQUESTED,
|
||||||
DNS_FAILED,
|
DNS_FAILED,
|
||||||
|
@ -28,7 +38,7 @@ enum DisconnectReason {
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ConnectionState {
|
export enum ConnectionState {
|
||||||
UNCONNECTED,
|
UNCONNECTED,
|
||||||
CONNECTING,
|
CONNECTING,
|
||||||
INITIALISING,
|
INITIALISING,
|
||||||
|
@ -36,7 +46,7 @@ enum ConnectionState {
|
||||||
DISCONNECTING
|
DISCONNECTING
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ViewReasonId {
|
export enum ViewReasonId {
|
||||||
VREASON_USER_ACTION = 0,
|
VREASON_USER_ACTION = 0,
|
||||||
VREASON_MOVED = 1,
|
VREASON_MOVED = 1,
|
||||||
VREASON_SYSTEM = 2,
|
VREASON_SYSTEM = 2,
|
||||||
|
@ -51,7 +61,7 @@ enum ViewReasonId {
|
||||||
VREASON_SERVER_SHUTDOWN = 11
|
VREASON_SERVER_SHUTDOWN = 11
|
||||||
}
|
}
|
||||||
|
|
||||||
interface VoiceStatus {
|
export interface VoiceStatus {
|
||||||
input_hardware: boolean;
|
input_hardware: boolean;
|
||||||
input_muted: boolean;
|
input_muted: boolean;
|
||||||
output_muted: boolean;
|
output_muted: boolean;
|
||||||
|
@ -68,7 +78,7 @@ interface VoiceStatus {
|
||||||
queries_visible: boolean;
|
queries_visible: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ConnectParameters {
|
export interface ConnectParameters {
|
||||||
nickname?: string;
|
nickname?: string;
|
||||||
channel?: {
|
channel?: {
|
||||||
target: string | number;
|
target: string | number;
|
||||||
|
@ -79,10 +89,10 @@ interface ConnectParameters {
|
||||||
auto_reconnect_attempt?: boolean;
|
auto_reconnect_attempt?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConnectionHandler {
|
export class ConnectionHandler {
|
||||||
channelTree: ChannelTree;
|
channelTree: ChannelTree;
|
||||||
|
|
||||||
serverConnection: connection.AbstractServerConnection;
|
serverConnection: AbstractServerConnection;
|
||||||
|
|
||||||
fileManager: FileManager;
|
fileManager: FileManager;
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,26 @@
|
||||||
/// <reference path="connection/CommandHandler.ts" />
|
import {ChannelEntry} from "./channel-tree/channel";
|
||||||
/// <reference path="connection/ConnectionBase.ts" />
|
import {AbstractCommandHandler, ServerCommand} from "./connection/ConnectionBase";
|
||||||
|
import {ConnectionHandler} from "./ConnectionHandler";
|
||||||
|
import {CommandResult} from "./connection/ServerConnectionDeclaration";
|
||||||
|
import {log, LogCategory} from "./log";
|
||||||
|
import {ClientEntry} from "./channel-tree/client";
|
||||||
|
import {hex} from "./crypto/hex";
|
||||||
|
|
||||||
class FileEntry {
|
export class FileEntry {
|
||||||
name: string;
|
name: string;
|
||||||
datetime: number;
|
datetime: number;
|
||||||
type: number;
|
type: number;
|
||||||
size: number;
|
size: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
class FileListRequest {
|
export class FileListRequest {
|
||||||
path: string;
|
path: string;
|
||||||
entries: FileEntry[];
|
entries: FileEntry[];
|
||||||
|
|
||||||
callback: (entries: FileEntry[]) => void;
|
callback: (entries: FileEntry[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace transfer {
|
export namespace transfer {
|
||||||
export interface TransferKey {
|
export interface TransferKey {
|
||||||
client_transfer_id: number;
|
client_transfer_id: number;
|
||||||
server_transfer_id: number;
|
server_transfer_id: number;
|
||||||
|
@ -152,7 +157,7 @@ class RequestFileUpload implements transfer.UploadTransfer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FileManager extends connection.AbstractCommandHandler {
|
class FileManager extends AbstractCommandHandler {
|
||||||
handle: ConnectionHandler;
|
handle: ConnectionHandler;
|
||||||
icons: IconManager;
|
icons: IconManager;
|
||||||
avatars: AvatarManager;
|
avatars: AvatarManager;
|
||||||
|
@ -191,7 +196,7 @@ class FileManager extends connection.AbstractCommandHandler {
|
||||||
this.avatars = undefined;
|
this.avatars = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_command(command: connection.ServerCommand): boolean {
|
handle_command(command: ServerCommand): boolean {
|
||||||
switch (command.command) {
|
switch (command.command) {
|
||||||
case "notifyfilelist":
|
case "notifyfilelist":
|
||||||
this.notifyFileList(command.arguments);
|
this.notifyFileList(command.arguments);
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
namespace messages.formatter {
|
import {Settings, settings} from "./settings";
|
||||||
|
import {contextmenu} from "./ui/elements/context_menu";
|
||||||
|
import {image_preview} from "./ui/frames/image_preview";
|
||||||
|
import {guid} from "./crypto/uid";
|
||||||
|
|
||||||
|
declare const xbbcode;
|
||||||
|
export namespace messages.formatter {
|
||||||
export namespace bbcode {
|
export namespace bbcode {
|
||||||
const sanitizer_escaped = (key: string) => "[-- sescaped: " + key + " --]";
|
const sanitizer_escaped = (key: string) => "[-- sescaped: " + key + " --]";
|
||||||
const sanitizer_escaped_regex = /\[-- sescaped: ([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}) --]/;
|
const sanitizer_escaped_regex = /\[-- sescaped: ([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}) --]/;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
enum KeyCode {
|
export enum KeyCode {
|
||||||
KEY_CANCEL = 3,
|
KEY_CANCEL = 3,
|
||||||
KEY_HELP = 6,
|
KEY_HELP = 6,
|
||||||
KEY_BACK_SPACE = 8,
|
KEY_BACK_SPACE = 8,
|
||||||
|
@ -118,7 +118,7 @@ enum KeyCode {
|
||||||
KEY_META = 224
|
KEY_META = 224
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ppt {
|
export namespace ppt {
|
||||||
export enum EventType {
|
export enum EventType {
|
||||||
KEY_PRESS,
|
KEY_PRESS,
|
||||||
KEY_RELEASE,
|
KEY_RELEASE,
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
namespace bookmarks {
|
import {profiles} from "./profiles/ConnectionProfile";
|
||||||
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 namespace bookmarks {
|
||||||
export const boorkmak_connect = (mark: Bookmark, new_tab?: boolean) => {
|
export const boorkmak_connect = (mark: Bookmark, new_tab?: boolean) => {
|
||||||
const profile = profiles.find_profile(mark.connect_profile) || profiles.default_profile();
|
const profile = profiles.find_profile(mark.connect_profile) || profiles.default_profile();
|
||||||
if(profile.valid()) {
|
if(profile.valid()) {
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
/// <reference path="view.ts" />
|
/// <reference path="view.ts" />
|
||||||
/// <reference path="../utils/helpers.ts" />
|
/// <reference path="../utils/helpers.ts" />
|
||||||
|
|
||||||
enum ChannelType {
|
import {ChannelTree} from "./view";
|
||||||
|
import {ClientEntry} from "./client";
|
||||||
|
|
||||||
|
export enum ChannelType {
|
||||||
PERMANENT,
|
PERMANENT,
|
||||||
SEMI_PERMANENT,
|
SEMI_PERMANENT,
|
||||||
TEMPORARY
|
TEMPORARY
|
||||||
}
|
}
|
||||||
namespace ChannelType {
|
export namespace ChannelType {
|
||||||
export function normalize(mode: ChannelType) {
|
export function normalize(mode: ChannelType) {
|
||||||
let value: string = ChannelType[mode];
|
let value: string = ChannelType[mode];
|
||||||
value = value.toLowerCase();
|
value = value.toLowerCase();
|
||||||
|
@ -14,13 +17,13 @@ namespace ChannelType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ChannelSubscribeMode {
|
export enum ChannelSubscribeMode {
|
||||||
SUBSCRIBED,
|
SUBSCRIBED,
|
||||||
UNSUBSCRIBED,
|
UNSUBSCRIBED,
|
||||||
INHERITED
|
INHERITED
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChannelProperties {
|
export class ChannelProperties {
|
||||||
channel_order: number = 0;
|
channel_order: number = 0;
|
||||||
channel_name: string = "";
|
channel_name: string = "";
|
||||||
channel_name_phonetic: string = "";
|
channel_name_phonetic: string = "";
|
||||||
|
@ -55,7 +58,7 @@ class ChannelProperties {
|
||||||
channel_conversation_history_length: number = -1;
|
channel_conversation_history_length: number = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChannelEntry {
|
export class ChannelEntry {
|
||||||
channelTree: ChannelTree;
|
channelTree: ChannelTree;
|
||||||
channelId: number;
|
channelId: number;
|
||||||
parent?: ChannelEntry;
|
parent?: ChannelEntry;
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
/// <reference path="channel.ts" />
|
/// <reference path="channel.ts" />
|
||||||
/// <reference path="modal/ModalChangeVolume.ts" />
|
/// <reference path="../ui/modal/ModalChangeVolume.ts" />
|
||||||
/// <reference path="client_move.ts" />
|
/// <reference path="../ui/client_move.ts" />
|
||||||
|
|
||||||
enum ClientType {
|
import {ChannelEntry} from "./channel";
|
||||||
|
import {ChannelTree} from "./view";
|
||||||
|
|
||||||
|
export enum ClientType {
|
||||||
CLIENT_VOICE,
|
CLIENT_VOICE,
|
||||||
CLIENT_QUERY,
|
CLIENT_QUERY,
|
||||||
CLIENT_INTERNAL,
|
CLIENT_INTERNAL,
|
||||||
|
@ -11,7 +14,7 @@ enum ClientType {
|
||||||
CLIENT_UNDEFINED
|
CLIENT_UNDEFINED
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClientProperties {
|
export class ClientProperties {
|
||||||
client_type: ClientType = ClientType.CLIENT_VOICE; //TeamSpeaks type
|
client_type: ClientType = ClientType.CLIENT_VOICE; //TeamSpeaks type
|
||||||
client_type_exact: ClientType = ClientType.CLIENT_VOICE;
|
client_type_exact: ClientType = ClientType.CLIENT_VOICE;
|
||||||
|
|
||||||
|
@ -57,7 +60,7 @@ class ClientProperties {
|
||||||
client_is_priority_speaker: boolean = false;
|
client_is_priority_speaker: boolean = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClientConnectionInfo {
|
export class ClientConnectionInfo {
|
||||||
connection_bandwidth_received_last_minute_control: number = -1;
|
connection_bandwidth_received_last_minute_control: number = -1;
|
||||||
connection_bandwidth_received_last_minute_keepalive: number = -1;
|
connection_bandwidth_received_last_minute_keepalive: number = -1;
|
||||||
connection_bandwidth_received_last_minute_speech: number = -1;
|
connection_bandwidth_received_last_minute_speech: number = -1;
|
||||||
|
@ -109,7 +112,7 @@ class ClientConnectionInfo {
|
||||||
connection_client_port: number = -1;
|
connection_client_port: number = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClientEntry {
|
export class ClientEntry {
|
||||||
readonly events: events.Registry<events.channel_tree.client>;
|
readonly events: events.Registry<events.channel_tree.client>;
|
||||||
|
|
||||||
protected _clientId: number;
|
protected _clientId: number;
|
||||||
|
@ -1123,7 +1126,7 @@ class ClientEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalClientEntry extends ClientEntry {
|
export class LocalClientEntry extends ClientEntry {
|
||||||
handle: ConnectionHandler;
|
handle: ConnectionHandler;
|
||||||
|
|
||||||
private renaming: boolean;
|
private renaming: boolean;
|
||||||
|
@ -1232,7 +1235,7 @@ class LocalClientEntry extends ClientEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MusicClientProperties extends ClientProperties {
|
export class MusicClientProperties extends ClientProperties {
|
||||||
player_state: number = 0;
|
player_state: number = 0;
|
||||||
player_volume: number = 0;
|
player_volume: number = 0;
|
||||||
|
|
||||||
|
@ -1264,7 +1267,7 @@ class MusicClientProperties extends ClientProperties {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class SongInfo {
|
export class SongInfo {
|
||||||
song_id: number = 0;
|
song_id: number = 0;
|
||||||
song_url: string = "";
|
song_url: string = "";
|
||||||
song_invoker: number = 0;
|
song_invoker: number = 0;
|
||||||
|
@ -1277,7 +1280,7 @@ class SongInfo {
|
||||||
song_length: number = 0;
|
song_length: number = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MusicClientPlayerInfo extends SongInfo {
|
export class MusicClientPlayerInfo extends SongInfo {
|
||||||
bot_id: number = 0;
|
bot_id: number = 0;
|
||||||
player_state: number = 0;
|
player_state: number = 0;
|
||||||
|
|
||||||
|
@ -1290,7 +1293,7 @@ class MusicClientPlayerInfo extends SongInfo {
|
||||||
player_description: string = "";
|
player_description: string = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
class MusicClientEntry extends ClientEntry {
|
export class MusicClientEntry extends ClientEntry {
|
||||||
private _info_promise: Promise<MusicClientPlayerInfo>;
|
private _info_promise: Promise<MusicClientPlayerInfo>;
|
||||||
private _info_promise_age: number = 0;
|
private _info_promise_age: number = 0;
|
||||||
private _info_promise_resolve: any;
|
private _info_promise_resolve: any;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
/// <reference path="channel.ts" />
|
/// <reference path="channel.ts" />
|
||||||
/// <reference path="modal/ModalServerEdit.ts" />
|
/// <reference path="../ui/modal/ModalServerEdit.ts" />
|
||||||
|
|
||||||
class ServerProperties {
|
import {ChannelTree} from "./view";
|
||||||
|
|
||||||
|
export class ServerProperties {
|
||||||
virtualserver_host: string = "";
|
virtualserver_host: string = "";
|
||||||
virtualserver_port: number = 0;
|
virtualserver_port: number = 0;
|
||||||
|
|
||||||
|
@ -78,7 +80,7 @@ class ServerProperties {
|
||||||
virtualserver_total_bytes_uploaded: number = 0;
|
virtualserver_total_bytes_uploaded: number = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServerConnectionInfo {
|
export interface ServerConnectionInfo {
|
||||||
connection_filetransfer_bandwidth_sent: number;
|
connection_filetransfer_bandwidth_sent: number;
|
||||||
connection_filetransfer_bandwidth_received: number;
|
connection_filetransfer_bandwidth_received: number;
|
||||||
|
|
||||||
|
@ -103,12 +105,12 @@ interface ServerConnectionInfo {
|
||||||
connection_ping: number;
|
connection_ping: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServerAddress {
|
export interface ServerAddress {
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerEntry {
|
export class ServerEntry {
|
||||||
remote_address: ServerAddress;
|
remote_address: ServerAddress;
|
||||||
channelTree: ChannelTree;
|
channelTree: ChannelTree;
|
||||||
properties: ServerProperties;
|
properties: ServerProperties;
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
/// <reference path="client.ts" />
|
/// <reference path="client.ts" />
|
||||||
/// <reference path="server.ts" />
|
/// <reference path="server.ts" />
|
||||||
/// <reference path="../bookmarks.ts" />
|
/// <reference path="../bookmarks.ts" />
|
||||||
/// <reference path="elements/context_menu.ts" />
|
/// <reference path="../ui/elements/context_menu.ts" />
|
||||||
/// <reference path="modal/ModalCreateChannel.ts" />
|
/// <reference path="../ui/modal/ModalCreateChannel.ts" />
|
||||||
/// <reference path="../../backend/ppt.d.ts" />
|
/// <reference path="../../backend/ppt.d.ts" />
|
||||||
|
|
||||||
|
|
||||||
class ChannelTree {
|
export class ChannelTree {
|
||||||
client: ConnectionHandler;
|
client: ConnectionHandler;
|
||||||
server: ServerEntry;
|
server: ServerEntry;
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,448 +1,460 @@
|
||||||
namespace connection {
|
import {
|
||||||
export class CommandHelper extends AbstractCommandHandler {
|
ClientNameInfo,
|
||||||
private _who_am_i: any;
|
CommandResult,
|
||||||
private _awaiters_unique_ids: {[unique_id: string]:((resolved: ClientNameInfo) => any)[]} = {};
|
ErrorID,
|
||||||
private _awaiters_unique_dbid: {[database_id: number]:((resolved: ClientNameInfo) => any)[]} = {};
|
Playlist, PlaylistInfo, PlaylistSong,
|
||||||
|
QueryList,
|
||||||
|
QueryListEntry, ServerGroupClient
|
||||||
|
} from "./ServerConnectionDeclaration";
|
||||||
|
import {ChannelEntry} from "../channel-tree/channel";
|
||||||
|
import {ChatType} from "../ui/frames/chat";
|
||||||
|
import {ClientEntry} from "../channel-tree/client";
|
||||||
|
import {AbstractCommandHandler, ServerCommand, SingleCommandHandler} from "./ConnectionBase";
|
||||||
|
import {log, LogCategory} from "../log";
|
||||||
|
|
||||||
constructor(connection) {
|
export class CommandHelper extends AbstractCommandHandler {
|
||||||
super(connection);
|
private _who_am_i: any;
|
||||||
|
private _awaiters_unique_ids: {[unique_id: string]:((resolved: ClientNameInfo) => any)[]} = {};
|
||||||
|
private _awaiters_unique_dbid: {[database_id: number]:((resolved: ClientNameInfo) => any)[]} = {};
|
||||||
|
|
||||||
this.volatile_handler_boss = false;
|
constructor(connection) {
|
||||||
this.ignore_consumed = true;
|
super(connection);
|
||||||
|
|
||||||
|
this.volatile_handler_boss = false;
|
||||||
|
this.ignore_consumed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.connection.command_handler_boss().register_handler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
if(this.connection) {
|
||||||
|
const hboss = this.connection.command_handler_boss();
|
||||||
|
hboss && hboss.unregister_handler(this);
|
||||||
|
}
|
||||||
|
this._awaiters_unique_ids = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_command(command: ServerCommand): boolean {
|
||||||
|
if(command.command == "notifyclientnamefromuid")
|
||||||
|
this.handle_notifyclientnamefromuid(command.arguments);
|
||||||
|
if(command.command == "notifyclientgetnamefromdbid")
|
||||||
|
this.handle_notifyclientgetnamefromdbid(command.arguments);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
joinChannel(channel: ChannelEntry, password?: string) : Promise<CommandResult> {
|
||||||
|
return this.connection.send_command("clientmove", {
|
||||||
|
"clid": this.connection.client.getClientId(),
|
||||||
|
"cid": channel.getChannelId(),
|
||||||
|
"cpw": password || ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(message: string, type: ChatType, target?: ChannelEntry | ClientEntry) : Promise<CommandResult> {
|
||||||
|
if(type == ChatType.SERVER)
|
||||||
|
return this.connection.send_command("sendtextmessage", {"targetmode": 3, "target": 0, "msg": message});
|
||||||
|
else if(type == ChatType.CHANNEL)
|
||||||
|
return this.connection.send_command("sendtextmessage", {"targetmode": 2, "target": (target as ChannelEntry).getChannelId(), "msg": message});
|
||||||
|
else if(type == ChatType.CLIENT)
|
||||||
|
return this.connection.send_command("sendtextmessage", {"targetmode": 1, "target": (target as ClientEntry).clientId(), "msg": message});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateClient(key: string, value: string) : Promise<CommandResult> {
|
||||||
|
let data = {};
|
||||||
|
data[key] = value;
|
||||||
|
return this.connection.send_command("clientupdate", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async info_from_uid(..._unique_ids: string[]) : Promise<ClientNameInfo[]> {
|
||||||
|
const response: ClientNameInfo[] = [];
|
||||||
|
const request = [];
|
||||||
|
const unique_ids = new Set(_unique_ids);
|
||||||
|
if(!unique_ids.size) return [];
|
||||||
|
|
||||||
|
const unique_id_resolvers: {[unique_id: string]: (resolved: ClientNameInfo) => any} = {};
|
||||||
|
|
||||||
|
|
||||||
|
for(const unique_id of unique_ids) {
|
||||||
|
request.push({'cluid': unique_id});
|
||||||
|
(this._awaiters_unique_ids[unique_id] || (this._awaiters_unique_ids[unique_id] = []))
|
||||||
|
.push(unique_id_resolvers[unique_id] = info => response.push(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
try {
|
||||||
this.connection.command_handler_boss().register_handler(this);
|
await this.connection.send_command("clientgetnamefromuid", request);
|
||||||
}
|
} catch(error) {
|
||||||
|
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) {
|
||||||
destroy() {
|
/* nothing */
|
||||||
if(this.connection) {
|
} else {
|
||||||
const hboss = this.connection.command_handler_boss();
|
throw error;
|
||||||
hboss && hboss.unregister_handler(this);
|
|
||||||
}
|
}
|
||||||
this._awaiters_unique_ids = undefined;
|
} finally {
|
||||||
|
/* cleanup */
|
||||||
|
for(const unique_id of Object.keys(unique_id_resolvers))
|
||||||
|
(this._awaiters_unique_ids[unique_id] || []).remove(unique_id_resolvers[unique_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_command(command: connection.ServerCommand): boolean {
|
return response;
|
||||||
if(command.command == "notifyclientnamefromuid")
|
}
|
||||||
this.handle_notifyclientnamefromuid(command.arguments);
|
|
||||||
if(command.command == "notifyclientgetnamefromdbid")
|
private handle_notifyclientgetnamefromdbid(json: any[]) {
|
||||||
this.handle_notifyclientgetnamefromdbid(command.arguments);
|
for(const entry of json) {
|
||||||
else
|
const info: ClientNameInfo = {
|
||||||
return false;
|
client_unique_id: entry["cluid"],
|
||||||
return true;
|
client_nickname: entry["clname"],
|
||||||
|
client_database_id: parseInt(entry["cldbid"])
|
||||||
|
};
|
||||||
|
|
||||||
|
const functions = this._awaiters_unique_dbid[info.client_database_id] || [];
|
||||||
|
delete this._awaiters_unique_dbid[info.client_database_id];
|
||||||
|
|
||||||
|
for(const fn of functions)
|
||||||
|
fn(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async info_from_cldbid(..._cldbid: number[]) : Promise<ClientNameInfo[]> {
|
||||||
|
const response: ClientNameInfo[] = [];
|
||||||
|
const request = [];
|
||||||
|
const unique_cldbid = new Set(_cldbid);
|
||||||
|
if(!unique_cldbid.size) return [];
|
||||||
|
|
||||||
|
const unique_cldbid_resolvers: {[dbid: number]: (resolved: ClientNameInfo) => any} = {};
|
||||||
|
|
||||||
|
|
||||||
|
for(const cldbid of unique_cldbid) {
|
||||||
|
request.push({'cldbid': cldbid});
|
||||||
|
(this._awaiters_unique_dbid[cldbid] || (this._awaiters_unique_dbid[cldbid] = []))
|
||||||
|
.push(unique_cldbid_resolvers[cldbid] = info => response.push(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
joinChannel(channel: ChannelEntry, password?: string) : Promise<CommandResult> {
|
try {
|
||||||
return this.connection.send_command("clientmove", {
|
await this.connection.send_command("clientgetnamefromdbid", request);
|
||||||
"clid": this.connection.client.getClientId(),
|
} catch(error) {
|
||||||
"cid": channel.getChannelId(),
|
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) {
|
||||||
"cpw": password || ""
|
/* nothing */
|
||||||
});
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
/* cleanup */
|
||||||
|
for(const cldbid of Object.keys(unique_cldbid_resolvers))
|
||||||
|
(this._awaiters_unique_dbid[cldbid] || []).remove(unique_cldbid_resolvers[cldbid]);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage(message: string, type: ChatType, target?: ChannelEntry | ClientEntry) : Promise<CommandResult> {
|
return response;
|
||||||
if(type == ChatType.SERVER)
|
}
|
||||||
return this.connection.send_command("sendtextmessage", {"targetmode": 3, "target": 0, "msg": message});
|
|
||||||
else if(type == ChatType.CHANNEL)
|
private handle_notifyclientnamefromuid(json: any[]) {
|
||||||
return this.connection.send_command("sendtextmessage", {"targetmode": 2, "target": (target as ChannelEntry).getChannelId(), "msg": message});
|
for(const entry of json) {
|
||||||
else if(type == ChatType.CLIENT)
|
const info: ClientNameInfo = {
|
||||||
return this.connection.send_command("sendtextmessage", {"targetmode": 1, "target": (target as ClientEntry).clientId(), "msg": message});
|
client_unique_id: entry["cluid"],
|
||||||
}
|
client_nickname: entry["clname"],
|
||||||
|
client_database_id: parseInt(entry["cldbid"])
|
||||||
|
};
|
||||||
|
|
||||||
|
const functions = this._awaiters_unique_ids[entry["cluid"]] || [];
|
||||||
|
delete this._awaiters_unique_ids[entry["cluid"]];
|
||||||
|
|
||||||
|
for(const fn of functions)
|
||||||
|
fn(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request_query_list(server_id: number = undefined) : Promise<QueryList> {
|
||||||
|
return new Promise<QueryList>((resolve, reject) => {
|
||||||
|
const single_handler = {
|
||||||
|
command: "notifyquerylist",
|
||||||
|
function: command => {
|
||||||
|
const json = command.arguments;
|
||||||
|
|
||||||
|
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 = parseInt(entry["client_bound_server"]);
|
||||||
|
rentry.username = entry["client_login_name"];
|
||||||
|
rentry.unique_id = entry["client_unique_identifier"];
|
||||||
|
|
||||||
|
result.queries.push(rentry);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(result);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.handler_boss.register_single_handler(single_handler);
|
||||||
|
|
||||||
updateClient(key: string, value: string) : Promise<CommandResult> {
|
|
||||||
let data = {};
|
let data = {};
|
||||||
data[key] = value;
|
if(server_id !== undefined)
|
||||||
return this.connection.send_command("clientupdate", data);
|
data["server_id"] = server_id;
|
||||||
}
|
|
||||||
|
|
||||||
async info_from_uid(..._unique_ids: string[]) : Promise<ClientNameInfo[]> {
|
this.connection.send_command("querylist", data).catch(error => {
|
||||||
const response: ClientNameInfo[] = [];
|
this.handler_boss.remove_single_handler(single_handler);
|
||||||
const request = [];
|
|
||||||
const unique_ids = new Set(_unique_ids);
|
|
||||||
if(!unique_ids.size) return [];
|
|
||||||
|
|
||||||
const unique_id_resolvers: {[unique_id: string]: (resolved: ClientNameInfo) => any} = {};
|
if(error instanceof CommandResult) {
|
||||||
|
if(error.id == ErrorID.EMPTY_RESULT) {
|
||||||
|
resolve(undefined);
|
||||||
for(const unique_id of unique_ids) {
|
return;
|
||||||
request.push({'cluid': unique_id});
|
}
|
||||||
(this._awaiters_unique_ids[unique_id] || (this._awaiters_unique_ids[unique_id] = []))
|
|
||||||
.push(unique_id_resolvers[unique_id] = info => response.push(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.connection.send_command("clientgetnamefromuid", request);
|
|
||||||
} catch(error) {
|
|
||||||
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) {
|
|
||||||
/* nothing */
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
} finally {
|
reject(error);
|
||||||
/* cleanup */
|
});
|
||||||
for(const unique_id of Object.keys(unique_id_resolvers))
|
});
|
||||||
(this._awaiters_unique_ids[unique_id] || []).remove(unique_id_resolvers[unique_id]);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
request_playlist_list() : Promise<Playlist[]> {
|
||||||
}
|
return new Promise((resolve, reject) => {
|
||||||
|
const single_handler: SingleCommandHandler = {
|
||||||
|
command: "notifyplaylistlist",
|
||||||
|
function: command => {
|
||||||
|
const json = command.arguments;
|
||||||
|
const result: Playlist[] = [];
|
||||||
|
|
||||||
private handle_notifyclientgetnamefromdbid(json: any[]) {
|
for(const entry of json) {
|
||||||
for(const entry of json) {
|
try {
|
||||||
const info: ClientNameInfo = {
|
result.push({
|
||||||
client_unique_id: entry["cluid"],
|
playlist_id: parseInt(entry["playlist_id"]),
|
||||||
client_nickname: entry["clname"],
|
playlist_bot_id: parseInt(entry["playlist_bot_id"]),
|
||||||
client_database_id: parseInt(entry["cldbid"])
|
playlist_title: entry["playlist_title"],
|
||||||
};
|
playlist_type: parseInt(entry["playlist_type"]),
|
||||||
|
playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]),
|
||||||
|
playlist_owner_name: entry["playlist_owner_name"],
|
||||||
|
|
||||||
const functions = this._awaiters_unique_dbid[info.client_database_id] || [];
|
needed_power_modify: parseInt(entry["needed_power_modify"]),
|
||||||
delete this._awaiters_unique_dbid[info.client_database_id];
|
needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]),
|
||||||
|
needed_power_delete: parseInt(entry["needed_power_delete"]),
|
||||||
|
needed_power_song_add: parseInt(entry["needed_power_song_add"]),
|
||||||
|
needed_power_song_move: parseInt(entry["needed_power_song_move"]),
|
||||||
|
needed_power_song_remove: parseInt(entry["needed_power_song_remove"])
|
||||||
|
});
|
||||||
|
} catch(error) {
|
||||||
|
log.error(LogCategory.NETWORKING, tr("Failed to parse playlist entry: %o"), error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for(const fn of functions)
|
resolve(result);
|
||||||
fn(info);
|
return true;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async info_from_cldbid(..._cldbid: number[]) : Promise<ClientNameInfo[]> {
|
|
||||||
const response: ClientNameInfo[] = [];
|
|
||||||
const request = [];
|
|
||||||
const unique_cldbid = new Set(_cldbid);
|
|
||||||
if(!unique_cldbid.size) return [];
|
|
||||||
|
|
||||||
const unique_cldbid_resolvers: {[dbid: number]: (resolved: ClientNameInfo) => any} = {};
|
|
||||||
|
|
||||||
|
|
||||||
for(const cldbid of unique_cldbid) {
|
|
||||||
request.push({'cldbid': cldbid});
|
|
||||||
(this._awaiters_unique_dbid[cldbid] || (this._awaiters_unique_dbid[cldbid] = []))
|
|
||||||
.push(unique_cldbid_resolvers[cldbid] = info => response.push(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.connection.send_command("clientgetnamefromdbid", request);
|
|
||||||
} catch(error) {
|
|
||||||
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) {
|
|
||||||
/* nothing */
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
} finally {
|
};
|
||||||
/* cleanup */
|
this.handler_boss.register_single_handler(single_handler);
|
||||||
for(const cldbid of Object.keys(unique_cldbid_resolvers))
|
|
||||||
(this._awaiters_unique_dbid[cldbid] || []).remove(unique_cldbid_resolvers[cldbid]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
this.connection.send_command("playlistlist").catch(error => {
|
||||||
}
|
this.handler_boss.remove_single_handler(single_handler);
|
||||||
|
|
||||||
private handle_notifyclientnamefromuid(json: any[]) {
|
if(error instanceof CommandResult) {
|
||||||
for(const entry of json) {
|
if(error.id == ErrorID.EMPTY_RESULT) {
|
||||||
const info: ClientNameInfo = {
|
|
||||||
client_unique_id: entry["cluid"],
|
|
||||||
client_nickname: entry["clname"],
|
|
||||||
client_database_id: parseInt(entry["cldbid"])
|
|
||||||
};
|
|
||||||
|
|
||||||
const functions = this._awaiters_unique_ids[entry["cluid"]] || [];
|
|
||||||
delete this._awaiters_unique_ids[entry["cluid"]];
|
|
||||||
|
|
||||||
for(const fn of functions)
|
|
||||||
fn(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
request_query_list(server_id: number = undefined) : Promise<QueryList> {
|
|
||||||
return new Promise<QueryList>((resolve, reject) => {
|
|
||||||
const single_handler = {
|
|
||||||
command: "notifyquerylist",
|
|
||||||
function: command => {
|
|
||||||
const json = command.arguments;
|
|
||||||
|
|
||||||
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 = parseInt(entry["client_bound_server"]);
|
|
||||||
rentry.username = entry["client_login_name"];
|
|
||||||
rentry.unique_id = entry["client_unique_identifier"];
|
|
||||||
|
|
||||||
result.queries.push(rentry);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(result);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.handler_boss.register_single_handler(single_handler);
|
|
||||||
|
|
||||||
let data = {};
|
|
||||||
if(server_id !== undefined)
|
|
||||||
data["server_id"] = server_id;
|
|
||||||
|
|
||||||
this.connection.send_command("querylist", data).catch(error => {
|
|
||||||
this.handler_boss.remove_single_handler(single_handler);
|
|
||||||
|
|
||||||
if(error instanceof CommandResult) {
|
|
||||||
if(error.id == ErrorID.EMPTY_RESULT) {
|
|
||||||
resolve(undefined);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
request_playlist_list() : Promise<Playlist[]> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const single_handler: SingleCommandHandler = {
|
|
||||||
command: "notifyplaylistlist",
|
|
||||||
function: command => {
|
|
||||||
const json = command.arguments;
|
|
||||||
const result: Playlist[] = [];
|
|
||||||
|
|
||||||
for(const entry of json) {
|
|
||||||
try {
|
|
||||||
result.push({
|
|
||||||
playlist_id: parseInt(entry["playlist_id"]),
|
|
||||||
playlist_bot_id: parseInt(entry["playlist_bot_id"]),
|
|
||||||
playlist_title: entry["playlist_title"],
|
|
||||||
playlist_type: parseInt(entry["playlist_type"]),
|
|
||||||
playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]),
|
|
||||||
playlist_owner_name: entry["playlist_owner_name"],
|
|
||||||
|
|
||||||
needed_power_modify: parseInt(entry["needed_power_modify"]),
|
|
||||||
needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]),
|
|
||||||
needed_power_delete: parseInt(entry["needed_power_delete"]),
|
|
||||||
needed_power_song_add: parseInt(entry["needed_power_song_add"]),
|
|
||||||
needed_power_song_move: parseInt(entry["needed_power_song_move"]),
|
|
||||||
needed_power_song_remove: parseInt(entry["needed_power_song_remove"])
|
|
||||||
});
|
|
||||||
} catch(error) {
|
|
||||||
log.error(LogCategory.NETWORKING, tr("Failed to parse playlist entry: %o"), error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(result);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.handler_boss.register_single_handler(single_handler);
|
|
||||||
|
|
||||||
this.connection.send_command("playlistlist").catch(error => {
|
|
||||||
this.handler_boss.remove_single_handler(single_handler);
|
|
||||||
|
|
||||||
if(error instanceof CommandResult) {
|
|
||||||
if(error.id == ErrorID.EMPTY_RESULT) {
|
|
||||||
resolve([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reject(error);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
request_playlist_songs(playlist_id: number) : Promise<PlaylistSong[]> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const single_handler: SingleCommandHandler = {
|
|
||||||
command: "notifyplaylistsonglist",
|
|
||||||
function: command => {
|
|
||||||
const json = command.arguments;
|
|
||||||
|
|
||||||
if(json[0]["playlist_id"] != playlist_id) {
|
|
||||||
log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist songs"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result: PlaylistSong[] = [];
|
|
||||||
|
|
||||||
for(const entry of json) {
|
|
||||||
try {
|
|
||||||
result.push({
|
|
||||||
song_id: parseInt(entry["song_id"]),
|
|
||||||
song_invoker: entry["song_invoker"],
|
|
||||||
song_previous_song_id: parseInt(entry["song_previous_song_id"]),
|
|
||||||
song_url: entry["song_url"],
|
|
||||||
song_url_loader: entry["song_url_loader"],
|
|
||||||
|
|
||||||
song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1",
|
|
||||||
song_metadata: entry["song_metadata"]
|
|
||||||
});
|
|
||||||
} catch(error) {
|
|
||||||
log.error(LogCategory.NETWORKING, tr("Failed to parse playlist song entry: %o"), error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(result);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.handler_boss.register_single_handler(single_handler);
|
|
||||||
|
|
||||||
this.connection.send_command("playlistsonglist", {playlist_id: playlist_id}).catch(error => {
|
|
||||||
this.handler_boss.remove_single_handler(single_handler);
|
|
||||||
if(error instanceof CommandResult) {
|
|
||||||
if(error.id == ErrorID.EMPTY_RESULT) {
|
|
||||||
resolve([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reject(error);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
request_playlist_client_list(playlist_id: number) : Promise<number[]> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const single_handler: SingleCommandHandler = {
|
|
||||||
command: "notifyplaylistclientlist",
|
|
||||||
function: command => {
|
|
||||||
const json = command.arguments;
|
|
||||||
|
|
||||||
if(json[0]["playlist_id"] != playlist_id) {
|
|
||||||
log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist clients"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result: number[] = [];
|
|
||||||
|
|
||||||
for(const entry of json)
|
|
||||||
result.push(parseInt(entry["cldbid"]));
|
|
||||||
|
|
||||||
resolve(result.filter(e => !isNaN(e)));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.handler_boss.register_single_handler(single_handler);
|
|
||||||
|
|
||||||
this.connection.send_command("playlistclientlist", {playlist_id: playlist_id}).catch(error => {
|
|
||||||
this.handler_boss.remove_single_handler(single_handler);
|
|
||||||
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) {
|
|
||||||
resolve([]);
|
resolve([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
reject(error);
|
}
|
||||||
})
|
reject(error);
|
||||||
});
|
})
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async request_clients_by_server_group(group_id: number) : Promise<ServerGroupClient[]> {
|
request_playlist_songs(playlist_id: number) : Promise<PlaylistSong[]> {
|
||||||
//servergroupclientlist sgid=2
|
return new Promise((resolve, reject) => {
|
||||||
//notifyservergroupclientlist sgid=6 cldbid=2 client_nickname=WolverinDEV client_unique_identifier=xxjnc14LmvTk+Lyrm8OOeo4tOqw=
|
const single_handler: SingleCommandHandler = {
|
||||||
return new Promise<ServerGroupClient[]>((resolve, reject) => {
|
command: "notifyplaylistsonglist",
|
||||||
const single_handler: SingleCommandHandler = {
|
function: command => {
|
||||||
command: "notifyservergroupclientlist",
|
const json = command.arguments;
|
||||||
function: command => {
|
|
||||||
if (command.arguments[0]["sgid"] != group_id) {
|
|
||||||
log.error(LogCategory.NETWORKING, tr("Received invalid notification for server group client list"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
if(json[0]["playlist_id"] != playlist_id) {
|
||||||
const result: ServerGroupClient[] = [];
|
log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist songs"));
|
||||||
for(const entry of command.arguments)
|
return false;
|
||||||
result.push({
|
|
||||||
client_database_id: parseInt(entry["cldbid"]),
|
|
||||||
client_nickname: entry["client_nickname"],
|
|
||||||
client_unique_identifier: entry["client_unique_identifier"]
|
|
||||||
});
|
|
||||||
resolve(result);
|
|
||||||
} catch (error) {
|
|
||||||
log.error(LogCategory.NETWORKING, tr("Failed to parse server group client list: %o"), error);
|
|
||||||
reject("failed to parse info");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
this.handler_boss.register_single_handler(single_handler);
|
|
||||||
|
|
||||||
this.connection.send_command("servergroupclientlist", {sgid: group_id}).catch(error => {
|
const result: PlaylistSong[] = [];
|
||||||
this.handler_boss.remove_single_handler(single_handler);
|
|
||||||
reject(error);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
request_playlist_info(playlist_id: number) : Promise<PlaylistInfo> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const single_handler: SingleCommandHandler = {
|
|
||||||
command: "notifyplaylistinfo",
|
|
||||||
function: command => {
|
|
||||||
const json = command.arguments[0];
|
|
||||||
if (json["playlist_id"] != playlist_id) {
|
|
||||||
log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist info"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for(const entry of json) {
|
||||||
try {
|
try {
|
||||||
//resolve
|
result.push({
|
||||||
resolve({
|
song_id: parseInt(entry["song_id"]),
|
||||||
playlist_id: parseInt(json["playlist_id"]),
|
song_invoker: entry["song_invoker"],
|
||||||
playlist_title: json["playlist_title"],
|
song_previous_song_id: parseInt(entry["song_previous_song_id"]),
|
||||||
playlist_description: json["playlist_description"],
|
song_url: entry["song_url"],
|
||||||
playlist_type: parseInt(json["playlist_type"]),
|
song_url_loader: entry["song_url_loader"],
|
||||||
|
|
||||||
playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]),
|
song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1",
|
||||||
playlist_owner_name: json["playlist_owner_name"],
|
song_metadata: entry["song_metadata"]
|
||||||
|
|
||||||
playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1",
|
|
||||||
playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1",
|
|
||||||
playlist_replay_mode: parseInt(json["playlist_replay_mode"]),
|
|
||||||
playlist_current_song_id: parseInt(json["playlist_current_song_id"]),
|
|
||||||
|
|
||||||
playlist_max_songs: parseInt(json["playlist_max_songs"])
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch(error) {
|
||||||
log.error(LogCategory.NETWORKING, tr("Failed to parse playlist info: %o"), error);
|
log.error(LogCategory.NETWORKING, tr("Failed to parse playlist song entry: %o"), error);
|
||||||
reject("failed to parse info");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
this.handler_boss.register_single_handler(single_handler);
|
|
||||||
|
|
||||||
this.connection.send_command("playlistinfo", {playlist_id: playlist_id}).catch(error => {
|
resolve(result);
|
||||||
this.handler_boss.remove_single_handler(single_handler);
|
return true;
|
||||||
reject(error);
|
}
|
||||||
})
|
};
|
||||||
});
|
this.handler_boss.register_single_handler(single_handler);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
this.connection.send_command("playlistsonglist", {playlist_id: playlist_id}).catch(error => {
|
||||||
* @deprecated
|
this.handler_boss.remove_single_handler(single_handler);
|
||||||
* Its just a workaround for the query management.
|
if(error instanceof CommandResult) {
|
||||||
* There is no garante that the whoami trick will work forever
|
if(error.id == ErrorID.EMPTY_RESULT) {
|
||||||
*/
|
resolve([]);
|
||||||
current_virtual_server_id() : Promise<number> {
|
return;
|
||||||
if(this._who_am_i)
|
|
||||||
return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"]));
|
|
||||||
|
|
||||||
return new Promise<number>((resolve, reject) => {
|
|
||||||
const single_handler: SingleCommandHandler = {
|
|
||||||
function: command => {
|
|
||||||
if(command.command != "" && command.command.indexOf("=") == -1)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
this._who_am_i = command.arguments[0];
|
|
||||||
resolve(parseInt(this._who_am_i["virtualserver_id"]));
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
this.handler_boss.register_single_handler(single_handler);
|
reject(error);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.connection.send_command("whoami").catch(error => {
|
request_playlist_client_list(playlist_id: number) : Promise<number[]> {
|
||||||
this.handler_boss.remove_single_handler(single_handler);
|
return new Promise((resolve, reject) => {
|
||||||
reject(error);
|
const single_handler: SingleCommandHandler = {
|
||||||
});
|
command: "notifyplaylistclientlist",
|
||||||
|
function: command => {
|
||||||
|
const json = command.arguments;
|
||||||
|
|
||||||
|
if(json[0]["playlist_id"] != playlist_id) {
|
||||||
|
log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist clients"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: number[] = [];
|
||||||
|
|
||||||
|
for(const entry of json)
|
||||||
|
result.push(parseInt(entry["cldbid"]));
|
||||||
|
|
||||||
|
resolve(result.filter(e => !isNaN(e)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.handler_boss.register_single_handler(single_handler);
|
||||||
|
|
||||||
|
this.connection.send_command("playlistclientlist", {playlist_id: playlist_id}).catch(error => {
|
||||||
|
this.handler_boss.remove_single_handler(single_handler);
|
||||||
|
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) {
|
||||||
|
resolve([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reject(error);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async request_clients_by_server_group(group_id: number) : Promise<ServerGroupClient[]> {
|
||||||
|
//servergroupclientlist sgid=2
|
||||||
|
//notifyservergroupclientlist sgid=6 cldbid=2 client_nickname=WolverinDEV client_unique_identifier=xxjnc14LmvTk+Lyrm8OOeo4tOqw=
|
||||||
|
return new Promise<ServerGroupClient[]>((resolve, reject) => {
|
||||||
|
const single_handler: SingleCommandHandler = {
|
||||||
|
command: "notifyservergroupclientlist",
|
||||||
|
function: command => {
|
||||||
|
if (command.arguments[0]["sgid"] != group_id) {
|
||||||
|
log.error(LogCategory.NETWORKING, tr("Received invalid notification for server group client list"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result: ServerGroupClient[] = [];
|
||||||
|
for(const entry of command.arguments)
|
||||||
|
result.push({
|
||||||
|
client_database_id: parseInt(entry["cldbid"]),
|
||||||
|
client_nickname: entry["client_nickname"],
|
||||||
|
client_unique_identifier: entry["client_unique_identifier"]
|
||||||
|
});
|
||||||
|
resolve(result);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(LogCategory.NETWORKING, tr("Failed to parse server group client list: %o"), error);
|
||||||
|
reject("failed to parse info");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.handler_boss.register_single_handler(single_handler);
|
||||||
|
|
||||||
|
this.connection.send_command("servergroupclientlist", {sgid: group_id}).catch(error => {
|
||||||
|
this.handler_boss.remove_single_handler(single_handler);
|
||||||
|
reject(error);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
request_playlist_info(playlist_id: number) : Promise<PlaylistInfo> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const single_handler: SingleCommandHandler = {
|
||||||
|
command: "notifyplaylistinfo",
|
||||||
|
function: command => {
|
||||||
|
const json = command.arguments[0];
|
||||||
|
if (json["playlist_id"] != playlist_id) {
|
||||||
|
log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist info"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
//resolve
|
||||||
|
resolve({
|
||||||
|
playlist_id: parseInt(json["playlist_id"]),
|
||||||
|
playlist_title: json["playlist_title"],
|
||||||
|
playlist_description: json["playlist_description"],
|
||||||
|
playlist_type: parseInt(json["playlist_type"]),
|
||||||
|
|
||||||
|
playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]),
|
||||||
|
playlist_owner_name: json["playlist_owner_name"],
|
||||||
|
|
||||||
|
playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1",
|
||||||
|
playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1",
|
||||||
|
playlist_replay_mode: parseInt(json["playlist_replay_mode"]),
|
||||||
|
playlist_current_song_id: parseInt(json["playlist_current_song_id"]),
|
||||||
|
|
||||||
|
playlist_max_songs: parseInt(json["playlist_max_songs"])
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
log.error(LogCategory.NETWORKING, tr("Failed to parse playlist info: %o"), error);
|
||||||
|
reject("failed to parse info");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.handler_boss.register_single_handler(single_handler);
|
||||||
|
|
||||||
|
this.connection.send_command("playlistinfo", {playlist_id: playlist_id}).catch(error => {
|
||||||
|
this.handler_boss.remove_single_handler(single_handler);
|
||||||
|
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) => {
|
||||||
|
const single_handler: SingleCommandHandler = {
|
||||||
|
function: command => {
|
||||||
|
if(command.command != "" && command.command.indexOf("=") == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this._who_am_i = command.arguments[0];
|
||||||
|
resolve(parseInt(this._who_am_i["virtualserver_id"]));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.handler_boss.register_single_handler(single_handler);
|
||||||
|
|
||||||
|
this.connection.send_command("whoami").catch(error => {
|
||||||
|
this.handler_boss.remove_single_handler(single_handler);
|
||||||
|
reject(error);
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,216 +1,222 @@
|
||||||
namespace connection {
|
import {ConnectionHandler, ConnectionState} from "../ConnectionHandler";
|
||||||
export interface CommandOptions {
|
import {ServerAddress} from "../channel-tree/server";
|
||||||
flagset?: string[]; /* default: [] */
|
import {CommandResult} from "./ServerConnectionDeclaration";
|
||||||
process_result?: boolean; /* default: true */
|
import {RecorderProfile} from "../voice/RecorderProfile";
|
||||||
|
import {CommandHelper} from "./CommandHelper";
|
||||||
|
import {connection} from "./HandshakeHandler";
|
||||||
|
import HandshakeHandler = connection.HandshakeHandler;
|
||||||
|
|
||||||
timeout?: number /* default: 1000 */;
|
export interface CommandOptions {
|
||||||
|
flagset?: string[]; /* default: [] */
|
||||||
|
process_result?: boolean; /* default: true */
|
||||||
|
|
||||||
|
timeout?: number /* default: 1000 */;
|
||||||
|
}
|
||||||
|
export const CommandOptionDefaults: CommandOptions = {
|
||||||
|
flagset: [],
|
||||||
|
process_result: true,
|
||||||
|
timeout: 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ConnectionStateListener = (old_state: ConnectionState, new_state: ConnectionState) => any;
|
||||||
|
export abstract class AbstractServerConnection {
|
||||||
|
readonly client: ConnectionHandler;
|
||||||
|
readonly command_helper: CommandHelper;
|
||||||
|
|
||||||
|
protected constructor(client: ConnectionHandler) {
|
||||||
|
this.client = client;
|
||||||
|
|
||||||
|
this.command_helper = new CommandHelper(this);
|
||||||
}
|
}
|
||||||
export const CommandOptionDefaults: CommandOptions = {
|
|
||||||
flagset: [],
|
/* resolved as soon a connection has been established. This does not means that the authentication had yet been done! */
|
||||||
process_result: true,
|
abstract connect(address: ServerAddress, handshake: HandshakeHandler, timeout?: number) : Promise<void>;
|
||||||
timeout: 1000
|
|
||||||
|
abstract connected() : boolean;
|
||||||
|
abstract disconnect(reason?: string) : Promise<void>;
|
||||||
|
|
||||||
|
abstract support_voice() : boolean;
|
||||||
|
abstract voice_connection() : voice.AbstractVoiceConnection | undefined;
|
||||||
|
|
||||||
|
abstract command_handler_boss() : AbstractCommandHandlerBoss;
|
||||||
|
abstract send_command(command: string, data?: any | any[], options?: CommandOptions) : Promise<CommandResult>;
|
||||||
|
|
||||||
|
abstract get onconnectionstatechanged() : ConnectionStateListener;
|
||||||
|
abstract set onconnectionstatechanged(listener: ConnectionStateListener);
|
||||||
|
|
||||||
|
abstract remote_address() : ServerAddress; /* only valid when connected */
|
||||||
|
abstract handshake_handler() : HandshakeHandler; /* only valid when connected */
|
||||||
|
|
||||||
|
abstract ping() : {
|
||||||
|
native: number,
|
||||||
|
javascript?: number
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type ConnectionStateListener = (old_state: ConnectionState, new_state: ConnectionState) => any;
|
export namespace voice {
|
||||||
export abstract class AbstractServerConnection {
|
export enum PlayerState {
|
||||||
readonly client: ConnectionHandler;
|
PREBUFFERING,
|
||||||
readonly command_helper: CommandHelper;
|
PLAYING,
|
||||||
|
BUFFERING,
|
||||||
|
STOPPING,
|
||||||
|
STOPPED
|
||||||
|
}
|
||||||
|
|
||||||
protected constructor(client: ConnectionHandler) {
|
export type LatencySettings = {
|
||||||
this.client = client;
|
min_buffer: number; /* milliseconds */
|
||||||
|
max_buffer: number; /* milliseconds */
|
||||||
|
}
|
||||||
|
|
||||||
this.command_helper = new CommandHelper(this);
|
export interface VoiceClient {
|
||||||
|
client_id: number;
|
||||||
|
|
||||||
|
callback_playback: () => any;
|
||||||
|
callback_stopped: () => any;
|
||||||
|
|
||||||
|
callback_state_changed: (new_state: PlayerState) => any;
|
||||||
|
|
||||||
|
get_state() : PlayerState;
|
||||||
|
|
||||||
|
get_volume() : number;
|
||||||
|
set_volume(volume: number) : void;
|
||||||
|
|
||||||
|
abort_replay();
|
||||||
|
|
||||||
|
support_latency_settings() : boolean;
|
||||||
|
|
||||||
|
reset_latency_settings();
|
||||||
|
latency_settings(settings?: LatencySettings) : LatencySettings;
|
||||||
|
|
||||||
|
support_flush() : boolean;
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class AbstractVoiceConnection {
|
||||||
|
readonly connection: AbstractServerConnection;
|
||||||
|
|
||||||
|
protected constructor(connection: AbstractServerConnection) {
|
||||||
|
this.connection = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* resolved as soon a connection has been established. This does not means that the authentication had yet been done! */
|
|
||||||
abstract connect(address: ServerAddress, handshake: HandshakeHandler, timeout?: number) : Promise<void>;
|
|
||||||
|
|
||||||
abstract connected() : boolean;
|
abstract connected() : boolean;
|
||||||
abstract disconnect(reason?: string) : Promise<void>;
|
abstract encoding_supported(codec: number) : boolean;
|
||||||
|
abstract decoding_supported(codec: number) : boolean;
|
||||||
|
|
||||||
abstract support_voice() : boolean;
|
abstract register_client(client_id: number) : VoiceClient;
|
||||||
abstract voice_connection() : voice.AbstractVoiceConnection | undefined;
|
abstract available_clients() : VoiceClient[];
|
||||||
|
abstract unregister_client(client: VoiceClient) : Promise<void>;
|
||||||
|
|
||||||
abstract command_handler_boss() : AbstractCommandHandlerBoss;
|
abstract voice_recorder() : RecorderProfile;
|
||||||
abstract send_command(command: string, data?: any | any[], options?: CommandOptions) : Promise<CommandResult>;
|
abstract acquire_voice_recorder(recorder: RecorderProfile | undefined) : Promise<void>;
|
||||||
|
|
||||||
abstract get onconnectionstatechanged() : ConnectionStateListener;
|
abstract get_encoder_codec() : number;
|
||||||
abstract set onconnectionstatechanged(listener: ConnectionStateListener);
|
abstract set_encoder_codec(codec: number);
|
||||||
|
}
|
||||||
abstract remote_address() : ServerAddress; /* only valid when connected */
|
}
|
||||||
abstract handshake_handler() : HandshakeHandler; /* only valid when connected */
|
|
||||||
|
export class ServerCommand {
|
||||||
abstract ping() : {
|
command: string;
|
||||||
native: number,
|
arguments: any[];
|
||||||
javascript?: number
|
}
|
||||||
};
|
|
||||||
}
|
export abstract class AbstractCommandHandler {
|
||||||
|
readonly connection: AbstractServerConnection;
|
||||||
export namespace voice {
|
|
||||||
export enum PlayerState {
|
handler_boss: AbstractCommandHandlerBoss | undefined;
|
||||||
PREBUFFERING,
|
volatile_handler_boss: boolean = false; /* if true than the command handler could be registered twice to two or more handlers */
|
||||||
PLAYING,
|
|
||||||
BUFFERING,
|
ignore_consumed: boolean = false;
|
||||||
STOPPING,
|
|
||||||
STOPPED
|
protected constructor(connection: AbstractServerConnection) {
|
||||||
}
|
this.connection = connection;
|
||||||
|
}
|
||||||
export type LatencySettings = {
|
|
||||||
min_buffer: number; /* milliseconds */
|
/**
|
||||||
max_buffer: number; /* milliseconds */
|
* @return If the command should be consumed
|
||||||
}
|
*/
|
||||||
|
abstract handle_command(command: ServerCommand) : boolean;
|
||||||
export interface VoiceClient {
|
}
|
||||||
client_id: number;
|
|
||||||
|
export interface SingleCommandHandler {
|
||||||
callback_playback: () => any;
|
name?: string;
|
||||||
callback_stopped: () => any;
|
command?: string;
|
||||||
|
timeout?: number;
|
||||||
callback_state_changed: (new_state: PlayerState) => any;
|
|
||||||
|
/* if the return is true then the command handler will be removed */
|
||||||
get_state() : PlayerState;
|
function: (command: ServerCommand) => boolean;
|
||||||
|
}
|
||||||
get_volume() : number;
|
|
||||||
set_volume(volume: number) : void;
|
export abstract class AbstractCommandHandlerBoss {
|
||||||
|
readonly connection: AbstractServerConnection;
|
||||||
abort_replay();
|
protected command_handlers: AbstractCommandHandler[] = [];
|
||||||
|
/* TODO: Timeout */
|
||||||
support_latency_settings() : boolean;
|
protected single_command_handler: SingleCommandHandler[] = [];
|
||||||
|
|
||||||
reset_latency_settings();
|
protected constructor(connection: AbstractServerConnection) {
|
||||||
latency_settings(settings?: LatencySettings) : LatencySettings;
|
this.connection = connection;
|
||||||
|
}
|
||||||
support_flush() : boolean;
|
|
||||||
flush();
|
destroy() {
|
||||||
}
|
this.command_handlers = undefined;
|
||||||
|
this.single_command_handler = undefined;
|
||||||
export abstract class AbstractVoiceConnection {
|
}
|
||||||
readonly connection: AbstractServerConnection;
|
|
||||||
|
register_handler(handler: AbstractCommandHandler) {
|
||||||
protected constructor(connection: AbstractServerConnection) {
|
if(!handler.volatile_handler_boss && handler.handler_boss)
|
||||||
this.connection = connection;
|
throw "handler already registered";
|
||||||
}
|
|
||||||
|
this.command_handlers.remove(handler); /* just to be sure */
|
||||||
abstract connected() : boolean;
|
this.command_handlers.push(handler);
|
||||||
abstract encoding_supported(codec: number) : boolean;
|
handler.handler_boss = this;
|
||||||
abstract decoding_supported(codec: number) : boolean;
|
}
|
||||||
|
|
||||||
abstract register_client(client_id: number) : VoiceClient;
|
unregister_handler(handler: AbstractCommandHandler) {
|
||||||
abstract available_clients() : VoiceClient[];
|
if(!handler.volatile_handler_boss && handler.handler_boss !== this) {
|
||||||
abstract unregister_client(client: VoiceClient) : Promise<void>;
|
console.warn(tr("Tried to unregister command handler which does not belong to the handler boss"));
|
||||||
|
return;
|
||||||
abstract voice_recorder() : RecorderProfile;
|
}
|
||||||
abstract acquire_voice_recorder(recorder: RecorderProfile | undefined) : Promise<void>;
|
|
||||||
|
this.command_handlers.remove(handler);
|
||||||
abstract get_encoder_codec() : number;
|
handler.handler_boss = undefined;
|
||||||
abstract set_encoder_codec(codec: number);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
register_single_handler(handler: SingleCommandHandler) {
|
||||||
export class ServerCommand {
|
this.single_command_handler.push(handler);
|
||||||
command: string;
|
}
|
||||||
arguments: any[];
|
|
||||||
}
|
remove_single_handler(handler: SingleCommandHandler) {
|
||||||
|
this.single_command_handler.remove(handler);
|
||||||
export abstract class AbstractCommandHandler {
|
}
|
||||||
readonly connection: AbstractServerConnection;
|
|
||||||
|
handlers() : AbstractCommandHandler[] {
|
||||||
handler_boss: AbstractCommandHandlerBoss | undefined;
|
return this.command_handlers;
|
||||||
volatile_handler_boss: boolean = false; /* if true than the command handler could be registered twice to two or more handlers */
|
}
|
||||||
|
|
||||||
ignore_consumed: boolean = false;
|
invoke_handle(command: ServerCommand) : boolean {
|
||||||
|
let flag_consumed = false;
|
||||||
protected constructor(connection: AbstractServerConnection) {
|
|
||||||
this.connection = connection;
|
for(const handler of this.command_handlers) {
|
||||||
}
|
try {
|
||||||
|
if(!flag_consumed || handler.ignore_consumed)
|
||||||
/**
|
flag_consumed = flag_consumed || handler.handle_command(command);
|
||||||
* @return If the command should be consumed
|
} catch(error) {
|
||||||
*/
|
console.error(tr("Failed to invoke command handler. Invocation results in an exception: %o"), error);
|
||||||
abstract handle_command(command: ServerCommand) : boolean;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SingleCommandHandler {
|
for(const handler of [...this.single_command_handler]) {
|
||||||
name?: string;
|
if(handler.command && handler.command != command.command)
|
||||||
command?: string;
|
continue;
|
||||||
timeout?: number;
|
|
||||||
|
try {
|
||||||
/* if the return is true then the command handler will be removed */
|
if(handler.function(command))
|
||||||
function: (command: ServerCommand) => boolean;
|
this.single_command_handler.remove(handler);
|
||||||
}
|
} catch(error) {
|
||||||
|
console.error(tr("Failed to invoke single command handler. Invocation results in an exception: %o"), error);
|
||||||
export abstract class AbstractCommandHandlerBoss {
|
}
|
||||||
readonly connection: AbstractServerConnection;
|
}
|
||||||
protected command_handlers: AbstractCommandHandler[] = [];
|
|
||||||
/* TODO: Timeout */
|
return flag_consumed;
|
||||||
protected single_command_handler: SingleCommandHandler[] = [];
|
|
||||||
|
|
||||||
protected constructor(connection: AbstractServerConnection) {
|
|
||||||
this.connection = connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this.command_handlers = undefined;
|
|
||||||
this.single_command_handler = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
register_handler(handler: AbstractCommandHandler) {
|
|
||||||
if(!handler.volatile_handler_boss && handler.handler_boss)
|
|
||||||
throw "handler already registered";
|
|
||||||
|
|
||||||
this.command_handlers.remove(handler); /* just to be sure */
|
|
||||||
this.command_handlers.push(handler);
|
|
||||||
handler.handler_boss = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
unregister_handler(handler: AbstractCommandHandler) {
|
|
||||||
if(!handler.volatile_handler_boss && handler.handler_boss !== this) {
|
|
||||||
console.warn(tr("Tried to unregister command handler which does not belong to the handler boss"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.command_handlers.remove(handler);
|
|
||||||
handler.handler_boss = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
register_single_handler(handler: SingleCommandHandler) {
|
|
||||||
this.single_command_handler.push(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
remove_single_handler(handler: SingleCommandHandler) {
|
|
||||||
this.single_command_handler.remove(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
handlers() : AbstractCommandHandler[] {
|
|
||||||
return this.command_handlers;
|
|
||||||
}
|
|
||||||
|
|
||||||
invoke_handle(command: ServerCommand) : boolean {
|
|
||||||
let flag_consumed = false;
|
|
||||||
|
|
||||||
for(const handler of this.command_handlers) {
|
|
||||||
try {
|
|
||||||
if(!flag_consumed || handler.ignore_consumed)
|
|
||||||
flag_consumed = flag_consumed || handler.handle_command(command);
|
|
||||||
} catch(error) {
|
|
||||||
console.error(tr("Failed to invoke command handler. Invocation results in an exception: %o"), error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const handler of [...this.single_command_handler]) {
|
|
||||||
if(handler.command && handler.command != command.command)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if(handler.function(command))
|
|
||||||
this.single_command_handler.remove(handler);
|
|
||||||
} catch(error) {
|
|
||||||
console.error(tr("Failed to invoke single command handler. Invocation results in an exception: %o"), error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return flag_consumed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,15 @@
|
||||||
namespace connection {
|
import {AbstractServerConnection} from "./ConnectionBase";
|
||||||
|
import {ConnectParameters, DisconnectReason} from "../ConnectionHandler";
|
||||||
|
import {profiles} from "../profiles/ConnectionProfile";
|
||||||
|
import {profiles as iprofiles} from "../profiles/Identity";
|
||||||
|
import {profiles as tiprofiles} from "../profiles/identities/TeamSpeakIdentity";
|
||||||
|
import {native_client} from "../main";
|
||||||
|
import {settings} from "../settings";
|
||||||
|
import {CommandResult} from "./ServerConnectionDeclaration";
|
||||||
|
|
||||||
|
export namespace connection {
|
||||||
|
import identities = iprofiles.identities;
|
||||||
|
|
||||||
export interface HandshakeIdentityHandler {
|
export interface HandshakeIdentityHandler {
|
||||||
connection: AbstractServerConnection;
|
connection: AbstractServerConnection;
|
||||||
|
|
||||||
|
@ -48,7 +59,7 @@ namespace connection {
|
||||||
|
|
||||||
on_teamspeak() {
|
on_teamspeak() {
|
||||||
const type = this.profile.selected_type();
|
const type = this.profile.selected_type();
|
||||||
if(type == profiles.identities.IdentitifyType.TEAMSPEAK)
|
if(type == identities.IdentitifyType.TEAMSPEAK)
|
||||||
this.handshake_finished();
|
this.handshake_finished();
|
||||||
else {
|
else {
|
||||||
|
|
||||||
|
@ -122,8 +133,8 @@ namespace connection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* required to keep compatibility */
|
/* required to keep compatibility */
|
||||||
if(this.profile.selected_type() === profiles.identities.IdentitifyType.TEAMSPEAK) {
|
if(this.profile.selected_type() === identities.IdentitifyType.TEAMSPEAK) {
|
||||||
data["client_key_offset"] = (this.profile.selected_identity() as profiles.identities.TeaSpeakIdentity).hash_number;
|
data["client_key_offset"] = (this.profile.selected_identity() as tiprofiles.identities.TeaSpeakIdentity).hash_number;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.connection.send_command("clientinit", data).catch(error => {
|
this.connection.send_command("clientinit", data).catch(error => {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
enum ErrorID {
|
import {LaterPromise} from "../utils/helpers";
|
||||||
|
|
||||||
|
export enum ErrorID {
|
||||||
NOT_IMPLEMENTED = 0x2,
|
NOT_IMPLEMENTED = 0x2,
|
||||||
COMMAND_NOT_FOUND = 0x100,
|
COMMAND_NOT_FOUND = 0x100,
|
||||||
|
|
||||||
|
@ -15,7 +17,7 @@ enum ErrorID {
|
||||||
CONVERSATION_IS_PRIVATE = 0x2202
|
CONVERSATION_IS_PRIVATE = 0x2202
|
||||||
}
|
}
|
||||||
|
|
||||||
class CommandResult {
|
export class CommandResult {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
id: number;
|
id: number;
|
||||||
message: string;
|
message: string;
|
||||||
|
@ -35,39 +37,39 @@ class CommandResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ClientNameInfo {
|
export interface ClientNameInfo {
|
||||||
//cluid=tYzKUryn\/\/Y8VBMf8PHUT6B1eiE= name=Exp clname=Exp cldbid=9
|
//cluid=tYzKUryn\/\/Y8VBMf8PHUT6B1eiE= name=Exp clname=Exp cldbid=9
|
||||||
client_unique_id: string;
|
client_unique_id: string;
|
||||||
client_nickname: string;
|
client_nickname: string;
|
||||||
client_database_id: number;
|
client_database_id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ClientNameFromUid {
|
export interface ClientNameFromUid {
|
||||||
promise: LaterPromise<ClientNameInfo[]>,
|
promise: LaterPromise<ClientNameInfo[]>,
|
||||||
keys: string[],
|
keys: string[],
|
||||||
response: ClientNameInfo[]
|
response: ClientNameInfo[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServerGroupClient {
|
export interface ServerGroupClient {
|
||||||
client_nickname: string;
|
client_nickname: string;
|
||||||
client_unique_identifier: string;
|
client_unique_identifier: string;
|
||||||
client_database_id: number;
|
client_database_id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface QueryListEntry {
|
export interface QueryListEntry {
|
||||||
username: string;
|
username: string;
|
||||||
unique_id: string;
|
unique_id: string;
|
||||||
bounded_server: number;
|
bounded_server: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface QueryList {
|
export interface QueryList {
|
||||||
flag_own: boolean;
|
flag_own: boolean;
|
||||||
flag_all: boolean;
|
flag_all: boolean;
|
||||||
|
|
||||||
queries: QueryListEntry[];
|
queries: QueryListEntry[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Playlist {
|
export interface Playlist {
|
||||||
playlist_id: number;
|
playlist_id: number;
|
||||||
playlist_bot_id: number;
|
playlist_bot_id: number;
|
||||||
playlist_title: string;
|
playlist_title: string;
|
||||||
|
@ -83,7 +85,7 @@ interface Playlist {
|
||||||
needed_power_song_remove: number;
|
needed_power_song_remove: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PlaylistInfo {
|
export interface PlaylistInfo {
|
||||||
playlist_id: number,
|
playlist_id: number,
|
||||||
playlist_title: string,
|
playlist_title: string,
|
||||||
playlist_description: string,
|
playlist_description: string,
|
||||||
|
@ -100,7 +102,7 @@ interface PlaylistInfo {
|
||||||
playlist_max_songs: number
|
playlist_max_songs: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PlaylistSong {
|
export interface PlaylistSong {
|
||||||
song_id: number;
|
song_id: number;
|
||||||
song_previous_song_id: number;
|
song_previous_song_id: number;
|
||||||
song_invoker: string;
|
song_invoker: string;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
namespace asn1 {
|
export namespace asn1 {
|
||||||
declare class Int10 {
|
declare class Int10 {
|
||||||
constructor(value?: any);
|
constructor(value?: any);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class Crc32 {
|
export class Crc32 {
|
||||||
private static readonly lookup = [
|
private static readonly lookup = [
|
||||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
|
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
|
||||||
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
|
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace hex {
|
export namespace hex {
|
||||||
export function encode(buffer) {
|
export function encode(buffer) {
|
||||||
let hexCodes = [];
|
let hexCodes = [];
|
||||||
let view = new DataView(buffer);
|
let view = new DataView(buffer);
|
||||||
|
|
|
@ -10,7 +10,7 @@ interface Window {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace sha {
|
export namespace sha {
|
||||||
/*
|
/*
|
||||||
* [js-sha1]{@link https://github.com/emn178/js-sha1}
|
* [js-sha1]{@link https://github.com/emn178/js-sha1}
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
export function guid() {
|
||||||
|
function s4() {
|
||||||
|
return Math.floor((1 + Math.random()) * 0x10000)
|
||||||
|
.toString(16)
|
||||||
|
.substring(1);
|
||||||
|
}
|
||||||
|
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
namespace dns {
|
export namespace dns {
|
||||||
export interface AddressTarget {
|
export interface AddressTarget {
|
||||||
target_ip: string;
|
target_ip: string;
|
||||||
target_port?: number;
|
target_port?: number;
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
namespace events {
|
import {guid} from "./crypto/uid";
|
||||||
|
import {PlaylistSong} from "./connection/ServerConnectionDeclaration";
|
||||||
|
import {MusicClientEntry, SongInfo} from "./channel-tree/client";
|
||||||
|
|
||||||
|
export namespace events {
|
||||||
export interface EventConvert<All> {
|
export interface EventConvert<All> {
|
||||||
as<T extends keyof All>() : All[T];
|
as<T extends keyof All>() : All[T];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
|
export namespace i18n {
|
||||||
namespace i18n {
|
|
||||||
interface CountryInfo {
|
interface CountryInfo {
|
||||||
name: string;
|
name: string;
|
||||||
alpha_2: string;
|
alpha_2: string;
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
function guid() {
|
import {guid} from "../crypto/uid";
|
||||||
function s4() {
|
import {log, LogCategory} from "../log";
|
||||||
return Math.floor((1 + Math.random()) * 0x10000)
|
import {MessageHelper} from "../ui/frames/chat";
|
||||||
.toString(16)
|
import {StaticSettings} from "../settings";
|
||||||
.substring(1);
|
import {createErrorModal} from "../ui/elements/modal";
|
||||||
}
|
|
||||||
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace i18n {
|
export namespace i18n {
|
||||||
export interface TranslationKey {
|
export interface TranslationKey {
|
||||||
message: string;
|
message: string;
|
||||||
line?: number;
|
line?: number;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
//Used by CertAccept popup
|
//Used by CertAccept popup
|
||||||
|
|
||||||
enum LogCategory {
|
import {settings} from "./settings";
|
||||||
|
|
||||||
|
export enum LogCategory {
|
||||||
CHANNEL,
|
CHANNEL,
|
||||||
CHANNEL_PROPERTIES, /* separating channel and channel properties because on channel init logging is a big bottleneck */
|
CHANNEL_PROPERTIES, /* separating channel and channel properties because on channel init logging is a big bottleneck */
|
||||||
CLIENT,
|
CLIENT,
|
||||||
|
@ -19,7 +21,7 @@ enum LogCategory {
|
||||||
DNS
|
DNS
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace log {
|
export namespace log {
|
||||||
export enum LogType {
|
export enum LogType {
|
||||||
TRACE,
|
TRACE,
|
||||||
DEBUG,
|
DEBUG,
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
/// <reference path="ui/frames/chat.ts" />
|
|
||||||
/// <reference path="ui/modal/ModalConnect.ts" />
|
|
||||||
/// <reference path="ui/modal/ModalCreateChannel.ts" />
|
|
||||||
/// <reference path="ui/modal/ModalBanClient.ts" />
|
|
||||||
/// <reference path="ui/modal/ModalYesNo.ts" />
|
|
||||||
/// <reference path="ui/modal/ModalBanList.ts" />
|
|
||||||
/// <reference path="settings.ts" />
|
|
||||||
/// <reference path="log.ts" />
|
|
||||||
/// <reference path="PPTListener.ts" />
|
|
||||||
|
|
||||||
import spawnYesNo = Modals.spawnYesNo;
|
import spawnYesNo = Modals.spawnYesNo;
|
||||||
|
import {ConnectionHandler} from "./ConnectionHandler";
|
||||||
|
import {bipc} from "./BrowserIPC";
|
||||||
|
import {log, LogCategory} from "./log";
|
||||||
|
import {profiles} from "./profiles/ConnectionProfile";
|
||||||
|
import {Modals} from "./ui/modal/ModalConnect";
|
||||||
|
import {settings, Settings} from "./settings";
|
||||||
|
import {i18n} from "./i18n/localize";
|
||||||
|
import {createInfoModal} from "./ui/elements/modal";
|
||||||
|
import {MessageHelper} from "./ui/frames/chat";
|
||||||
|
|
||||||
const js_render = window.jsrender || $;
|
export const js_render = window.jsrender || $;
|
||||||
const native_client = window.require !== undefined;
|
export const native_client = window.require !== undefined;
|
||||||
|
|
||||||
function getUserMediaFunctionPromise() : (constraints: MediaStreamConstraints) => Promise<MediaStream> {
|
export function getUserMediaFunctionPromise() : (constraints: MediaStreamConstraints) => Promise<MediaStream> {
|
||||||
if('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices)
|
if('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices)
|
||||||
return constraints => navigator.mediaDevices.getUserMedia(constraints);
|
return constraints => navigator.mediaDevices.getUserMedia(constraints);
|
||||||
|
|
||||||
|
@ -24,11 +23,12 @@ function getUserMediaFunctionPromise() : (constraints: MediaStreamConstraints) =
|
||||||
return constraints => new Promise<MediaStream>((resolve, reject) => _callbacked_function(constraints, resolve, reject));
|
return constraints => new Promise<MediaStream>((resolve, reject) => _callbacked_function(constraints, resolve, reject));
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Window {
|
export interface Window {
|
||||||
open_connected_question: () => Promise<boolean>;
|
open_connected_question: () => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup_close() {
|
export declare const nodeRequire: typeof require;
|
||||||
|
export function setup_close() {
|
||||||
window.onbeforeunload = event => {
|
window.onbeforeunload = event => {
|
||||||
if(profiles.requires_save())
|
if(profiles.requires_save())
|
||||||
profiles.save();
|
profiles.save();
|
||||||
|
@ -50,7 +50,7 @@ function setup_close() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const exit = () => {
|
const exit = () => {
|
||||||
const {remote} = require('electron');
|
const {remote} = nodeRequire('electron');
|
||||||
remote.getCurrentWindow().close();
|
remote.getCurrentWindow().close();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -80,8 +80,8 @@ function setup_close() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
declare function moment(...arguments) : any;
|
export declare function moment(...arguments) : any;
|
||||||
function setup_jsrender() : boolean {
|
export function setup_jsrender() : boolean {
|
||||||
if(!js_render) {
|
if(!js_render) {
|
||||||
loader.critical_error("Missing jsrender extension!");
|
loader.critical_error("Missing jsrender extension!");
|
||||||
return false;
|
return false;
|
||||||
|
@ -115,7 +115,7 @@ function setup_jsrender() : boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initialize() {
|
export async function initialize() {
|
||||||
Settings.initialize();
|
Settings.initialize();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -129,7 +129,7 @@ async function initialize() {
|
||||||
bipc.setup();
|
bipc.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initialize_app() {
|
export async function initialize_app() {
|
||||||
try { //Initialize main template
|
try { //Initialize main template
|
||||||
const main = $("#tmpl_main").renderTag({
|
const main = $("#tmpl_main").renderTag({
|
||||||
multi_session: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION),
|
multi_session: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION),
|
||||||
|
@ -180,7 +180,7 @@ async function initialize_app() {
|
||||||
setup_close();
|
setup_close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function str2ab8(str) {
|
export function str2ab8(str) {
|
||||||
const buf = new ArrayBuffer(str.length);
|
const buf = new ArrayBuffer(str.length);
|
||||||
const bufView = new Uint8Array(buf);
|
const bufView = new Uint8Array(buf);
|
||||||
for (let i = 0, strLen = str.length; i < strLen; i++) {
|
for (let i = 0, strLen = str.length; i < strLen; i++) {
|
||||||
|
@ -190,7 +190,7 @@ function str2ab8(str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME Dont use atob, because it sucks for non UTF-8 tings */
|
/* FIXME Dont use atob, because it sucks for non UTF-8 tings */
|
||||||
function arrayBufferBase64(base64: string) {
|
export function arrayBufferBase64(base64: string) {
|
||||||
base64 = atob(base64);
|
base64 = atob(base64);
|
||||||
const buf = new ArrayBuffer(base64.length);
|
const buf = new ArrayBuffer(base64.length);
|
||||||
const bufView = new Uint8Array(buf);
|
const bufView = new Uint8Array(buf);
|
||||||
|
@ -200,7 +200,7 @@ function arrayBufferBase64(base64: string) {
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
function base64_encode_ab(source: ArrayBufferLike) {
|
export function base64_encode_ab(source: ArrayBufferLike) {
|
||||||
const encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
const encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
let base64 = "";
|
let base64 = "";
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
/// <reference path="../connection/ConnectionBase.ts" />
|
/// <reference path="../connection/ConnectionBase.ts" />
|
||||||
|
|
||||||
enum GroupType {
|
import {LaterPromise} from "../utils/helpers";
|
||||||
|
import {PermissionManager, PermissionValue} from "./PermissionManager";
|
||||||
|
import {AbstractCommandHandler, ServerCommand} from "../connection/ConnectionBase";
|
||||||
|
import {ConnectionHandler} from "../ConnectionHandler";
|
||||||
|
import {log, LogCategory} from "../log";
|
||||||
|
import {CommandResult} from "../connection/ServerConnectionDeclaration";
|
||||||
|
|
||||||
|
export enum GroupType {
|
||||||
QUERY,
|
QUERY,
|
||||||
TEMPLATE,
|
TEMPLATE,
|
||||||
NORMAL
|
NORMAL
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GroupTarget {
|
export enum GroupTarget {
|
||||||
SERVER,
|
SERVER,
|
||||||
CHANNEL
|
CHANNEL
|
||||||
}
|
}
|
||||||
|
|
||||||
class GroupProperties {
|
export class GroupProperties {
|
||||||
iconid: number = 0;
|
iconid: number = 0;
|
||||||
|
|
||||||
sortid: number = 0;
|
sortid: number = 0;
|
||||||
|
@ -19,12 +26,12 @@ class GroupProperties {
|
||||||
namemode: number = 0;
|
namemode: number = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
class GroupPermissionRequest {
|
export class GroupPermissionRequest {
|
||||||
group_id: number;
|
group_id: number;
|
||||||
promise: LaterPromise<PermissionValue[]>;
|
promise: LaterPromise<PermissionValue[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Group {
|
export class Group {
|
||||||
properties: GroupProperties = new GroupProperties();
|
properties: GroupProperties = new GroupProperties();
|
||||||
|
|
||||||
readonly handle: GroupManager;
|
readonly handle: GroupManager;
|
||||||
|
@ -63,7 +70,7 @@ class Group {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GroupManager extends connection.AbstractCommandHandler {
|
export class GroupManager extends AbstractCommandHandler {
|
||||||
readonly handle: ConnectionHandler;
|
readonly handle: ConnectionHandler;
|
||||||
|
|
||||||
serverGroups: Group[] = [];
|
serverGroups: Group[] = [];
|
||||||
|
@ -83,7 +90,7 @@ class GroupManager extends connection.AbstractCommandHandler {
|
||||||
this.channelGroups = undefined;
|
this.channelGroups = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_command(command: connection.ServerCommand): boolean {
|
handle_command(command: ServerCommand): boolean {
|
||||||
switch (command.command) {
|
switch (command.command) {
|
||||||
case "notifyservergrouplist":
|
case "notifyservergrouplist":
|
||||||
case "notifychannelgrouplist":
|
case "notifychannelgrouplist":
|
||||||
|
|
|
@ -2,7 +2,14 @@
|
||||||
/// <reference path="../connection/ConnectionBase.ts" />
|
/// <reference path="../connection/ConnectionBase.ts" />
|
||||||
/// <reference path="../i18n/localize.ts" />
|
/// <reference path="../i18n/localize.ts" />
|
||||||
|
|
||||||
enum PermissionType {
|
import {ConnectionHandler} from "../ConnectionHandler";
|
||||||
|
import {log, LogCategory} from "../log";
|
||||||
|
import {LaterPromise} from "../utils/helpers";
|
||||||
|
import {AbstractCommandHandler, ServerCommand} from "../connection/ConnectionBase";
|
||||||
|
import LogType = log.LogType;
|
||||||
|
import {CommandResult, ErrorID} from "../connection/ServerConnectionDeclaration";
|
||||||
|
|
||||||
|
export enum PermissionType {
|
||||||
B_SERVERINSTANCE_HELP_VIEW = "b_serverinstance_help_view",
|
B_SERVERINSTANCE_HELP_VIEW = "b_serverinstance_help_view",
|
||||||
B_SERVERINSTANCE_VERSION_VIEW = "b_serverinstance_version_view",
|
B_SERVERINSTANCE_VERSION_VIEW = "b_serverinstance_version_view",
|
||||||
B_SERVERINSTANCE_INFO_VIEW = "b_serverinstance_info_view",
|
B_SERVERINSTANCE_INFO_VIEW = "b_serverinstance_info_view",
|
||||||
|
@ -352,7 +359,7 @@ enum PermissionType {
|
||||||
I_FT_QUOTA_MB_UPLOAD_PER_CLIENT = "i_ft_quota_mb_upload_per_client"
|
I_FT_QUOTA_MB_UPLOAD_PER_CLIENT = "i_ft_quota_mb_upload_per_client"
|
||||||
}
|
}
|
||||||
|
|
||||||
class PermissionInfo {
|
export class PermissionInfo {
|
||||||
name: string;
|
name: string;
|
||||||
id: number;
|
id: number;
|
||||||
description: string;
|
description: string;
|
||||||
|
@ -363,21 +370,21 @@ class PermissionInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PermissionGroup {
|
export class PermissionGroup {
|
||||||
begin: number;
|
begin: number;
|
||||||
end: number;
|
end: number;
|
||||||
deep: number;
|
deep: number;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class GroupedPermissions {
|
export class GroupedPermissions {
|
||||||
group: PermissionGroup;
|
group: PermissionGroup;
|
||||||
permissions: PermissionInfo[];
|
permissions: PermissionInfo[];
|
||||||
children: GroupedPermissions[];
|
children: GroupedPermissions[];
|
||||||
parent: GroupedPermissions;
|
parent: GroupedPermissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PermissionValue {
|
export class PermissionValue {
|
||||||
readonly type: PermissionInfo;
|
readonly type: PermissionInfo;
|
||||||
value: number;
|
value: number;
|
||||||
flag_skip: boolean;
|
flag_skip: boolean;
|
||||||
|
@ -411,13 +418,13 @@ class PermissionValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NeededPermissionValue extends PermissionValue {
|
export class NeededPermissionValue extends PermissionValue {
|
||||||
constructor(type, value) {
|
constructor(type, value) {
|
||||||
super(type, value);
|
super(type, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace permissions {
|
export namespace permissions {
|
||||||
export type PermissionRequestKeys = {
|
export type PermissionRequestKeys = {
|
||||||
client_id?: number;
|
client_id?: number;
|
||||||
channel_id?: number;
|
channel_id?: number;
|
||||||
|
@ -473,13 +480,13 @@ namespace permissions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type RequestLists =
|
export type RequestLists =
|
||||||
"requests_channel_permissions" |
|
"requests_channel_permissions" |
|
||||||
"requests_client_permissions" |
|
"requests_client_permissions" |
|
||||||
"requests_client_channel_permissions" |
|
"requests_client_channel_permissions" |
|
||||||
"requests_playlist_permissions" |
|
"requests_playlist_permissions" |
|
||||||
"requests_playlist_client_permissions";
|
"requests_playlist_client_permissions";
|
||||||
class PermissionManager extends connection.AbstractCommandHandler {
|
export class PermissionManager extends AbstractCommandHandler {
|
||||||
readonly handle: ConnectionHandler;
|
readonly handle: ConnectionHandler;
|
||||||
|
|
||||||
permissionList: PermissionInfo[] = [];
|
permissionList: PermissionInfo[] = [];
|
||||||
|
@ -603,7 +610,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
||||||
this._cacheNeededPermissions = undefined;
|
this._cacheNeededPermissions = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_command(command: connection.ServerCommand): boolean {
|
handle_command(command: ServerCommand): boolean {
|
||||||
switch (command.command) {
|
switch (command.command) {
|
||||||
case "notifyclientneededpermissions":
|
case "notifyclientneededpermissions":
|
||||||
this.onNeededPermissions(command.arguments);
|
this.onNeededPermissions(command.arguments);
|
||||||
|
|
|
@ -1,251 +1,260 @@
|
||||||
namespace profiles {
|
import {MessageHelper} from "../ui/frames/chat";
|
||||||
export class ConnectionProfile {
|
import {createErrorModal} from "../ui/elements/modal";
|
||||||
id: string;
|
import {guid} from "../crypto/uid";
|
||||||
|
import {decode_identity, IdentitifyType, Identity} from "./Identity";
|
||||||
|
import {static_forum_identity} from "./identities/TeaForumIdentity";
|
||||||
|
import {TeaSpeakIdentity} from "./identities/TeamSpeakIdentity";
|
||||||
|
import {AbstractServerConnection} from "../connection/ConnectionBase";
|
||||||
|
import {connection} from "../connection/HandshakeHandler";
|
||||||
|
|
||||||
profile_name: string;
|
import HandshakeIdentityHandler = connection.HandshakeIdentityHandler;
|
||||||
default_username: string;
|
|
||||||
default_password: string;
|
|
||||||
|
|
||||||
selected_identity_type: string = "unset";
|
export class ConnectionProfile {
|
||||||
identities: { [key: string]: identities.Identity } = {};
|
id: string;
|
||||||
|
|
||||||
constructor(id: string) {
|
profile_name: string;
|
||||||
this.id = id;
|
default_username: string;
|
||||||
}
|
default_password: string;
|
||||||
|
|
||||||
connect_username(): string {
|
selected_identity_type: string = "unset";
|
||||||
if (this.default_username && this.default_username !== "Another TeaSpeak user")
|
identities: { [key: string]: Identity } = {};
|
||||||
return this.default_username;
|
|
||||||
|
|
||||||
let selected = this.selected_identity();
|
constructor(id: string) {
|
||||||
let name = selected ? selected.fallback_name() : undefined;
|
this.id = id;
|
||||||
return name || "Another TeaSpeak user";
|
}
|
||||||
}
|
|
||||||
|
|
||||||
selected_identity(current_type?: identities.IdentitifyType): identities.Identity {
|
connect_username(): string {
|
||||||
if (!current_type)
|
if (this.default_username && this.default_username !== "Another TeaSpeak user")
|
||||||
current_type = this.selected_type();
|
return this.default_username;
|
||||||
|
|
||||||
if (current_type === undefined)
|
let selected = this.selected_identity();
|
||||||
return undefined;
|
let name = selected ? selected.fallback_name() : undefined;
|
||||||
|
return name || "Another TeaSpeak user";
|
||||||
|
}
|
||||||
|
|
||||||
if (current_type == identities.IdentitifyType.TEAFORO) {
|
selected_identity(current_type?: IdentitifyType): Identity {
|
||||||
return identities.static_forum_identity();
|
if (!current_type)
|
||||||
} else if (current_type == identities.IdentitifyType.TEAMSPEAK || current_type == identities.IdentitifyType.NICKNAME) {
|
current_type = this.selected_type();
|
||||||
return this.identities[identities.IdentitifyType[current_type].toLowerCase()];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (current_type === undefined)
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
||||||
|
if (current_type == IdentitifyType.TEAFORO) {
|
||||||
|
return static_forum_identity();
|
||||||
|
} else if (current_type == IdentitifyType.TEAMSPEAK || current_type == IdentitifyType.NICKNAME) {
|
||||||
|
return this.identities[IdentitifyType[current_type].toLowerCase()];
|
||||||
}
|
}
|
||||||
|
|
||||||
selected_type?(): identities.IdentitifyType {
|
return undefined;
|
||||||
return this.selected_identity_type ? identities.IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
set_identity(type: identities.IdentitifyType, identity: identities.Identity) {
|
|
||||||
this.identities[identities.IdentitifyType[type].toLowerCase()] = identity;
|
|
||||||
}
|
|
||||||
|
|
||||||
spawn_identity_handshake_handler?(connection: connection.AbstractServerConnection): connection.HandshakeIdentityHandler {
|
|
||||||
const identity = this.selected_identity();
|
|
||||||
if (!identity)
|
|
||||||
return undefined;
|
|
||||||
return identity.spawn_identity_handshake_handler(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
encode?(): string {
|
|
||||||
const identity_data = {};
|
|
||||||
for (const key in this.identities)
|
|
||||||
if (this.identities[key])
|
|
||||||
identity_data[key] = this.identities[key].encode();
|
|
||||||
|
|
||||||
return JSON.stringify({
|
|
||||||
version: 1,
|
|
||||||
username: this.default_username,
|
|
||||||
password: this.default_password,
|
|
||||||
profile_name: this.profile_name,
|
|
||||||
identity_type: this.selected_identity_type,
|
|
||||||
identity_data: identity_data,
|
|
||||||
id: this.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
valid(): boolean {
|
|
||||||
const identity = this.selected_identity();
|
|
||||||
if (!identity || !identity.valid()) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function decode_profile(data): Promise<ConnectionProfile | string> {
|
selected_type?(): IdentitifyType {
|
||||||
data = JSON.parse(data);
|
return this.selected_identity_type ? IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined;
|
||||||
if (data.version !== 1)
|
|
||||||
return "invalid version";
|
|
||||||
|
|
||||||
const result: ConnectionProfile = new ConnectionProfile(data.id);
|
|
||||||
result.default_username = data.username;
|
|
||||||
result.default_password = data.password;
|
|
||||||
result.profile_name = data.profile_name;
|
|
||||||
result.selected_identity_type = (data.identity_type || "").toLowerCase();
|
|
||||||
|
|
||||||
if (data.identity_data) {
|
|
||||||
for (const key in data.identity_data) {
|
|
||||||
const type = identities.IdentitifyType[key.toUpperCase() as string];
|
|
||||||
const _data = data.identity_data[key];
|
|
||||||
if (type == undefined) continue;
|
|
||||||
|
|
||||||
const identity = await identities.decode_identity(type, _data);
|
|
||||||
if (identity == undefined) continue;
|
|
||||||
|
|
||||||
result.identities[key.toLowerCase()] = identity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProfilesData {
|
set_identity(type: IdentitifyType, identity: Identity) {
|
||||||
version: number;
|
this.identities[IdentitifyType[type].toLowerCase()] = identity;
|
||||||
profiles: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let available_profiles: ConnectionProfile[] = [];
|
spawn_identity_handshake_handler?(connection: AbstractServerConnection): HandshakeIdentityHandler {
|
||||||
|
const identity = this.selected_identity();
|
||||||
export async function load() {
|
if (!identity)
|
||||||
available_profiles = [];
|
return undefined;
|
||||||
|
return identity.spawn_identity_handshake_handler(connection);
|
||||||
const profiles_json = localStorage.getItem("profiles");
|
|
||||||
let profiles_data: ProfilesData = (() => {
|
|
||||||
try {
|
|
||||||
return profiles_json ? JSON.parse(profiles_json) : {version: 0} as any;
|
|
||||||
} catch (error) {
|
|
||||||
debugger;
|
|
||||||
console.error(tr("Invalid profile json! Resetting profiles :( (%o)"), profiles_json);
|
|
||||||
createErrorModal(tr("Profile data invalid"), MessageHelper.formatMessage(tr("The profile data is invalid.{:br:}This might cause data loss."))).open();
|
|
||||||
return {version: 0};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (profiles_data.version === 0) {
|
|
||||||
profiles_data = {
|
|
||||||
version: 1,
|
|
||||||
profiles: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (profiles_data.version == 1) {
|
|
||||||
for (const profile_data of profiles_data.profiles) {
|
|
||||||
const profile = await decode_profile(profile_data);
|
|
||||||
if (typeof (profile) === 'string') {
|
|
||||||
console.error(tr("Failed to load profile. Reason: %s, Profile data: %s"), profile, profiles_data);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
available_profiles.push(profile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!find_profile("default")) { //Create a default profile and teaforo profile
|
|
||||||
{
|
|
||||||
const profile = create_new_profile("default", "default");
|
|
||||||
profile.default_password = "";
|
|
||||||
profile.default_username = "";
|
|
||||||
profile.profile_name = "Default Profile";
|
|
||||||
|
|
||||||
/* generate default identity */
|
|
||||||
try {
|
|
||||||
const identity = await identities.TeaSpeakIdentity.generate_new();
|
|
||||||
let active = true;
|
|
||||||
setTimeout(() => {
|
|
||||||
active = false;
|
|
||||||
}, 1000);
|
|
||||||
await identity.improve_level(8, 1, () => active);
|
|
||||||
profile.set_identity(identities.IdentitifyType.TEAMSPEAK, identity);
|
|
||||||
profile.selected_identity_type = identities.IdentitifyType[identities.IdentitifyType.TEAMSPEAK];
|
|
||||||
} catch (error) {
|
|
||||||
createErrorModal(tr("Failed to generate default identity"), tr("Failed to generate default identity!<br>Please manually generate the identity within your settings => profiles")).open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{ /* forum identity (works only when connected to the forum) */
|
|
||||||
const profile = create_new_profile("TeaSpeak Forum", "teaforo");
|
|
||||||
profile.default_password = "";
|
|
||||||
profile.default_username = "";
|
|
||||||
profile.profile_name = "TeaSpeak Forum profile";
|
|
||||||
|
|
||||||
profile.set_identity(identities.IdentitifyType.TEAFORO, identities.static_forum_identity());
|
|
||||||
profile.selected_identity_type = identities.IdentitifyType[identities.IdentitifyType.TEAFORO];
|
|
||||||
}
|
|
||||||
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function create_new_profile(name: string, id?: string): ConnectionProfile {
|
encode?(): string {
|
||||||
const profile = new ConnectionProfile(id || guid());
|
const identity_data = {};
|
||||||
profile.profile_name = name;
|
for (const key in this.identities)
|
||||||
profile.default_username = "";
|
if (this.identities[key])
|
||||||
available_profiles.push(profile);
|
identity_data[key] = this.identities[key].encode();
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
let _requires_save = false;
|
return JSON.stringify({
|
||||||
|
|
||||||
export function save() {
|
|
||||||
const profiles: string[] = [];
|
|
||||||
for (const profile of available_profiles)
|
|
||||||
profiles.push(profile.encode());
|
|
||||||
|
|
||||||
const data = JSON.stringify({
|
|
||||||
version: 1,
|
version: 1,
|
||||||
profiles: profiles
|
username: this.default_username,
|
||||||
|
password: this.default_password,
|
||||||
|
profile_name: this.profile_name,
|
||||||
|
identity_type: this.selected_identity_type,
|
||||||
|
identity_data: identity_data,
|
||||||
|
id: this.id
|
||||||
});
|
});
|
||||||
localStorage.setItem("profiles", data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mark_need_save() {
|
valid(): boolean {
|
||||||
_requires_save = true;
|
const identity = this.selected_identity();
|
||||||
}
|
if (!identity || !identity.valid()) return false;
|
||||||
|
|
||||||
export function requires_save(): boolean {
|
return true;
|
||||||
return _requires_save;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function profiles(): ConnectionProfile[] {
|
|
||||||
return available_profiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function find_profile(id: string): ConnectionProfile | undefined {
|
|
||||||
for (const profile of profiles())
|
|
||||||
if (profile.id == id)
|
|
||||||
return profile;
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function find_profile_by_name(name: string): ConnectionProfile | undefined {
|
|
||||||
name = name.toLowerCase();
|
|
||||||
for (const profile of profiles())
|
|
||||||
if ((profile.profile_name || "").toLowerCase() == name)
|
|
||||||
return profile;
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function default_profile(): ConnectionProfile {
|
|
||||||
return find_profile("default");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function set_default_profile(profile: ConnectionProfile) {
|
|
||||||
const old_default = default_profile();
|
|
||||||
if (old_default && old_default != profile) {
|
|
||||||
old_default.id = guid();
|
|
||||||
}
|
|
||||||
profile.id = "default";
|
|
||||||
return old_default;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function delete_profile(profile: ConnectionProfile) {
|
|
||||||
available_profiles.remove(profile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function decode_profile(data): Promise<ConnectionProfile | string> {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
if (data.version !== 1)
|
||||||
|
return "invalid version";
|
||||||
|
|
||||||
|
const result: ConnectionProfile = new ConnectionProfile(data.id);
|
||||||
|
result.default_username = data.username;
|
||||||
|
result.default_password = data.password;
|
||||||
|
result.profile_name = data.profile_name;
|
||||||
|
result.selected_identity_type = (data.identity_type || "").toLowerCase();
|
||||||
|
|
||||||
|
if (data.identity_data) {
|
||||||
|
for (const key in data.identity_data) {
|
||||||
|
const type = IdentitifyType[key.toUpperCase() as string];
|
||||||
|
const _data = data.identity_data[key];
|
||||||
|
if (type == undefined) continue;
|
||||||
|
|
||||||
|
const identity = await decode_identity(type, _data);
|
||||||
|
if (identity == undefined) continue;
|
||||||
|
|
||||||
|
result.identities[key.toLowerCase()] = identity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProfilesData {
|
||||||
|
version: number;
|
||||||
|
profiles: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
let available_profiles: ConnectionProfile[] = [];
|
||||||
|
|
||||||
|
export async function load() {
|
||||||
|
available_profiles = [];
|
||||||
|
|
||||||
|
const profiles_json = localStorage.getItem("profiles");
|
||||||
|
let profiles_data: ProfilesData = (() => {
|
||||||
|
try {
|
||||||
|
return profiles_json ? JSON.parse(profiles_json) : {version: 0} as any;
|
||||||
|
} catch (error) {
|
||||||
|
debugger;
|
||||||
|
console.error(tr("Invalid profile json! Resetting profiles :( (%o)"), profiles_json);
|
||||||
|
createErrorModal(tr("Profile data invalid"), MessageHelper.formatMessage(tr("The profile data is invalid.{:br:}This might cause data loss."))).open();
|
||||||
|
return {version: 0};
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (profiles_data.version === 0) {
|
||||||
|
profiles_data = {
|
||||||
|
version: 1,
|
||||||
|
profiles: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (profiles_data.version == 1) {
|
||||||
|
for (const profile_data of profiles_data.profiles) {
|
||||||
|
const profile = await decode_profile(profile_data);
|
||||||
|
if (typeof (profile) === 'string') {
|
||||||
|
console.error(tr("Failed to load profile. Reason: %s, Profile data: %s"), profile, profiles_data);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
available_profiles.push(profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!find_profile("default")) { //Create a default profile and teaforo profile
|
||||||
|
{
|
||||||
|
const profile = create_new_profile("default", "default");
|
||||||
|
profile.default_password = "";
|
||||||
|
profile.default_username = "";
|
||||||
|
profile.profile_name = "Default Profile";
|
||||||
|
|
||||||
|
/* generate default identity */
|
||||||
|
try {
|
||||||
|
const identity = await TeaSpeakIdentity.generate_new();
|
||||||
|
let active = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
active = false;
|
||||||
|
}, 1000);
|
||||||
|
await identity.improve_level(8, 1, () => active);
|
||||||
|
profile.set_identity(IdentitifyType.TEAMSPEAK, identity);
|
||||||
|
profile.selected_identity_type = IdentitifyType[IdentitifyType.TEAMSPEAK];
|
||||||
|
} catch (error) {
|
||||||
|
createErrorModal(tr("Failed to generate default identity"), tr("Failed to generate default identity!<br>Please manually generate the identity within your settings => profiles")).open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* forum identity (works only when connected to the forum) */
|
||||||
|
const profile = create_new_profile("TeaSpeak Forum", "teaforo");
|
||||||
|
profile.default_password = "";
|
||||||
|
profile.default_username = "";
|
||||||
|
profile.profile_name = "TeaSpeak Forum profile";
|
||||||
|
|
||||||
|
profile.set_identity(IdentitifyType.TEAFORO, static_forum_identity());
|
||||||
|
profile.selected_identity_type = IdentitifyType[IdentitifyType.TEAFORO];
|
||||||
|
}
|
||||||
|
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function create_new_profile(name: string, id?: string): ConnectionProfile {
|
||||||
|
const profile = new ConnectionProfile(id || guid());
|
||||||
|
profile.profile_name = name;
|
||||||
|
profile.default_username = "";
|
||||||
|
available_profiles.push(profile);
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _requires_save = false;
|
||||||
|
|
||||||
|
export function save() {
|
||||||
|
const profiles: string[] = [];
|
||||||
|
for (const profile of available_profiles)
|
||||||
|
profiles.push(profile.encode());
|
||||||
|
|
||||||
|
const data = JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
profiles: profiles
|
||||||
|
});
|
||||||
|
localStorage.setItem("profiles", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mark_need_save() {
|
||||||
|
_requires_save = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function requires_save(): boolean {
|
||||||
|
return _requires_save;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function profiles(): ConnectionProfile[] {
|
||||||
|
return available_profiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function find_profile(id: string): ConnectionProfile | undefined {
|
||||||
|
for (const profile of profiles())
|
||||||
|
if (profile.id == id)
|
||||||
|
return profile;
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function find_profile_by_name(name: string): ConnectionProfile | undefined {
|
||||||
|
name = name.toLowerCase();
|
||||||
|
for (const profile of profiles())
|
||||||
|
if ((profile.profile_name || "").toLowerCase() == name)
|
||||||
|
return profile;
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function default_profile(): ConnectionProfile {
|
||||||
|
return find_profile("default");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function set_default_profile(profile: ConnectionProfile) {
|
||||||
|
const old_default = default_profile();
|
||||||
|
if (old_default && old_default != profile) {
|
||||||
|
old_default.id = guid();
|
||||||
|
}
|
||||||
|
profile.id = "default";
|
||||||
|
return old_default;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function delete_profile(profile: ConnectionProfile) {
|
||||||
|
available_profiles.remove(profile);
|
||||||
|
}
|
|
@ -1,110 +1,116 @@
|
||||||
namespace profiles.identities {
|
import {AbstractCommandHandler, AbstractServerConnection, ServerCommand} from "../connection/ConnectionBase";
|
||||||
export enum IdentitifyType {
|
import {connection} from "../connection/HandshakeHandler";
|
||||||
TEAFORO,
|
|
||||||
TEAMSPEAK,
|
import HandshakeIdentityHandler = connection.HandshakeIdentityHandler;
|
||||||
NICKNAME
|
import {NameIdentity} from "./identities/NameIdentity";
|
||||||
|
import {TeaForumIdentity} from "./identities/TeaForumIdentity";
|
||||||
|
import {TeaSpeakIdentity} from "./identities/TeamSpeakIdentity";
|
||||||
|
|
||||||
|
export enum IdentitifyType {
|
||||||
|
TEAFORO,
|
||||||
|
TEAMSPEAK,
|
||||||
|
NICKNAME
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Identity {
|
||||||
|
fallback_name(): string | undefined ;
|
||||||
|
uid() : string;
|
||||||
|
type() : IdentitifyType;
|
||||||
|
|
||||||
|
valid() : boolean;
|
||||||
|
|
||||||
|
encode?() : string;
|
||||||
|
decode(data: string) : Promise<void>;
|
||||||
|
|
||||||
|
spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function decode_identity(type: IdentitifyType, data: string) : Promise<Identity> {
|
||||||
|
let identity: Identity;
|
||||||
|
switch (type) {
|
||||||
|
case IdentitifyType.NICKNAME:
|
||||||
|
identity = new NameIdentity();
|
||||||
|
break;
|
||||||
|
case IdentitifyType.TEAFORO:
|
||||||
|
identity = new TeaForumIdentity(undefined);
|
||||||
|
break;
|
||||||
|
case IdentitifyType.TEAMSPEAK:
|
||||||
|
identity = new TeaSpeakIdentity(undefined, undefined);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!identity)
|
||||||
|
return undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await identity.decode(data)
|
||||||
|
} catch(error) {
|
||||||
|
/* todo better error handling! */
|
||||||
|
console.error(error);
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Identity {
|
return identity;
|
||||||
fallback_name(): string | undefined ;
|
}
|
||||||
uid() : string;
|
|
||||||
type() : IdentitifyType;
|
|
||||||
|
|
||||||
valid() : boolean;
|
export function create_identity(type: IdentitifyType) {
|
||||||
|
let identity: Identity;
|
||||||
|
switch (type) {
|
||||||
|
case IdentitifyType.NICKNAME:
|
||||||
|
identity = new NameIdentity();
|
||||||
|
break;
|
||||||
|
case IdentitifyType.TEAFORO:
|
||||||
|
identity = new TeaForumIdentity(undefined);
|
||||||
|
break;
|
||||||
|
case IdentitifyType.TEAMSPEAK:
|
||||||
|
identity = new TeaSpeakIdentity(undefined, undefined);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
|
||||||
encode?() : string;
|
export class HandshakeCommandHandler<T extends AbstractHandshakeIdentityHandler> extends AbstractCommandHandler {
|
||||||
decode(data: string) : Promise<void>;
|
readonly handle: T;
|
||||||
|
|
||||||
spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler;
|
constructor(connection: AbstractServerConnection, handle: T) {
|
||||||
|
super(connection);
|
||||||
|
this.handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function decode_identity(type: IdentitifyType, data: string) : Promise<Identity> {
|
|
||||||
let identity: Identity;
|
handle_command(command: ServerCommand): boolean {
|
||||||
switch (type) {
|
if($.isFunction(this[command.command]))
|
||||||
case IdentitifyType.NICKNAME:
|
this[command.command](command.arguments);
|
||||||
identity = new NameIdentity();
|
else if(command.command == "error") {
|
||||||
break;
|
return false;
|
||||||
case IdentitifyType.TEAFORO:
|
} else {
|
||||||
identity = new TeaForumIdentity(undefined);
|
console.warn(tr("Received unknown command while handshaking (%o)"), command);
|
||||||
break;
|
|
||||||
case IdentitifyType.TEAMSPEAK:
|
|
||||||
identity = new TeaSpeakIdentity(undefined, undefined);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(!identity)
|
|
||||||
return undefined;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await identity.decode(data)
|
|
||||||
} catch(error) {
|
|
||||||
/* todo better error handling! */
|
|
||||||
console.error(error);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return identity;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function create_identity(type: IdentitifyType) {
|
|
||||||
let identity: Identity;
|
|
||||||
switch (type) {
|
|
||||||
case IdentitifyType.NICKNAME:
|
|
||||||
identity = new NameIdentity();
|
|
||||||
break;
|
|
||||||
case IdentitifyType.TEAFORO:
|
|
||||||
identity = new TeaForumIdentity(undefined);
|
|
||||||
break;
|
|
||||||
case IdentitifyType.TEAMSPEAK:
|
|
||||||
identity = new TeaSpeakIdentity(undefined, undefined);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return identity;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HandshakeCommandHandler<T extends AbstractHandshakeIdentityHandler> extends connection.AbstractCommandHandler {
|
|
||||||
readonly handle: T;
|
|
||||||
|
|
||||||
constructor(connection: connection.AbstractServerConnection, handle: T) {
|
|
||||||
super(connection);
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
handle_command(command: connection.ServerCommand): boolean {
|
|
||||||
if($.isFunction(this[command.command]))
|
|
||||||
this[command.command](command.arguments);
|
|
||||||
else if(command.command == "error") {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
console.warn(tr("Received unknown command while handshaking (%o)"), command);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class AbstractHandshakeIdentityHandler implements connection.HandshakeIdentityHandler {
|
|
||||||
connection: connection.AbstractServerConnection;
|
|
||||||
|
|
||||||
protected callbacks: ((success: boolean, message?: string) => any)[] = [];
|
|
||||||
|
|
||||||
protected constructor(connection: connection.AbstractServerConnection) {
|
|
||||||
this.connection = connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
register_callback(callback: (success: boolean, message?: string) => any) {
|
|
||||||
this.callbacks.push(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract start_handshake();
|
|
||||||
|
|
||||||
protected trigger_success() {
|
|
||||||
for(const callback of this.callbacks)
|
|
||||||
callback(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected trigger_fail(message: string) {
|
|
||||||
for(const callback of this.callbacks)
|
|
||||||
callback(false, message);
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class AbstractHandshakeIdentityHandler implements connection.HandshakeIdentityHandler {
|
||||||
|
connection: AbstractServerConnection;
|
||||||
|
|
||||||
|
protected callbacks: ((success: boolean, message?: string) => any)[] = [];
|
||||||
|
|
||||||
|
protected constructor(connection: AbstractServerConnection) {
|
||||||
|
this.connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
register_callback(callback: (success: boolean, message?: string) => any) {
|
||||||
|
this.callbacks.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract start_handshake();
|
||||||
|
|
||||||
|
protected trigger_success() {
|
||||||
|
for(const callback of this.callbacks)
|
||||||
|
callback(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected trigger_fail(message: string) {
|
||||||
|
for(const callback of this.callbacks)
|
||||||
|
callback(false, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,88 +1,92 @@
|
||||||
/// <reference path="../Identity.ts" />
|
import {CommandResult} from "../../connection/ServerConnectionDeclaration";
|
||||||
|
import {log, LogCategory} from "../../log";
|
||||||
|
import {AbstractServerConnection} from "../../connection/ConnectionBase";
|
||||||
|
import {connection} from "../../connection/HandshakeHandler";
|
||||||
|
|
||||||
namespace profiles.identities {
|
import HandshakeIdentityHandler = connection.HandshakeIdentityHandler;
|
||||||
class NameHandshakeHandler extends AbstractHandshakeIdentityHandler {
|
import {AbstractHandshakeIdentityHandler, HandshakeCommandHandler, IdentitifyType, Identity} from "../Identity";
|
||||||
readonly identity: NameIdentity;
|
|
||||||
handler: HandshakeCommandHandler<NameHandshakeHandler>;
|
|
||||||
|
|
||||||
constructor(connection: connection.AbstractServerConnection, identity: profiles.identities.NameIdentity) {
|
class NameHandshakeHandler extends AbstractHandshakeIdentityHandler {
|
||||||
super(connection);
|
readonly identity: NameIdentity;
|
||||||
this.identity = identity;
|
handler: HandshakeCommandHandler<NameHandshakeHandler>;
|
||||||
|
|
||||||
this.handler = new HandshakeCommandHandler(connection, this);
|
constructor(connection: AbstractServerConnection, identity: NameIdentity) {
|
||||||
this.handler["handshakeidentityproof"] = () => this.trigger_fail("server requested unexpected proof");
|
super(connection);
|
||||||
}
|
this.identity = identity;
|
||||||
|
|
||||||
start_handshake() {
|
this.handler = new HandshakeCommandHandler(connection, this);
|
||||||
this.connection.command_handler_boss().register_handler(this.handler);
|
this.handler["handshakeidentityproof"] = () => this.trigger_fail("server requested unexpected proof");
|
||||||
this.connection.send_command("handshakebegin", {
|
|
||||||
intention: 0,
|
|
||||||
authentication_method: this.identity.type(),
|
|
||||||
client_nickname: this.identity.name()
|
|
||||||
}).catch(error => {
|
|
||||||
log.error(LogCategory.IDENTITIES, tr("Failed to initialize name based handshake. Error: %o"), error);
|
|
||||||
if(error instanceof CommandResult)
|
|
||||||
error = error.extra_message || error.message;
|
|
||||||
this.trigger_fail("failed to execute begin (" + error + ")");
|
|
||||||
}).then(() => this.trigger_success());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected trigger_fail(message: string) {
|
|
||||||
this.connection.command_handler_boss().unregister_handler(this.handler);
|
|
||||||
super.trigger_fail(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected trigger_success() {
|
|
||||||
this.connection.command_handler_boss().unregister_handler(this.handler);
|
|
||||||
super.trigger_success();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NameIdentity implements Identity {
|
start_handshake() {
|
||||||
private _name: string;
|
this.connection.command_handler_boss().register_handler(this.handler);
|
||||||
|
this.connection.send_command("handshakebegin", {
|
||||||
|
intention: 0,
|
||||||
|
authentication_method: this.identity.type(),
|
||||||
|
client_nickname: this.identity.name()
|
||||||
|
}).catch(error => {
|
||||||
|
log.error(LogCategory.IDENTITIES, tr("Failed to initialize name based handshake. Error: %o"), error);
|
||||||
|
if(error instanceof CommandResult)
|
||||||
|
error = error.extra_message || error.message;
|
||||||
|
this.trigger_fail("failed to execute begin (" + error + ")");
|
||||||
|
}).then(() => this.trigger_success());
|
||||||
|
}
|
||||||
|
|
||||||
constructor(name?: string) {
|
protected trigger_fail(message: string) {
|
||||||
this._name = name;
|
this.connection.command_handler_boss().unregister_handler(this.handler);
|
||||||
}
|
super.trigger_fail(message);
|
||||||
|
}
|
||||||
|
|
||||||
set_name(name: string) { this._name = name; }
|
protected trigger_success() {
|
||||||
|
this.connection.command_handler_boss().unregister_handler(this.handler);
|
||||||
name() : string { return this._name; }
|
super.trigger_success();
|
||||||
|
}
|
||||||
fallback_name(): string | undefined {
|
}
|
||||||
return this._name;
|
|
||||||
}
|
export class NameIdentity implements Identity {
|
||||||
|
private _name: string;
|
||||||
uid(): string {
|
|
||||||
return btoa(this._name); //FIXME hash!
|
constructor(name?: string) {
|
||||||
}
|
this._name = name;
|
||||||
|
}
|
||||||
type(): IdentitifyType {
|
|
||||||
return IdentitifyType.NICKNAME;
|
set_name(name: string) { this._name = name; }
|
||||||
}
|
|
||||||
|
name() : string { return this._name; }
|
||||||
valid(): boolean {
|
|
||||||
return this._name != undefined && this._name.length >= 5;
|
fallback_name(): string | undefined {
|
||||||
}
|
return this._name;
|
||||||
|
}
|
||||||
decode(data) : Promise<void> {
|
|
||||||
data = JSON.parse(data);
|
uid(): string {
|
||||||
if(data.version !== 1)
|
return btoa(this._name); //FIXME hash!
|
||||||
throw "invalid version";
|
}
|
||||||
|
|
||||||
this._name = data["name"];
|
type(): IdentitifyType {
|
||||||
return;
|
return IdentitifyType.NICKNAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
encode?() : string {
|
valid(): boolean {
|
||||||
return JSON.stringify({
|
return this._name != undefined && this._name.length >= 5;
|
||||||
version: 1,
|
}
|
||||||
name: this._name
|
|
||||||
});
|
decode(data) : Promise<void> {
|
||||||
}
|
data = JSON.parse(data);
|
||||||
|
if(data.version !== 1)
|
||||||
spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler {
|
throw "invalid version";
|
||||||
return new NameHandshakeHandler(connection, this);
|
|
||||||
}
|
this._name = data["name"];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
encode?() : string {
|
||||||
|
return JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
name: this._name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler {
|
||||||
|
return new NameHandshakeHandler(connection, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,122 +1,126 @@
|
||||||
/// <reference path="../Identity.ts" />
|
import {AbstractServerConnection} from "../../connection/ConnectionBase";
|
||||||
|
import {log, LogCategory} from "../../log";
|
||||||
|
import {CommandResult} from "../../connection/ServerConnectionDeclaration";
|
||||||
|
import {forum} from "./teaspeak-forum";
|
||||||
|
import {connection} from "../../connection/HandshakeHandler";
|
||||||
|
import HandshakeIdentityHandler = connection.HandshakeIdentityHandler;
|
||||||
|
import {AbstractHandshakeIdentityHandler, HandshakeCommandHandler, IdentitifyType, Identity} from "../Identity";
|
||||||
|
|
||||||
namespace profiles.identities {
|
class TeaForumHandshakeHandler extends AbstractHandshakeIdentityHandler {
|
||||||
class TeaForumHandshakeHandler extends AbstractHandshakeIdentityHandler {
|
readonly identity: TeaForumIdentity;
|
||||||
readonly identity: TeaForumIdentity;
|
handler: HandshakeCommandHandler<TeaForumHandshakeHandler>;
|
||||||
handler: HandshakeCommandHandler<TeaForumHandshakeHandler>;
|
|
||||||
|
|
||||||
constructor(connection: connection.AbstractServerConnection, identity: profiles.identities.TeaForumIdentity) {
|
constructor(connection: AbstractServerConnection, identity: TeaForumIdentity) {
|
||||||
super(connection);
|
super(connection);
|
||||||
this.identity = identity;
|
this.identity = identity;
|
||||||
this.handler = new HandshakeCommandHandler(connection, this);
|
this.handler = new HandshakeCommandHandler(connection, this);
|
||||||
this.handler["handshakeidentityproof"] = this.handle_proof.bind(this);
|
this.handler["handshakeidentityproof"] = this.handle_proof.bind(this);
|
||||||
}
|
|
||||||
|
|
||||||
start_handshake() {
|
|
||||||
this.connection.command_handler_boss().register_handler(this.handler);
|
|
||||||
this.connection.send_command("handshakebegin", {
|
|
||||||
intention: 0,
|
|
||||||
authentication_method: this.identity.type(),
|
|
||||||
data: this.identity.data().data_json()
|
|
||||||
}).catch(error => {
|
|
||||||
log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeaForum based handshake. Error: %o"), error);
|
|
||||||
|
|
||||||
if(error instanceof CommandResult)
|
|
||||||
error = error.extra_message || error.message;
|
|
||||||
this.trigger_fail("failed to execute begin (" + error + ")");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private handle_proof(json) {
|
|
||||||
this.connection.send_command("handshakeindentityproof", {
|
|
||||||
proof: this.identity.data().data_sign()
|
|
||||||
}).catch(error => {
|
|
||||||
log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error);
|
|
||||||
|
|
||||||
if(error instanceof CommandResult)
|
|
||||||
error = error.extra_message || error.message;
|
|
||||||
this.trigger_fail("failed to execute proof (" + error + ")");
|
|
||||||
}).then(() => this.trigger_success());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected trigger_fail(message: string) {
|
|
||||||
this.connection.command_handler_boss().unregister_handler(this.handler);
|
|
||||||
super.trigger_fail(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected trigger_success() {
|
|
||||||
this.connection.command_handler_boss().unregister_handler(this.handler);
|
|
||||||
super.trigger_success();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TeaForumIdentity implements Identity {
|
start_handshake() {
|
||||||
private readonly identity_data: forum.Data;
|
this.connection.command_handler_boss().register_handler(this.handler);
|
||||||
|
this.connection.send_command("handshakebegin", {
|
||||||
|
intention: 0,
|
||||||
|
authentication_method: this.identity.type(),
|
||||||
|
data: this.identity.data().data_json()
|
||||||
|
}).catch(error => {
|
||||||
|
log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeaForum based handshake. Error: %o"), error);
|
||||||
|
|
||||||
valid() : boolean {
|
if(error instanceof CommandResult)
|
||||||
return !!this.identity_data && !this.identity_data.is_expired();
|
error = error.extra_message || error.message;
|
||||||
}
|
this.trigger_fail("failed to execute begin (" + error + ")");
|
||||||
|
});
|
||||||
constructor(data: forum.Data) {
|
|
||||||
this.identity_data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
data() : forum.Data {
|
|
||||||
return this.identity_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
decode(data) : Promise<void> {
|
|
||||||
data = JSON.parse(data);
|
|
||||||
if(data.version !== 1)
|
|
||||||
throw "invalid version";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
encode() : string {
|
|
||||||
return JSON.stringify({
|
|
||||||
version: 1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler {
|
|
||||||
return new TeaForumHandshakeHandler(connection, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
fallback_name(): string | undefined {
|
|
||||||
return this.identity_data ? this.identity_data.name() : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
type(): profiles.identities.IdentitifyType {
|
|
||||||
return IdentitifyType.TEAFORO;
|
|
||||||
}
|
|
||||||
|
|
||||||
uid(): string {
|
|
||||||
//FIXME: Real UID!
|
|
||||||
return "TeaForo#" + ((this.identity_data ? this.identity_data.name() : "Another TeaSpeak user"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let static_identity: TeaForumIdentity;
|
|
||||||
|
|
||||||
export function set_static_identity(identity: TeaForumIdentity) {
|
private handle_proof(json) {
|
||||||
static_identity = identity;
|
this.connection.send_command("handshakeindentityproof", {
|
||||||
|
proof: this.identity.data().data_sign()
|
||||||
|
}).catch(error => {
|
||||||
|
log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error);
|
||||||
|
|
||||||
|
if(error instanceof CommandResult)
|
||||||
|
error = error.extra_message || error.message;
|
||||||
|
this.trigger_fail("failed to execute proof (" + error + ")");
|
||||||
|
}).then(() => this.trigger_success());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function update_forum() {
|
protected trigger_fail(message: string) {
|
||||||
if(forum.logged_in() && (!static_identity || static_identity.data() !== forum.data())) {
|
this.connection.command_handler_boss().unregister_handler(this.handler);
|
||||||
static_identity = new TeaForumIdentity(forum.data());
|
super.trigger_fail(message);
|
||||||
} else {
|
|
||||||
static_identity = undefined;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function valid_static_forum_identity() : boolean {
|
protected trigger_success() {
|
||||||
return static_identity && static_identity.valid();
|
this.connection.command_handler_boss().unregister_handler(this.handler);
|
||||||
}
|
super.trigger_success();
|
||||||
|
|
||||||
export function static_forum_identity() : TeaForumIdentity | undefined {
|
|
||||||
return static_identity;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TeaForumIdentity implements Identity {
|
||||||
|
private readonly identity_data: forum.Data;
|
||||||
|
|
||||||
|
valid() : boolean {
|
||||||
|
return !!this.identity_data && !this.identity_data.is_expired();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(data: forum.Data) {
|
||||||
|
this.identity_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
data() : forum.Data {
|
||||||
|
return this.identity_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
decode(data) : Promise<void> {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
if(data.version !== 1)
|
||||||
|
throw "invalid version";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
encode() : string {
|
||||||
|
return JSON.stringify({
|
||||||
|
version: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler {
|
||||||
|
return new TeaForumHandshakeHandler(connection, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
fallback_name(): string | undefined {
|
||||||
|
return this.identity_data ? this.identity_data.name() : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
type(): IdentitifyType {
|
||||||
|
return IdentitifyType.TEAFORO;
|
||||||
|
}
|
||||||
|
|
||||||
|
uid(): string {
|
||||||
|
//FIXME: Real UID!
|
||||||
|
return "TeaForo#" + ((this.identity_data ? this.identity_data.name() : "Another TeaSpeak user"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let static_identity: TeaForumIdentity;
|
||||||
|
|
||||||
|
export function set_static_identity(identity: TeaForumIdentity) {
|
||||||
|
static_identity = identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update_forum() {
|
||||||
|
if(forum.logged_in() && (!static_identity || static_identity.data() !== forum.data())) {
|
||||||
|
static_identity = new TeaForumIdentity(forum.data());
|
||||||
|
} else {
|
||||||
|
static_identity = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function valid_static_forum_identity() : boolean {
|
||||||
|
return static_identity && static_identity.valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function static_forum_identity() : TeaForumIdentity | undefined {
|
||||||
|
return static_identity;
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,11 @@
|
||||||
interface Window {
|
import {Settings, settings} from "../../settings";
|
||||||
|
import {update_forum} from "./TeaForumIdentity";
|
||||||
|
|
||||||
|
declare interface Window {
|
||||||
grecaptcha: GReCaptcha;
|
grecaptcha: GReCaptcha;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GReCaptcha {
|
export interface GReCaptcha {
|
||||||
render(container: string | HTMLElement, parameters: {
|
render(container: string | HTMLElement, parameters: {
|
||||||
sitekey: string;
|
sitekey: string;
|
||||||
theme?: "dark" | "light";
|
theme?: "dark" | "light";
|
||||||
|
@ -18,10 +21,10 @@ interface GReCaptcha {
|
||||||
reset(widget_id?: string);
|
reset(widget_id?: string);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace forum {
|
export namespace forum {
|
||||||
export namespace gcaptcha {
|
export namespace gcaptcha {
|
||||||
export async function initialize() {
|
export async function initialize() {
|
||||||
if(typeof(window.grecaptcha) === "undefined") {
|
if(typeof((window as any).grecaptcha) === "undefined") {
|
||||||
let script = document.createElement("script");
|
let script = document.createElement("script");
|
||||||
script.async = true;
|
script.async = true;
|
||||||
|
|
||||||
|
@ -50,7 +53,7 @@ namespace forum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(typeof(window.grecaptcha) === "undefined")
|
if(typeof((window as any).grecaptcha) === "undefined")
|
||||||
throw tr("failed to load recaptcha");
|
throw tr("failed to load recaptcha");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,9 +65,9 @@ namespace forum {
|
||||||
throw tr("initialisation failed");
|
throw tr("initialisation failed");
|
||||||
}
|
}
|
||||||
if(container.attr("captcha-uuid"))
|
if(container.attr("captcha-uuid"))
|
||||||
window.grecaptcha.reset(container.attr("captcha-uuid"));
|
(window as any).grecaptcha.reset(container.attr("captcha-uuid"));
|
||||||
else {
|
else {
|
||||||
container.attr("captcha-uuid", window.grecaptcha.render(container[0], {
|
container.attr("captcha-uuid", (window as any).grecaptcha.render(container[0], {
|
||||||
"sitekey": key,
|
"sitekey": key,
|
||||||
callback: callback_data
|
callback: callback_data
|
||||||
}));
|
}));
|
||||||
|
@ -206,7 +209,7 @@ namespace forum {
|
||||||
localStorage.setItem("teaspeak-forum-data", response["data"]);
|
localStorage.setItem("teaspeak-forum-data", response["data"]);
|
||||||
localStorage.setItem("teaspeak-forum-sign", response["sign"]);
|
localStorage.setItem("teaspeak-forum-sign", response["sign"]);
|
||||||
localStorage.setItem("teaspeak-forum-auth", response["auth-key"]);
|
localStorage.setItem("teaspeak-forum-auth", response["auth-key"]);
|
||||||
profiles.identities.update_forum();
|
update_forum();
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.error(tr("Failed to parse forum given data: %o"), error);
|
console.error(tr("Failed to parse forum given data: %o"), error);
|
||||||
return {
|
return {
|
||||||
|
@ -266,7 +269,7 @@ namespace forum {
|
||||||
_data = new Data(_data.auth_key, response["data"], response["sign"]);
|
_data = new Data(_data.auth_key, response["data"], response["sign"]);
|
||||||
localStorage.setItem("teaspeak-forum-data", response["data"]);
|
localStorage.setItem("teaspeak-forum-data", response["data"]);
|
||||||
localStorage.setItem("teaspeak-forum-sign", response["sign"]);
|
localStorage.setItem("teaspeak-forum-sign", response["sign"]);
|
||||||
profiles.identities.update_forum();
|
update_forum();
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.error(tr("Failed to parse forum given data: %o"), error);
|
console.error(tr("Failed to parse forum given data: %o"), error);
|
||||||
throw tr("failed to parse data");
|
throw tr("failed to parse data");
|
||||||
|
@ -320,7 +323,7 @@ namespace forum {
|
||||||
localStorage.removeItem("teaspeak-forum-data");
|
localStorage.removeItem("teaspeak-forum-data");
|
||||||
localStorage.removeItem("teaspeak-forum-sign");
|
localStorage.removeItem("teaspeak-forum-sign");
|
||||||
localStorage.removeItem("teaspeak-forum-auth");
|
localStorage.removeItem("teaspeak-forum-auth");
|
||||||
profiles.identities.update_forum();
|
update_forum();
|
||||||
}
|
}
|
||||||
|
|
||||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
//Used by CertAccept popup
|
|
||||||
|
|
||||||
interface Array<T> {
|
interface Array<T> {
|
||||||
remove(elem?: T): boolean;
|
remove(elem?: T): boolean;
|
||||||
last?(): T;
|
last?(): T;
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
/// <reference path="ui/elements/modal.ts" />
|
/// <reference path="ui/elements/modal.ts" />
|
||||||
//Used by CertAccept popup
|
//Used by CertAccept popup
|
||||||
|
|
||||||
|
import {log, LogCategory} from "./log";
|
||||||
|
import {createErrorModal} from "./ui/elements/modal";
|
||||||
|
|
||||||
if(typeof(customElements) !== "undefined") {
|
if(typeof(customElements) !== "undefined") {
|
||||||
try {
|
try {
|
||||||
class X_Properties extends HTMLElement {}
|
class X_Properties extends HTMLElement {}
|
||||||
|
@ -14,7 +17,7 @@ if(typeof(customElements) !== "undefined") {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* T = value type */
|
/* T = value type */
|
||||||
interface SettingsKey<T> {
|
export interface SettingsKey<T> {
|
||||||
key: string;
|
key: string;
|
||||||
|
|
||||||
fallback_keys?: string | string[];
|
fallback_keys?: string | string[];
|
||||||
|
@ -25,7 +28,7 @@ interface SettingsKey<T> {
|
||||||
require_restart?: boolean;
|
require_restart?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SettingsBase {
|
export class SettingsBase {
|
||||||
protected static readonly UPDATE_DIRECT: boolean = true;
|
protected static readonly UPDATE_DIRECT: boolean = true;
|
||||||
|
|
||||||
protected static transformStO?<T>(input?: string, _default?: T, default_type?: string) : T {
|
protected static transformStO?<T>(input?: string, _default?: T, default_type?: string) : T {
|
||||||
|
@ -77,7 +80,7 @@ class SettingsBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StaticSettings extends SettingsBase {
|
export class StaticSettings extends SettingsBase {
|
||||||
private static _instance: StaticSettings;
|
private static _instance: StaticSettings;
|
||||||
static get instance() : StaticSettings {
|
static get instance() : StaticSettings {
|
||||||
if(!this._instance)
|
if(!this._instance)
|
||||||
|
@ -139,7 +142,7 @@ class StaticSettings extends SettingsBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Settings extends StaticSettings {
|
export class Settings extends StaticSettings {
|
||||||
static readonly KEY_USER_IS_NEW: SettingsKey<boolean> = {
|
static readonly KEY_USER_IS_NEW: SettingsKey<boolean> = {
|
||||||
key: 'user_is_new_user',
|
key: 'user_is_new_user',
|
||||||
default_value: true
|
default_value: true
|
||||||
|
@ -433,7 +436,7 @@ class Settings extends StaticSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerSettings extends SettingsBase {
|
export class ServerSettings extends SettingsBase {
|
||||||
private cacheServer = {};
|
private cacheServer = {};
|
||||||
private _server_unique_id: string;
|
private _server_unique_id: string;
|
||||||
private _server_save_worker: NodeJS.Timer;
|
private _server_save_worker: NodeJS.Timer;
|
||||||
|
@ -511,4 +514,4 @@ class ServerSettings extends SettingsBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let settings: Settings;
|
export let settings: Settings;
|
|
@ -1,4 +1,8 @@
|
||||||
enum Sound {
|
import {settings} from "../settings";
|
||||||
|
import {log, LogCategory} from "../log";
|
||||||
|
import {ConnectionHandler} from "../ConnectionHandler";
|
||||||
|
|
||||||
|
export enum Sound {
|
||||||
SOUND_TEST = "sound.test",
|
SOUND_TEST = "sound.test",
|
||||||
SOUND_EGG = "sound.egg",
|
SOUND_EGG = "sound.egg",
|
||||||
|
|
||||||
|
@ -61,7 +65,7 @@ enum Sound {
|
||||||
GROUP_CHANNEL_CHANGED_SELF = "group.channel.changed.self"
|
GROUP_CHANNEL_CHANGED_SELF = "group.channel.changed.self"
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace sound {
|
export namespace sound {
|
||||||
export interface SoundHandle {
|
export interface SoundHandle {
|
||||||
key: string;
|
key: string;
|
||||||
filename: string;
|
filename: string;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
namespace stats {
|
import {log, LogCategory} from "./log";
|
||||||
|
|
||||||
|
export namespace stats {
|
||||||
const LOG_PREFIX = "[Statistics] ";
|
const LOG_PREFIX = "[Statistics] ";
|
||||||
|
|
||||||
export enum CloseCodes {
|
export enum CloseCodes {
|
||||||
|
|
81
shared/js/ui/channel-tree/channel.css
Normal file
81
shared/js/ui/channel-tree/channel.css
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
.channel-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.channel-container .container-channel {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: stretch;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 16px;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.channel-container .container-channel .channel-type {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
.channel-container .container-channel .container-channel-name {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
justify-content: left;
|
||||||
|
max-width: 100%;
|
||||||
|
/* important for the repetitive channel name! */
|
||||||
|
overflow-x: hidden;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
.channel-container .container-channel .container-channel-name.align-right {
|
||||||
|
justify-content: right;
|
||||||
|
}
|
||||||
|
.channel-container .container-channel .container-channel-name.align-center, .channel-container .container-channel .container-channel-name.align-repetitive {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.channel-container .container-channel .container-channel-name .channel-name {
|
||||||
|
align-self: center;
|
||||||
|
color: #828282;
|
||||||
|
min-width: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.channel-container .container-channel .container-channel-name.align-repetitive .channel-name {
|
||||||
|
text-overflow: clip;
|
||||||
|
}
|
||||||
|
.channel-container .container-channel .icons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.channel-container .container-channel.move-selected {
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
}
|
||||||
|
.channel-container .container-channel .show-channel-normal-only {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.channel-container .container-channel .show-channel-normal-only.channel-normal {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.channel-container .container-channel .icon_no_sound {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.channel-container .container-channel .icon_no_sound .background {
|
||||||
|
width: 10px;
|
||||||
|
height: 14px;
|
||||||
|
background: red;
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
left: 3px;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
.channel-container .container-clients {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=channel.css.map */
|
1
shared/js/ui/channel-tree/channel.css.map
Normal file
1
shared/js/ui/channel-tree/channel.css.map
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"sourceRoot":"","sources":["channel.scss","colors.scss"],"names":[],"mappings":"AAEA;EACI;EACA;;AAEA;EACI;EAEA;EACA;EACA;EAEA;EACA;EAEA;EACA;;AAEA;EACI;EACA;EAEA;;AAGJ;EACI;EACA;EAEA;EACA;EAEA;EAEA;AAAiB;EACjB;EACA;;AAEA;EACI;;AAGJ;EACI;;AAGJ;EACI;EACA,OC/CW;EDiDX;EACA;EACA;EACA;;AAIA;EACI;;AAKZ;EACI;EACA;EAEA;EACA;;AAGJ;EACI;;AAGJ;EACI;;AAEA;EACI;;AAIR;EACI;EACA;;AAEA;EACI;EACA;EAEA;EACA;EACA;EACA;EACA;;AAKZ;EACI;EACA","file":"channel.css"}
|
|
@ -0,0 +1,106 @@
|
||||||
|
@import "colors";
|
||||||
|
|
||||||
|
.channel-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.container-channel {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
min-height: 16px;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.channel-type {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-channel-name {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
|
||||||
|
justify-content: left;
|
||||||
|
|
||||||
|
max-width: 100%; /* important for the repetitive channel name! */
|
||||||
|
overflow-x: hidden;
|
||||||
|
height: 16px;
|
||||||
|
|
||||||
|
&.align-right {
|
||||||
|
justify-content: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.align-center, &.align-repetitive {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-name {
|
||||||
|
align-self: center;
|
||||||
|
color: $channel-tree-entry-color;
|
||||||
|
|
||||||
|
min-width: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.align-repetitive {
|
||||||
|
.channel-name {
|
||||||
|
text-overflow: clip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.move-selected {
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-channel-normal-only {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&.channel-normal {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_no_sound {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.background {
|
||||||
|
width: 10px;
|
||||||
|
height: 14px;
|
||||||
|
|
||||||
|
background: red;
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
left: 3px;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-clients {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import {ChannelEntry} from "../../channel-tree/channel";
|
||||||
|
|
||||||
|
const cssChannel = require("./channel.scss");
|
||||||
|
const cssTree = require("./tree.scss");
|
||||||
|
|
||||||
|
class ChannelIcon extends React.Component<{ channel: ChannelEntry }, {}> {
|
||||||
|
channel_icon_classes() {
|
||||||
|
const class_list = [];
|
||||||
|
const channel = this.props.channel;
|
||||||
|
|
||||||
|
if(channel.formattedChannelName() !== channel.channelName())
|
||||||
|
return "channel-spacer";
|
||||||
|
|
||||||
|
let icon_color;
|
||||||
|
if(channel.properties.channel_flag_password && !channel.cached_password())
|
||||||
|
icon_color = "yellow";
|
||||||
|
else if(channel.properties.channel_flag_maxclients_unlimited && channel.clients().length >= channel.properties.channel_maxclients)
|
||||||
|
icon_color = "red";
|
||||||
|
else if(channel.properties.channel_flag_maxfamilyclients_unlimited && channel.clients(true).length >= channel.properties.channel_maxfamilyclients)
|
||||||
|
icon_color = "red";
|
||||||
|
else
|
||||||
|
icon_color = "green";
|
||||||
|
|
||||||
|
|
||||||
|
return "channel-normal client-channel_" + icon_color + (channel.flag_subscribed ? "_subscribed" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <div className={this.channel_icon_classes()} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChannelIcons extends React.Component<{channel: ChannelEntry}, {}> {
|
||||||
|
render_icon(target: HTMLDivElement) {
|
||||||
|
const props = this.props.channel.properties;
|
||||||
|
if(!props.channel_icon_id) return;
|
||||||
|
|
||||||
|
const tag = this.props.channel.channelTree.client.fileManager.icons.generateTag(props.channel_icon_id);
|
||||||
|
tag.appendTo($(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const icons = [];
|
||||||
|
|
||||||
|
const props = this.props.channel.properties;
|
||||||
|
if(props.channel_flag_default) {
|
||||||
|
icons.push(<div className={"show-channel-normal-only icon_entry icon_default icon client-channel_default"} title={tr("Default channel")} />);
|
||||||
|
}
|
||||||
|
if(props.channel_flag_password) {
|
||||||
|
icons.push(<div className={"show-channel-normal-only icon_entry icon_password icon client-register"} title={tr("The channel is password protected")} />);
|
||||||
|
}
|
||||||
|
if(props.channel_codec_quality > 4) {
|
||||||
|
icons.push(<div className={"show-channel-normal-only icon_entry icon_moderated icon client-moderated"} title={tr("Music quality")} />);
|
||||||
|
}
|
||||||
|
if(props.channel_needed_talk_power > 0) {
|
||||||
|
icons.push(<div className={"show-channel-normal-only icon_entry icon_moderated icon client-moderated"} title={tr("Channel is moderated")} />);
|
||||||
|
}
|
||||||
|
if(props.channel_icon_id != 0) {
|
||||||
|
icons.push(<div className={"show-channel-normal-only icon_entry channel_icon"} title={tr("Channel icon")} ref={e => this.render_icon(e) }/>)
|
||||||
|
}
|
||||||
|
if(!audio.codec.supported(props.channel_codec)) {
|
||||||
|
icons.push(<div className={cssChannel.icon_no_sound}>
|
||||||
|
<div className={cssChannel.background} />
|
||||||
|
<div className={"icon_entry icon client-conflict-icon"} title={tr("You don't support the channel codec")} />
|
||||||
|
</div>)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (<div className={cssChannel.icons}>{icons}</div>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChannelLine extends React.Component<{ channel: ChannelEntry }, {}> {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let depth = 1;
|
||||||
|
let parent = this.props.channel;
|
||||||
|
while((parent = parent.parent))
|
||||||
|
depth++;
|
||||||
|
|
||||||
|
//TODO: On handle spacer alignments in channel name!
|
||||||
|
return (
|
||||||
|
<div className={cssChannel.containerChannel}>
|
||||||
|
<div className={cssTree.markerTextUnread} />
|
||||||
|
<div className={cssTree.depthFiller} style={{ width: depth + "em" }} />
|
||||||
|
<ChannelIcon channel={this.props.channel} />
|
||||||
|
<div className={cssChannel.containerChannelName}>
|
||||||
|
<a className={cssChannel.channelName}>{this.props.channel.formattedChannelName()}</a>
|
||||||
|
</div>
|
||||||
|
<ChannelIcons channel={this.props.channel} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChannelClientsView extends React.Component<{}, {}> {
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubChannelView extends React.Component<{ channels: ChannelEntry[] }, {}> {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return this.props.channels.map(e => <ChannelLine channel={e} />);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Channel extends React.Component<{ channel: ChannelEntry }, {}> {
|
||||||
|
children: ChannelEntry[];
|
||||||
|
|
||||||
|
channel_entry() : ChannelEntry { return this.props.channel; }
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className={[cssTree.entry, cssChannel.channelContainer].join(" ")}>
|
||||||
|
<ChannelLine channel={this.props.channel} />
|
||||||
|
<ChannelClientsView />
|
||||||
|
<SubChannelView channels={[]} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
3
shared/js/ui/channel-tree/colors.css
Normal file
3
shared/js/ui/channel-tree/colors.css
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
|
/*# sourceMappingURL=colors.css.map */
|
1
shared/js/ui/channel-tree/colors.css.map
Normal file
1
shared/js/ui/channel-tree/colors.css.map
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"sourceRoot":"","sources":[],"names":[],"mappings":"","file":"colors.css"}
|
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
$channel-tree-new-message-color: #a814147F;
|
||||||
|
$channel-tree-entry-color: #828282;
|
38
shared/js/ui/channel-tree/tree.css
Normal file
38
shared/js/ui/channel-tree/tree.css
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/* Some general browser helpers */
|
||||||
|
.tree-container .tree .entry .depth-filler {
|
||||||
|
font-size: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
.tree-container .marker-text-unread {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 1px;
|
||||||
|
background-color: #a814147F;
|
||||||
|
opacity: 1;
|
||||||
|
-moz-transition: opacity 0.25s;
|
||||||
|
-o-transition: opacity 0.25s;
|
||||||
|
-webkit-transition: opacity 0.25s;
|
||||||
|
transition: opacity 0.25s;
|
||||||
|
}
|
||||||
|
.tree-container .marker-text-unread:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 24px;
|
||||||
|
background: -moz-linear-gradient(left, rgba(168, 20, 20, 0.18) 0%, rgba(168, 20, 20, 0) 100%);
|
||||||
|
/* FF3.6-15 */
|
||||||
|
background: -webkit-linear-gradient(left, rgba(168, 20, 20, 0.18) 0%, rgba(168, 20, 20, 0) 100%);
|
||||||
|
/* Chrome10-25,Safari5.1-6 */
|
||||||
|
background: linear-gradient(to right, rgba(168, 20, 20, 0.18) 0%, rgba(168, 20, 20, 0) 100%);
|
||||||
|
/* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
||||||
|
}
|
||||||
|
.tree-container .marker-text-unread.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=tree.css.map */
|
1
shared/js/ui/channel-tree/tree.css.map
Normal file
1
shared/js/ui/channel-tree/tree.css.map
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"sourceRoot":"","sources":["../../../css/static/mixin.scss","tree.scss"],"names":[],"mappings":"AAAA;ACQY;EACI;EAEA;EACA;;AAKZ;EACI;EACA;EACA;EACA;EAEA;EACA;EAEA;EDvBP,iBC4CO;ED3CP,eC2CO;ED1CP,oBC0CO;EDzCP,YCyCO;;AAnBA;EACI;EACA;EAEA;EACA;EACA;EAEA;EAEA;AAAyF;EACzF;AAA2F;EAC3F;AAAuF;;AAG3F;EACI","file":"tree.css"}
|
|
@ -0,0 +1,50 @@
|
||||||
|
@import "../../../css/static/mixin";
|
||||||
|
@import "../../../css/static/properties";
|
||||||
|
|
||||||
|
.tree-container {
|
||||||
|
.tree {
|
||||||
|
|
||||||
|
.entry {
|
||||||
|
|
||||||
|
.depth-filler {
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.marker-text-unread {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
width: 1px;
|
||||||
|
background-color: #a814147F;
|
||||||
|
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
width: 24px;
|
||||||
|
|
||||||
|
background: -moz-linear-gradient(left, rgba(168,20,20,.18) 0%, rgba(168,20,20,0) 100%); /* FF3.6-15 */
|
||||||
|
background: -webkit-linear-gradient(left, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* Chrome10-25,Safari5.1-6 */
|
||||||
|
background: linear-gradient(to right, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include transition(opacity $button_hover_animation_time);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
export class TreeView {
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
/// <reference path="client.ts" />
|
export class ClientMover {
|
||||||
|
|
||||||
class ClientMover {
|
|
||||||
static readonly listener_root = $(document);
|
static readonly listener_root = $(document);
|
||||||
static readonly move_element = $("#mouse-move");
|
static readonly move_element = $("#mouse-move");
|
||||||
readonly channel_tree: ChannelTree;
|
readonly channel_tree: ChannelTree;
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
|
import {settings} from "../../settings";
|
||||||
|
import {log, LogCategory} from "../../log";
|
||||||
|
|
||||||
|
declare const $: any;
|
||||||
interface JQuery<TElement = HTMLElement> {
|
interface JQuery<TElement = HTMLElement> {
|
||||||
dividerfy() : this;
|
dividerfy() : this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$.fn.dividerfy) {
|
if(!$.fn.dividerfy) {
|
||||||
$.fn.dividerfy = function<T extends HTMLElement>(this: JQuery<T>) : JQuery<T> {
|
$.fn.dividerfy = function<T extends HTMLElement>(this: JQuery<T>) : JQuery<T> {
|
||||||
this.find(".container-seperator").each(function (this: T) {
|
(this as any).find(".container-seperator").each(function (this: T) {
|
||||||
if(!this.previousElementSibling) return;
|
if(!this.previousElementSibling) return;
|
||||||
if(!this.nextElementSibling) return;
|
if(!this.nextElementSibling) return;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace contextmenu {
|
export namespace contextmenu {
|
||||||
export interface MenuEntry {
|
export interface MenuEntry {
|
||||||
callback?: () => void;
|
callback?: () => void;
|
||||||
type: MenuEntryType;
|
type: MenuEntryType;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
/// <reference path="../../PPTListener.ts" />
|
import {KeyCode} from "../../PPTListener";
|
||||||
|
|
||||||
enum ElementType {
|
export enum ElementType {
|
||||||
HEADER,
|
HEADER,
|
||||||
BODY,
|
BODY,
|
||||||
FOOTER
|
FOOTER
|
||||||
}
|
}
|
||||||
|
|
||||||
type BodyCreator = (() => JQuery | JQuery[] | string) | string | JQuery | JQuery[];
|
export type BodyCreator = (() => JQuery | JQuery[] | string) | string | JQuery | JQuery[];
|
||||||
const ModalFunctions = {
|
export const ModalFunctions = {
|
||||||
divify: function (val: JQuery) {
|
divify: function (val: JQuery) {
|
||||||
if(val.length > 1)
|
if(val.length > 1)
|
||||||
return $.spawn("div").append(val);
|
return $.spawn("div").append(val);
|
||||||
|
@ -54,7 +54,7 @@ const ModalFunctions = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModalProperties {
|
export class ModalProperties {
|
||||||
template?: string;
|
template?: string;
|
||||||
header: BodyCreator = () => "HEADER";
|
header: BodyCreator = () => "HEADER";
|
||||||
body: BodyCreator = () => "BODY";
|
body: BodyCreator = () => "BODY";
|
||||||
|
@ -89,7 +89,7 @@ class ModalProperties {
|
||||||
full_size?: boolean = false;
|
full_size?: boolean = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace modal {
|
export namespace modal {
|
||||||
export function initialize_modals() {
|
export function initialize_modals() {
|
||||||
register_global_events();
|
register_global_events();
|
||||||
}
|
}
|
||||||
|
@ -184,7 +184,7 @@ let _global_modal_count = 0;
|
||||||
let _global_modal_last: HTMLElement;
|
let _global_modal_last: HTMLElement;
|
||||||
let _global_modal_last_time: number;
|
let _global_modal_last_time: number;
|
||||||
|
|
||||||
class Modal {
|
export class Modal {
|
||||||
private _htmlTag: JQuery;
|
private _htmlTag: JQuery;
|
||||||
properties: ModalProperties;
|
properties: ModalProperties;
|
||||||
shown: boolean;
|
shown: boolean;
|
||||||
|
@ -296,11 +296,11 @@ class Modal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createModal(data: ModalProperties | any) : Modal {
|
export function createModal(data: ModalProperties | any) : Modal {
|
||||||
return new Modal(ModalFunctions.warpProperties(data));
|
return new Modal(ModalFunctions.warpProperties(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
class InputModalProperties extends ModalProperties {
|
export class InputModalProperties extends ModalProperties {
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
|
|
||||||
field_title?: string;
|
field_title?: string;
|
||||||
|
@ -310,7 +310,7 @@ class InputModalProperties extends ModalProperties {
|
||||||
error_message?: string;
|
error_message?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createInputModal(headMessage: BodyCreator, question: BodyCreator, validator: (input: string) => boolean, callback: (flag: boolean | string) => void, props: InputModalProperties | any = {}) : Modal {
|
export function createInputModal(headMessage: BodyCreator, question: BodyCreator, validator: (input: string) => boolean, callback: (flag: boolean | string) => void, props: InputModalProperties | any = {}) : Modal {
|
||||||
props = ModalFunctions.warpProperties(props);
|
props = ModalFunctions.warpProperties(props);
|
||||||
props.template_properties || (props.template_properties = {});
|
props.template_properties || (props.template_properties = {});
|
||||||
props.template_properties.field_title = props.field_title;
|
props.template_properties.field_title = props.field_title;
|
||||||
|
@ -370,7 +370,7 @@ function createInputModal(headMessage: BodyCreator, question: BodyCreator, valid
|
||||||
return modal;
|
return modal;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createErrorModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) {
|
export function createErrorModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) {
|
||||||
props = ModalFunctions.warpProperties(props);
|
props = ModalFunctions.warpProperties(props);
|
||||||
(props.template_properties || (props.template_properties = {})).header_class = "modal-header-error";
|
(props.template_properties || (props.template_properties = {})).header_class = "modal-header-error";
|
||||||
|
|
||||||
|
@ -382,7 +382,7 @@ function createErrorModal(header: BodyCreator, message: BodyCreator, props: Moda
|
||||||
return modal;
|
return modal;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createInfoModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) {
|
export function createInfoModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) {
|
||||||
props = ModalFunctions.warpProperties(props);
|
props = ModalFunctions.warpProperties(props);
|
||||||
(props.template_properties || (props.template_properties = {})).header_class = "modal-header-info";
|
(props.template_properties || (props.template_properties = {})).header_class = "modal-header-info";
|
||||||
|
|
||||||
|
@ -393,73 +393,3 @@ function createInfoModal(header: BodyCreator, message: BodyCreator, props: Modal
|
||||||
modal.htmlTag.find(".modal-body").addClass("modal-info");
|
modal.htmlTag.find(".modal-body").addClass("modal-info");
|
||||||
return modal;
|
return modal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* extend jquery */
|
|
||||||
|
|
||||||
interface ModalElements {
|
|
||||||
header?: BodyCreator;
|
|
||||||
body?: BodyCreator;
|
|
||||||
footer?: BodyCreator;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface JQuery<TElement = HTMLElement> {
|
|
||||||
modalize(entry_callback?: (header: JQuery, body: JQuery, footer: JQuery) => ModalElements | void, properties?: ModalProperties | any) : Modal;
|
|
||||||
}
|
|
||||||
|
|
||||||
$.fn.modalize = function (this: JQuery, entry_callback?: (header: JQuery, body: JQuery, footer: JQuery) => ModalElements | void, properties?: ModalProperties | any) : Modal {
|
|
||||||
properties = properties || {} as ModalProperties;
|
|
||||||
entry_callback = entry_callback || ((a,b,c) => undefined);
|
|
||||||
|
|
||||||
let tag_modal = this[0].tagName.toLowerCase() == "modal" ? this : undefined; /* TODO may throw exception? */
|
|
||||||
|
|
||||||
let tag_head = tag_modal ? tag_modal.find("modal-header") : ModalFunctions.jqueriefy(properties.header);
|
|
||||||
let tag_body = tag_modal ? tag_modal.find("modal-body") : this;
|
|
||||||
let tag_footer = tag_modal ? tag_modal.find("modal-footer") : ModalFunctions.jqueriefy(properties.footer);
|
|
||||||
|
|
||||||
const result = entry_callback(tag_head as any, tag_body, tag_footer as any) || {};
|
|
||||||
properties.header = result.header || tag_head;
|
|
||||||
properties.body = result.body || tag_body;
|
|
||||||
properties.footer = result.footer || tag_footer;
|
|
||||||
return createModal(properties);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace net.graph {
|
export namespace net.graph {
|
||||||
export type Entry = {
|
export type Entry = {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
interface SliderOptions {
|
export interface SliderOptions {
|
||||||
min_value?: number;
|
min_value?: number;
|
||||||
max_value?: number;
|
max_value?: number;
|
||||||
initial_value?: number;
|
initial_value?: number;
|
||||||
|
@ -8,11 +8,11 @@ interface SliderOptions {
|
||||||
value_field?: JQuery | JQuery[];
|
value_field?: JQuery | JQuery[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Slider {
|
export interface Slider {
|
||||||
value(value?: number) : number;
|
value(value?: number) : number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sliderfy(slider: JQuery, options?: SliderOptions) : Slider {
|
export function sliderfy(slider: JQuery, options?: SliderOptions) : Slider {
|
||||||
options = Object.assign( {
|
options = Object.assign( {
|
||||||
initial_value: 0,
|
initial_value: 0,
|
||||||
min_value: 0,
|
min_value: 0,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
function tooltip(entry: JQuery) {
|
export function tooltip(entry: JQuery) {
|
||||||
return tooltip.initialize(entry);
|
return tooltip.initialize(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace tooltip {
|
export namespace tooltip {
|
||||||
let _global_tooltip: JQuery;
|
let _global_tooltip: JQuery;
|
||||||
export type Handle = {
|
export type Handle = {
|
||||||
show();
|
show();
|
||||||
|
|
|
@ -13,12 +13,27 @@
|
||||||
client_away_message Value: ''
|
client_away_message Value: ''
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let control_bar: ControlBar; /* global variable to access the control bar */
|
import {ConnectionHandler, DisconnectReason} from "../../ConnectionHandler";
|
||||||
|
import {top_menu} from "./MenuBar";
|
||||||
|
import {Settings, settings} from "../../settings";
|
||||||
|
import {createErrorModal, createInfoModal, createInputModal} from "../elements/modal";
|
||||||
|
import {default_recorder} from "../../voice/RecorderProfile";
|
||||||
|
import {sound, Sound} from "../../sound/Sounds";
|
||||||
|
import {Modals} from "../modal/ModalConnect";
|
||||||
|
import {Modal as ModalsS} from "../modal/ModalSettings";
|
||||||
|
import {log} from "./server_log";
|
||||||
|
import {MessageHelper} from "./chat";
|
||||||
|
import {CommandResult} from "../../connection/ServerConnectionDeclaration";
|
||||||
|
import {PermissionType} from "../../permission/PermissionManager";
|
||||||
|
import {bookmarks} from "../../bookmarks";
|
||||||
|
import {contextmenu} from "../elements/context_menu";
|
||||||
|
|
||||||
type MicrophoneState = "disabled" | "muted" | "enabled";
|
export let control_bar: ControlBar; /* global variable to access the control bar */
|
||||||
type HeadphoneState = "muted" | "enabled";
|
|
||||||
type AwayState = "away-global" | "away" | "online";
|
export type MicrophoneState = "disabled" | "muted" | "enabled";
|
||||||
class ControlBar {
|
export type HeadphoneState = "muted" | "enabled";
|
||||||
|
export type AwayState = "away-global" | "away" | "online";
|
||||||
|
export class ControlBar {
|
||||||
private _button_away_active: AwayState;
|
private _button_away_active: AwayState;
|
||||||
private _button_microphone: MicrophoneState;
|
private _button_microphone: MicrophoneState;
|
||||||
private _button_speakers: HeadphoneState;
|
private _button_speakers: HeadphoneState;
|
||||||
|
@ -421,7 +436,7 @@ class ControlBar {
|
||||||
}
|
}
|
||||||
|
|
||||||
private on_open_settings() {
|
private on_open_settings() {
|
||||||
Modals.spawnSettingsModal();
|
ModalsS.spawnSettingsModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
private on_open_connect() {
|
private on_open_connect() {
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
namespace top_menu {
|
import {bookmarks} from "../../bookmarks";
|
||||||
|
import {Modals} from "../modal/ModalBookmarks";
|
||||||
|
import {DisconnectReason} from "../../ConnectionHandler";
|
||||||
|
import {control_bar} from "./ControlBar";
|
||||||
|
import {createErrorModal} from "../elements/modal";
|
||||||
|
import {PermissionType} from "../../permission/PermissionManager";
|
||||||
|
import {Sound} from "../../sound/Sounds";
|
||||||
|
|
||||||
|
export namespace top_menu {
|
||||||
export interface HRItem { }
|
export interface HRItem { }
|
||||||
|
|
||||||
export interface MenuItem {
|
export interface MenuItem {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
enum ChatType {
|
import {log, LogCategory} from "../../log";
|
||||||
|
|
||||||
|
export enum ChatType {
|
||||||
GENERAL,
|
GENERAL,
|
||||||
SERVER,
|
SERVER,
|
||||||
CHANNEL,
|
CHANNEL,
|
||||||
|
@ -6,7 +8,7 @@ enum ChatType {
|
||||||
}
|
}
|
||||||
|
|
||||||
declare const xbbcode: any;
|
declare const xbbcode: any;
|
||||||
namespace MessageHelper {
|
export namespace MessageHelper {
|
||||||
export function htmlEscape(message: string) : string[] {
|
export function htmlEscape(message: string) : string[] {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.innerText = message;
|
div.innerText = message;
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
/* the bar on the right with the chats (Channel & Client) */
|
/* the bar on the right with the chats (Channel & Client) */
|
||||||
namespace chat {
|
import {ChannelEntry} from "../../channel-tree/channel";
|
||||||
|
import {Modals} from "../modal/ModalMusicManage";
|
||||||
|
import {MessageHelper} from "./chat";
|
||||||
|
import {ServerEntry} from "../../channel-tree/server";
|
||||||
|
import {ConnectionHandler} from "../../ConnectionHandler";
|
||||||
|
|
||||||
|
export namespace chat {
|
||||||
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||||
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
|
import {ConnectionHandler, DisconnectReason} from "../../ConnectionHandler";
|
||||||
|
import {Settings, settings} from "../../settings";
|
||||||
|
import {control_bar} from "./ControlBar";
|
||||||
|
import {top_menu} from "./MenuBar";
|
||||||
|
|
||||||
let server_connections: ServerConnectionManager;
|
export let server_connections: ServerConnectionManager;
|
||||||
|
|
||||||
class ServerConnectionManager {
|
export class ServerConnectionManager {
|
||||||
private connection_handlers: ConnectionHandler[] = [];
|
private connection_handlers: ConnectionHandler[] = [];
|
||||||
private active_handler: ConnectionHandler | undefined;
|
private active_handler: ConnectionHandler | undefined;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
class Hostbanner {
|
import {ConnectionHandler} from "../../ConnectionHandler";
|
||||||
|
import {Settings, settings} from "../../settings";
|
||||||
|
import {log, LogCategory} from "../../log";
|
||||||
|
|
||||||
|
export class Hostbanner {
|
||||||
readonly html_tag: JQuery<HTMLElement>;
|
readonly html_tag: JQuery<HTMLElement>;
|
||||||
readonly client: ConnectionHandler;
|
readonly client: ConnectionHandler;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace image_preview {
|
export namespace image_preview {
|
||||||
let preview_overlay: JQuery<HTMLDivElement>;
|
let preview_overlay: JQuery<HTMLDivElement>;
|
||||||
let container_image: JQuery<HTMLDivElement>;
|
let container_image: JQuery<HTMLDivElement>;
|
||||||
let button_open_in_browser: JQuery;
|
let button_open_in_browser: JQuery;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
||||||
namespace chat {
|
export namespace chat {
|
||||||
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||||
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace chat {
|
export namespace chat {
|
||||||
export namespace helpers {
|
export namespace helpers {
|
||||||
//https://regex101.com/r/YQbfcX/2
|
//https://regex101.com/r/YQbfcX/2
|
||||||
//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;
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
namespace chat {
|
import {ClientEntry, LocalClientEntry} from "../../../channel-tree/client";
|
||||||
|
import {Modals} from "../../modal/ModalClientInfo";
|
||||||
|
import {htmltags} from "../../htmltags";
|
||||||
|
import {image_preview} from "../image_preview";
|
||||||
|
import {i18n} from "../../../i18n/country";
|
||||||
|
import {GroupManager} from "../../../permission/GroupManager";
|
||||||
|
|
||||||
|
export namespace chat {
|
||||||
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||||
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
namespace chat {
|
import {htmltags} from "../../htmltags";
|
||||||
|
import {MessageHelper} from "../chat";
|
||||||
|
import {CommandResult, ErrorID} from "../../../connection/ServerConnectionDeclaration";
|
||||||
|
import {log, LogCategory} from "../../../log";
|
||||||
|
import {createErrorModal} from "../../elements/modal";
|
||||||
|
|
||||||
|
export namespace chat {
|
||||||
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||||
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
namespace chat {
|
import {MusicClientEntry} from "../../../channel-tree/client";
|
||||||
import PlayerState = connection.voice.PlayerState;
|
import {image_preview} from "../image_preview";
|
||||||
|
|
||||||
|
export namespace chat {
|
||||||
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||||
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
/* the bar on the right with the chats (Channel & Client) */
|
/* the bar on the right with the chats (Channel & Client) */
|
||||||
namespace chat {
|
import {log, LogCategory} from "../../../log";
|
||||||
|
import {htmltags} from "../../htmltags";
|
||||||
|
import {MessageHelper} from "../chat";
|
||||||
|
|
||||||
|
export namespace chat {
|
||||||
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||||
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace htmltags {
|
export namespace htmltags {
|
||||||
let mouse_coordinates: {x: number, y: number} = {x: 0, y: 0};
|
let mouse_coordinates: {x: number, y: number} = {x: 0, y: 0};
|
||||||
|
|
||||||
function initialize() {
|
function initialize() {
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
function format_date(date: number) {
|
function format_date(date: number) {
|
||||||
const d = new Date(date);
|
const d = new Date(date);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
//TODO: Test if we could render this image and not only the browser by knowing the type.
|
//TODO: Test if we could render this image and not only the browser by knowing the type.
|
||||||
export function spawnAvatarUpload(callback_data: (data: ArrayBuffer | undefined | null) => any) {
|
export function spawnAvatarUpload(callback_data: (data: ArrayBuffer | undefined | null) => any) {
|
||||||
const modal = createModal({
|
const modal = createModal({
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
const avatar_to_uid = (id: string) => {
|
const avatar_to_uid = (id: string) => {
|
||||||
const buffer = new Uint8Array(id.length / 2);
|
const buffer = new Uint8Array(id.length / 2);
|
||||||
for(let index = 0; index < id.length; index += 2) {
|
for(let index = 0; index < id.length; index += 2) {
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
export type BanEntry = {
|
export type BanEntry = {
|
||||||
name?: string;
|
name?: string;
|
||||||
unique_id: string;
|
unique_id: string;
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
|
||||||
/// <reference path="../../i18n/localize.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
export function openBanList(client: ConnectionHandler) {
|
export function openBanList(client: ConnectionHandler) {
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
export function spawnBookmarkModal() {
|
export function spawnBookmarkModal() {
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
modal = createModal({
|
modal = createModal({
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
export function spawnChangeLatency(client: ClientEntry, current: connection.voice.LatencySettings, reset: () => connection.voice.LatencySettings, apply: (settings: connection.voice.LatencySettings) => any, callback_flush?: () => any) {
|
export function spawnChangeLatency(client: ClientEntry, current: connection.voice.LatencySettings, reset: () => connection.voice.LatencySettings, apply: (settings: connection.voice.LatencySettings) => any, callback_flush?: () => any) {
|
||||||
if(modal) modal.close();
|
if(modal) modal.close();
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
//TODO: Use the max limit!
|
//TODO: Use the max limit!
|
||||||
|
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Modals {
|
export namespace Modals {
|
||||||
export function openChannelInfo(channel: ChannelEntry) {
|
export function openChannelInfo(channel: ChannelEntry) {
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Modals {
|
export namespace Modals {
|
||||||
type InfoUpdateCallback = (info: ClientConnectionInfo) => any;
|
type InfoUpdateCallback = (info: ClientConnectionInfo) => any;
|
||||||
export function openClientInfo(client: ClientEntry) {
|
export function openClientInfo(client: ClientEntry) {
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
|
||||||
|
|
||||||
//FIXME: Move this shit out of this file!
|
//FIXME: Move this shit out of this file!
|
||||||
namespace connection_log {
|
export namespace connection_log {
|
||||||
//TODO: Save password data
|
//TODO: Save password data
|
||||||
export type ConnectionData = {
|
export type ConnectionData = {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -91,7 +89,7 @@ namespace connection_log {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Modals {
|
export namespace Modals {
|
||||||
export function spawnConnectModal(options: {
|
export function spawnConnectModal(options: {
|
||||||
default_connect_new_tab?: boolean /* default false */
|
default_connect_new_tab?: boolean /* default false */
|
||||||
}, defaultHost: { url: string, enforce: boolean} = { url: "ts.TeaSpeak.de", enforce: false}, connect_profile?: { profile: profiles.ConnectionProfile, enforce: boolean}) {
|
}, defaultHost: { url: string, enforce: boolean} = { url: "ts.TeaSpeak.de", enforce: false}, connect_profile?: { profile: profiles.ConnectionProfile, enforce: boolean}) {
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
export function createChannelModal(connection: ConnectionHandler, 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({
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Modals {
|
export namespace Modals {
|
||||||
let current_modal: Modal;
|
let current_modal: Modal;
|
||||||
export function createServerGroupAssignmentModal(client: ClientEntry, callback: (groups: number[], flag: boolean) => Promise<boolean>) {
|
export function createServerGroupAssignmentModal(client: ClientEntry, callback: (groups: number[], flag: boolean) => Promise<boolean>) {
|
||||||
if(current_modal)
|
if(current_modal)
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id: number) => any, selected_icon?: number) {
|
export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id: number) => any, selected_icon?: number) {
|
||||||
selected_icon = selected_icon || 0;
|
selected_icon = selected_icon || 0;
|
||||||
let allow_manage = client.permissions.neededPermission(PermissionType.B_ICON_MANAGE).granted(1);
|
let allow_manage = client.permissions.neededPermission(PermissionType.B_ICON_MANAGE).granted(1);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Modals {
|
export namespace Modals {
|
||||||
export function spawnTeamSpeakIdentityImprove(identity: profiles.identities.TeaSpeakIdentity, name: string): Modal {
|
export function spawnTeamSpeakIdentityImprove(identity: profiles.identities.TeaSpeakIdentity, name: string): Modal {
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
let elapsed_timer: NodeJS.Timer;
|
let elapsed_timer: NodeJS.Timer;
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
type URLGeneratorSettings = {
|
type URLGeneratorSettings = {
|
||||||
flag_direct: boolean,
|
flag_direct: boolean,
|
||||||
flag_resolved: boolean
|
flag_resolved: boolean
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Modals {
|
export namespace Modals {
|
||||||
export function spawnKeySelect(callback: (key?: ppt.KeyEvent) => void) {
|
export function spawnKeySelect(callback: (key?: ppt.KeyEvent) => void) {
|
||||||
let modal = createModal({
|
let modal = createModal({
|
||||||
header: tr("Select a key"),
|
header: tr("Select a key"),
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
export function openMusicManage(client: ConnectionHandler, bot: MusicClientEntry) {
|
export function openMusicManage(client: ConnectionHandler, bot: MusicClientEntry) {
|
||||||
const ev_registry = new events.Registry<events.modal.music_manage>();
|
const ev_registry = new events.Registry<events.modal.music_manage>();
|
||||||
ev_registry.enable_debug("music-manage");
|
ev_registry.enable_debug("music-manage");
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
const next_step: {[key: string]:string} = {
|
const next_step: {[key: string]:string} = {
|
||||||
"welcome": "microphone",
|
"welcome": "microphone",
|
||||||
//"microphone": app.is_web() ? "identity" : "speaker", /* speaker setup only for the native client! */
|
//"microphone": app.is_web() ? "identity" : "speaker", /* speaker setup only for the native client! */
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
export function spawnPlaylistSongInfo(song: PlaylistSong) {
|
export function spawnPlaylistSongInfo(song: PlaylistSong) {
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../i18n/localize.ts" />
|
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
export function spawnPlaylistManage(client: ConnectionHandler) {
|
export function spawnPlaylistManage(client: ConnectionHandler) {
|
||||||
{
|
{
|
||||||
createErrorModal(tr("Not implemented"), tr("Playlist management hasn't yet been implemented")).open();
|
createErrorModal(tr("Not implemented"), tr("Playlist management hasn't yet been implemented")).open();
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
let global_modal: PokeModal;
|
let global_modal: PokeModal;
|
||||||
|
|
||||||
interface ServerEntry {
|
interface ServerEntry {
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
export function spawnQueryCreate(connection: ConnectionHandler, callback_created?: (user, pass) => any) {
|
export function spawnQueryCreate(connection: ConnectionHandler, callback_created?: (user, pass) => any) {
|
||||||
let modal;
|
let modal;
|
||||||
modal = createModal({
|
modal = createModal({
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
/// <reference path="../../ui/elements/modal.ts" />
|
export namespace Modals {
|
||||||
/// <reference path="../../ConnectionHandler.ts" />
|
|
||||||
/// <reference path="../../proto.ts" />
|
|
||||||
|
|
||||||
namespace Modals {
|
|
||||||
/*
|
/*
|
||||||
export function spawnQueryManage(client: ConnectionHandler) {
|
export function spawnQueryManage(client: ConnectionHandler) {
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Modals {
|
export namespace Modals {
|
||||||
export function createServerModal(server: ServerEntry, callback: (properties?: ServerProperties) => Promise<void>) {
|
export function createServerModal(server: ServerEntry, callback: (properties?: ServerProperties) => Promise<void>) {
|
||||||
const properties = Object.assign({}, server.properties);
|
const properties = Object.assign({}, server.properties);
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue