Refactored the app to use webpack
This commit is contained in:
parent
447e84ed0f
commit
0e3a415983
153 changed files with 34926 additions and 34209 deletions
169
file.ts
169
file.ts
|
@ -444,6 +444,7 @@ const CLIENT_APP_FILE_LIST = [
|
|||
...APP_FILE_LIST_CLIENT_SOURCE
|
||||
];
|
||||
|
||||
/*
|
||||
const WEB_APP_FILE_LIST = [
|
||||
...APP_FILE_LIST_SHARED_SOURCE,
|
||||
...APP_FILE_LIST_SHARED_VENDORS,
|
||||
|
@ -451,6 +452,174 @@ const WEB_APP_FILE_LIST = [
|
|||
...APP_FILE_LIST_WEB_TEASPEAK,
|
||||
...CERTACCEPT_FILE_LIST,
|
||||
];
|
||||
*/
|
||||
const WEB_APP_FILE_LIST = [
|
||||
...APP_FILE_LIST_SHARED_VENDORS,
|
||||
{ /* shared html and php files */
|
||||
"type": "html",
|
||||
"search-pattern": /^.*([a-zA-Z]+)\.(html|php|json)$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "./",
|
||||
"local-path": "./shared/html/"
|
||||
},
|
||||
{ /* javascript loader for releases */
|
||||
"type": "js",
|
||||
"search-pattern": /.*$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "js/",
|
||||
"local-path": "./dist/"
|
||||
},
|
||||
|
||||
{ /* shared javascript files (WebRTC adapter) */
|
||||
"type": "js",
|
||||
"search-pattern": /.*\.js$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "adapter/",
|
||||
"local-path": "./shared/adapter/"
|
||||
},
|
||||
|
||||
{ /* shared generated worker codec */
|
||||
"type": "js",
|
||||
"search-pattern": /(WorkerPOW.js)$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "js/workers/",
|
||||
"local-path": "./shared/js/workers/"
|
||||
},
|
||||
{ /* shared developer single css files */
|
||||
"type": "css",
|
||||
"search-pattern": /.*\.css$/,
|
||||
"build-target": "dev",
|
||||
|
||||
"path": "css/",
|
||||
"local-path": "./shared/css/"
|
||||
},
|
||||
{ /* shared css mapping files (development mode only) */
|
||||
"type": "css",
|
||||
"search-pattern": /.*\.(css.map|scss)$/,
|
||||
"build-target": "dev",
|
||||
|
||||
"path": "css/",
|
||||
"local-path": "./shared/css/",
|
||||
"req-parm": ["--mappings"]
|
||||
},
|
||||
{ /* shared release css files */
|
||||
"type": "css",
|
||||
"search-pattern": /.*\.css$/,
|
||||
"build-target": "rel",
|
||||
|
||||
"path": "css/",
|
||||
"local-path": "./shared/generated/"
|
||||
},
|
||||
{ /* shared release css files */
|
||||
"type": "css",
|
||||
"search-pattern": /.*\.css$/,
|
||||
"build-target": "rel",
|
||||
|
||||
"path": "css/loader/",
|
||||
"local-path": "./shared/css/loader/"
|
||||
},
|
||||
{ /* shared release css files */
|
||||
"type": "css",
|
||||
"search-pattern": /.*\.css$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "css/theme/",
|
||||
"local-path": "./shared/css/theme/"
|
||||
},
|
||||
{ /* shared sound files */
|
||||
"type": "wav",
|
||||
"search-pattern": /.*\.wav$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "audio/",
|
||||
"local-path": "./shared/audio/"
|
||||
},
|
||||
{ /* shared data sound files */
|
||||
"type": "json",
|
||||
"search-pattern": /.*\.json/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "audio/",
|
||||
"local-path": "./shared/audio/"
|
||||
},
|
||||
{ /* shared image files */
|
||||
"type": "img",
|
||||
"search-pattern": /.*\.(svg|png)/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "img/",
|
||||
"local-path": "./shared/img/"
|
||||
},
|
||||
{ /* own webassembly files */
|
||||
"type": "wasm",
|
||||
"search-pattern": /.*\.(wasm)/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "wat/",
|
||||
"local-path": "./shared/wat/"
|
||||
},
|
||||
|
||||
|
||||
/* web specific */
|
||||
{ /* generated assembly files */
|
||||
"web-only": true,
|
||||
"type": "wasm",
|
||||
"search-pattern": /.*\.(wasm)/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "wasm/",
|
||||
"local-path": "./asm/generated/"
|
||||
},
|
||||
{ /* generated assembly javascript files */
|
||||
"web-only": true,
|
||||
"type": "js",
|
||||
"search-pattern": /.*\.(js)/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "wasm/",
|
||||
"local-path": "./asm/generated/"
|
||||
},
|
||||
{ /* web generated worker codec */
|
||||
"web-only": true,
|
||||
"type": "js",
|
||||
"search-pattern": /(WorkerCodec.js)$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "js/workers/",
|
||||
"local-path": "./web/js/workers/"
|
||||
},
|
||||
{ /* web css files */
|
||||
"web-only": true,
|
||||
"type": "css",
|
||||
"search-pattern": /.*\.css$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "css/",
|
||||
"local-path": "./web/css/"
|
||||
},
|
||||
{ /* web html files */
|
||||
"web-only": true,
|
||||
"type": "html",
|
||||
"search-pattern": /.*\.(php|html)/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "./",
|
||||
"local-path": "./web/html/"
|
||||
},
|
||||
{ /* translations */
|
||||
"web-only": true, /* Only required for the web client */
|
||||
"type": "i18n",
|
||||
"search-pattern": /.*\.(translation|json)/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "i18n/",
|
||||
"local-path": "./shared/i18n/"
|
||||
}
|
||||
] as any;
|
||||
|
||||
//@ts-ignore
|
||||
declare module "fs-extra" {
|
||||
|
|
|
@ -1,22 +1,12 @@
|
|||
/// <reference path="loader.ts" />
|
||||
import * as loader from "./loader";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
$: JQuery;
|
||||
native_client: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
namespace app {
|
||||
export enum Type {
|
||||
UNKNOWN,
|
||||
CLIENT_RELEASE,
|
||||
CLIENT_DEBUG,
|
||||
WEB_DEBUG,
|
||||
WEB_RELEASE
|
||||
}
|
||||
export let type: Type = Type.UNKNOWN;
|
||||
|
||||
export function is_web() {
|
||||
return type == Type.WEB_RELEASE || type == Type.WEB_DEBUG;
|
||||
}
|
||||
const node_require: typeof require = window.require;
|
||||
|
||||
let _ui_version;
|
||||
export function ui_version() {
|
||||
|
@ -31,11 +21,19 @@ namespace app {
|
|||
}
|
||||
return _ui_version;
|
||||
}
|
||||
}
|
||||
|
||||
/* all javascript loaders */
|
||||
const loader_javascript = {
|
||||
detect_type: async () => {
|
||||
//TODO: Detect real version!
|
||||
loader.set_version({
|
||||
backend: "-",
|
||||
ui: ui_version(),
|
||||
debug_mode: true,
|
||||
type: "web"
|
||||
});
|
||||
window.native_client = false;
|
||||
return;
|
||||
if(window.require) {
|
||||
const request = new Request("js/proto.js");
|
||||
let file_path = request.url;
|
||||
|
@ -43,11 +41,11 @@ const loader_javascript = {
|
|||
throw "Invalid file path (" + file_path + ")";
|
||||
file_path = file_path.substring(process.platform === "win32" ? 8 : 7);
|
||||
|
||||
const fs = require('fs');
|
||||
const fs = node_require('fs');
|
||||
if(fs.existsSync(file_path)) {
|
||||
app.type = app.Type.CLIENT_DEBUG;
|
||||
//type = Type.CLIENT_DEBUG;
|
||||
} else {
|
||||
app.type = app.Type.CLIENT_RELEASE;
|
||||
//type = Type.CLIENT_RELEASE;
|
||||
}
|
||||
} else {
|
||||
/* test if js/proto.js is available. If so we're in debug mode */
|
||||
|
@ -58,9 +56,9 @@ const loader_javascript = {
|
|||
request.onreadystatechange = () => {
|
||||
if (request.readyState === 4){
|
||||
if (request.status === 404) {
|
||||
app.type = app.Type.WEB_RELEASE;
|
||||
//type = Type.WEB_RELEASE;
|
||||
} else {
|
||||
app.type = app.Type.WEB_DEBUG;
|
||||
//type = Type.WEB_DEBUG;
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
|
@ -101,7 +99,7 @@ const loader_javascript = {
|
|||
["vendor/emoji-picker/src/jquery.lsxemojipicker.js"]
|
||||
]);
|
||||
|
||||
if(app.type == app.Type.WEB_RELEASE || app.type == app.Type.CLIENT_RELEASE) {
|
||||
if(!loader.version().debug_mode) {
|
||||
loader.register_task(loader.Stage.JAVASCRIPT, {
|
||||
name: "scripts release",
|
||||
priority: 20,
|
||||
|
@ -116,176 +114,8 @@ const loader_javascript = {
|
|||
}
|
||||
},
|
||||
load_scripts_debug: async () => {
|
||||
/* test if we're loading as TeaClient or WebClient */
|
||||
if(!window.require) {
|
||||
loader.register_task(loader.Stage.JAVASCRIPT, {
|
||||
name: "javascript web",
|
||||
priority: 10,
|
||||
function: loader_javascript.load_scripts_debug_web
|
||||
});
|
||||
} else {
|
||||
loader.register_task(loader.Stage.JAVASCRIPT, {
|
||||
name: "javascript client",
|
||||
priority: 10,
|
||||
function: loader_javascript.load_scripts_debug_client
|
||||
});
|
||||
}
|
||||
|
||||
/* load some extends classes */
|
||||
await loader.load_scripts([
|
||||
["js/connection/ConnectionBase.js"]
|
||||
]);
|
||||
|
||||
/* load the main app */
|
||||
await loader.load_scripts([
|
||||
//Load general API's
|
||||
"js/proto.js",
|
||||
"js/i18n/localize.js",
|
||||
"js/i18n/country.js",
|
||||
"js/log.js",
|
||||
"js/events.js",
|
||||
|
||||
"js/sound/Sounds.js",
|
||||
|
||||
"js/utils/helpers.js",
|
||||
|
||||
"js/crypto/sha.js",
|
||||
"js/crypto/hex.js",
|
||||
"js/crypto/asn1.js",
|
||||
"js/crypto/crc32.js",
|
||||
|
||||
//load the profiles
|
||||
"js/profiles/ConnectionProfile.js",
|
||||
"js/profiles/Identity.js",
|
||||
"js/profiles/identities/teaspeak-forum.js",
|
||||
|
||||
//Basic UI elements
|
||||
"js/ui/elements/context_divider.js",
|
||||
"js/ui/elements/context_menu.js",
|
||||
"js/ui/elements/modal.js",
|
||||
"js/ui/elements/tab.js",
|
||||
"js/ui/elements/slider.js",
|
||||
"js/ui/elements/tooltip.js",
|
||||
"js/ui/elements/net_graph.js",
|
||||
|
||||
//Load permissions
|
||||
"js/permission/PermissionManager.js",
|
||||
"js/permission/GroupManager.js",
|
||||
|
||||
//Load UI
|
||||
"js/ui/modal/ModalAbout.js",
|
||||
"js/ui/modal/ModalAvatar.js",
|
||||
"js/ui/modal/ModalAvatarList.js",
|
||||
"js/ui/modal/ModalClientInfo.js",
|
||||
"js/ui/modal/ModalChannelInfo.js",
|
||||
"js/ui/modal/ModalServerInfo.js",
|
||||
"js/ui/modal/ModalServerInfoBandwidth.js",
|
||||
"js/ui/modal/ModalQuery.js",
|
||||
"js/ui/modal/ModalQueryManage.js",
|
||||
"js/ui/modal/ModalPlaylistList.js",
|
||||
"js/ui/modal/ModalPlaylistEdit.js",
|
||||
"js/ui/modal/ModalBookmarks.js",
|
||||
"js/ui/modal/ModalConnect.js",
|
||||
"js/ui/modal/ModalSettings.js",
|
||||
"js/ui/modal/ModalNewcomer.js",
|
||||
"js/ui/modal/ModalCreateChannel.js",
|
||||
"js/ui/modal/ModalServerEdit.js",
|
||||
"js/ui/modal/ModalChangeVolume.js",
|
||||
"js/ui/modal/ModalChangeLatency.js",
|
||||
"js/ui/modal/ModalBanClient.js",
|
||||
"js/ui/modal/ModalIconSelect.js",
|
||||
"js/ui/modal/ModalInvite.js",
|
||||
"js/ui/modal/ModalIdentity.js",
|
||||
"js/ui/modal/ModalBanList.js",
|
||||
"js/ui/modal/ModalMusicManage.js",
|
||||
"js/ui/modal/ModalYesNo.js",
|
||||
"js/ui/modal/ModalPoke.js",
|
||||
"js/ui/modal/ModalKeySelect.js",
|
||||
"js/ui/modal/ModalGroupAssignment.js",
|
||||
"js/ui/modal/permission/ModalPermissionEdit.js",
|
||||
{url: "js/ui/modal/permission/SenselessPermissions.js", depends: ["js/permission/PermissionManager.js"]},
|
||||
{url: "js/ui/modal/permission/CanvasPermissionEditor.js", depends: ["js/ui/modal/permission/ModalPermissionEdit.js"]},
|
||||
{url: "js/ui/modal/permission/HTMLPermissionEditor.js", depends: ["js/ui/modal/permission/ModalPermissionEdit.js"]},
|
||||
|
||||
"js/channel-tree/channel.js",
|
||||
"js/channel-tree/client.js",
|
||||
"js/channel-tree/server.js",
|
||||
"js/channel-tree/view.js",
|
||||
"js/ui/client_move.js",
|
||||
"js/ui/htmltags.js",
|
||||
|
||||
|
||||
"js/ui/frames/side/chat_helper.js",
|
||||
"js/ui/frames/side/chat_box.js",
|
||||
"js/ui/frames/side/client_info.js",
|
||||
"js/ui/frames/side/music_info.js",
|
||||
"js/ui/frames/side/conversations.js",
|
||||
"js/ui/frames/side/private_conversations.js",
|
||||
|
||||
"js/ui/frames/ControlBar.js",
|
||||
"js/ui/frames/chat.js",
|
||||
"js/ui/frames/chat_frame.js",
|
||||
"js/ui/frames/connection_handlers.js",
|
||||
"js/ui/frames/server_log.js",
|
||||
"js/ui/frames/hostbanner.js",
|
||||
"js/ui/frames/MenuBar.js",
|
||||
"js/ui/frames/image_preview.js",
|
||||
|
||||
//Load audio
|
||||
"js/voice/RecorderBase.js",
|
||||
"js/voice/RecorderProfile.js",
|
||||
|
||||
//Load general stuff
|
||||
"js/settings.js",
|
||||
"js/bookmarks.js",
|
||||
"js/FileManager.js",
|
||||
"js/ConnectionHandler.js",
|
||||
"js/BrowserIPC.js",
|
||||
"js/MessageFormatter.js",
|
||||
|
||||
//Connection
|
||||
"js/connection/CommandHandler.js",
|
||||
"js/connection/CommandHelper.js",
|
||||
"js/connection/HandshakeHandler.js",
|
||||
"js/connection/ServerConnectionDeclaration.js",
|
||||
|
||||
"js/stats.js",
|
||||
"js/PPTListener.js",
|
||||
|
||||
"js/profiles/identities/NameIdentity.js", //Depends on Identity
|
||||
"js/profiles/identities/TeaForumIdentity.js", //Depends on Identity
|
||||
"js/profiles/identities/TeamSpeakIdentity.js", //Depends on Identity
|
||||
]);
|
||||
|
||||
await loader.load_script("js/main.js");
|
||||
await loader.load_scripts(["js/shared-app.js"])
|
||||
},
|
||||
load_scripts_debug_web: async () => {
|
||||
await loader.load_scripts([
|
||||
"js/audio/AudioPlayer.js",
|
||||
"js/audio/sounds.js",
|
||||
"js/audio/WebCodec.js",
|
||||
"js/WebPPTListener.js",
|
||||
|
||||
"js/voice/AudioResampler.js",
|
||||
"js/voice/JavascriptRecorder.js",
|
||||
"js/voice/VoiceHandler.js",
|
||||
"js/voice/VoiceClient.js",
|
||||
|
||||
//Connection
|
||||
"js/connection/ServerConnection.js",
|
||||
"js/dns_impl.js",
|
||||
|
||||
//Load codec
|
||||
"js/codec/Codec.js",
|
||||
"js/codec/BasicCodec.js",
|
||||
{url: "js/codec/CodecWrapperWorker.js", depends: ["js/codec/BasicCodec.js"]},
|
||||
]);
|
||||
},
|
||||
load_scripts_debug_client: async () => {
|
||||
await loader.load_scripts([
|
||||
]);
|
||||
},
|
||||
|
||||
load_release: async () => {
|
||||
console.log("Load for release!");
|
||||
|
||||
|
@ -329,7 +159,7 @@ const loader_style = {
|
|||
["vendor/highlight/styles/darcula.css", ""], /* empty string means not required */
|
||||
]);
|
||||
|
||||
if(app.type == app.Type.WEB_DEBUG || app.type == app.Type.CLIENT_DEBUG) {
|
||||
if(loader.version().debug_mode) {
|
||||
await loader_style.load_style_debug();
|
||||
} else {
|
||||
await loader_style.load_style_release();
|
||||
|
@ -494,23 +324,10 @@ loader.register_task(loader.Stage.TEMPLATES, {
|
|||
|
||||
loader.register_task(loader.Stage.LOADED, {
|
||||
name: "loaded handler",
|
||||
function: async () => {
|
||||
fadeoutLoader();
|
||||
},
|
||||
function: async () => loader.hide_overlay(),
|
||||
priority: 5
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.LOADED, {
|
||||
name: "error task",
|
||||
function: async () => {
|
||||
if(Settings.instance.static(Settings.KEY_LOAD_DUMMY_ERROR, false)) {
|
||||
loader.critical_error("The tea is cold!", "Argh, this is evil! Cold tea dosn't taste good.");
|
||||
throw "The tea is cold!";
|
||||
}
|
||||
},
|
||||
priority: 20
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "lsx emoji picker setup",
|
||||
function: async () => await (window as any).setup_lsx_emoji_picker({twemoji: typeof(window.twemoji) !== "undefined"}),
|
||||
|
@ -576,23 +393,17 @@ loader.register_task(loader.Stage.SETUP, {
|
|||
priority: 100
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "log enabled initialisation",
|
||||
function: async () => log.initialize(app.type === app.Type.CLIENT_DEBUG || app.type === app.Type.WEB_DEBUG ? log.LogType.TRACE : log.LogType.INFO),
|
||||
priority: 150
|
||||
});
|
||||
|
||||
export function run() {
|
||||
window["Module"] = (window["Module"] || {}) as any;
|
||||
/* TeaClient */
|
||||
if(window.require) {
|
||||
const path = require("path");
|
||||
const remote = require('electron').remote;
|
||||
if(node_require) {
|
||||
const path = node_require("path");
|
||||
const remote = node_require('electron').remote;
|
||||
module.paths.push(path.join(remote.app.getAppPath(), "/modules"));
|
||||
module.paths.push(path.join(path.dirname(remote.getGlobal("browser-root")), "js"));
|
||||
|
||||
const connector = require("renderer");
|
||||
console.log(connector);
|
||||
|
||||
//TODO: HERE!
|
||||
const connector = node_require("renderer");
|
||||
loader.register_task(loader.Stage.INITIALIZING, {
|
||||
name: "teaclient initialize",
|
||||
function: connector.initialize,
|
||||
|
@ -604,3 +415,4 @@ if(!loader.running()) {
|
|||
/* we know that we want to load the app */
|
||||
loader.execute_managed();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
/// <reference path="loader.ts" />
|
||||
import * as loader from "./loader";
|
||||
|
||||
let is_debug = false;
|
||||
|
||||
|
@ -25,34 +25,8 @@ const loader_javascript = {
|
|||
|
||||
load_scripts: async () => {
|
||||
await loader.load_script(["vendor/jquery/jquery.min.js"]);
|
||||
|
||||
if(!is_debug) {
|
||||
loader.register_task(loader.Stage.JAVASCRIPT, {
|
||||
name: "scripts release",
|
||||
priority: 20,
|
||||
function: loader_javascript.load_release
|
||||
});
|
||||
} else {
|
||||
loader.register_task(loader.Stage.JAVASCRIPT, {
|
||||
name: "scripts debug",
|
||||
priority: 20,
|
||||
function: loader_javascript.load_scripts_debug
|
||||
});
|
||||
}
|
||||
},
|
||||
load_scripts_debug: async () => {
|
||||
await loader.load_scripts([
|
||||
["js/proto.js"],
|
||||
["js/log.js"],
|
||||
["js/BrowserIPC.js"],
|
||||
["js/settings.js"],
|
||||
["js/main.js"]
|
||||
]);
|
||||
},
|
||||
|
||||
load_release: async () => {
|
||||
await loader.load_scripts([
|
||||
["js/certaccept.min.js", "js/certaccept.js"]
|
||||
["dist/certificate-popup.js"],
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
@ -99,9 +73,7 @@ loader.register_task(loader.Stage.STYLE, {
|
|||
|
||||
loader.register_task(loader.Stage.LOADED, {
|
||||
name: "loaded handler",
|
||||
function: async () => {
|
||||
fadeoutLoader();
|
||||
},
|
||||
function: async () => loader.hide_overlay(),
|
||||
priority: 0
|
||||
});
|
||||
|
||||
|
@ -123,25 +95,6 @@ loader.register_task(loader.Stage.INITIALIZING, {
|
|||
priority: 50
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "settings initialisation",
|
||||
function: async () => Settings.initialize(),
|
||||
priority: 200
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "bipc initialisation",
|
||||
function: async () => bipc.setup(),
|
||||
priority: 100
|
||||
});
|
||||
|
||||
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "log enabled initialisation",
|
||||
function: async () => log.initialize(is_debug ? log.LogType.TRACE : log.LogType.INFO),
|
||||
priority: 150
|
||||
});
|
||||
|
||||
if(!loader.running()) {
|
||||
/* we know that we want to load the app */
|
||||
loader.execute_managed();
|
5
loader/app/index.ts
Normal file
5
loader/app/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import * as loader from "./app";
|
||||
import * as loader_base from "./loader";
|
||||
|
||||
export = loader_base;
|
||||
loader.run();
|
712
loader/app/loader.ts
Normal file
712
loader/app/loader.ts
Normal file
|
@ -0,0 +1,712 @@
|
|||
import {AppVersion} from "../exports/loader";
|
||||
import {type} from "os";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
tr(message: string) : string;
|
||||
tra(message: string, ...args: any[]);
|
||||
|
||||
log: any;
|
||||
StaticSettings: any;
|
||||
}
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
loader_groups: boolean;
|
||||
verbose: boolean;
|
||||
error: boolean;
|
||||
}
|
||||
|
||||
export let config: Config = {
|
||||
loader_groups: false,
|
||||
verbose: false,
|
||||
error: true
|
||||
};
|
||||
|
||||
export type Task = {
|
||||
name: string,
|
||||
priority: number, /* tasks with the same priority will be executed in sync */
|
||||
function: () => Promise<void>
|
||||
};
|
||||
|
||||
export enum Stage {
|
||||
/*
|
||||
loading loader required files (incl this)
|
||||
*/
|
||||
INITIALIZING,
|
||||
/*
|
||||
setting up the loading process
|
||||
*/
|
||||
SETUP,
|
||||
/*
|
||||
loading all style sheet files
|
||||
*/
|
||||
STYLE,
|
||||
/*
|
||||
loading all javascript files
|
||||
*/
|
||||
JAVASCRIPT,
|
||||
/*
|
||||
loading all template files
|
||||
*/
|
||||
TEMPLATES,
|
||||
/*
|
||||
initializing static/global stuff
|
||||
*/
|
||||
JAVASCRIPT_INITIALIZING,
|
||||
/*
|
||||
finalizing load process
|
||||
*/
|
||||
FINALIZING,
|
||||
/*
|
||||
invoking main task
|
||||
*/
|
||||
LOADED,
|
||||
|
||||
DONE
|
||||
}
|
||||
|
||||
let cache_tag: string | undefined;
|
||||
let current_stage: Stage = undefined;
|
||||
const tasks: {[key:number]:Task[]} = {};
|
||||
|
||||
/* test if all files shall be load from cache or fetch again */
|
||||
function loader_cache_tag() {
|
||||
const app_version = (() => {
|
||||
const version_node = document.getElementById("app_version");
|
||||
if(!version_node) return undefined;
|
||||
|
||||
const version = version_node.hasAttribute("value") ? version_node.getAttribute("value") : undefined;
|
||||
if(!version) return undefined;
|
||||
|
||||
if(!version || version == "unknown" || version.replace(/0+/, "").length == 0)
|
||||
return undefined;
|
||||
|
||||
return version;
|
||||
})();
|
||||
if(config.verbose) console.log("Found current app version: %o", app_version);
|
||||
|
||||
if(!app_version) {
|
||||
/* TODO add warning */
|
||||
cache_tag = "?_ts=" + Date.now();
|
||||
return;
|
||||
}
|
||||
const cached_version = localStorage.getItem("cached_version");
|
||||
if(!cached_version || cached_version != app_version) {
|
||||
register_task(Stage.LOADED, {
|
||||
priority: 0,
|
||||
name: "cached version updater",
|
||||
function: async () => {
|
||||
localStorage.setItem("cached_version", app_version);
|
||||
}
|
||||
});
|
||||
}
|
||||
cache_tag = "?_version=" + app_version;
|
||||
}
|
||||
|
||||
export function get_cache_version() { return cache_tag; }
|
||||
|
||||
export function finished() {
|
||||
return current_stage == Stage.DONE;
|
||||
}
|
||||
export function running() { return typeof(current_stage) !== "undefined"; }
|
||||
|
||||
export function register_task(stage: Stage, task: Task) {
|
||||
if(current_stage > stage) {
|
||||
if(config.error)
|
||||
console.warn("Register loading task, but it had already been finished. Executing task anyways!");
|
||||
task.function().catch(error => {
|
||||
if(config.error) {
|
||||
console.error("Failed to execute delayed loader task!");
|
||||
console.log(" - %s: %o", task.name, error);
|
||||
}
|
||||
|
||||
critical_error(error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const task_array = tasks[stage] || [];
|
||||
task_array.push(task);
|
||||
tasks[stage] = task_array.sort((a, b) => a.priority - b.priority);
|
||||
}
|
||||
|
||||
export async function execute() {
|
||||
document.getElementById("loader-overlay").classList.add("started");
|
||||
loader_cache_tag();
|
||||
|
||||
const load_begin = Date.now();
|
||||
|
||||
let begin: number = 0;
|
||||
let end: number = Date.now();
|
||||
while(current_stage <= Stage.LOADED || typeof(current_stage) === "undefined") {
|
||||
|
||||
let current_tasks: Task[] = [];
|
||||
while((tasks[current_stage] || []).length > 0) {
|
||||
if(current_tasks.length == 0 || current_tasks[0].priority == tasks[current_stage][0].priority) {
|
||||
current_tasks.push(tasks[current_stage].pop());
|
||||
} else break;
|
||||
}
|
||||
|
||||
const errors: {
|
||||
error: any,
|
||||
task: Task
|
||||
}[] = [];
|
||||
|
||||
const promises: Promise<void>[] = [];
|
||||
for(const task of current_tasks) {
|
||||
try {
|
||||
if(config.verbose) console.debug("Executing loader %s (%d)", task.name, task.priority);
|
||||
promises.push(task.function().catch(error => {
|
||||
errors.push({
|
||||
task: task,
|
||||
error: error
|
||||
});
|
||||
return Promise.resolve();
|
||||
}));
|
||||
} catch(error) {
|
||||
errors.push({
|
||||
task: task,
|
||||
error: error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if(promises.length > 0) {
|
||||
await Promise.all([...promises]);
|
||||
}
|
||||
|
||||
if(errors.length > 0) {
|
||||
if(config.loader_groups) console.groupEnd();
|
||||
console.error("Failed to execute loader. The following tasks failed (%d):", errors.length);
|
||||
for(const error of errors)
|
||||
console.error(" - %s: %o", error.task.name, error.error);
|
||||
|
||||
throw "failed to process step " + Stage[current_stage];
|
||||
}
|
||||
|
||||
if(current_tasks.length == 0) {
|
||||
if(typeof(current_stage) === "undefined") {
|
||||
current_stage = -1;
|
||||
if(config.verbose) console.debug("[loader] Booting app");
|
||||
} else if(current_stage < Stage.INITIALIZING) {
|
||||
if(config.loader_groups) console.groupEnd();
|
||||
if(config.verbose) console.debug("[loader] Entering next state (%s). Last state took %dms", Stage[current_stage + 1], (end = Date.now()) - begin);
|
||||
} else {
|
||||
if(config.loader_groups) console.groupEnd();
|
||||
if(config.verbose) console.debug("[loader] Finish invoke took %dms", (end = Date.now()) - begin);
|
||||
}
|
||||
|
||||
begin = end;
|
||||
current_stage += 1;
|
||||
|
||||
if(current_stage != Stage.DONE && config.loader_groups)
|
||||
console.groupCollapsed("Executing loading stage %s", Stage[current_stage]);
|
||||
}
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
{
|
||||
_script_promises = {};
|
||||
}
|
||||
if(config.verbose) console.debug("[loader] finished loader. (Total time: %dms)", Date.now() - load_begin);
|
||||
}
|
||||
export function execute_managed() {
|
||||
execute().then(() => {
|
||||
if(config.verbose) {
|
||||
let message;
|
||||
if(typeof(window.tr) !== "undefined")
|
||||
message = tr("App loaded successfully!");
|
||||
else
|
||||
message = "App loaded successfully!";
|
||||
|
||||
if(typeof(window.log) !== "undefined") {
|
||||
/* We're having our log module */
|
||||
window.log.info(window.log.LogCategory.GENERAL, message);
|
||||
} else {
|
||||
console.log(message);
|
||||
}
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error("App loading failed: %o", error);
|
||||
critical_error("Failed to execute loader", "Lookup the console for more detail");
|
||||
});
|
||||
}
|
||||
|
||||
export type DependSource = {
|
||||
url: string;
|
||||
depends: string[];
|
||||
}
|
||||
export type SourcePath = string | DependSource | string[];
|
||||
|
||||
function script_name(path: SourcePath, html: boolean) {
|
||||
if(Array.isArray(path)) {
|
||||
let buffer = "";
|
||||
let _or = " or ";
|
||||
for(let entry of path)
|
||||
buffer += _or + script_name(entry, html);
|
||||
return buffer.slice(_or.length);
|
||||
} else if(typeof(path) === "string")
|
||||
return html ? "<code>" + path + "</code>" : path;
|
||||
else
|
||||
return html ? "<code>" + path.url + "</code>" : path.url;
|
||||
}
|
||||
|
||||
class SyntaxError {
|
||||
source: any;
|
||||
|
||||
constructor(source: any) {
|
||||
this.source = source;
|
||||
}
|
||||
}
|
||||
|
||||
let _script_promises: {[key: string]: Promise<void>} = {};
|
||||
export async function load_script(path: SourcePath) : Promise<void> {
|
||||
if(Array.isArray(path)) { //We have some fallback
|
||||
return load_script(path[0]).catch(error => {
|
||||
console.log(typeof error + " - " + (error instanceof SyntaxError));
|
||||
if(error instanceof SyntaxError)
|
||||
return Promise.reject(error);
|
||||
|
||||
if(path.length > 1)
|
||||
return load_script(path.slice(1));
|
||||
|
||||
return Promise.reject(error);
|
||||
});
|
||||
} else {
|
||||
const source = typeof(path) === "string" ? {url: path, depends: []} : path;
|
||||
if(source.url.length == 0) return Promise.resolve();
|
||||
|
||||
return _script_promises[source.url] = (async () => {
|
||||
/* await depends */
|
||||
for(const depend of source.depends) {
|
||||
if(!_script_promises[depend])
|
||||
throw "Missing dependency " + depend;
|
||||
await _script_promises[depend];
|
||||
}
|
||||
|
||||
const tag: HTMLScriptElement = document.createElement("script");
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
let error = false;
|
||||
const error_handler = (event: ErrorEvent) => {
|
||||
if(event.filename == tag.src && event.message.indexOf("Illegal constructor") == -1) { //Our tag throw an uncaught error
|
||||
if(config.verbose) console.log("msg: %o, url: %o, line: %o, col: %o, error: %o", event.message, event.filename, event.lineno, event.colno, event.error);
|
||||
window.removeEventListener('error', error_handler as any);
|
||||
|
||||
reject(new SyntaxError(event.error));
|
||||
event.preventDefault();
|
||||
error = true;
|
||||
}
|
||||
};
|
||||
window.addEventListener('error', error_handler as any);
|
||||
|
||||
const cleanup = () => {
|
||||
tag.onerror = undefined;
|
||||
tag.onload = undefined;
|
||||
|
||||
clearTimeout(timeout_handle);
|
||||
window.removeEventListener('error', error_handler as any);
|
||||
};
|
||||
const timeout_handle = setTimeout(() => {
|
||||
cleanup();
|
||||
reject("timeout");
|
||||
}, 5000);
|
||||
tag.type = "application/javascript";
|
||||
tag.async = true;
|
||||
tag.defer = true;
|
||||
tag.onerror = error => {
|
||||
cleanup();
|
||||
tag.remove();
|
||||
reject(error);
|
||||
};
|
||||
tag.onload = () => {
|
||||
cleanup();
|
||||
|
||||
if(config.verbose) console.debug("Script %o loaded", path);
|
||||
setTimeout(resolve, 100);
|
||||
};
|
||||
|
||||
document.getElementById("scripts").appendChild(tag);
|
||||
|
||||
tag.src = source.url + (cache_tag || "");
|
||||
});
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
export async function load_scripts(paths: SourcePath[]) : Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
const errors: {
|
||||
script: SourcePath,
|
||||
error: any
|
||||
}[] = [];
|
||||
|
||||
for(const script of paths)
|
||||
promises.push(load_script(script).catch(error => {
|
||||
errors.push({
|
||||
script: script,
|
||||
error: error
|
||||
});
|
||||
return Promise.resolve();
|
||||
}));
|
||||
|
||||
await Promise.all([...promises]);
|
||||
|
||||
if(errors.length > 0) {
|
||||
if(config.error) {
|
||||
console.error("Failed to load the following scripts:");
|
||||
for(const script of errors) {
|
||||
const sname = script_name(script.script, false);
|
||||
if(script.error instanceof SyntaxError) {
|
||||
const source = script.error.source as Error;
|
||||
if(source.name === "TypeError") {
|
||||
let prefix = "";
|
||||
while(prefix.length < sname.length + 7) prefix += " ";
|
||||
console.log(" - %s: %s:\n%s", sname, source.message, source.stack.split("\n").map(e => prefix + e.trim()).slice(1).join("\n"));
|
||||
} else {
|
||||
console.log(" - %s: %o", sname, source);
|
||||
}
|
||||
} else {
|
||||
console.log(" - %s: %o", sname, script.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
critical_error("Failed to load script " + script_name(errors[0].script, true) + " <br>" + "View the browser console for more information!");
|
||||
throw "failed to load script " + script_name(errors[0].script, false);
|
||||
}
|
||||
}
|
||||
|
||||
export async function load_style(path: SourcePath) : Promise<void> {
|
||||
if(Array.isArray(path)) { //We have some fallback
|
||||
return load_style(path[0]).catch(error => {
|
||||
if(error instanceof SyntaxError)
|
||||
return Promise.reject(error.source);
|
||||
|
||||
if(path.length > 1)
|
||||
return load_script(path.slice(1));
|
||||
|
||||
return Promise.reject(error);
|
||||
});
|
||||
} else {
|
||||
if(!path) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const tag: HTMLLinkElement = document.createElement("link");
|
||||
|
||||
let error = false;
|
||||
const error_handler = (event: ErrorEvent) => {
|
||||
if(config.verbose) console.log("msg: %o, url: %o, line: %o, col: %o, error: %o", event.message, event.filename, event.lineno, event.colno, event.error);
|
||||
if(event.filename == tag.href) { //FIXME!
|
||||
window.removeEventListener('error', error_handler as any);
|
||||
|
||||
reject(new SyntaxError(event.error));
|
||||
event.preventDefault();
|
||||
error = true;
|
||||
}
|
||||
};
|
||||
window.addEventListener('error', error_handler as any);
|
||||
|
||||
tag.type = "text/css";
|
||||
tag.rel = "stylesheet";
|
||||
|
||||
const cleanup = () => {
|
||||
tag.onerror = undefined;
|
||||
tag.onload = undefined;
|
||||
|
||||
clearTimeout(timeout_handle);
|
||||
window.removeEventListener('error', error_handler as any);
|
||||
};
|
||||
|
||||
const timeout_handle = setTimeout(() => {
|
||||
cleanup();
|
||||
reject("timeout");
|
||||
}, 5000);
|
||||
|
||||
tag.onerror = error => {
|
||||
cleanup();
|
||||
tag.remove();
|
||||
if(config.error)
|
||||
console.error("File load error for file %s: %o", path, error);
|
||||
reject("failed to load file " + path);
|
||||
};
|
||||
tag.onload = () => {
|
||||
cleanup();
|
||||
{
|
||||
const css: CSSStyleSheet = tag.sheet as CSSStyleSheet;
|
||||
const rules = css.cssRules;
|
||||
const rules_remove: number[] = [];
|
||||
const rules_add: string[] = [];
|
||||
|
||||
for(let index = 0; index < rules.length; index++) {
|
||||
const rule = rules.item(index);
|
||||
let rule_text = rule.cssText;
|
||||
|
||||
if(rule.cssText.indexOf("%%base_path%%") != -1) {
|
||||
rules_remove.push(index);
|
||||
rules_add.push(rule_text.replace("%%base_path%%", document.location.origin + document.location.pathname));
|
||||
}
|
||||
}
|
||||
|
||||
for(const index of rules_remove.sort((a, b) => b > a ? 1 : 0)) {
|
||||
if(css.removeRule)
|
||||
css.removeRule(index);
|
||||
else
|
||||
css.deleteRule(index);
|
||||
}
|
||||
for(const rule of rules_add)
|
||||
css.insertRule(rule, rules_remove[0]);
|
||||
}
|
||||
|
||||
if(config.verbose) console.debug("Style sheet %o loaded", path);
|
||||
setTimeout(resolve, 100);
|
||||
};
|
||||
|
||||
document.getElementById("style").appendChild(tag);
|
||||
tag.href = path + (cache_tag || "");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function load_styles(paths: SourcePath[]) : Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
const errors: {
|
||||
sheet: SourcePath,
|
||||
error: any
|
||||
}[] = [];
|
||||
|
||||
for(const sheet of paths)
|
||||
promises.push(load_style(sheet).catch(error => {
|
||||
errors.push({
|
||||
sheet: sheet,
|
||||
error: error
|
||||
});
|
||||
return Promise.resolve();
|
||||
}));
|
||||
|
||||
await Promise.all([...promises]);
|
||||
|
||||
if(errors.length > 0) {
|
||||
if(config.error) {
|
||||
console.error("Failed to load the following style sheet:");
|
||||
for(const sheet of errors)
|
||||
console.log(" - %o: %o", sheet.sheet, sheet.error);
|
||||
}
|
||||
|
||||
critical_error("Failed to load style sheet " + script_name(errors[0].sheet, true) + " <br>" + "View the browser console for more information!");
|
||||
throw "failed to load style sheet " + script_name(errors[0].sheet, false);
|
||||
}
|
||||
}
|
||||
|
||||
export async function load_template(path: SourcePath) : Promise<void> {
|
||||
try {
|
||||
const response = await $.ajax(path + (cache_tag || ""));
|
||||
|
||||
let node = document.createElement("html");
|
||||
node.innerHTML = response;
|
||||
let tags: HTMLCollection;
|
||||
if(node.getElementsByTagName("body").length > 0)
|
||||
tags = node.getElementsByTagName("body")[0].children;
|
||||
else
|
||||
tags = node.children;
|
||||
|
||||
let root = document.getElementById("templates");
|
||||
if(!root) {
|
||||
critical_error("Failed to find template tag!");
|
||||
throw "Failed to find template tag";
|
||||
}
|
||||
while(tags.length > 0){
|
||||
let tag = tags.item(0);
|
||||
root.appendChild(tag);
|
||||
|
||||
}
|
||||
} catch(error) {
|
||||
let msg;
|
||||
if('responseText' in error)
|
||||
msg = error.responseText;
|
||||
else if(error instanceof Error)
|
||||
msg = error.message;
|
||||
|
||||
critical_error("failed to load template " + script_name(path, true), msg);
|
||||
throw "template error";
|
||||
}
|
||||
}
|
||||
|
||||
export async function load_templates(paths: SourcePath[]) : Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
const errors: {
|
||||
template: SourcePath,
|
||||
error: any
|
||||
}[] = [];
|
||||
|
||||
for(const template of paths)
|
||||
promises.push(load_template(template).catch(error => {
|
||||
errors.push({
|
||||
template: template,
|
||||
error: error
|
||||
});
|
||||
return Promise.resolve();
|
||||
}));
|
||||
|
||||
await Promise.all([...promises]);
|
||||
|
||||
if(errors.length > 0) {
|
||||
if (config.error) {
|
||||
console.error("Failed to load the following templates:");
|
||||
for (const sheet of errors)
|
||||
console.log(" - %s: %o", script_name(sheet.template, false), sheet.error);
|
||||
}
|
||||
|
||||
critical_error("Failed to load template " + script_name(errors[0].template, true) + " <br>" + "View the browser console for more information!");
|
||||
throw "failed to load template " + script_name(errors[0].template, false);
|
||||
}
|
||||
}
|
||||
|
||||
let version_: AppVersion;
|
||||
export function version() : AppVersion { return version_; }
|
||||
export function set_version(version: AppVersion) { version_ = version; }
|
||||
|
||||
export type ErrorHandler = (message: string, detail: string) => void;
|
||||
|
||||
let _callback_critical_error: ErrorHandler;
|
||||
let _callback_critical_called: boolean = false;
|
||||
|
||||
export function critical_error(message: string, detail?: string) {
|
||||
if(_callback_critical_called) {
|
||||
console.warn("[CRITICAL] %s", message);
|
||||
if(typeof(detail) === "string")
|
||||
console.warn("[CRITICAL] %s", detail);
|
||||
return;
|
||||
}
|
||||
|
||||
_callback_critical_called = true;
|
||||
if(_callback_critical_error) {
|
||||
_callback_critical_error(message, detail);
|
||||
return;
|
||||
}
|
||||
|
||||
/* default handling */
|
||||
let tag = document.getElementById("critical-load");
|
||||
|
||||
{
|
||||
const error_tags = tag.getElementsByClassName("error");
|
||||
error_tags[0].innerHTML = message;
|
||||
}
|
||||
|
||||
if(typeof(detail) === "string") {
|
||||
let node_detail = tag.getElementsByClassName("detail")[0];
|
||||
node_detail.innerHTML = detail;
|
||||
}
|
||||
|
||||
tag.classList.add("shown");
|
||||
}
|
||||
|
||||
export function critical_error_handler(handler?: ErrorHandler, override?: boolean) : ErrorHandler {
|
||||
if((typeof(handler) === "object" && handler !== _callback_critical_error) || override)
|
||||
_callback_critical_error = handler;
|
||||
return _callback_critical_error;
|
||||
}
|
||||
|
||||
let _fadeout_warned;
|
||||
export function hide_overlay() {
|
||||
if(typeof($) === "undefined") {
|
||||
if(!_fadeout_warned)
|
||||
console.warn("Could not fadeout loader screen. Missing jquery functions.");
|
||||
_fadeout_warned = true;
|
||||
return;
|
||||
}
|
||||
const animation_duration = 750;
|
||||
|
||||
$(".loader .bookshelf_wrapper").animate({top: 0, opacity: 0}, animation_duration);
|
||||
$(".loader .half").animate({width: 0}, animation_duration, () => {
|
||||
$(".loader").detach();
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
const hello_world = () => {
|
||||
const clog = console.log;
|
||||
const print_security = () => {
|
||||
{
|
||||
const css = [
|
||||
"display: block",
|
||||
"text-align: center",
|
||||
"font-size: 42px",
|
||||
"font-weight: bold",
|
||||
"-webkit-text-stroke: 2px black",
|
||||
"color: red"
|
||||
].join(";");
|
||||
clog("%c ", "font-size: 100px;");
|
||||
clog("%cSecurity warning:", css);
|
||||
}
|
||||
{
|
||||
const css = [
|
||||
"display: block",
|
||||
"text-align: center",
|
||||
"font-size: 18px",
|
||||
"font-weight: bold"
|
||||
].join(";");
|
||||
|
||||
clog("%cPasting anything in here could give attackers access to your data.", css);
|
||||
clog("%cUnless you understand exactly what you are doing, close this window and stay safe.", css);
|
||||
clog("%c ", "font-size: 100px;");
|
||||
}
|
||||
};
|
||||
|
||||
/* print the hello world */
|
||||
{
|
||||
const css = [
|
||||
"display: block",
|
||||
"text-align: center",
|
||||
"font-size: 72px",
|
||||
"font-weight: bold",
|
||||
"-webkit-text-stroke: 2px black",
|
||||
"color: #18BC9C"
|
||||
].join(";");
|
||||
clog("%cHey, hold on!", css);
|
||||
}
|
||||
{
|
||||
const css = [
|
||||
"display: block",
|
||||
"text-align: center",
|
||||
"font-size: 26px",
|
||||
"font-weight: bold"
|
||||
].join(";");
|
||||
|
||||
const css_2 = [
|
||||
"display: block",
|
||||
"text-align: center",
|
||||
"font-size: 26px",
|
||||
"font-weight: bold",
|
||||
"color: blue"
|
||||
].join(";");
|
||||
|
||||
const display_detect = /./;
|
||||
display_detect.toString = function() { print_security(); return ""; };
|
||||
|
||||
clog("%cLovely to see you using and debugging the TeaSpeak-Web client.", css);
|
||||
clog("%cIf you have some good ideas or already done some incredible changes,", css);
|
||||
clog("%cyou'll be may interested to share them here: %chttps://github.com/TeaSpeak/TeaWeb", css, css_2);
|
||||
clog("%c ", display_detect);
|
||||
}
|
||||
};
|
||||
|
||||
try { /* lets try to print it as VM code :)*/
|
||||
let hello_world_code = hello_world.toString();
|
||||
hello_world_code = hello_world_code.substr(hello_world_code.indexOf('() => {') + 8);
|
||||
hello_world_code = hello_world_code.substring(0, hello_world_code.lastIndexOf("}"));
|
||||
|
||||
//Look aheads are not possible with firefox
|
||||
//hello_world_code = hello_world_code.replace(/(?<!const|let)(?<=^([^"'/]|"[^"]*"|'[^']*'|`[^`]*`|\/[^/]*\/)*) /gm, ""); /* replace all spaces */
|
||||
hello_world_code = hello_world_code.replace(/[\n\r]/g, ""); /* replace as new lines */
|
||||
|
||||
eval(hello_world_code);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
hello_world();
|
||||
}
|
||||
}
|
85
loader/exports/loader.d.ts
vendored
Normal file
85
loader/exports/loader.d.ts
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
export interface Config {
|
||||
loader_groups: boolean;
|
||||
verbose: boolean;
|
||||
error: boolean;
|
||||
}
|
||||
|
||||
export enum BackendType {
|
||||
WEB,
|
||||
NATIVE
|
||||
}
|
||||
|
||||
export interface AppVersion {
|
||||
ui: string;
|
||||
backend: string;
|
||||
|
||||
type: "web" | "native";
|
||||
debug_mode: boolean;
|
||||
}
|
||||
|
||||
export let config: Config;
|
||||
|
||||
export type Task = {
|
||||
name: string,
|
||||
priority: number, /* tasks with the same priority will be executed in sync */
|
||||
function: () => Promise<void>
|
||||
};
|
||||
export enum Stage {
|
||||
/*
|
||||
loading loader required files (incl this)
|
||||
*/
|
||||
INITIALIZING,
|
||||
/*
|
||||
setting up the loading process
|
||||
*/
|
||||
SETUP,
|
||||
/*
|
||||
loading all style sheet files
|
||||
*/
|
||||
STYLE,
|
||||
/*
|
||||
loading all javascript files
|
||||
*/
|
||||
JAVASCRIPT,
|
||||
/*
|
||||
loading all template files
|
||||
*/
|
||||
TEMPLATES,
|
||||
/*
|
||||
initializing static/global stuff
|
||||
*/
|
||||
JAVASCRIPT_INITIALIZING,
|
||||
/*
|
||||
finalizing load process
|
||||
*/
|
||||
FINALIZING,
|
||||
/*
|
||||
invoking main task
|
||||
*/
|
||||
LOADED,
|
||||
|
||||
DONE
|
||||
}
|
||||
|
||||
export function version() : AppVersion;
|
||||
|
||||
export function finished();
|
||||
export function running();
|
||||
export function register_task(stage: Stage, task: Task);
|
||||
export function execute() : Promise<void>;
|
||||
export function execute_managed();
|
||||
export type DependSource = {
|
||||
url: string;
|
||||
depends: string[];
|
||||
}
|
||||
export type SourcePath = string | DependSource | string[];
|
||||
export function load_script(path: SourcePath) : Promise<void>;
|
||||
export function load_scripts(paths: SourcePath[]) : Promise<void>;
|
||||
export function load_style(path: SourcePath) : Promise<void>;
|
||||
export function load_styles(paths: SourcePath[]) : Promise<void>;
|
||||
export function load_template(path: SourcePath) : Promise<void>;
|
||||
export function load_templates(paths: SourcePath[]) : Promise<void>;
|
||||
export type ErrorHandler = (message: string, detail: string) => void;
|
||||
export function critical_error(message: string, detail?: string);
|
||||
export function critical_error_handler(handler?: ErrorHandler, override?: boolean);
|
||||
export function hide_overlay();
|
62
loader/webpack.config.js
Normal file
62
loader/webpack.config.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
const path = require('path');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
module.exports = {
|
||||
entry: path.join(__dirname, "app/index.ts"),
|
||||
devtool: 'inline-source-map',
|
||||
mode: "development",
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: isDevelopment ? '[name].css' : '[name].[hash].css',
|
||||
chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css'
|
||||
})
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.s[ac]ss$/,
|
||||
loader: [
|
||||
//isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
modules: true,
|
||||
sourceMap: isDevelopment
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
sourceMap: isDevelopment
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
exclude: /node_modules/,
|
||||
|
||||
loader: [
|
||||
{
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
transpileOnly: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js', ".scss"],
|
||||
},
|
||||
output: {
|
||||
filename: 'loader.js',
|
||||
path: path.resolve(__dirname, '../dist'),
|
||||
library: "loader",
|
||||
//libraryTarget: "umd" //"var" | "assign" | "this" | "window" | "self" | "global" | "commonjs" | "commonjs2" | "commonjs-module" | "amd" | "amd-require" | "umd" | "umd2" | "jsonp" | "system"
|
||||
},
|
||||
optimization: { }
|
||||
};
|
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -1087,6 +1087,11 @@
|
|||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"circular-dependency-plugin": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz",
|
||||
"integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw=="
|
||||
},
|
||||
"clap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz",
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
"minify-web-rel-file": "terser --compress --mangle --ecma 6 --keep_classnames --keep_fnames --output",
|
||||
"start": "npm run compile-file-helper && node file.js ndevelop",
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"watch": "webpack --watch"
|
||||
"build-loader": "webpack --config loader/webpack.config.js",
|
||||
"watch": "webpack --watch",
|
||||
"watch-loader": "webpack --watch --config loader/webpack.config.js"
|
||||
},
|
||||
"author": "TeaSpeak (WolverinDEV)",
|
||||
"license": "ISC",
|
||||
|
@ -63,6 +65,7 @@
|
|||
"homepage": "https://www.teaspeak.de",
|
||||
"dependencies": {
|
||||
"@types/fs-extra": "^8.0.1",
|
||||
"circular-dependency-plugin": "^5.2.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1"
|
||||
}
|
||||
|
|
19
shared/backend.d/audio/player.d.ts
vendored
Normal file
19
shared/backend.d/audio/player.d.ts
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
import {Device} from "tc-shared/audio/player";
|
||||
|
||||
export function initialize() : boolean;
|
||||
export function initialized() : boolean;
|
||||
|
||||
export function context() : AudioContext;
|
||||
export function get_master_volume() : number;
|
||||
export function set_master_volume(volume: number);
|
||||
|
||||
export function destination() : AudioNode;
|
||||
|
||||
export function on_ready(cb: () => any);
|
||||
|
||||
export function available_devices() : Promise<Device[]>;
|
||||
export function set_device(device_id: string) : Promise<void>;
|
||||
|
||||
export function current_device() : Device;
|
||||
|
||||
export function initializeFromGesture();
|
9
shared/backend.d/audio/recorder.d.ts
vendored
Normal file
9
shared/backend.d/audio/recorder.d.ts
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
import {AbstractInput, InputDevice, LevelMeter} from "tc-shared/voice/RecorderBase";
|
||||
|
||||
export function devices() : InputDevice[];
|
||||
|
||||
export function device_refresh_available() : boolean;
|
||||
export function refresh_devices() : Promise<void>;
|
||||
|
||||
export function create_input() : AbstractInput;
|
||||
export function create_levelmeter(device: InputDevice) : Promise<LevelMeter>;
|
3
shared/backend.d/audio/sounds.d.ts
vendored
Normal file
3
shared/backend.d/audio/sounds.d.ts
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
import {SoundFile} from "tc-shared/sound/Sounds";
|
||||
|
||||
export function play_sound(file: SoundFile) : Promise<void>;
|
5
shared/backend.d/connection.d.ts
vendored
Normal file
5
shared/backend.d/connection.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase";
|
||||
|
||||
export function spawn_server_connection(handle: ConnectionHandler) : AbstractServerConnection;
|
||||
export function destroy_server_connection(handle: AbstractServerConnection);
|
5
shared/backend.d/dns.d.ts
vendored
Normal file
5
shared/backend.d/dns.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
import {AddressTarget, ResolveOptions} from "tc-shared/dns";
|
||||
import {ServerAddress} from "tc-shared/ui/server";
|
||||
|
||||
export function supported();
|
||||
export function resolve_address(address: ServerAddress, options?: ResolveOptions) : Promise<AddressTarget>;
|
12
shared/backend.d/ppt.d.ts
vendored
Normal file
12
shared/backend.d/ppt.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
import {KeyEvent, KeyHook, SpecialKey} from "tc-shared/PPTListener";
|
||||
|
||||
export function initialize() : Promise<void>;
|
||||
export function finalize(); // most the times not really required
|
||||
|
||||
export function register_key_listener(listener: (_: KeyEvent) => any);
|
||||
export function unregister_key_listener(listener: (_: KeyEvent) => any);
|
||||
|
||||
export function register_key_hook(hook: KeyHook);
|
||||
export function unregister_key_hook(hook: KeyHook);
|
||||
|
||||
export function key_pressed(code: string | SpecialKey) : boolean;
|
35
shared/backend/audio.d.ts
vendored
35
shared/backend/audio.d.ts
vendored
|
@ -1,35 +0,0 @@
|
|||
declare namespace audio {
|
||||
export namespace player {
|
||||
export function initialize() : boolean;
|
||||
export function initialized() : boolean;
|
||||
|
||||
export function context() : AudioContext;
|
||||
export function get_master_volume() : number;
|
||||
export function set_master_volume(volume: number);
|
||||
|
||||
export function destination() : AudioNode;
|
||||
|
||||
export function on_ready(cb: () => any);
|
||||
|
||||
export function available_devices() : Promise<Device[]>;
|
||||
export function set_device(device_id: string) : Promise<void>;
|
||||
|
||||
export function current_device() : Device;
|
||||
|
||||
export function initializeFromGesture();
|
||||
}
|
||||
|
||||
export namespace recorder {
|
||||
export function devices() : InputDevice[];
|
||||
|
||||
export function device_refresh_available() : boolean;
|
||||
export function refresh_devices() : Promise<void>;
|
||||
|
||||
export function create_input() : AbstractInput;
|
||||
export function create_levelmeter(device: InputDevice) : Promise<LevelMeter>;
|
||||
}
|
||||
|
||||
export namespace sounds {
|
||||
export function play_sound(file: sound.SoundFile) : Promise<void>;
|
||||
}
|
||||
}
|
4
shared/backend/connection.d.ts
vendored
4
shared/backend/connection.d.ts
vendored
|
@ -1,4 +0,0 @@
|
|||
declare namespace connection {
|
||||
export function spawn_server_connection(handle: ConnectionHandler) : AbstractServerConnection;
|
||||
export function destroy_server_connection(handle: AbstractServerConnection);
|
||||
}
|
4
shared/backend/dns.d.ts
vendored
4
shared/backend/dns.d.ts
vendored
|
@ -1,4 +0,0 @@
|
|||
declare namespace dns {
|
||||
export function supported();
|
||||
export function resolve_address(address: ServerAddress, options?: ResolveOptions) : Promise<AddressTarget>;
|
||||
}
|
12
shared/backend/ppt.d.ts
vendored
12
shared/backend/ppt.d.ts
vendored
|
@ -1,12 +0,0 @@
|
|||
declare namespace ppt {
|
||||
export function initialize() : Promise<void>;
|
||||
export function finalize(); // most the times not really required
|
||||
|
||||
export function register_key_listener(listener: (_: KeyEvent) => any);
|
||||
export function unregister_key_listener(listener: (_: KeyEvent) => any);
|
||||
|
||||
export function register_key_hook(hook: KeyHook);
|
||||
export function unregister_key_hook(hook: KeyHook);
|
||||
|
||||
export function key_pressed(code: string | SpecialKey) : boolean;
|
||||
}
|
|
@ -94,7 +94,96 @@
|
|||
}
|
||||
|
||||
&.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 {
|
||||
|
@ -183,6 +272,40 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -145,9 +145,11 @@
|
|||
|
||||
<meta name="app-loader-target" content="app">
|
||||
<div id="scripts">
|
||||
<!--
|
||||
<script type="application/javascript" src="loader/loader_app.min.js" async defer></script>
|
||||
<script type="application/javascript" src="loader/loader_app.js" async defer></script>
|
||||
<script type="application/javascript" src="loader/loader.js?_<?php echo time() ?>" async defer></script>
|
||||
-->
|
||||
<script type="application/javascript" src="js/loader.js?_<?php echo time() ?>" async defer></script>
|
||||
</div>
|
||||
|
||||
<!-- Loading screen -->
|
||||
|
|
|
@ -2736,13 +2736,13 @@
|
|||
</div>
|
||||
{{else type == "default" }}
|
||||
<div class="entry default {{if selected}}selected{{/if}}">
|
||||
<div class="country flag-gb" title="{{*:i18n.country_name('gb')}}"></div>
|
||||
<div class="country flag-gb" title="{{>fallback_country_name}}"></div>
|
||||
<div class="name">{{tr "English (Default / Fallback)" /}}</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="entry translation {{if selected}}selected{{/if}}" parent-repository="{{:id}}">
|
||||
<div class="country flag-{{:country_code}}"
|
||||
title="{{*:i18n.country_name(data.country_code || 'XX')}}"></div>
|
||||
title="{{>country_name}}"></div>
|
||||
<div class="name">{{> name}}</div>
|
||||
<div class="button button-info">
|
||||
<div class="icon client-about"></div>
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
interface Window {
|
||||
BroadcastChannel: BroadcastChannel;
|
||||
}
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
|
||||
namespace bipc {
|
||||
export interface BroadcastMessage {
|
||||
timestamp: number;
|
||||
receiver: string;
|
||||
|
@ -724,4 +722,3 @@ namespace bipc {
|
|||
/* ios does not support this */
|
||||
return typeof(window.BroadcastChannel) !== "undefined";
|
||||
}
|
||||
}
|
|
@ -1,14 +1,38 @@
|
|||
/// <reference path="log.ts" />
|
||||
/// <reference path="proto.ts" />
|
||||
/// <reference path="ui/view.ts" />
|
||||
/// <reference path="settings.ts" />
|
||||
/// <reference path="FileManager.ts" />
|
||||
/// <reference path="permission/PermissionManager.ts" />
|
||||
/// <reference path="permission/GroupManager.ts" />
|
||||
/// <reference path="ui/frames/ControlBar.ts" />
|
||||
/// <reference path="connection/ConnectionBase.ts" />
|
||||
import {ChannelTree} from "tc-shared/ui/view";
|
||||
import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase";
|
||||
import {PermissionManager} from "tc-shared/permission/PermissionManager";
|
||||
import {GroupManager} from "tc-shared/permission/GroupManager";
|
||||
import {ServerSettings, Settings, StaticSettings} from "tc-shared/settings";
|
||||
import {Sound, SoundManager} from "tc-shared/sound/Sounds";
|
||||
import {LocalClientEntry} from "tc-shared/ui/client";
|
||||
import {ServerLog} from "tc-shared/ui/frames/server_log";
|
||||
import {ConnectionProfile, default_profile, find_profile} from "tc-shared/profiles/ConnectionProfile";
|
||||
import {ServerAddress} from "tc-shared/ui/server";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as server_log from "tc-shared/ui/frames/server_log";
|
||||
import {createErrorModal, createInfoModal, createInputModal, Modal} from "tc-shared/ui/elements/Modal";
|
||||
import {hashPassword} from "tc-shared/utils/helpers";
|
||||
import {HandshakeHandler} from "tc-shared/connection/HandshakeHandler";
|
||||
import * as htmltags from "./ui/htmltags";
|
||||
import {ChannelEntry} from "tc-shared/ui/channel";
|
||||
import {InputStartResult, InputState} from "tc-shared/voice/RecorderBase";
|
||||
import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {guid} from "tc-shared/crypto/uid";
|
||||
import * as bipc from "./BrowserIPC";
|
||||
import {FileManager, spawn_upload_transfer, UploadKey} from "tc-shared/FileManager";
|
||||
import {RecorderProfile} from "tc-shared/voice/RecorderProfile";
|
||||
import {Frame} from "tc-shared/ui/frames/chat_frame";
|
||||
import {Hostbanner} from "tc-shared/ui/frames/hostbanner";
|
||||
import {server_connections} from "tc-shared/ui/frames/connection_handlers";
|
||||
import {connection_log, Regex} from "tc-shared/ui/modal/ModalConnect";
|
||||
import {control_bar} from "tc-shared/ui/frames/ControlBar";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {spawnAvatarUpload} from "tc-shared/ui/modal/ModalAvatar";
|
||||
import * as connection from "tc-backend/connection";
|
||||
import * as dns from "tc-backend/dns";
|
||||
|
||||
enum DisconnectReason {
|
||||
export enum DisconnectReason {
|
||||
HANDLER_DESTROYED,
|
||||
REQUESTED,
|
||||
DNS_FAILED,
|
||||
|
@ -28,7 +52,7 @@ enum DisconnectReason {
|
|||
UNKNOWN
|
||||
}
|
||||
|
||||
enum ConnectionState {
|
||||
export enum ConnectionState {
|
||||
UNCONNECTED,
|
||||
CONNECTING,
|
||||
INITIALISING,
|
||||
|
@ -36,7 +60,7 @@ enum ConnectionState {
|
|||
DISCONNECTING
|
||||
}
|
||||
|
||||
enum ViewReasonId {
|
||||
export enum ViewReasonId {
|
||||
VREASON_USER_ACTION = 0,
|
||||
VREASON_MOVED = 1,
|
||||
VREASON_SYSTEM = 2,
|
||||
|
@ -51,7 +75,7 @@ enum ViewReasonId {
|
|||
VREASON_SERVER_SHUTDOWN = 11
|
||||
}
|
||||
|
||||
interface VoiceStatus {
|
||||
export interface VoiceStatus {
|
||||
input_hardware: boolean;
|
||||
input_muted: boolean;
|
||||
output_muted: boolean;
|
||||
|
@ -68,7 +92,7 @@ interface VoiceStatus {
|
|||
queries_visible: boolean;
|
||||
}
|
||||
|
||||
interface ConnectParameters {
|
||||
export interface ConnectParameters {
|
||||
nickname?: string;
|
||||
channel?: {
|
||||
target: string | number;
|
||||
|
@ -79,20 +103,21 @@ interface ConnectParameters {
|
|||
auto_reconnect_attempt?: boolean;
|
||||
}
|
||||
|
||||
class ConnectionHandler {
|
||||
declare const native_client;
|
||||
export class ConnectionHandler {
|
||||
channelTree: ChannelTree;
|
||||
|
||||
serverConnection: connection.AbstractServerConnection;
|
||||
serverConnection: AbstractServerConnection;
|
||||
|
||||
fileManager: FileManager;
|
||||
|
||||
permissions: PermissionManager;
|
||||
groups: GroupManager;
|
||||
|
||||
side_bar: chat.Frame;
|
||||
side_bar: Frame;
|
||||
|
||||
settings: ServerSettings;
|
||||
sound: sound.SoundManager;
|
||||
sound: SoundManager;
|
||||
|
||||
hostbanner: Hostbanner;
|
||||
|
||||
|
@ -121,15 +146,15 @@ class ConnectionHandler {
|
|||
};
|
||||
|
||||
invoke_resized_on_activate: boolean = false;
|
||||
log: log.ServerLog;
|
||||
log: ServerLog;
|
||||
|
||||
constructor() {
|
||||
this.settings = new ServerSettings();
|
||||
|
||||
this.log = new log.ServerLog(this);
|
||||
this.log = new ServerLog(this);
|
||||
this.channelTree = new ChannelTree(this);
|
||||
this.side_bar = new chat.Frame(this);
|
||||
this.sound = new sound.SoundManager(this);
|
||||
this.side_bar = new Frame(this);
|
||||
this.sound = new SoundManager(this);
|
||||
this.hostbanner = new Hostbanner(this);
|
||||
|
||||
this.serverConnection = connection.spawn_server_connection(this);
|
||||
|
@ -169,7 +194,7 @@ class ConnectionHandler {
|
|||
|
||||
setup() { }
|
||||
|
||||
async startConnection(addr: string, profile: profiles.ConnectionProfile, user_action: boolean, parameters: ConnectParameters) {
|
||||
async startConnection(addr: string, profile: ConnectionProfile, user_action: boolean, parameters: ConnectParameters) {
|
||||
this.tab_set_name(tr("Connecting"));
|
||||
this.cancel_reconnect(false);
|
||||
this._reconnect_attempt = parameters.auto_reconnect_attempt || false;
|
||||
|
@ -192,7 +217,7 @@ class ConnectionHandler {
|
|||
}
|
||||
}
|
||||
log.info(LogCategory.CLIENT, tr("Start connection to %s:%d"), server_address.host, server_address.port);
|
||||
this.log.log(log.server.Type.CONNECTION_BEGIN, {
|
||||
this.log.log(server_log.Type.CONNECTION_BEGIN, {
|
||||
address: {
|
||||
server_hostname: server_address.host,
|
||||
server_port: server_address.port
|
||||
|
@ -203,7 +228,7 @@ class ConnectionHandler {
|
|||
|
||||
if(parameters.password && !parameters.password.hashed){
|
||||
try {
|
||||
const password = await helpers.hashPassword(parameters.password.password);
|
||||
const password = await hashPassword(parameters.password.password);
|
||||
parameters.password = {
|
||||
hashed: true,
|
||||
password: password
|
||||
|
@ -221,9 +246,9 @@ class ConnectionHandler {
|
|||
}
|
||||
|
||||
const original_address = {host: server_address.host, port: server_address.port};
|
||||
if(dns.supported() && !server_address.host.match(Modals.Regex.IP_V4) && !server_address.host.match(Modals.Regex.IP_V6)) {
|
||||
if(dns.supported() && !server_address.host.match(Regex.IP_V4) && !server_address.host.match(Regex.IP_V6)) {
|
||||
const id = ++this._connect_initialize_id;
|
||||
this.log.log(log.server.Type.CONNECTION_HOSTNAME_RESOLVE, {});
|
||||
this.log.log(server_log.Type.CONNECTION_HOSTNAME_RESOLVE, {});
|
||||
try {
|
||||
const resolved = await dns.resolve_address(server_address, { timeout: 5000 }) || {} as any;
|
||||
if(id != this._connect_initialize_id)
|
||||
|
@ -231,7 +256,7 @@ class ConnectionHandler {
|
|||
|
||||
server_address.host = typeof(resolved.target_ip) === "string" ? resolved.target_ip : server_address.host;
|
||||
server_address.port = typeof(resolved.target_port) === "number" ? resolved.target_port : server_address.port;
|
||||
this.log.log(log.server.Type.CONNECTION_HOSTNAME_RESOLVED, {
|
||||
this.log.log(server_log.Type.CONNECTION_HOSTNAME_RESOLVED, {
|
||||
address: {
|
||||
server_port: server_address.port,
|
||||
server_hostname: server_address.host
|
||||
|
@ -245,7 +270,7 @@ class ConnectionHandler {
|
|||
}
|
||||
}
|
||||
|
||||
await this.serverConnection.connect(server_address, new connection.HandshakeHandler(profile, parameters));
|
||||
await this.serverConnection.connect(server_address, new HandshakeHandler(profile, parameters));
|
||||
setTimeout(() => {
|
||||
const connected = this.serverConnection.connected();
|
||||
if(user_action && connected) {
|
||||
|
@ -270,7 +295,7 @@ class ConnectionHandler {
|
|||
return this._clientId;
|
||||
}
|
||||
|
||||
getServerConnection() : connection.AbstractServerConnection { return this.serverConnection; }
|
||||
getServerConnection() : AbstractServerConnection { return this.serverConnection; }
|
||||
|
||||
|
||||
/**
|
||||
|
@ -380,7 +405,7 @@ class ConnectionHandler {
|
|||
|
||||
popup.close(); /* no need, but nicer */
|
||||
|
||||
const profile = profiles.find_profile(properties.connect_profile) || profiles.default_profile();
|
||||
const profile = find_profile(properties.connect_profile) || default_profile();
|
||||
const cprops = this.reconnect_properties(profile);
|
||||
this.startConnection(properties.connect_address, profile, true, cprops);
|
||||
});
|
||||
|
@ -418,12 +443,12 @@ class ConnectionHandler {
|
|||
case DisconnectReason.HANDLER_DESTROYED:
|
||||
if(data) {
|
||||
this.sound.play(Sound.CONNECTION_DISCONNECTED);
|
||||
this.log.log(log.server.Type.DISCONNECTED, {});
|
||||
this.log.log(server_log.Type.DISCONNECTED, {});
|
||||
}
|
||||
break;
|
||||
case DisconnectReason.DNS_FAILED:
|
||||
log.error(LogCategory.CLIENT, tr("Failed to resolve hostname: %o"), data);
|
||||
this.log.log(log.server.Type.CONNECTION_HOSTNAME_RESOLVE_ERROR, {
|
||||
this.log.log(server_log.Type.CONNECTION_HOSTNAME_RESOLVE_ERROR, {
|
||||
message: data as any
|
||||
});
|
||||
this.sound.play(Sound.CONNECTION_REFUSED);
|
||||
|
@ -431,7 +456,7 @@ class ConnectionHandler {
|
|||
case DisconnectReason.CONNECT_FAILURE:
|
||||
if(this._reconnect_attempt) {
|
||||
auto_reconnect = true;
|
||||
this.log.log(log.server.Type.CONNECTION_FAILED, {});
|
||||
this.log.log(server_log.Type.CONNECTION_FAILED, {});
|
||||
break;
|
||||
}
|
||||
if(data)
|
||||
|
@ -452,7 +477,7 @@ class ConnectionHandler {
|
|||
|
||||
this._certificate_modal = createErrorModal(
|
||||
tr("Could not connect"),
|
||||
MessageHelper.formatMessage(tr(error_message_format), this.generate_ssl_certificate_accept())
|
||||
formatMessage(tr(error_message_format), this.generate_ssl_certificate_accept())
|
||||
);
|
||||
this._certificate_modal.close_listener.push(() => this._certificate_modal = undefined);
|
||||
this._certificate_modal.open();
|
||||
|
@ -470,7 +495,7 @@ class ConnectionHandler {
|
|||
case DisconnectReason.HANDSHAKE_TEAMSPEAK_REQUIRED:
|
||||
createErrorModal(
|
||||
tr("Target server is a TeamSpeak server"),
|
||||
MessageHelper.formatMessage(tr("The target server is a TeamSpeak 3 server!{:br:}Only TeamSpeak 3 based identities are able to connect.{:br:}Please select another profile or change the identify type."))
|
||||
formatMessage(tr("The target server is a TeamSpeak 3 server!{:br:}Only TeamSpeak 3 based identities are able to connect.{:br:}Please select another profile or change the identify type."))
|
||||
).open();
|
||||
this.sound.play(Sound.CONNECTION_DISCONNECTED);
|
||||
auto_reconnect = false;
|
||||
|
@ -478,7 +503,7 @@ class ConnectionHandler {
|
|||
case DisconnectReason.IDENTITY_TOO_LOW:
|
||||
createErrorModal(
|
||||
tr("Identity level is too low"),
|
||||
MessageHelper.formatMessage(tr("You've been disconnected, because your Identity level is too low.{:br:}You need at least a level of {0}"), data["extra_message"])
|
||||
formatMessage(tr("You've been disconnected, because your Identity level is too low.{:br:}You need at least a level of {0}"), data["extra_message"])
|
||||
).open();
|
||||
this.sound.play(Sound.CONNECTION_DISCONNECTED);
|
||||
|
||||
|
@ -506,7 +531,7 @@ class ConnectionHandler {
|
|||
|
||||
break;
|
||||
case DisconnectReason.SERVER_CLOSED:
|
||||
this.log.log(log.server.Type.SERVER_CLOSED, {message: data.reasonmsg});
|
||||
this.log.log(server_log.Type.SERVER_CLOSED, {message: data.reasonmsg});
|
||||
|
||||
createErrorModal(
|
||||
tr("Server closed"),
|
||||
|
@ -518,7 +543,7 @@ class ConnectionHandler {
|
|||
auto_reconnect = true;
|
||||
break;
|
||||
case DisconnectReason.SERVER_REQUIRES_PASSWORD:
|
||||
this.log.log(log.server.Type.SERVER_REQUIRES_PASSWORD, {});
|
||||
this.log.log(server_log.Type.SERVER_REQUIRES_PASSWORD, {});
|
||||
|
||||
createInputModal(tr("Server password"), tr("Enter server password:"), password => password.length != 0, password => {
|
||||
if(!(typeof password === "string")) return;
|
||||
|
@ -540,7 +565,7 @@ class ConnectionHandler {
|
|||
const have_invoker = typeof(data["invokerid"]) !== "undefined" && parseInt(data["invokerid"]) !== 0;
|
||||
const modal = createErrorModal(
|
||||
tr("You've been kicked"),
|
||||
MessageHelper.formatMessage(
|
||||
formatMessage(
|
||||
have_invoker ? tr("You've been kicked from the server by {0}:{:br:}{1}") : tr("You've been kicked from the server:{:br:}{1}"),
|
||||
have_invoker ?
|
||||
htmltags.generate_client_object({ client_id: parseInt(data["invokerid"]), client_unique_id: data["invokeruid"], client_name: data["invokername"]}) :
|
||||
|
@ -559,7 +584,7 @@ class ConnectionHandler {
|
|||
this.sound.play(Sound.CONNECTION_BANNED);
|
||||
break;
|
||||
case DisconnectReason.CLIENT_BANNED:
|
||||
this.log.log(log.server.Type.SERVER_BANNED, {
|
||||
this.log.log(server_log.Type.SERVER_BANNED, {
|
||||
invoker: {
|
||||
client_name: data["invokername"],
|
||||
client_id: parseInt(data["invokerid"]),
|
||||
|
@ -592,7 +617,7 @@ class ConnectionHandler {
|
|||
log.info(LogCategory.NETWORKING, tr("Allowed to auto reconnect but cant reconnect because we dont have any information left..."));
|
||||
return;
|
||||
}
|
||||
this.log.log(log.server.Type.RECONNECT_SCHEDULED, {timeout: 5000});
|
||||
this.log.log(server_log.Type.RECONNECT_SCHEDULED, {timeout: 5000});
|
||||
|
||||
log.info(LogCategory.NETWORKING, tr("Allowed to auto reconnect. Reconnecting in 5000ms"));
|
||||
const server_address = this.serverConnection.remote_address();
|
||||
|
@ -600,7 +625,7 @@ class ConnectionHandler {
|
|||
|
||||
this._reconnect_timer = setTimeout(() => {
|
||||
this._reconnect_timer = undefined;
|
||||
this.log.log(log.server.Type.RECONNECT_EXECUTE, {});
|
||||
this.log.log(server_log.Type.RECONNECT_EXECUTE, {});
|
||||
log.info(LogCategory.NETWORKING, tr("Reconnecting..."));
|
||||
|
||||
this.startConnection(server_address.host + ":" + server_address.port, profile, false, Object.assign(this.reconnect_properties(profile), {auto_reconnect_attempt: true}));
|
||||
|
@ -610,7 +635,7 @@ class ConnectionHandler {
|
|||
|
||||
cancel_reconnect(log_event: boolean) {
|
||||
if(this._reconnect_timer) {
|
||||
if(log_event) this.log.log(log.server.Type.RECONNECT_CANCELED, {});
|
||||
if(log_event) this.log.log(server_log.Type.RECONNECT_CANCELED, {});
|
||||
clearTimeout(this._reconnect_timer);
|
||||
this._reconnect_timer = undefined;
|
||||
}
|
||||
|
@ -665,7 +690,7 @@ class ConnectionHandler {
|
|||
if(Object.keys(property_update).length > 0) {
|
||||
this.serverConnection.send_command("clientupdate", property_update).catch(error => {
|
||||
log.warn(LogCategory.GENERAL, tr("Failed to update client audio hardware properties. Error: %o"), error);
|
||||
this.log.log(log.server.Type.ERROR_CUSTOM, {message: tr("Failed to update audio hardware properties.")});
|
||||
this.log.log(server_log.Type.ERROR_CUSTOM, {message: tr("Failed to update audio hardware properties.")});
|
||||
|
||||
/* Update these properties anyways (for case the server fails to handle the command) */
|
||||
const updates = [];
|
||||
|
@ -708,15 +733,15 @@ class ConnectionHandler {
|
|||
const input = vconnection.voice_recorder().input;
|
||||
if(input) {
|
||||
if(active && this.serverConnection.connected()) {
|
||||
if(input.current_state() === audio.recorder.InputState.PAUSED) {
|
||||
if(input.current_state() === InputState.PAUSED) {
|
||||
input.start().then(result => {
|
||||
if(result != audio.recorder.InputStartResult.EOK)
|
||||
if(result != InputStartResult.EOK)
|
||||
throw result;
|
||||
}).catch(error => {
|
||||
log.warn(LogCategory.VOICE, tr("Failed to start microphone input (%s)."), error);
|
||||
if(Date.now() - (this._last_record_error_popup || 0) > 10 * 1000) {
|
||||
this._last_record_error_popup = Date.now();
|
||||
createErrorModal(tr("Failed to start recording"), MessageHelper.formatMessage(tr("Microphone start failed.{:br:}Error: {}"), error)).open();
|
||||
createErrorModal(tr("Failed to start recording"), formatMessage(tr("Microphone start failed.{:br:}Error: {}"), error)).open();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -741,7 +766,7 @@ class ConnectionHandler {
|
|||
client_output_hardware: this.client_status.sound_playback_supported
|
||||
}).catch(error => {
|
||||
log.warn(LogCategory.GENERAL, tr("Failed to sync handler state with server. Error: %o"), error);
|
||||
this.log.log(log.server.Type.ERROR_CUSTOM, {message: tr("Failed to sync handler state with server.")});
|
||||
this.log.log(server_log.Type.ERROR_CUSTOM, {message: tr("Failed to sync handler state with server.")});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -761,7 +786,7 @@ class ConnectionHandler {
|
|||
client_away_message: typeof(this.client_status.away) === "string" ? this.client_status.away : "",
|
||||
}).catch(error => {
|
||||
log.warn(LogCategory.GENERAL, tr("Failed to update away status. Error: %o"), error);
|
||||
this.log.log(log.server.Type.ERROR_CUSTOM, {message: tr("Failed to update away status.")});
|
||||
this.log.log(server_log.Type.ERROR_CUSTOM, {message: tr("Failed to update away status.")});
|
||||
});
|
||||
|
||||
control_bar.update_button_away();
|
||||
|
@ -781,10 +806,10 @@ class ConnectionHandler {
|
|||
});
|
||||
}
|
||||
|
||||
reconnect_properties(profile?: profiles.ConnectionProfile) : ConnectParameters {
|
||||
reconnect_properties(profile?: ConnectionProfile) : ConnectParameters {
|
||||
const name = (this.getClient() ? this.getClient().clientNickName() : "") ||
|
||||
(this.serverConnection && this.serverConnection.handshake_handler() ? this.serverConnection.handshake_handler().parameters.nickname : "") ||
|
||||
settings.static_global(Settings.KEY_CONNECT_USERNAME, profile ? profile.default_username : undefined) ||
|
||||
StaticSettings.instance.static(Settings.KEY_CONNECT_USERNAME, profile ? profile.default_username : undefined) ||
|
||||
"Another TeaSpeak user";
|
||||
const channel = (this.getClient() && this.getClient().currentChannel() ? this.getClient().currentChannel().channelId : 0) ||
|
||||
(this.serverConnection && this.serverConnection.handshake_handler() ? (this.serverConnection.handshake_handler().parameters.channel || {} as any).target : "");
|
||||
|
@ -798,7 +823,7 @@ class ConnectionHandler {
|
|||
}
|
||||
|
||||
update_avatar() {
|
||||
Modals.spawnAvatarUpload(data => {
|
||||
spawnAvatarUpload(data => {
|
||||
if(typeof(data) === "undefined")
|
||||
return;
|
||||
if(data === null) {
|
||||
|
@ -814,16 +839,16 @@ class ConnectionHandler {
|
|||
|
||||
let message;
|
||||
if(error instanceof CommandResult)
|
||||
message = MessageHelper.formatMessage(tr("Failed to delete avatar.{:br:}Error: {0}"), error.extra_message || error.message);
|
||||
message = formatMessage(tr("Failed to delete avatar.{:br:}Error: {0}"), error.extra_message || error.message);
|
||||
if(!message)
|
||||
message = MessageHelper.formatMessage(tr("Failed to delete avatar.{:br:}Lookup the console for more details"));
|
||||
message = formatMessage(tr("Failed to delete avatar.{:br:}Lookup the console for more details"));
|
||||
createErrorModal(tr("Failed to delete avatar"), message).open();
|
||||
return;
|
||||
});
|
||||
} else {
|
||||
log.info(LogCategory.CLIENT, tr("Uploading new avatar"));
|
||||
(async () => {
|
||||
let key: transfer.UploadKey;
|
||||
let key: UploadKey;
|
||||
try {
|
||||
key = await this.fileManager.upload_file({
|
||||
size: data.byteLength,
|
||||
|
@ -840,28 +865,28 @@ class ConnectionHandler {
|
|||
//TODO: Resolve permission name
|
||||
//i_client_max_avatar_filesize
|
||||
if(error.id == ErrorID.PERMISSION_ERROR) {
|
||||
message = MessageHelper.formatMessage(tr("Failed to initialize avatar upload.{:br:}Missing permission {0}"), error["failed_permid"]);
|
||||
message = formatMessage(tr("Failed to initialize avatar upload.{:br:}Missing permission {0}"), error["failed_permid"]);
|
||||
} else {
|
||||
message = MessageHelper.formatMessage(tr("Failed to initialize avatar upload.{:br:}Error: {0}"), error.extra_message || error.message);
|
||||
message = formatMessage(tr("Failed to initialize avatar upload.{:br:}Error: {0}"), error.extra_message || error.message);
|
||||
}
|
||||
}
|
||||
if(!message)
|
||||
message = MessageHelper.formatMessage(tr("Failed to initialize avatar upload.{:br:}Lookup the console for more details"));
|
||||
message = formatMessage(tr("Failed to initialize avatar upload.{:br:}Lookup the console for more details"));
|
||||
createErrorModal(tr("Failed to upload avatar"), message).open();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await transfer.spawn_upload_transfer(key).put_data(data);
|
||||
await spawn_upload_transfer(key).put_data(data);
|
||||
} catch(error) {
|
||||
log.error(LogCategory.GENERAL, tr("Failed to upload avatar: %o"), error);
|
||||
|
||||
let message;
|
||||
if(typeof(error) === "string")
|
||||
message = MessageHelper.formatMessage(tr("Failed to upload avatar.{:br:}Error: {0}"), error);
|
||||
message = formatMessage(tr("Failed to upload avatar.{:br:}Error: {0}"), error);
|
||||
|
||||
if(!message)
|
||||
message = MessageHelper.formatMessage(tr("Failed to initialize avatar upload.{:br:}Lookup the console for more details"));
|
||||
message = formatMessage(tr("Failed to initialize avatar upload.{:br:}Lookup the console for more details"));
|
||||
createErrorModal(tr("Failed to upload avatar"), message).open();
|
||||
return;
|
||||
}
|
||||
|
@ -874,9 +899,9 @@ class ConnectionHandler {
|
|||
|
||||
let message;
|
||||
if(error instanceof CommandResult)
|
||||
message = MessageHelper.formatMessage(tr("Failed to update avatar flag.{:br:}Error: {0}"), error.extra_message || error.message);
|
||||
message = formatMessage(tr("Failed to update avatar flag.{:br:}Error: {0}"), error.extra_message || error.message);
|
||||
if(!message)
|
||||
message = MessageHelper.formatMessage(tr("Failed to update avatar flag.{:br:}Lookup the console for more details"));
|
||||
message = formatMessage(tr("Failed to update avatar flag.{:br:}Lookup the console for more details"));
|
||||
createErrorModal(tr("Failed to set avatar"), message).open();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
/// <reference path="connection/CommandHandler.ts" />
|
||||
/// <reference path="connection/ConnectionBase.ts" />
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {ChannelEntry} from "tc-shared/ui/channel";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {ServerCommand} from "tc-shared/connection/ConnectionBase";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {ClientEntry} from "tc-shared/ui/client";
|
||||
import {AbstractCommandHandler} from "tc-shared/connection/AbstractCommandHandler";
|
||||
|
||||
class FileEntry {
|
||||
export class FileEntry {
|
||||
name: string;
|
||||
datetime: number;
|
||||
type: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
class FileListRequest {
|
||||
export class FileListRequest {
|
||||
path: string;
|
||||
entries: FileEntry[];
|
||||
|
||||
callback: (entries: FileEntry[]) => void;
|
||||
}
|
||||
|
||||
namespace transfer {
|
||||
export interface TransferKey {
|
||||
client_transfer_id: number;
|
||||
server_transfer_id: number;
|
||||
|
@ -65,12 +70,11 @@ namespace transfer {
|
|||
export function spawn_upload_transfer(key: UploadKey) : UploadTransfer {
|
||||
return new RequestFileUpload(key);
|
||||
}
|
||||
}
|
||||
|
||||
class RequestFileDownload implements transfer.DownloadTransfer {
|
||||
readonly transfer_key: transfer.DownloadKey;
|
||||
export class RequestFileDownload implements DownloadTransfer {
|
||||
readonly transfer_key: DownloadKey;
|
||||
|
||||
constructor(key: transfer.DownloadKey) {
|
||||
constructor(key: DownloadKey) {
|
||||
this.transfer_key = key;
|
||||
}
|
||||
|
||||
|
@ -97,18 +101,18 @@ class RequestFileDownload implements transfer.DownloadTransfer {
|
|||
return response;
|
||||
}
|
||||
|
||||
get_key(): transfer.DownloadKey {
|
||||
get_key(): DownloadKey {
|
||||
return this.transfer_key;
|
||||
}
|
||||
}
|
||||
|
||||
class RequestFileUpload implements transfer.UploadTransfer {
|
||||
readonly transfer_key: transfer.UploadKey;
|
||||
constructor(key: transfer.DownloadKey) {
|
||||
export class RequestFileUpload implements UploadTransfer {
|
||||
readonly transfer_key: UploadKey;
|
||||
constructor(key: DownloadKey) {
|
||||
this.transfer_key = key;
|
||||
}
|
||||
|
||||
get_key(): transfer.UploadKey {
|
||||
get_key(): UploadKey {
|
||||
return this.transfer_key;
|
||||
}
|
||||
|
||||
|
@ -152,14 +156,14 @@ class RequestFileUpload implements transfer.UploadTransfer {
|
|||
}
|
||||
}
|
||||
|
||||
class FileManager extends connection.AbstractCommandHandler {
|
||||
export class FileManager extends AbstractCommandHandler {
|
||||
handle: ConnectionHandler;
|
||||
icons: IconManager;
|
||||
avatars: AvatarManager;
|
||||
|
||||
private listRequests: FileListRequest[] = [];
|
||||
private pending_download_requests: transfer.DownloadKey[] = [];
|
||||
private pending_upload_requests: transfer.UploadKey[] = [];
|
||||
private pending_download_requests: DownloadKey[] = [];
|
||||
private pending_upload_requests: UploadKey[] = [];
|
||||
|
||||
private transfer_counter : number = 1;
|
||||
|
||||
|
@ -191,7 +195,7 @@ class FileManager extends connection.AbstractCommandHandler {
|
|||
this.avatars = undefined;
|
||||
}
|
||||
|
||||
handle_command(command: connection.ServerCommand): boolean {
|
||||
handle_command(command: ServerCommand): boolean {
|
||||
switch (command.command) {
|
||||
case "notifyfilelist":
|
||||
this.notifyFileList(command.arguments);
|
||||
|
@ -276,15 +280,15 @@ class FileManager extends connection.AbstractCommandHandler {
|
|||
|
||||
|
||||
/******************************** File download/upload ********************************/
|
||||
download_file(path: string, file: string, channel?: ChannelEntry, password?: string) : Promise<transfer.DownloadKey> {
|
||||
const transfer_data: transfer.DownloadKey = {
|
||||
download_file(path: string, file: string, channel?: ChannelEntry, password?: string) : Promise<DownloadKey> {
|
||||
const transfer_data: DownloadKey = {
|
||||
file_name: file,
|
||||
file_path: path,
|
||||
client_transfer_id: this.transfer_counter++
|
||||
} as any;
|
||||
|
||||
this.pending_download_requests.push(transfer_data);
|
||||
return new Promise<transfer.DownloadKey>((resolve, reject) => {
|
||||
return new Promise<DownloadKey>((resolve, reject) => {
|
||||
transfer_data["_callback"] = resolve;
|
||||
this.handle.serverConnection.send_command("ftinitdownload", {
|
||||
"path": path,
|
||||
|
@ -301,8 +305,8 @@ class FileManager extends connection.AbstractCommandHandler {
|
|||
});
|
||||
}
|
||||
|
||||
upload_file(options: transfer.UploadOptions) : Promise<transfer.UploadKey> {
|
||||
const transfer_data: transfer.UploadKey = {
|
||||
upload_file(options: UploadOptions) : Promise<UploadKey> {
|
||||
const transfer_data: UploadKey = {
|
||||
file_path: options.path,
|
||||
file_name: options.name,
|
||||
client_transfer_id: this.transfer_counter++,
|
||||
|
@ -310,7 +314,7 @@ class FileManager extends connection.AbstractCommandHandler {
|
|||
} as any;
|
||||
|
||||
this.pending_upload_requests.push(transfer_data);
|
||||
return new Promise<transfer.UploadKey>((resolve, reject) => {
|
||||
return new Promise<UploadKey>((resolve, reject) => {
|
||||
transfer_data["_callback"] = resolve;
|
||||
this.handle.serverConnection.send_command("ftinitupload", {
|
||||
"path": options.path,
|
||||
|
@ -333,7 +337,7 @@ class FileManager extends connection.AbstractCommandHandler {
|
|||
json = json[0];
|
||||
|
||||
let clientftfid = parseInt(json["clientftfid"]);
|
||||
let transfer: transfer.DownloadKey;
|
||||
let transfer: DownloadKey;
|
||||
for(let e of this.pending_download_requests)
|
||||
if(e.client_transfer_id == clientftfid) {
|
||||
transfer = e;
|
||||
|
@ -355,14 +359,14 @@ class FileManager extends connection.AbstractCommandHandler {
|
|||
if(transfer.peer.hosts[0].length == 0 || transfer.peer.hosts[0] == '0.0.0.0')
|
||||
transfer.peer.hosts[0] = this.handle.serverConnection.remote_address().host;
|
||||
|
||||
(transfer["_callback"] as (val: transfer.DownloadKey) => void)(transfer);
|
||||
(transfer["_callback"] as (val: DownloadKey) => void)(transfer);
|
||||
this.pending_download_requests.remove(transfer);
|
||||
}
|
||||
|
||||
private notifyStartUpload(json) {
|
||||
json = json[0];
|
||||
|
||||
let transfer: transfer.UploadKey;
|
||||
let transfer: UploadKey;
|
||||
let clientftfid = parseInt(json["clientftfid"]);
|
||||
for(let e of this.pending_upload_requests)
|
||||
if(e.client_transfer_id == clientftfid) {
|
||||
|
@ -384,7 +388,7 @@ class FileManager extends connection.AbstractCommandHandler {
|
|||
if(transfer.peer.hosts[0].length == 0 || transfer.peer.hosts[0] == '0.0.0.0')
|
||||
transfer.peer.hosts[0] = this.handle.serverConnection.remote_address().host;
|
||||
|
||||
(transfer["_callback"] as (val: transfer.UploadKey) => void)(transfer);
|
||||
(transfer["_callback"] as (val: UploadKey) => void)(transfer);
|
||||
this.pending_upload_requests.remove(transfer);
|
||||
}
|
||||
|
||||
|
@ -411,12 +415,12 @@ class FileManager extends connection.AbstractCommandHandler {
|
|||
}
|
||||
}
|
||||
|
||||
class Icon {
|
||||
export class Icon {
|
||||
id: number;
|
||||
url: string;
|
||||
}
|
||||
|
||||
enum ImageType {
|
||||
export enum ImageType {
|
||||
UNKNOWN,
|
||||
BITMAP,
|
||||
PNG,
|
||||
|
@ -425,7 +429,7 @@ enum ImageType {
|
|||
JPEG
|
||||
}
|
||||
|
||||
function media_image_type(type: ImageType, file?: boolean) {
|
||||
export function media_image_type(type: ImageType, file?: boolean) {
|
||||
switch (type) {
|
||||
case ImageType.BITMAP:
|
||||
return "bmp";
|
||||
|
@ -442,7 +446,7 @@ function media_image_type(type: ImageType, file?: boolean) {
|
|||
}
|
||||
}
|
||||
|
||||
function image_type(encoded_data: string | ArrayBuffer, base64_encoded?: boolean) {
|
||||
export function image_type(encoded_data: string | ArrayBuffer, base64_encoded?: boolean) {
|
||||
const ab2str10 = () => {
|
||||
const buf = new Uint8Array(encoded_data as ArrayBuffer);
|
||||
if(buf.byteLength < 10)
|
||||
|
@ -472,7 +476,7 @@ function image_type(encoded_data: string | ArrayBuffer, base64_encoded?: boolean
|
|||
return ImageType.UNKNOWN;
|
||||
}
|
||||
|
||||
class CacheManager {
|
||||
export class CacheManager {
|
||||
readonly cache_name: string;
|
||||
|
||||
private _cache_category: Cache;
|
||||
|
@ -547,7 +551,7 @@ class CacheManager {
|
|||
}
|
||||
}
|
||||
|
||||
class IconManager {
|
||||
export class IconManager {
|
||||
private static cache: CacheManager = new CacheManager("icons");
|
||||
|
||||
handle: FileManager;
|
||||
|
@ -590,7 +594,7 @@ class IconManager {
|
|||
return this.handle.requestFileList("/icons");
|
||||
}
|
||||
|
||||
create_icon_download(id: number) : Promise<transfer.DownloadKey> {
|
||||
create_icon_download(id: number) : Promise<DownloadKey> {
|
||||
return this.handle.download_file("", "/icon_" + id);
|
||||
}
|
||||
|
||||
|
@ -665,7 +669,7 @@ class IconManager {
|
|||
|
||||
private async _load_icon(id: number) : Promise<Icon> {
|
||||
try {
|
||||
let download_key: transfer.DownloadKey;
|
||||
let download_key: DownloadKey;
|
||||
try {
|
||||
download_key = await this.create_icon_download(id);
|
||||
} catch(error) {
|
||||
|
@ -673,7 +677,7 @@ class IconManager {
|
|||
throw "Failed to request icon";
|
||||
}
|
||||
|
||||
const downloader = transfer.spawn_download_transfer(download_key);
|
||||
const downloader = spawn_download_transfer(download_key);
|
||||
let response: Response;
|
||||
try {
|
||||
response = await downloader.request_file();
|
||||
|
@ -801,14 +805,14 @@ class IconManager {
|
|||
}
|
||||
}
|
||||
|
||||
class Avatar {
|
||||
export class Avatar {
|
||||
client_avatar_id: string; /* the base64 uid thing from a-m */
|
||||
avatar_id: string; /* client_flag_avatar */
|
||||
url: string;
|
||||
type: ImageType;
|
||||
}
|
||||
|
||||
class AvatarManager {
|
||||
export class AvatarManager {
|
||||
handle: FileManager;
|
||||
|
||||
private static cache: CacheManager;
|
||||
|
@ -867,14 +871,14 @@ class AvatarManager {
|
|||
};
|
||||
}
|
||||
|
||||
create_avatar_download(client_avatar_id: string) : Promise<transfer.DownloadKey> {
|
||||
create_avatar_download(client_avatar_id: string) : Promise<DownloadKey> {
|
||||
log.debug(LogCategory.GENERAL, "Requesting download for avatar %s", client_avatar_id);
|
||||
return this.handle.download_file("", "/avatar_" + client_avatar_id);
|
||||
}
|
||||
|
||||
private async _load_avatar(client_avatar_id: string, avatar_version: string) {
|
||||
try {
|
||||
let download_key: transfer.DownloadKey;
|
||||
let download_key: DownloadKey;
|
||||
try {
|
||||
download_key = await this.create_avatar_download(client_avatar_id);
|
||||
} catch(error) {
|
||||
|
@ -882,7 +886,7 @@ class AvatarManager {
|
|||
throw "failed to request avatar download";
|
||||
}
|
||||
|
||||
const downloader = transfer.spawn_download_transfer(download_key);
|
||||
const downloader = spawn_download_transfer(download_key);
|
||||
let response: Response;
|
||||
try {
|
||||
response = await downloader.request_file();
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
namespace messages.formatter {
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import * as contextmenu from "tc-shared/ui/elements/ContextMenu";
|
||||
import {copy_to_clipboard} from "tc-shared/utils/helpers";
|
||||
import {guid} from "tc-shared/crypto/uid";
|
||||
import * as loader from "tc-loader";
|
||||
import * as image_preview from "./ui/frames/image_preview"
|
||||
|
||||
declare const xbbcode;
|
||||
export namespace bbcode {
|
||||
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}) --]/;
|
||||
|
@ -118,7 +125,7 @@ namespace messages.formatter {
|
|||
},
|
||||
name: tr("Open URL in Browser"),
|
||||
type: contextmenu.MenuEntryType.ENTRY,
|
||||
visible: !app.is_web() && false // Currently not possible
|
||||
visible: loader.version().type === "native" && false // Currently not possible
|
||||
}, contextmenu.Entry.HR(), {
|
||||
callback: () => copy_to_clipboard(url),
|
||||
name: tr("Copy URL to clipboard"),
|
||||
|
@ -247,4 +254,27 @@ namespace messages.formatter {
|
|||
]
|
||||
})).text();
|
||||
}
|
||||
|
||||
export function formatDate(secs: number) : string {
|
||||
let years = Math.floor(secs / (60 * 60 * 24 * 365));
|
||||
let days = Math.floor(secs / (60 * 60 * 24)) % 365;
|
||||
let hours = Math.floor(secs / (60 * 60)) % 24;
|
||||
let minutes = Math.floor(secs / 60) % 60;
|
||||
let seconds = Math.floor(secs % 60);
|
||||
|
||||
let result = "";
|
||||
if(years > 0)
|
||||
result += years + " " + tr("years") + " ";
|
||||
if(years > 0 || days > 0)
|
||||
result += days + " " + tr("days") + " ";
|
||||
if(years > 0 || days > 0 || hours > 0)
|
||||
result += hours + " " + tr("hours") + " ";
|
||||
if(years > 0 || days > 0 || hours > 0 || minutes > 0)
|
||||
result += minutes + " " + tr("minutes") + " ";
|
||||
if(years > 0 || days > 0 || hours > 0 || minutes > 0 || seconds > 0)
|
||||
result += seconds + " " + tr("seconds") + " ";
|
||||
else
|
||||
result = tr("now") + " ";
|
||||
|
||||
return result.substr(0, result.length - 1);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
enum KeyCode {
|
||||
export enum KeyCode {
|
||||
KEY_CANCEL = 3,
|
||||
KEY_HELP = 6,
|
||||
KEY_BACK_SPACE = 8,
|
||||
|
@ -118,7 +118,6 @@ enum KeyCode {
|
|||
KEY_META = 224
|
||||
}
|
||||
|
||||
namespace ppt {
|
||||
export enum EventType {
|
||||
KEY_PRESS,
|
||||
KEY_RELEASE,
|
||||
|
@ -173,4 +172,3 @@ namespace ppt {
|
|||
result += " + " + key.key_code;
|
||||
return result.substr(3);
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
namespace audio {
|
||||
export namespace player {
|
||||
export interface Device {
|
||||
device_id: string;
|
||||
|
||||
driver: string;
|
||||
name: string;
|
||||
}
|
||||
}
|
||||
}
|
6
shared/js/audio/player.ts
Normal file
6
shared/js/audio/player.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export interface Device {
|
||||
device_id: string;
|
||||
|
||||
driver: string;
|
||||
name: string;
|
||||
}
|
|
@ -1,16 +1,15 @@
|
|||
namespace bookmarks {
|
||||
function guid() {
|
||||
function s4() {
|
||||
return Math
|
||||
.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
}
|
||||
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
|
||||
}
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {guid} from "tc-shared/crypto/uid";
|
||||
import {createErrorModal, createInfoModal, createInputModal} from "tc-shared/ui/elements/Modal";
|
||||
import {default_profile, find_profile} from "tc-shared/profiles/ConnectionProfile";
|
||||
import {server_connections} from "tc-shared/ui/frames/connection_handlers";
|
||||
import {spawnConnectModal} from "tc-shared/ui/modal/ModalConnect";
|
||||
import {control_bar} from "tc-shared/ui/frames/ControlBar";
|
||||
import * as top_menu from "./ui/frames/MenuBar";
|
||||
|
||||
export const boorkmak_connect = (mark: Bookmark, new_tab?: boolean) => {
|
||||
const profile = profiles.find_profile(mark.connect_profile) || profiles.default_profile();
|
||||
const profile = find_profile(mark.connect_profile) || default_profile();
|
||||
if(profile.valid()) {
|
||||
const connection = (typeof(new_tab) !== "boolean" || !new_tab) ? server_connections.active_connection_handler() : server_connections.spawn_server_connection_handler();
|
||||
server_connections.set_active_connection_handler(connection);
|
||||
|
@ -30,7 +29,7 @@ namespace bookmarks {
|
|||
}
|
||||
);
|
||||
} else {
|
||||
Modals.spawnConnectModal({}, {
|
||||
spawnConnectModal({}, {
|
||||
url: mark.server_properties.server_address + ":" + mark.server_properties.server_port,
|
||||
enforce: true
|
||||
}, {
|
||||
|
@ -259,4 +258,3 @@ namespace bookmarks {
|
|||
createErrorModal(tr("You have to be connected"), tr("You have to be connected!")).open();
|
||||
}
|
||||
}
|
||||
}
|
98
shared/js/connection/AbstractCommandHandler.ts
Normal file
98
shared/js/connection/AbstractCommandHandler.ts
Normal file
|
@ -0,0 +1,98 @@
|
|||
import {
|
||||
AbstractServerConnection,
|
||||
ServerCommand,
|
||||
SingleCommandHandler
|
||||
} from "tc-shared/connection/ConnectionBase";
|
||||
|
||||
export abstract class AbstractCommandHandler {
|
||||
readonly connection: AbstractServerConnection;
|
||||
|
||||
handler_boss: AbstractCommandHandlerBoss | undefined;
|
||||
volatile_handler_boss: boolean = false; /* if true than the command handler could be registered twice to two or more handlers */
|
||||
|
||||
ignore_consumed: boolean = false;
|
||||
|
||||
protected constructor(connection: AbstractServerConnection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If the command should be consumed
|
||||
*/
|
||||
abstract handle_command(command: ServerCommand) : boolean;
|
||||
}
|
||||
|
||||
export abstract class AbstractCommandHandlerBoss {
|
||||
readonly connection: AbstractServerConnection;
|
||||
protected command_handlers: AbstractCommandHandler[] = [];
|
||||
/* TODO: Timeout */
|
||||
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,8 +1,28 @@
|
|||
/// <reference path="ConnectionBase.ts" />
|
||||
|
||||
namespace connection {
|
||||
import Conversation = chat.channel.Conversation;
|
||||
import MusicInfo = chat.MusicInfo;
|
||||
import * as log from "tc-shared/log";
|
||||
import * as server_log from "tc-shared/ui/frames/server_log";
|
||||
import {
|
||||
AbstractServerConnection, CommandOptions, ServerCommand
|
||||
} from "tc-shared/connection/ConnectionBase";
|
||||
import {Sound} from "tc-shared/sound/Sounds";
|
||||
import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {createErrorModal, createInfoModal, createInputModal, createModal} from "tc-shared/ui/elements/Modal";
|
||||
import {
|
||||
ClientConnectionInfo,
|
||||
ClientEntry,
|
||||
ClientType,
|
||||
LocalClientEntry,
|
||||
MusicClientEntry,
|
||||
SongInfo
|
||||
} from "tc-shared/ui/client";
|
||||
import {ChannelEntry} from "tc-shared/ui/channel";
|
||||
import {ConnectionHandler, DisconnectReason, ViewReasonId} from "tc-shared/ConnectionHandler";
|
||||
import {bbcode_chat, formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {server_connections} from "tc-shared/ui/frames/connection_handlers";
|
||||
import {spawnPoke} from "tc-shared/ui/modal/ModalPoke";
|
||||
import {PrivateConversationState} from "tc-shared/ui/frames/side/private_conversations";
|
||||
import {Conversation} from "tc-shared/ui/frames/side/conversations";
|
||||
import {AbstractCommandHandler, AbstractCommandHandlerBoss} from "tc-shared/connection/AbstractCommandHandler";
|
||||
|
||||
export class ServerConnectionCommandBoss extends AbstractCommandHandlerBoss {
|
||||
constructor(connection: AbstractServerConnection) {
|
||||
|
@ -65,7 +85,7 @@ namespace connection {
|
|||
this["notifyplaylistsongloaded"] = this.handleNotifyPlaylistSongLoaded;
|
||||
}
|
||||
|
||||
private loggable_invoker(unique_id, client_id, name) : log.server.base.Client | undefined {
|
||||
private loggable_invoker(unique_id, client_id, name) : server_log.base.Client | undefined {
|
||||
const id = parseInt(client_id);
|
||||
if(typeof(client_id) === "undefined" || Number.isNaN(id))
|
||||
return undefined;
|
||||
|
@ -84,7 +104,7 @@ namespace connection {
|
|||
};
|
||||
}
|
||||
|
||||
proxy_command_promise(promise: Promise<CommandResult>, options: connection.CommandOptions) {
|
||||
proxy_command_promise(promise: Promise<CommandResult>, options: CommandOptions) {
|
||||
if(!options.process_result)
|
||||
return promise;
|
||||
|
||||
|
@ -96,18 +116,18 @@ namespace connection {
|
|||
if(res.id == ErrorID.PERMISSION_ERROR) { //Permission error
|
||||
const permission = this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number);
|
||||
res.message = tr("Insufficient client permissions. Failed on permission ") + (permission ? permission.name : "unknown");
|
||||
this.connection_handler.log.log(log.server.Type.ERROR_PERMISSION, {
|
||||
this.connection_handler.log.log(server_log.Type.ERROR_PERMISSION, {
|
||||
permission: this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number)
|
||||
});
|
||||
this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
|
||||
} else if(res.id != ErrorID.EMPTY_RESULT) {
|
||||
this.connection_handler.log.log(log.server.Type.ERROR_CUSTOM, {
|
||||
this.connection_handler.log.log(server_log.Type.ERROR_CUSTOM, {
|
||||
message: res.extra_message.length == 0 ? res.message : res.extra_message
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if(typeof(ex) === "string") {
|
||||
this.connection_handler.log.log(log.server.Type.CONNECTION_COMMAND_ERROR, {error: ex});
|
||||
this.connection_handler.log.log(server_log.Type.CONNECTION_COMMAND_ERROR, {error: ex});
|
||||
} else {
|
||||
log.error(LogCategory.NETWORKING, tr("Invalid promise result type: %s. Result: %o"), typeof (ex), ex);
|
||||
}
|
||||
|
@ -190,21 +210,21 @@ namespace connection {
|
|||
if(properties.virtualserver_hostmessage_mode > 0) {
|
||||
if(properties.virtualserver_hostmessage_mode == 1) {
|
||||
/* show in log */
|
||||
this.connection_handler.log.log(log.server.Type.SERVER_HOST_MESSAGE, {
|
||||
this.connection_handler.log.log(server_log.Type.SERVER_HOST_MESSAGE, {
|
||||
message: properties.virtualserver_hostmessage
|
||||
});
|
||||
} else {
|
||||
/* create modal/create modal and quit */
|
||||
createModal({
|
||||
header: tr("Host message"),
|
||||
body: MessageHelper.bbcode_chat(properties.virtualserver_hostmessage),
|
||||
body: bbcode_chat(properties.virtualserver_hostmessage),
|
||||
footer: undefined
|
||||
}).open();
|
||||
|
||||
if(properties.virtualserver_hostmessage_mode == 3) {
|
||||
/* first let the client initialize his stuff */
|
||||
setTimeout(() => {
|
||||
this.connection_handler.log.log(log.server.Type.SERVER_HOST_MESSAGE_DISCONNECT, {
|
||||
this.connection_handler.log.log(server_log.Type.SERVER_HOST_MESSAGE_DISCONNECT, {
|
||||
message: properties.virtualserver_welcomemessage
|
||||
});
|
||||
|
||||
|
@ -218,7 +238,7 @@ namespace connection {
|
|||
|
||||
/* welcome message */
|
||||
if(properties.virtualserver_welcomemessage) {
|
||||
this.connection_handler.log.log(log.server.Type.SERVER_WELCOME_MESSAGE, {
|
||||
this.connection_handler.log.log(server_log.Type.SERVER_WELCOME_MESSAGE, {
|
||||
message: properties.virtualserver_welcomemessage
|
||||
});
|
||||
}
|
||||
|
@ -235,12 +255,12 @@ namespace connection {
|
|||
}).then(() => {
|
||||
createInfoModal(tr("Use privilege key"), tr("Privilege key successfully used!")).open();
|
||||
}).catch(error => {
|
||||
createErrorModal(tr("Use privilege key"), MessageHelper.formatMessage(tr("Failed to use privilege key: {}"), error instanceof CommandResult ? error.message : error)).open();
|
||||
createErrorModal(tr("Use privilege key"), formatMessage(tr("Failed to use privilege key: {}"), error instanceof CommandResult ? error.message : error)).open();
|
||||
});
|
||||
}, { field_placeholder: 'Enter Privilege Key' }).open();
|
||||
}
|
||||
|
||||
this.connection_handler.log.log(log.server.Type.CONNECTION_CONNECTED, {
|
||||
this.connection_handler.log.log(server_log.Type.CONNECTION_CONNECTED, {
|
||||
own_client: this.connection_handler.getClient().log_data()
|
||||
});
|
||||
this.connection_handler.sound.play(Sound.CONNECTION_CONNECTED);
|
||||
|
@ -414,7 +434,7 @@ namespace connection {
|
|||
|
||||
if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) {
|
||||
const own_channel = this.connection.client.getClient().currentChannel();
|
||||
this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_ENTER, {
|
||||
this.connection_handler.log.log(server_log.Type.CLIENT_VIEW_ENTER, {
|
||||
channel_from: old_channel ? old_channel.log_data() : undefined,
|
||||
channel_to: channel ? channel.log_data() : undefined,
|
||||
client: client.log_data(),
|
||||
|
@ -521,7 +541,7 @@ namespace connection {
|
|||
let channel_to = tree.findChannel(entry["ctid"]);
|
||||
|
||||
const is_own_channel = channel_from == own_channel;
|
||||
this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_LEAVE, {
|
||||
this.connection_handler.log.log(server_log.Type.CLIENT_VIEW_LEAVE, {
|
||||
channel_from: channel_from ? channel_from.log_data() : undefined,
|
||||
channel_to: channel_to ? channel_to.log_data() : undefined,
|
||||
client: client.log_data(),
|
||||
|
@ -564,7 +584,7 @@ namespace connection {
|
|||
attach: false
|
||||
});
|
||||
if(conversation) {
|
||||
conversation.set_state(chat.PrivateConversationState.DISCONNECTED);
|
||||
conversation.set_state(PrivateConversationState.DISCONNECTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -635,7 +655,7 @@ namespace connection {
|
|||
}
|
||||
|
||||
const own_channel = this.connection.client.getClient().currentChannel();
|
||||
this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_MOVE, {
|
||||
this.connection_handler.log.log(server_log.Type.CLIENT_VIEW_MOVE, {
|
||||
channel_from: channel_from ? {
|
||||
channel_id: channel_from.channelId,
|
||||
channel_name: channel_from.channelName()
|
||||
|
@ -750,7 +770,7 @@ namespace connection {
|
|||
const target_own = target_client_id === this.connection.client.getClientId();
|
||||
|
||||
if(target_own && target_client_id === json["invokerid"]) {
|
||||
log.error(LogCategory.NETWORKING, tr("Received conversation message from invalid client id. Data: %o", json));
|
||||
log.error(LogCategory.NETWORKING, tr("Received conversation message from invalid client id. Data: %o"), json);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -808,7 +828,7 @@ namespace connection {
|
|||
if(conversation.is_unread() && channel)
|
||||
channel.flag_text_unread = true;
|
||||
} else if(mode == 3) {
|
||||
this.connection_handler.log.log(log.server.Type.GLOBAL_MESSAGE, {
|
||||
this.connection_handler.log.log(server_log.Type.GLOBAL_MESSAGE, {
|
||||
message: json["msg"],
|
||||
sender: {
|
||||
client_unique_id: json["invokeruid"],
|
||||
|
@ -871,7 +891,7 @@ namespace connection {
|
|||
log.warn(LogCategory.GENERAL, tr("Received chat close for client, but we haven't a chat open."));
|
||||
return;
|
||||
}
|
||||
conversation.set_state(chat.PrivateConversationState.CLOSED);
|
||||
conversation.set_state(PrivateConversationState.CLOSED);
|
||||
}
|
||||
|
||||
handleNotifyClientUpdated(json) {
|
||||
|
@ -944,7 +964,7 @@ namespace connection {
|
|||
|
||||
handleNotifyClientPoke(json) {
|
||||
json = json[0];
|
||||
Modals.spawnPoke(this.connection_handler, {
|
||||
spawnPoke(this.connection_handler, {
|
||||
id: parseInt(json["invokerid"]),
|
||||
name: json["invokername"],
|
||||
unique_id: json["invokeruid"]
|
||||
|
@ -1157,4 +1177,3 @@ namespace connection {
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,18 @@
|
|||
namespace connection {
|
||||
import {ServerCommand, SingleCommandHandler} from "tc-shared/connection/ConnectionBase";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {
|
||||
ClientNameInfo,
|
||||
CommandResult,
|
||||
ErrorID, Playlist, PlaylistInfo, PlaylistSong,
|
||||
QueryList,
|
||||
QueryListEntry, ServerGroupClient
|
||||
} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {ChannelEntry} from "tc-shared/ui/channel";
|
||||
import {ClientEntry} from "tc-shared/ui/client";
|
||||
import {ChatType} from "tc-shared/ui/frames/chat";
|
||||
import {AbstractCommandHandler} from "tc-shared/connection/AbstractCommandHandler";
|
||||
|
||||
export class CommandHelper extends AbstractCommandHandler {
|
||||
private _who_am_i: any;
|
||||
private _awaiters_unique_ids: {[unique_id: string]:((resolved: ClientNameInfo) => any)[]} = {};
|
||||
|
@ -23,7 +37,7 @@ namespace connection {
|
|||
this._awaiters_unique_ids = undefined;
|
||||
}
|
||||
|
||||
handle_command(command: connection.ServerCommand): boolean {
|
||||
handle_command(command: ServerCommand): boolean {
|
||||
if(command.command == "notifyclientnamefromuid")
|
||||
this.handle_notifyclientnamefromuid(command.arguments);
|
||||
if(command.command == "notifyclientgetnamefromdbid")
|
||||
|
@ -445,4 +459,3 @@ namespace connection {
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,11 @@
|
|||
namespace connection {
|
||||
import {CommandHelper} from "tc-shared/connection/CommandHelper";
|
||||
import {HandshakeHandler} from "tc-shared/connection/HandshakeHandler";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {ServerAddress} from "tc-shared/ui/server";
|
||||
import {RecorderProfile} from "tc-shared/voice/RecorderProfile";
|
||||
import {ConnectionHandler, ConnectionState} from "tc-shared/ConnectionHandler";
|
||||
import {AbstractCommandHandlerBoss} from "tc-shared/connection/AbstractCommandHandler";
|
||||
|
||||
export interface CommandOptions {
|
||||
flagset?: string[]; /* default: [] */
|
||||
process_result?: boolean; /* default: true */
|
||||
|
@ -112,24 +119,6 @@ namespace connection {
|
|||
arguments: any[];
|
||||
}
|
||||
|
||||
export abstract class AbstractCommandHandler {
|
||||
readonly connection: AbstractServerConnection;
|
||||
|
||||
handler_boss: AbstractCommandHandlerBoss | undefined;
|
||||
volatile_handler_boss: boolean = false; /* if true than the command handler could be registered twice to two or more handlers */
|
||||
|
||||
ignore_consumed: boolean = false;
|
||||
|
||||
protected constructor(connection: AbstractServerConnection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If the command should be consumed
|
||||
*/
|
||||
abstract handle_command(command: ServerCommand) : boolean;
|
||||
}
|
||||
|
||||
export interface SingleCommandHandler {
|
||||
name?: string;
|
||||
command?: string;
|
||||
|
@ -138,79 +127,3 @@ namespace connection {
|
|||
/* if the return is true then the command handler will be removed */
|
||||
function: (command: ServerCommand) => boolean;
|
||||
}
|
||||
|
||||
export abstract class AbstractCommandHandlerBoss {
|
||||
readonly connection: AbstractServerConnection;
|
||||
protected command_handlers: AbstractCommandHandler[] = [];
|
||||
/* TODO: Timeout */
|
||||
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,11 @@
|
|||
namespace connection {
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {IdentitifyType} from "tc-shared/profiles/Identity";
|
||||
import {TeaSpeakIdentity} from "tc-shared/profiles/identities/TeamSpeakIdentity";
|
||||
import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase";
|
||||
import {ConnectionProfile} from "tc-shared/profiles/ConnectionProfile";
|
||||
import {settings} from "tc-shared/settings";
|
||||
import {ConnectParameters, DisconnectReason} from "tc-shared/ConnectionHandler";
|
||||
|
||||
export interface HandshakeIdentityHandler {
|
||||
connection: AbstractServerConnection;
|
||||
|
||||
|
@ -6,15 +13,16 @@ namespace connection {
|
|||
register_callback(callback: (success: boolean, message?: string) => any);
|
||||
}
|
||||
|
||||
declare const native_client;
|
||||
export class HandshakeHandler {
|
||||
private connection: AbstractServerConnection;
|
||||
private handshake_handler: HandshakeIdentityHandler;
|
||||
private failed = false;
|
||||
|
||||
readonly profile: profiles.ConnectionProfile;
|
||||
readonly profile: ConnectionProfile;
|
||||
readonly parameters: ConnectParameters;
|
||||
|
||||
constructor(profile: profiles.ConnectionProfile, parameters: ConnectParameters) {
|
||||
constructor(profile: ConnectionProfile, parameters: ConnectParameters) {
|
||||
this.profile = profile;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
@ -48,7 +56,7 @@ namespace connection {
|
|||
|
||||
on_teamspeak() {
|
||||
const type = this.profile.selected_type();
|
||||
if(type == profiles.identities.IdentitifyType.TEAMSPEAK)
|
||||
if(type == IdentitifyType.TEAMSPEAK)
|
||||
this.handshake_finished();
|
||||
else {
|
||||
|
||||
|
@ -122,8 +130,8 @@ namespace connection {
|
|||
}
|
||||
|
||||
/* required to keep compatibility */
|
||||
if(this.profile.selected_type() === profiles.identities.IdentitifyType.TEAMSPEAK) {
|
||||
data["client_key_offset"] = (this.profile.selected_identity() as profiles.identities.TeaSpeakIdentity).hash_number;
|
||||
if(this.profile.selected_type() === IdentitifyType.TEAMSPEAK) {
|
||||
data["client_key_offset"] = (this.profile.selected_identity() as TeaSpeakIdentity).hash_number;
|
||||
}
|
||||
|
||||
this.connection.send_command("clientinit", data).catch(error => {
|
||||
|
@ -143,4 +151,3 @@ namespace connection {
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
enum ErrorID {
|
||||
import {LaterPromise} from "tc-shared/utils/LaterPromise";
|
||||
|
||||
export enum ErrorID {
|
||||
NOT_IMPLEMENTED = 0x2,
|
||||
COMMAND_NOT_FOUND = 0x100,
|
||||
|
||||
|
@ -15,7 +17,7 @@ enum ErrorID {
|
|||
CONVERSATION_IS_PRIVATE = 0x2202
|
||||
}
|
||||
|
||||
class CommandResult {
|
||||
export class CommandResult {
|
||||
success: boolean;
|
||||
id: number;
|
||||
message: string;
|
||||
|
@ -35,39 +37,39 @@ class CommandResult {
|
|||
}
|
||||
}
|
||||
|
||||
interface ClientNameInfo {
|
||||
export interface ClientNameInfo {
|
||||
//cluid=tYzKUryn\/\/Y8VBMf8PHUT6B1eiE= name=Exp clname=Exp cldbid=9
|
||||
client_unique_id: string;
|
||||
client_nickname: string;
|
||||
client_database_id: number;
|
||||
}
|
||||
|
||||
interface ClientNameFromUid {
|
||||
export interface ClientNameFromUid {
|
||||
promise: LaterPromise<ClientNameInfo[]>,
|
||||
keys: string[],
|
||||
response: ClientNameInfo[]
|
||||
}
|
||||
|
||||
interface ServerGroupClient {
|
||||
export interface ServerGroupClient {
|
||||
client_nickname: string;
|
||||
client_unique_identifier: string;
|
||||
client_database_id: number;
|
||||
}
|
||||
|
||||
interface QueryListEntry {
|
||||
export interface QueryListEntry {
|
||||
username: string;
|
||||
unique_id: string;
|
||||
bounded_server: number;
|
||||
}
|
||||
|
||||
interface QueryList {
|
||||
export interface QueryList {
|
||||
flag_own: boolean;
|
||||
flag_all: boolean;
|
||||
|
||||
queries: QueryListEntry[];
|
||||
}
|
||||
|
||||
interface Playlist {
|
||||
export interface Playlist {
|
||||
playlist_id: number;
|
||||
playlist_bot_id: number;
|
||||
playlist_title: string;
|
||||
|
@ -83,7 +85,7 @@ interface Playlist {
|
|||
needed_power_song_remove: number;
|
||||
}
|
||||
|
||||
interface PlaylistInfo {
|
||||
export interface PlaylistInfo {
|
||||
playlist_id: number,
|
||||
playlist_title: string,
|
||||
playlist_description: string,
|
||||
|
@ -100,7 +102,7 @@ interface PlaylistInfo {
|
|||
playlist_max_songs: number
|
||||
}
|
||||
|
||||
interface PlaylistSong {
|
||||
export interface PlaylistSong {
|
||||
song_id: number;
|
||||
song_previous_song_id: number;
|
||||
song_invoker: string;
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
namespace asn1 {
|
||||
declare class Int10 {
|
||||
constructor(value?: any);
|
||||
|
||||
|
@ -544,4 +543,3 @@ namespace asn1 {
|
|||
export function decode(stream: string | ArrayBuffer) {
|
||||
return decode0(new Stream(stream, 0));
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
class Crc32 {
|
||||
export class Crc32 {
|
||||
private static readonly lookup = [
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
|
||||
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
namespace hex {
|
||||
export function encode(buffer) {
|
||||
let hexCodes = [];
|
||||
let view = new DataView(buffer);
|
||||
|
@ -17,4 +16,3 @@ namespace hex {
|
|||
|
||||
return hexCodes.join("");
|
||||
}
|
||||
}
|
|
@ -10,7 +10,6 @@ interface Window {
|
|||
}
|
||||
*/
|
||||
|
||||
namespace sha {
|
||||
/*
|
||||
* [js-sha1]{@link https://github.com/emn178/js-sha1}
|
||||
*
|
||||
|
@ -24,12 +23,6 @@ namespace sha {
|
|||
'use strict';
|
||||
|
||||
let root: any = typeof window === 'object' ? window : {};
|
||||
let NODE_JS = !root.JS_SHA1_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
|
||||
if (NODE_JS) {
|
||||
root = global;
|
||||
}
|
||||
let COMMON_JS = !root.JS_SHA1_NO_COMMON_JS && typeof module === 'object' && module.exports;
|
||||
let AMD = typeof define === 'function' && (define as any).amd;
|
||||
let HEX_CHARS = '0123456789abcdef'.split('');
|
||||
let EXTRA = [-2147483648, 8388608, 32768, 128];
|
||||
let SHIFT = [24, 16, 8, 0];
|
||||
|
@ -45,9 +38,6 @@ namespace sha {
|
|||
|
||||
let createMethod = function () {
|
||||
let method: any = createOutputMethod('hex');
|
||||
if (NODE_JS) {
|
||||
method = nodeWrap(method);
|
||||
}
|
||||
method.create = function () {
|
||||
return new (Sha1 as any)();
|
||||
};
|
||||
|
@ -61,22 +51,6 @@ namespace sha {
|
|||
return method;
|
||||
};
|
||||
|
||||
var nodeWrap = function (method) {
|
||||
var crypto = eval("require('crypto')");
|
||||
var Buffer = eval("require('buffer').Buffer");
|
||||
var nodeMethod = function (message) {
|
||||
if (typeof message === 'string') {
|
||||
return crypto.createHash('sha1').update(message, 'utf8').digest('hex');
|
||||
} else if (message.constructor === ArrayBuffer) {
|
||||
message = new Uint8Array(message);
|
||||
} else if (message.length === undefined) {
|
||||
return method(message);
|
||||
}
|
||||
return crypto.createHash('sha1').update(new Buffer(message)).digest('hex');
|
||||
};
|
||||
return nodeMethod;
|
||||
};
|
||||
|
||||
function Sha1(sharedMemory) {
|
||||
if (sharedMemory) {
|
||||
blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] =
|
||||
|
@ -359,8 +333,8 @@ namespace sha {
|
|||
Sha1.prototype.arrayBuffer = function () {
|
||||
this.finalize();
|
||||
|
||||
var buffer = new ArrayBuffer(20);
|
||||
var dataView = new DataView(buffer);
|
||||
const buffer = new ArrayBuffer(20);
|
||||
const dataView = new DataView(buffer);
|
||||
dataView.setUint32(0, this.h0);
|
||||
dataView.setUint32(4, this.h1);
|
||||
dataView.setUint32(8, this.h2);
|
||||
|
@ -369,18 +343,7 @@ namespace sha {
|
|||
return buffer;
|
||||
};
|
||||
|
||||
var exports = createMethod();
|
||||
|
||||
if (COMMON_JS) {
|
||||
module.exports = exports;
|
||||
} else {
|
||||
root._sha1 = exports;
|
||||
if (AMD) {
|
||||
define(function () {
|
||||
return exports;
|
||||
});
|
||||
}
|
||||
}
|
||||
createMethod();
|
||||
})();
|
||||
|
||||
export function encode_text(buffer: string) : ArrayBuffer {
|
||||
|
@ -394,6 +357,7 @@ namespace sha {
|
|||
}
|
||||
return result.buffer;
|
||||
}
|
||||
|
||||
export function sha1(message: string | ArrayBuffer) : PromiseLike<ArrayBuffer> {
|
||||
if(!(typeof(message) === "string" || message instanceof ArrayBuffer)) throw "Invalid type!";
|
||||
|
||||
|
@ -406,5 +370,3 @@ namespace sha {
|
|||
else
|
||||
return crypto.subtle.digest("SHA-1", buffer);
|
||||
}
|
||||
|
||||
}
|
10
shared/js/crypto/uid.ts
Normal file
10
shared/js/crypto/uid.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
function s4() {
|
||||
return Math
|
||||
.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
}
|
||||
|
||||
export function guid() {
|
||||
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
namespace dns {
|
||||
export interface AddressTarget {
|
||||
target_ip: string;
|
||||
target_port?: number;
|
||||
|
@ -21,4 +20,3 @@ namespace dns {
|
|||
allow_cache: true,
|
||||
max_depth: 5
|
||||
};
|
||||
}
|
|
@ -1,4 +1,8 @@
|
|||
namespace events {
|
||||
//TODO: Combine EventConvert and Event?
|
||||
import {MusicClientEntry, SongInfo} from "tc-shared/ui/client";
|
||||
import {PlaylistSong} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {guid} from "tc-shared/crypto/uid";
|
||||
|
||||
export interface EventConvert<All> {
|
||||
as<T extends keyof All>() : All[T];
|
||||
}
|
||||
|
@ -119,10 +123,6 @@ namespace events {
|
|||
}
|
||||
}
|
||||
|
||||
namespace global {
|
||||
|
||||
}
|
||||
|
||||
export namespace channel_tree {
|
||||
export interface client {
|
||||
"enter_view": {},
|
||||
|
@ -549,7 +549,7 @@ namespace events {
|
|||
vad_type: string,
|
||||
|
||||
vad_ppt: {
|
||||
key: ppt.KeyDescriptor,
|
||||
key: any, /* ppt.KeyDescriptor */
|
||||
release_delay: number,
|
||||
release_delay_active: boolean
|
||||
},
|
||||
|
@ -595,10 +595,11 @@ namespace events {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
//Some test code
|
||||
const eclient = new events.Registry<events.channel_tree.client>();
|
||||
const emusic = new events.Registry<events.sidebar.music>();
|
||||
|
||||
eclient.connect("playlist_song_loaded", emusic);
|
||||
eclient.connect("playlist_song_loaded", emusic);
|
||||
*/
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
namespace i18n {
|
||||
interface CountryInfo {
|
||||
name: string;
|
||||
alpha_2: string;
|
||||
|
@ -1501,4 +1499,3 @@ namespace i18n {
|
|||
fill_country_infos(country_infos);
|
||||
for(const country of country_infos)
|
||||
alpha_2_map[country.alpha_2] = country;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
function guid() {
|
||||
function s4() {
|
||||
return Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
}
|
||||
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
|
||||
}
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {guid} from "tc-shared/crypto/uid";
|
||||
import {StaticSettings} from "tc-shared/settings";
|
||||
import {createErrorModal} from "tc-shared/ui/elements/Modal";
|
||||
import * as loader from "tc-loader";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
|
||||
namespace i18n {
|
||||
export interface TranslationKey {
|
||||
message: string;
|
||||
line?: number;
|
||||
|
@ -73,7 +71,7 @@ namespace i18n {
|
|||
|
||||
export function tra(message: string, ...args: any[]) {
|
||||
message = tr(message);
|
||||
return MessageHelper.formatMessage(message, ...args);
|
||||
return formatMessage(message, ...args);
|
||||
}
|
||||
|
||||
async function load_translation_file(url: string, path: string) : Promise<TranslationFile> {
|
||||
|
@ -288,7 +286,7 @@ namespace i18n {
|
|||
config.save_translation_config();
|
||||
}
|
||||
|
||||
/* ATTENTION: This method is called before most other library inizialisations! */
|
||||
/* ATTENTION: This method is called before most other library initialisations! */
|
||||
export async function initialize() {
|
||||
const rcfg = config.repository_config(); /* initialize */
|
||||
const cfg = config.translation_config();
|
||||
|
@ -314,11 +312,6 @@ namespace i18n {
|
|||
// await load_file("http://localhost/home/TeaSpeak/TeaSpeak/Web-Client/web/environment/development/i18n/de_DE.translation");
|
||||
// await load_file("http://localhost/home/TeaSpeak/TeaSpeak/Web-Client/web/environment/development/i18n/test.json");
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const tr: typeof i18n.tr = i18n.tr;
|
||||
const tra: typeof i18n.tra = i18n.tra;
|
||||
|
||||
(window as any).tr = i18n.tr;
|
||||
(window as any).tra = i18n.tra;
|
||||
window.tr = tr;
|
||||
window.tra = tra;
|
|
@ -1,6 +1,8 @@
|
|||
//Used by CertAccept popup
|
||||
import {settings} from "tc-shared/settings";
|
||||
import * as loader from "tc-loader";
|
||||
|
||||
enum LogCategory {
|
||||
export enum LogCategory {
|
||||
CHANNEL,
|
||||
CHANNEL_PROPERTIES, /* separating channel and channel properties because on channel init logging is a big bottleneck */
|
||||
CLIENT,
|
||||
|
@ -19,7 +21,6 @@ enum LogCategory {
|
|||
DNS
|
||||
}
|
||||
|
||||
namespace log {
|
||||
export enum LogType {
|
||||
TRACE,
|
||||
DEBUG,
|
||||
|
@ -157,10 +158,10 @@ namespace log {
|
|||
return new Group(group_mode, level, category, name, optionalParams);
|
||||
}
|
||||
|
||||
export function table(level: LogType, category: LogCategory, title: string, arguments: any) {
|
||||
export function table(level: LogType, category: LogCategory, title: string, args: any) {
|
||||
if(group_mode == GroupMode.NATIVE) {
|
||||
console.groupCollapsed(title);
|
||||
console.table(arguments);
|
||||
console.table(args);
|
||||
console.groupEnd();
|
||||
} else {
|
||||
if(!enabled_mapping.get(category) || !level_mapping.get(level))
|
||||
|
@ -246,6 +247,12 @@ namespace log {
|
|||
this._log_prefix = prefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import LogType = log.LogType;
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "log enabled initialisation",
|
||||
function: async () => initialize(loader.version().debug_mode ? LogType.TRACE : LogType.INFO),
|
||||
priority: 150
|
||||
});
|
||||
|
||||
/* initialize global logging system, use by the loader for example */
|
||||
window.log = module.exports;
|
|
@ -1,32 +1,42 @@
|
|||
/// <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 * as loader from "tc-loader";
|
||||
import {settings, Settings} from "tc-shared/settings";
|
||||
import * as profiles from "tc-shared/profiles/ConnectionProfile";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as log from "tc-shared/log";
|
||||
import * as bipc from "./BrowserIPC";
|
||||
import * as sound from "./sound/Sounds";
|
||||
import * as i18n from "./i18n/localize";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {createInfoModal} from "tc-shared/ui/elements/Modal";
|
||||
import {tra} from "./i18n/localize";
|
||||
import {RequestFileUpload} from "tc-shared/FileManager";
|
||||
import * as stats from "./stats";
|
||||
import * as fidentity from "./profiles/identities/TeaForumIdentity";
|
||||
import {default_recorder, RecorderProfile, set_default_recorder} from "tc-shared/voice/RecorderProfile";
|
||||
import * as cmanager from "tc-shared/ui/frames/connection_handlers";
|
||||
import {server_connections, ServerConnectionManager} from "tc-shared/ui/frames/connection_handlers";
|
||||
import * as control_bar from "tc-shared/ui/frames/ControlBar";
|
||||
import {spawnConnectModal} from "tc-shared/ui/modal/ModalConnect";
|
||||
import * as top_menu from "./ui/frames/MenuBar";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {openModalNewcomer} from "tc-shared/ui/modal/ModalNewcomer";
|
||||
import * as aplayer from "tc-backend/audio/player";
|
||||
import * as arecorder from "tc-backend/audio/recorder";
|
||||
import * as ppt from "tc-backend/ppt";
|
||||
|
||||
import spawnYesNo = Modals.spawnYesNo;
|
||||
/* required import for init */
|
||||
require("./proto").initialize();
|
||||
require("./ui/elements/ContextDivider").initialize();
|
||||
|
||||
const js_render = window.jsrender || $;
|
||||
const native_client = window.require !== undefined;
|
||||
|
||||
function getUserMediaFunctionPromise() : (constraints: MediaStreamConstraints) => Promise<MediaStream> {
|
||||
if('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices)
|
||||
return constraints => navigator.mediaDevices.getUserMedia(constraints);
|
||||
|
||||
const _callbacked_function = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
|
||||
if(!_callbacked_function)
|
||||
return undefined;
|
||||
|
||||
return constraints => new Promise<MediaStream>((resolve, reject) => _callbacked_function(constraints, resolve, reject));
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
open_connected_question: () => Promise<boolean>;
|
||||
}
|
||||
}
|
||||
|
||||
function setup_close() {
|
||||
window.onbeforeunload = event => {
|
||||
|
@ -50,7 +60,7 @@ function setup_close() {
|
|||
}));
|
||||
|
||||
const exit = () => {
|
||||
const {remote} = require('electron');
|
||||
const {remote} = window.require('electron');
|
||||
remote.getCurrentWindow().close();
|
||||
};
|
||||
|
||||
|
@ -133,7 +143,7 @@ async function initialize_app() {
|
|||
try { //Initialize main template
|
||||
const main = $("#tmpl_main").renderTag({
|
||||
multi_session: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION),
|
||||
app_version: app.ui_version()
|
||||
app_version: loader.version().ui
|
||||
}).dividerfy();
|
||||
|
||||
$("body").append(main);
|
||||
|
@ -143,21 +153,21 @@ async function initialize_app() {
|
|||
return;
|
||||
}
|
||||
|
||||
control_bar = new ControlBar($("#control_bar")); /* setup the control bar */
|
||||
control_bar.set_control_bar(new control_bar.ControlBar($("#control_bar"))); /* setup the control bar */
|
||||
|
||||
if(!audio.player.initialize())
|
||||
if(!aplayer.initialize())
|
||||
console.warn(tr("Failed to initialize audio controller!"));
|
||||
|
||||
audio.player.on_ready(() => {
|
||||
if(audio.player.set_master_volume)
|
||||
audio.player.on_ready(() => audio.player.set_master_volume(settings.global(Settings.KEY_SOUND_MASTER) / 100));
|
||||
aplayer.on_ready(() => {
|
||||
if(aplayer.set_master_volume)
|
||||
aplayer.on_ready(() => aplayer.set_master_volume(settings.global(Settings.KEY_SOUND_MASTER) / 100));
|
||||
else
|
||||
log.warn(LogCategory.GENERAL, tr("Client does not support audio.player.set_master_volume()... May client is too old?"));
|
||||
if(audio.recorder.device_refresh_available())
|
||||
audio.recorder.refresh_devices();
|
||||
log.warn(LogCategory.GENERAL, tr("Client does not support aplayer.set_master_volume()... May client is too old?"));
|
||||
if(arecorder.device_refresh_available())
|
||||
arecorder.refresh_devices();
|
||||
});
|
||||
|
||||
default_recorder = new RecorderProfile("default");
|
||||
set_default_recorder(new RecorderProfile("default"));
|
||||
default_recorder.initialize().catch(error => {
|
||||
log.error(LogCategory.AUDIO, tr("Failed to initialize default recorder: %o"), error);
|
||||
});
|
||||
|
@ -180,78 +190,6 @@ async function initialize_app() {
|
|||
setup_close();
|
||||
}
|
||||
|
||||
function str2ab8(str) {
|
||||
const buf = new ArrayBuffer(str.length);
|
||||
const bufView = new Uint8Array(buf);
|
||||
for (let i = 0, strLen = str.length; i < strLen; i++) {
|
||||
bufView[i] = str.charCodeAt(i);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* FIXME Dont use atob, because it sucks for non UTF-8 tings */
|
||||
function arrayBufferBase64(base64: string) {
|
||||
base64 = atob(base64);
|
||||
const buf = new ArrayBuffer(base64.length);
|
||||
const bufView = new Uint8Array(buf);
|
||||
for (let i = 0, strLen = base64.length; i < strLen; i++) {
|
||||
bufView[i] = base64.charCodeAt(i);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
function base64_encode_ab(source: ArrayBufferLike) {
|
||||
const encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
let base64 = "";
|
||||
|
||||
const bytes = new Uint8Array(source);
|
||||
const byte_length = bytes.byteLength;
|
||||
const byte_reminder = byte_length % 3;
|
||||
const main_length = byte_length - byte_reminder;
|
||||
|
||||
let a, b, c, d;
|
||||
let chunk;
|
||||
|
||||
// Main loop deals with bytes in chunks of 3
|
||||
for (let i = 0; i < main_length; i = i + 3) {
|
||||
// Combine the three bytes into a single integer
|
||||
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
|
||||
|
||||
// Use bitmasks to extract 6-bit segments from the triplet
|
||||
a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
|
||||
b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
|
||||
c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
|
||||
d = (chunk & 63) >> 0; // 63 = (2^6 - 1) << 0
|
||||
|
||||
// Convert the raw binary segments to the appropriate ASCII encoding
|
||||
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
|
||||
}
|
||||
|
||||
// Deal with the remaining bytes and padding
|
||||
if (byte_reminder == 1) {
|
||||
chunk = bytes[main_length];
|
||||
|
||||
a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
|
||||
|
||||
// Set the 4 least significant bits to zero
|
||||
b = (chunk & 3) << 4; // 3 = 2^2 - 1
|
||||
|
||||
base64 += encodings[a] + encodings[b] + '==';
|
||||
} else if (byte_reminder == 2) {
|
||||
chunk = (bytes[main_length] << 8) | bytes[main_length + 1];
|
||||
|
||||
a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
|
||||
b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
|
||||
|
||||
// Set the 2 least significant bits to zero
|
||||
c = (chunk & 15) << 2; // 15 = 2^4 - 1
|
||||
|
||||
base64 += encodings[a] + encodings[b] + encodings[c] + '=';
|
||||
}
|
||||
|
||||
return base64
|
||||
}
|
||||
|
||||
/*
|
||||
class TestProxy extends bipc.MethodProxy {
|
||||
constructor(params: bipc.MethodProxyConnectParameters) {
|
||||
|
@ -313,7 +251,7 @@ function handle_connect_request(properties: bipc.connect.ConnectRequestData, con
|
|||
});
|
||||
server_connections.set_active_connection_handler(connection);
|
||||
} else {
|
||||
Modals.spawnConnectModal({},{
|
||||
spawnConnectModal({},{
|
||||
url: properties.address,
|
||||
enforce: true
|
||||
}, {
|
||||
|
@ -356,14 +294,14 @@ function main() {
|
|||
|
||||
top_menu.initialize();
|
||||
|
||||
server_connections = new ServerConnectionManager($("#connection-handlers"));
|
||||
control_bar.initialise(); /* before connection handler to allow property apply */
|
||||
cmanager.initialize(new ServerConnectionManager($("#connection-handlers")));
|
||||
control_bar.control_bar.initialise(); /* before connection handler to allow property apply */
|
||||
|
||||
const initial_handler = server_connections.spawn_server_connection_handler();
|
||||
initial_handler.acquire_recorder(default_recorder, false);
|
||||
control_bar.set_connection_handler(initial_handler);
|
||||
control_bar.control_bar.set_connection_handler(initial_handler);
|
||||
/** Setup the XF forum identity **/
|
||||
profiles.identities.update_forum();
|
||||
fidentity.update_forum();
|
||||
|
||||
let _resize_timeout: NodeJS.Timer;
|
||||
$(window).on('resize', event => {
|
||||
|
@ -481,7 +419,7 @@ function main() {
|
|||
|
||||
/* for testing */
|
||||
if(settings.static_global(Settings.KEY_USER_IS_NEW)) {
|
||||
const modal = Modals.openModalNewcomer();
|
||||
const modal = openModalNewcomer();
|
||||
modal.close_listener.push(() => settings.changeGlobal(Settings.KEY_USER_IS_NEW, false));
|
||||
}
|
||||
}
|
||||
|
@ -492,12 +430,12 @@ const task_teaweb_starter: loader.Task = {
|
|||
try {
|
||||
await initialize_app();
|
||||
main();
|
||||
if(!audio.player.initialized()) {
|
||||
if(!aplayer.initialized()) {
|
||||
log.info(LogCategory.VOICE, tr("Initialize audio controller later!"));
|
||||
if(!audio.player.initializeFromGesture) {
|
||||
console.error(tr("Missing audio.player.initializeFromGesture"));
|
||||
if(!aplayer.initializeFromGesture) {
|
||||
console.error(tr("Missing aplayer.initializeFromGesture"));
|
||||
} else
|
||||
$(document).one('click', event => audio.player.initializeFromGesture());
|
||||
$(document).one('click', event => aplayer.initializeFromGesture());
|
||||
}
|
||||
} catch (ex) {
|
||||
console.error(ex.stack);
|
||||
|
@ -543,7 +481,7 @@ const task_connect_handler: loader.Task = {
|
|||
"You could now close this page.";
|
||||
createInfoModal(
|
||||
tr("Connecting successfully within other instance"),
|
||||
MessageHelper.formatMessage(tr(message), connect_data.address),
|
||||
formatMessage(tr(message), connect_data.address),
|
||||
{
|
||||
closeable: false,
|
||||
footer: undefined
|
||||
|
@ -610,7 +548,7 @@ const task_certificate_callback: loader.Task = {
|
|||
"This page will close in {0} seconds.";
|
||||
createInfoModal(
|
||||
tr("Certificate acccepted successfully"),
|
||||
MessageHelper.formatMessage(tr(message), seconds_tag),
|
||||
formatMessage(tr(message), seconds_tag),
|
||||
{
|
||||
closeable: false,
|
||||
footer: undefined
|
||||
|
@ -650,7 +588,7 @@ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
|||
try {
|
||||
await initialize();
|
||||
|
||||
if(app.is_web()) {
|
||||
if(loader.version().type == "web") {
|
||||
loader.register_task(loader.Stage.LOADED, task_certificate_callback);
|
||||
} else {
|
||||
loader.register_task(loader.Stage.LOADED, task_teaweb_starter);
|
||||
|
@ -667,3 +605,15 @@ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
|||
priority: 1000
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.LOADED, {
|
||||
name: "error task",
|
||||
function: async () => {
|
||||
if(Settings.instance.static(Settings.KEY_LOAD_DUMMY_ERROR, false)) {
|
||||
loader.critical_error("The tea is cold!", "Argh, this is evil! Cold tea dosn't taste good.");
|
||||
throw "The tea is cold!";
|
||||
}
|
||||
},
|
||||
priority: 20
|
||||
});
|
||||
|
||||
export = {};
|
|
@ -1,17 +1,24 @@
|
|||
/// <reference path="../connection/ConnectionBase.ts" />
|
||||
import {LaterPromise} from "tc-shared/utils/LaterPromise";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {PermissionManager, PermissionValue} from "tc-shared/permission/PermissionManager";
|
||||
import {ServerCommand} from "tc-shared/connection/ConnectionBase";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {AbstractCommandHandler} from "tc-shared/connection/AbstractCommandHandler";
|
||||
|
||||
enum GroupType {
|
||||
export enum GroupType {
|
||||
QUERY,
|
||||
TEMPLATE,
|
||||
NORMAL
|
||||
}
|
||||
|
||||
enum GroupTarget {
|
||||
export enum GroupTarget {
|
||||
SERVER,
|
||||
CHANNEL
|
||||
}
|
||||
|
||||
class GroupProperties {
|
||||
export class GroupProperties {
|
||||
iconid: number = 0;
|
||||
|
||||
sortid: number = 0;
|
||||
|
@ -19,12 +26,12 @@ class GroupProperties {
|
|||
namemode: number = 0;
|
||||
}
|
||||
|
||||
class GroupPermissionRequest {
|
||||
export class GroupPermissionRequest {
|
||||
group_id: number;
|
||||
promise: LaterPromise<PermissionValue[]>;
|
||||
}
|
||||
|
||||
class Group {
|
||||
export class Group {
|
||||
properties: GroupProperties = new GroupProperties();
|
||||
|
||||
readonly handle: GroupManager;
|
||||
|
@ -63,7 +70,7 @@ class Group {
|
|||
}
|
||||
}
|
||||
|
||||
class GroupManager extends connection.AbstractCommandHandler {
|
||||
export class GroupManager extends AbstractCommandHandler {
|
||||
readonly handle: ConnectionHandler;
|
||||
|
||||
serverGroups: Group[] = [];
|
||||
|
@ -83,7 +90,7 @@ class GroupManager extends connection.AbstractCommandHandler {
|
|||
this.channelGroups = undefined;
|
||||
}
|
||||
|
||||
handle_command(command: connection.ServerCommand): boolean {
|
||||
handle_command(command: ServerCommand): boolean {
|
||||
switch (command.command) {
|
||||
case "notifyservergrouplist":
|
||||
case "notifychannelgrouplist":
|
||||
|
@ -160,7 +167,7 @@ class GroupManager extends connection.AbstractCommandHandler {
|
|||
}
|
||||
|
||||
let group = new Group(this,parseInt(target == GroupTarget.SERVER ? groupData["sgid"] : groupData["cgid"]), target, type, groupData["name"]);
|
||||
for(let key in groupData as any) {
|
||||
for(let key in Object.keys(groupData)) {
|
||||
if(key == "sgid") continue;
|
||||
if(key == "cgid") continue;
|
||||
if(key == "type") continue;
|
||||
|
|
|
@ -1,358 +1,13 @@
|
|||
/// <reference path="../ConnectionHandler.ts" />
|
||||
/// <reference path="../connection/ConnectionBase.ts" />
|
||||
/// <reference path="../i18n/localize.ts" />
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory, LogType} from "tc-shared/log";
|
||||
import PermissionType from "tc-shared/permission/PermissionType";
|
||||
import {LaterPromise} from "tc-shared/utils/LaterPromise";
|
||||
import {ServerCommand} from "tc-shared/connection/ConnectionBase";
|
||||
import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {AbstractCommandHandler} from "tc-shared/connection/AbstractCommandHandler";
|
||||
|
||||
enum PermissionType {
|
||||
B_SERVERINSTANCE_HELP_VIEW = "b_serverinstance_help_view",
|
||||
B_SERVERINSTANCE_VERSION_VIEW = "b_serverinstance_version_view",
|
||||
B_SERVERINSTANCE_INFO_VIEW = "b_serverinstance_info_view",
|
||||
B_SERVERINSTANCE_VIRTUALSERVER_LIST = "b_serverinstance_virtualserver_list",
|
||||
B_SERVERINSTANCE_BINDING_LIST = "b_serverinstance_binding_list",
|
||||
B_SERVERINSTANCE_PERMISSION_LIST = "b_serverinstance_permission_list",
|
||||
B_SERVERINSTANCE_PERMISSION_FIND = "b_serverinstance_permission_find",
|
||||
B_VIRTUALSERVER_CREATE = "b_virtualserver_create",
|
||||
B_VIRTUALSERVER_DELETE = "b_virtualserver_delete",
|
||||
B_VIRTUALSERVER_START_ANY = "b_virtualserver_start_any",
|
||||
B_VIRTUALSERVER_STOP_ANY = "b_virtualserver_stop_any",
|
||||
B_VIRTUALSERVER_CHANGE_MACHINE_ID = "b_virtualserver_change_machine_id",
|
||||
B_VIRTUALSERVER_CHANGE_TEMPLATE = "b_virtualserver_change_template",
|
||||
B_SERVERQUERY_LOGIN = "b_serverquery_login",
|
||||
B_SERVERINSTANCE_TEXTMESSAGE_SEND = "b_serverinstance_textmessage_send",
|
||||
B_SERVERINSTANCE_LOG_VIEW = "b_serverinstance_log_view",
|
||||
B_SERVERINSTANCE_LOG_ADD = "b_serverinstance_log_add",
|
||||
B_SERVERINSTANCE_STOP = "b_serverinstance_stop",
|
||||
B_SERVERINSTANCE_MODIFY_SETTINGS = "b_serverinstance_modify_settings",
|
||||
B_SERVERINSTANCE_MODIFY_QUERYGROUP = "b_serverinstance_modify_querygroup",
|
||||
B_SERVERINSTANCE_MODIFY_TEMPLATES = "b_serverinstance_modify_templates",
|
||||
B_VIRTUALSERVER_SELECT = "b_virtualserver_select",
|
||||
B_VIRTUALSERVER_SELECT_GODMODE = "b_virtualserver_select_godmode",
|
||||
B_VIRTUALSERVER_INFO_VIEW = "b_virtualserver_info_view",
|
||||
B_VIRTUALSERVER_CONNECTIONINFO_VIEW = "b_virtualserver_connectioninfo_view",
|
||||
B_VIRTUALSERVER_CHANNEL_LIST = "b_virtualserver_channel_list",
|
||||
B_VIRTUALSERVER_CHANNEL_SEARCH = "b_virtualserver_channel_search",
|
||||
B_VIRTUALSERVER_CLIENT_LIST = "b_virtualserver_client_list",
|
||||
B_VIRTUALSERVER_CLIENT_SEARCH = "b_virtualserver_client_search",
|
||||
B_VIRTUALSERVER_CLIENT_DBLIST = "b_virtualserver_client_dblist",
|
||||
B_VIRTUALSERVER_CLIENT_DBSEARCH = "b_virtualserver_client_dbsearch",
|
||||
B_VIRTUALSERVER_CLIENT_DBINFO = "b_virtualserver_client_dbinfo",
|
||||
B_VIRTUALSERVER_PERMISSION_FIND = "b_virtualserver_permission_find",
|
||||
B_VIRTUALSERVER_CUSTOM_SEARCH = "b_virtualserver_custom_search",
|
||||
B_VIRTUALSERVER_START = "b_virtualserver_start",
|
||||
B_VIRTUALSERVER_STOP = "b_virtualserver_stop",
|
||||
B_VIRTUALSERVER_TOKEN_LIST = "b_virtualserver_token_list",
|
||||
B_VIRTUALSERVER_TOKEN_ADD = "b_virtualserver_token_add",
|
||||
B_VIRTUALSERVER_TOKEN_USE = "b_virtualserver_token_use",
|
||||
B_VIRTUALSERVER_TOKEN_DELETE = "b_virtualserver_token_delete",
|
||||
B_VIRTUALSERVER_LOG_VIEW = "b_virtualserver_log_view",
|
||||
B_VIRTUALSERVER_LOG_ADD = "b_virtualserver_log_add",
|
||||
B_VIRTUALSERVER_JOIN_IGNORE_PASSWORD = "b_virtualserver_join_ignore_password",
|
||||
B_VIRTUALSERVER_NOTIFY_REGISTER = "b_virtualserver_notify_register",
|
||||
B_VIRTUALSERVER_NOTIFY_UNREGISTER = "b_virtualserver_notify_unregister",
|
||||
B_VIRTUALSERVER_SNAPSHOT_CREATE = "b_virtualserver_snapshot_create",
|
||||
B_VIRTUALSERVER_SNAPSHOT_DEPLOY = "b_virtualserver_snapshot_deploy",
|
||||
B_VIRTUALSERVER_PERMISSION_RESET = "b_virtualserver_permission_reset",
|
||||
B_VIRTUALSERVER_MODIFY_NAME = "b_virtualserver_modify_name",
|
||||
B_VIRTUALSERVER_MODIFY_WELCOMEMESSAGE = "b_virtualserver_modify_welcomemessage",
|
||||
B_VIRTUALSERVER_MODIFY_MAXCLIENTS = "b_virtualserver_modify_maxclients",
|
||||
B_VIRTUALSERVER_MODIFY_RESERVED_SLOTS = "b_virtualserver_modify_reserved_slots",
|
||||
B_VIRTUALSERVER_MODIFY_PASSWORD = "b_virtualserver_modify_password",
|
||||
B_VIRTUALSERVER_MODIFY_DEFAULT_SERVERGROUP = "b_virtualserver_modify_default_servergroup",
|
||||
B_VIRTUALSERVER_MODIFY_DEFAULT_MUSICGROUP = "b_virtualserver_modify_default_musicgroup",
|
||||
B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELGROUP = "b_virtualserver_modify_default_channelgroup",
|
||||
B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELADMINGROUP = "b_virtualserver_modify_default_channeladmingroup",
|
||||
B_VIRTUALSERVER_MODIFY_CHANNEL_FORCED_SILENCE = "b_virtualserver_modify_channel_forced_silence",
|
||||
B_VIRTUALSERVER_MODIFY_COMPLAIN = "b_virtualserver_modify_complain",
|
||||
B_VIRTUALSERVER_MODIFY_ANTIFLOOD = "b_virtualserver_modify_antiflood",
|
||||
B_VIRTUALSERVER_MODIFY_FT_SETTINGS = "b_virtualserver_modify_ft_settings",
|
||||
B_VIRTUALSERVER_MODIFY_FT_QUOTAS = "b_virtualserver_modify_ft_quotas",
|
||||
B_VIRTUALSERVER_MODIFY_HOSTMESSAGE = "b_virtualserver_modify_hostmessage",
|
||||
B_VIRTUALSERVER_MODIFY_HOSTBANNER = "b_virtualserver_modify_hostbanner",
|
||||
B_VIRTUALSERVER_MODIFY_HOSTBUTTON = "b_virtualserver_modify_hostbutton",
|
||||
B_VIRTUALSERVER_MODIFY_PORT = "b_virtualserver_modify_port",
|
||||
B_VIRTUALSERVER_MODIFY_HOST = "b_virtualserver_modify_host",
|
||||
B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES = "b_virtualserver_modify_default_messages",
|
||||
B_VIRTUALSERVER_MODIFY_AUTOSTART = "b_virtualserver_modify_autostart",
|
||||
B_VIRTUALSERVER_MODIFY_NEEDED_IDENTITY_SECURITY_LEVEL = "b_virtualserver_modify_needed_identity_security_level",
|
||||
B_VIRTUALSERVER_MODIFY_PRIORITY_SPEAKER_DIMM_MODIFICATOR = "b_virtualserver_modify_priority_speaker_dimm_modificator",
|
||||
B_VIRTUALSERVER_MODIFY_LOG_SETTINGS = "b_virtualserver_modify_log_settings",
|
||||
B_VIRTUALSERVER_MODIFY_MIN_CLIENT_VERSION = "b_virtualserver_modify_min_client_version",
|
||||
B_VIRTUALSERVER_MODIFY_ICON_ID = "b_virtualserver_modify_icon_id",
|
||||
B_VIRTUALSERVER_MODIFY_WEBLIST = "b_virtualserver_modify_weblist",
|
||||
B_VIRTUALSERVER_MODIFY_CODEC_ENCRYPTION_MODE = "b_virtualserver_modify_codec_encryption_mode",
|
||||
B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS = "b_virtualserver_modify_temporary_passwords",
|
||||
B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS_OWN = "b_virtualserver_modify_temporary_passwords_own",
|
||||
B_VIRTUALSERVER_MODIFY_CHANNEL_TEMP_DELETE_DELAY_DEFAULT = "b_virtualserver_modify_channel_temp_delete_delay_default",
|
||||
B_VIRTUALSERVER_MODIFY_MUSIC_BOT_LIMIT = "b_virtualserver_modify_music_bot_limit",
|
||||
B_VIRTUALSERVER_MODIFY_COUNTRY_CODE = "b_virtualserver_modify_country_code",
|
||||
I_CHANNEL_MIN_DEPTH = "i_channel_min_depth",
|
||||
I_CHANNEL_MAX_DEPTH = "i_channel_max_depth",
|
||||
B_CHANNEL_GROUP_INHERITANCE_END = "b_channel_group_inheritance_end",
|
||||
I_CHANNEL_PERMISSION_MODIFY_POWER = "i_channel_permission_modify_power",
|
||||
I_CHANNEL_NEEDED_PERMISSION_MODIFY_POWER = "i_channel_needed_permission_modify_power",
|
||||
B_CHANNEL_INFO_VIEW = "b_channel_info_view",
|
||||
B_CHANNEL_CREATE_CHILD = "b_channel_create_child",
|
||||
B_CHANNEL_CREATE_PERMANENT = "b_channel_create_permanent",
|
||||
B_CHANNEL_CREATE_SEMI_PERMANENT = "b_channel_create_semi_permanent",
|
||||
B_CHANNEL_CREATE_TEMPORARY = "b_channel_create_temporary",
|
||||
B_CHANNEL_CREATE_PRIVATE = "b_channel_create_private",
|
||||
B_CHANNEL_CREATE_WITH_TOPIC = "b_channel_create_with_topic",
|
||||
B_CHANNEL_CREATE_WITH_DESCRIPTION = "b_channel_create_with_description",
|
||||
B_CHANNEL_CREATE_WITH_PASSWORD = "b_channel_create_with_password",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX8 = "b_channel_create_modify_with_codec_speex8",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX16 = "b_channel_create_modify_with_codec_speex16",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX32 = "b_channel_create_modify_with_codec_speex32",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_CELTMONO48 = "b_channel_create_modify_with_codec_celtmono48",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE = "b_channel_create_modify_with_codec_opusvoice",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC = "b_channel_create_modify_with_codec_opusmusic",
|
||||
I_CHANNEL_CREATE_MODIFY_WITH_CODEC_MAXQUALITY = "i_channel_create_modify_with_codec_maxquality",
|
||||
I_CHANNEL_CREATE_MODIFY_WITH_CODEC_LATENCY_FACTOR_MIN = "i_channel_create_modify_with_codec_latency_factor_min",
|
||||
I_CHANNEL_CREATE_MODIFY_CONVERSATION_HISTORY_LENGTH = "i_channel_create_modify_conversation_history_length",
|
||||
B_CHANNEL_CREATE_MODIFY_CONVERSATION_HISTORY_UNLIMITED = "b_channel_create_modify_conversation_history_unlimited",
|
||||
B_CHANNEL_CREATE_MODIFY_CONVERSATION_PRIVATE = "b_channel_create_modify_conversation_private",
|
||||
B_CHANNEL_CREATE_WITH_MAXCLIENTS = "b_channel_create_with_maxclients",
|
||||
B_CHANNEL_CREATE_WITH_MAXFAMILYCLIENTS = "b_channel_create_with_maxfamilyclients",
|
||||
B_CHANNEL_CREATE_WITH_SORTORDER = "b_channel_create_with_sortorder",
|
||||
B_CHANNEL_CREATE_WITH_DEFAULT = "b_channel_create_with_default",
|
||||
B_CHANNEL_CREATE_WITH_NEEDED_TALK_POWER = "b_channel_create_with_needed_talk_power",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD = "b_channel_create_modify_with_force_password",
|
||||
I_CHANNEL_CREATE_MODIFY_WITH_TEMP_DELETE_DELAY = "i_channel_create_modify_with_temp_delete_delay",
|
||||
B_CHANNEL_MODIFY_PARENT = "b_channel_modify_parent",
|
||||
B_CHANNEL_MODIFY_MAKE_DEFAULT = "b_channel_modify_make_default",
|
||||
B_CHANNEL_MODIFY_MAKE_PERMANENT = "b_channel_modify_make_permanent",
|
||||
B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT = "b_channel_modify_make_semi_permanent",
|
||||
B_CHANNEL_MODIFY_MAKE_TEMPORARY = "b_channel_modify_make_temporary",
|
||||
B_CHANNEL_MODIFY_NAME = "b_channel_modify_name",
|
||||
B_CHANNEL_MODIFY_TOPIC = "b_channel_modify_topic",
|
||||
B_CHANNEL_MODIFY_DESCRIPTION = "b_channel_modify_description",
|
||||
B_CHANNEL_MODIFY_PASSWORD = "b_channel_modify_password",
|
||||
B_CHANNEL_MODIFY_CODEC = "b_channel_modify_codec",
|
||||
B_CHANNEL_MODIFY_CODEC_QUALITY = "b_channel_modify_codec_quality",
|
||||
B_CHANNEL_MODIFY_CODEC_LATENCY_FACTOR = "b_channel_modify_codec_latency_factor",
|
||||
B_CHANNEL_MODIFY_MAXCLIENTS = "b_channel_modify_maxclients",
|
||||
B_CHANNEL_MODIFY_MAXFAMILYCLIENTS = "b_channel_modify_maxfamilyclients",
|
||||
B_CHANNEL_MODIFY_SORTORDER = "b_channel_modify_sortorder",
|
||||
B_CHANNEL_MODIFY_NEEDED_TALK_POWER = "b_channel_modify_needed_talk_power",
|
||||
I_CHANNEL_MODIFY_POWER = "i_channel_modify_power",
|
||||
I_CHANNEL_NEEDED_MODIFY_POWER = "i_channel_needed_modify_power",
|
||||
B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED = "b_channel_modify_make_codec_encrypted",
|
||||
B_CHANNEL_MODIFY_TEMP_DELETE_DELAY = "b_channel_modify_temp_delete_delay",
|
||||
B_CHANNEL_DELETE_PERMANENT = "b_channel_delete_permanent",
|
||||
B_CHANNEL_DELETE_SEMI_PERMANENT = "b_channel_delete_semi_permanent",
|
||||
B_CHANNEL_DELETE_TEMPORARY = "b_channel_delete_temporary",
|
||||
B_CHANNEL_DELETE_FLAG_FORCE = "b_channel_delete_flag_force",
|
||||
I_CHANNEL_DELETE_POWER = "i_channel_delete_power",
|
||||
B_CHANNEL_CONVERSATION_MESSAGE_DELETE = "b_channel_conversation_message_delete",
|
||||
I_CHANNEL_NEEDED_DELETE_POWER = "i_channel_needed_delete_power",
|
||||
B_CHANNEL_JOIN_PERMANENT = "b_channel_join_permanent",
|
||||
B_CHANNEL_JOIN_SEMI_PERMANENT = "b_channel_join_semi_permanent",
|
||||
B_CHANNEL_JOIN_TEMPORARY = "b_channel_join_temporary",
|
||||
B_CHANNEL_JOIN_IGNORE_PASSWORD = "b_channel_join_ignore_password",
|
||||
B_CHANNEL_JOIN_IGNORE_MAXCLIENTS = "b_channel_join_ignore_maxclients",
|
||||
B_CHANNEL_IGNORE_VIEW_POWER = "b_channel_ignore_view_power",
|
||||
I_CHANNEL_JOIN_POWER = "i_channel_join_power",
|
||||
I_CHANNEL_NEEDED_JOIN_POWER = "i_channel_needed_join_power",
|
||||
B_CHANNEL_IGNORE_JOIN_POWER = "b_channel_ignore_join_power",
|
||||
B_CHANNEL_IGNORE_DESCRIPTION_VIEW_POWER = "b_channel_ignore_description_view_power",
|
||||
I_CHANNEL_VIEW_POWER = "i_channel_view_power",
|
||||
I_CHANNEL_NEEDED_VIEW_POWER = "i_channel_needed_view_power",
|
||||
I_CHANNEL_SUBSCRIBE_POWER = "i_channel_subscribe_power",
|
||||
I_CHANNEL_NEEDED_SUBSCRIBE_POWER = "i_channel_needed_subscribe_power",
|
||||
I_CHANNEL_DESCRIPTION_VIEW_POWER = "i_channel_description_view_power",
|
||||
I_CHANNEL_NEEDED_DESCRIPTION_VIEW_POWER = "i_channel_needed_description_view_power",
|
||||
I_ICON_ID = "i_icon_id",
|
||||
I_MAX_ICON_FILESIZE = "i_max_icon_filesize",
|
||||
I_MAX_PLAYLIST_SIZE = "i_max_playlist_size",
|
||||
I_MAX_PLAYLISTS = "i_max_playlists",
|
||||
B_ICON_MANAGE = "b_icon_manage",
|
||||
B_GROUP_IS_PERMANENT = "b_group_is_permanent",
|
||||
I_GROUP_AUTO_UPDATE_TYPE = "i_group_auto_update_type",
|
||||
I_GROUP_AUTO_UPDATE_MAX_VALUE = "i_group_auto_update_max_value",
|
||||
I_GROUP_SORT_ID = "i_group_sort_id",
|
||||
I_GROUP_SHOW_NAME_IN_TREE = "i_group_show_name_in_tree",
|
||||
B_VIRTUALSERVER_SERVERGROUP_CREATE = "b_virtualserver_servergroup_create",
|
||||
B_VIRTUALSERVER_SERVERGROUP_LIST = "b_virtualserver_servergroup_list",
|
||||
B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST = "b_virtualserver_servergroup_permission_list",
|
||||
B_VIRTUALSERVER_SERVERGROUP_CLIENT_LIST = "b_virtualserver_servergroup_client_list",
|
||||
B_VIRTUALSERVER_CHANNELGROUP_CREATE = "b_virtualserver_channelgroup_create",
|
||||
B_VIRTUALSERVER_CHANNELGROUP_LIST = "b_virtualserver_channelgroup_list",
|
||||
B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST = "b_virtualserver_channelgroup_permission_list",
|
||||
B_VIRTUALSERVER_CHANNELGROUP_CLIENT_LIST = "b_virtualserver_channelgroup_client_list",
|
||||
B_VIRTUALSERVER_CLIENT_PERMISSION_LIST = "b_virtualserver_client_permission_list",
|
||||
B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST = "b_virtualserver_channel_permission_list",
|
||||
B_VIRTUALSERVER_CHANNELCLIENT_PERMISSION_LIST = "b_virtualserver_channelclient_permission_list",
|
||||
B_VIRTUALSERVER_PLAYLIST_PERMISSION_LIST = "b_virtualserver_playlist_permission_list",
|
||||
I_SERVER_GROUP_MODIFY_POWER = "i_server_group_modify_power",
|
||||
I_SERVER_GROUP_NEEDED_MODIFY_POWER = "i_server_group_needed_modify_power",
|
||||
I_SERVER_GROUP_MEMBER_ADD_POWER = "i_server_group_member_add_power",
|
||||
I_SERVER_GROUP_SELF_ADD_POWER = "i_server_group_self_add_power",
|
||||
I_SERVER_GROUP_NEEDED_MEMBER_ADD_POWER = "i_server_group_needed_member_add_power",
|
||||
I_SERVER_GROUP_MEMBER_REMOVE_POWER = "i_server_group_member_remove_power",
|
||||
I_SERVER_GROUP_SELF_REMOVE_POWER = "i_server_group_self_remove_power",
|
||||
I_SERVER_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_server_group_needed_member_remove_power",
|
||||
I_CHANNEL_GROUP_MODIFY_POWER = "i_channel_group_modify_power",
|
||||
I_CHANNEL_GROUP_NEEDED_MODIFY_POWER = "i_channel_group_needed_modify_power",
|
||||
I_CHANNEL_GROUP_MEMBER_ADD_POWER = "i_channel_group_member_add_power",
|
||||
I_CHANNEL_GROUP_SELF_ADD_POWER = "i_channel_group_self_add_power",
|
||||
I_CHANNEL_GROUP_NEEDED_MEMBER_ADD_POWER = "i_channel_group_needed_member_add_power",
|
||||
I_CHANNEL_GROUP_MEMBER_REMOVE_POWER = "i_channel_group_member_remove_power",
|
||||
I_CHANNEL_GROUP_SELF_REMOVE_POWER = "i_channel_group_self_remove_power",
|
||||
I_CHANNEL_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_channel_group_needed_member_remove_power",
|
||||
I_GROUP_MEMBER_ADD_POWER = "i_group_member_add_power",
|
||||
I_GROUP_NEEDED_MEMBER_ADD_POWER = "i_group_needed_member_add_power",
|
||||
I_GROUP_MEMBER_REMOVE_POWER = "i_group_member_remove_power",
|
||||
I_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_group_needed_member_remove_power",
|
||||
I_GROUP_MODIFY_POWER = "i_group_modify_power",
|
||||
I_GROUP_NEEDED_MODIFY_POWER = "i_group_needed_modify_power",
|
||||
I_PERMISSION_MODIFY_POWER = "i_permission_modify_power",
|
||||
B_PERMISSION_MODIFY_POWER_IGNORE = "b_permission_modify_power_ignore",
|
||||
B_VIRTUALSERVER_SERVERGROUP_DELETE = "b_virtualserver_servergroup_delete",
|
||||
B_VIRTUALSERVER_CHANNELGROUP_DELETE = "b_virtualserver_channelgroup_delete",
|
||||
I_CLIENT_PERMISSION_MODIFY_POWER = "i_client_permission_modify_power",
|
||||
I_CLIENT_NEEDED_PERMISSION_MODIFY_POWER = "i_client_needed_permission_modify_power",
|
||||
I_CLIENT_MAX_CLONES_UID = "i_client_max_clones_uid",
|
||||
I_CLIENT_MAX_CLONES_IP = "i_client_max_clones_ip",
|
||||
I_CLIENT_MAX_CLONES_HWID = "i_client_max_clones_hwid",
|
||||
I_CLIENT_MAX_IDLETIME = "i_client_max_idletime",
|
||||
I_CLIENT_MAX_AVATAR_FILESIZE = "i_client_max_avatar_filesize",
|
||||
I_CLIENT_MAX_CHANNEL_SUBSCRIPTIONS = "i_client_max_channel_subscriptions",
|
||||
I_CLIENT_MAX_CHANNELS = "i_client_max_channels",
|
||||
I_CLIENT_MAX_TEMPORARY_CHANNELS = "i_client_max_temporary_channels",
|
||||
I_CLIENT_MAX_SEMI_CHANNELS = "i_client_max_semi_channels",
|
||||
I_CLIENT_MAX_PERMANENT_CHANNELS = "i_client_max_permanent_channels",
|
||||
B_CLIENT_USE_PRIORITY_SPEAKER = "b_client_use_priority_speaker",
|
||||
B_CLIENT_SKIP_CHANNELGROUP_PERMISSIONS = "b_client_skip_channelgroup_permissions",
|
||||
B_CLIENT_FORCE_PUSH_TO_TALK = "b_client_force_push_to_talk",
|
||||
B_CLIENT_IGNORE_BANS = "b_client_ignore_bans",
|
||||
B_CLIENT_IGNORE_VPN = "b_client_ignore_vpn",
|
||||
B_CLIENT_IGNORE_ANTIFLOOD = "b_client_ignore_antiflood",
|
||||
B_CLIENT_ENFORCE_VALID_HWID = "b_client_enforce_valid_hwid",
|
||||
B_CLIENT_ALLOW_INVALID_PACKET = "b_client_allow_invalid_packet",
|
||||
B_CLIENT_ALLOW_INVALID_BADGES = "b_client_allow_invalid_badges",
|
||||
B_CLIENT_ISSUE_CLIENT_QUERY_COMMAND = "b_client_issue_client_query_command",
|
||||
B_CLIENT_USE_RESERVED_SLOT = "b_client_use_reserved_slot",
|
||||
B_CLIENT_USE_CHANNEL_COMMANDER = "b_client_use_channel_commander",
|
||||
B_CLIENT_REQUEST_TALKER = "b_client_request_talker",
|
||||
B_CLIENT_AVATAR_DELETE_OTHER = "b_client_avatar_delete_other",
|
||||
B_CLIENT_IS_STICKY = "b_client_is_sticky",
|
||||
B_CLIENT_IGNORE_STICKY = "b_client_ignore_sticky",
|
||||
B_CLIENT_MUSIC_CREATE_PERMANENT = "b_client_music_create_permanent",
|
||||
B_CLIENT_MUSIC_CREATE_SEMI_PERMANENT = "b_client_music_create_semi_permanent",
|
||||
B_CLIENT_MUSIC_CREATE_TEMPORARY = "b_client_music_create_temporary",
|
||||
B_CLIENT_MUSIC_MODIFY_PERMANENT = "b_client_music_modify_permanent",
|
||||
B_CLIENT_MUSIC_MODIFY_SEMI_PERMANENT = "b_client_music_modify_semi_permanent",
|
||||
B_CLIENT_MUSIC_MODIFY_TEMPORARY = "b_client_music_modify_temporary",
|
||||
I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME = "i_client_music_create_modify_max_volume",
|
||||
I_CLIENT_MUSIC_LIMIT = "i_client_music_limit",
|
||||
I_CLIENT_MUSIC_NEEDED_DELETE_POWER = "i_client_music_needed_delete_power",
|
||||
I_CLIENT_MUSIC_DELETE_POWER = "i_client_music_delete_power",
|
||||
I_CLIENT_MUSIC_PLAY_POWER = "i_client_music_play_power",
|
||||
I_CLIENT_MUSIC_NEEDED_PLAY_POWER = "i_client_music_needed_play_power",
|
||||
I_CLIENT_MUSIC_MODIFY_POWER = "i_client_music_modify_power",
|
||||
I_CLIENT_MUSIC_NEEDED_MODIFY_POWER = "i_client_music_needed_modify_power",
|
||||
I_CLIENT_MUSIC_RENAME_POWER = "i_client_music_rename_power",
|
||||
I_CLIENT_MUSIC_NEEDED_RENAME_POWER = "i_client_music_needed_rename_power",
|
||||
B_PLAYLIST_CREATE = "b_playlist_create",
|
||||
I_PLAYLIST_VIEW_POWER = "i_playlist_view_power",
|
||||
I_PLAYLIST_NEEDED_VIEW_POWER = "i_playlist_needed_view_power",
|
||||
I_PLAYLIST_MODIFY_POWER = "i_playlist_modify_power",
|
||||
I_PLAYLIST_NEEDED_MODIFY_POWER = "i_playlist_needed_modify_power",
|
||||
I_PLAYLIST_PERMISSION_MODIFY_POWER = "i_playlist_permission_modify_power",
|
||||
I_PLAYLIST_NEEDED_PERMISSION_MODIFY_POWER = "i_playlist_needed_permission_modify_power",
|
||||
I_PLAYLIST_DELETE_POWER = "i_playlist_delete_power",
|
||||
I_PLAYLIST_NEEDED_DELETE_POWER = "i_playlist_needed_delete_power",
|
||||
I_PLAYLIST_SONG_ADD_POWER = "i_playlist_song_add_power",
|
||||
I_PLAYLIST_SONG_NEEDED_ADD_POWER = "i_playlist_song_needed_add_power",
|
||||
I_PLAYLIST_SONG_REMOVE_POWER = "i_playlist_song_remove_power",
|
||||
I_PLAYLIST_SONG_NEEDED_REMOVE_POWER = "i_playlist_song_needed_remove_power",
|
||||
B_CLIENT_INFO_VIEW = "b_client_info_view",
|
||||
B_CLIENT_PERMISSIONOVERVIEW_VIEW = "b_client_permissionoverview_view",
|
||||
B_CLIENT_PERMISSIONOVERVIEW_OWN = "b_client_permissionoverview_own",
|
||||
B_CLIENT_REMOTEADDRESS_VIEW = "b_client_remoteaddress_view",
|
||||
I_CLIENT_SERVERQUERY_VIEW_POWER = "i_client_serverquery_view_power",
|
||||
I_CLIENT_NEEDED_SERVERQUERY_VIEW_POWER = "i_client_needed_serverquery_view_power",
|
||||
B_CLIENT_CUSTOM_INFO_VIEW = "b_client_custom_info_view",
|
||||
B_CLIENT_MUSIC_CHANNEL_LIST = "b_client_music_channel_list",
|
||||
B_CLIENT_MUSIC_SERVER_LIST = "b_client_music_server_list",
|
||||
I_CLIENT_MUSIC_INFO = "i_client_music_info",
|
||||
I_CLIENT_MUSIC_NEEDED_INFO = "i_client_music_needed_info",
|
||||
I_CLIENT_KICK_FROM_SERVER_POWER = "i_client_kick_from_server_power",
|
||||
I_CLIENT_NEEDED_KICK_FROM_SERVER_POWER = "i_client_needed_kick_from_server_power",
|
||||
I_CLIENT_KICK_FROM_CHANNEL_POWER = "i_client_kick_from_channel_power",
|
||||
I_CLIENT_NEEDED_KICK_FROM_CHANNEL_POWER = "i_client_needed_kick_from_channel_power",
|
||||
I_CLIENT_BAN_POWER = "i_client_ban_power",
|
||||
I_CLIENT_NEEDED_BAN_POWER = "i_client_needed_ban_power",
|
||||
I_CLIENT_MOVE_POWER = "i_client_move_power",
|
||||
I_CLIENT_NEEDED_MOVE_POWER = "i_client_needed_move_power",
|
||||
I_CLIENT_COMPLAIN_POWER = "i_client_complain_power",
|
||||
I_CLIENT_NEEDED_COMPLAIN_POWER = "i_client_needed_complain_power",
|
||||
B_CLIENT_COMPLAIN_LIST = "b_client_complain_list",
|
||||
B_CLIENT_COMPLAIN_DELETE_OWN = "b_client_complain_delete_own",
|
||||
B_CLIENT_COMPLAIN_DELETE = "b_client_complain_delete",
|
||||
B_CLIENT_BAN_LIST = "b_client_ban_list",
|
||||
B_CLIENT_BAN_LIST_GLOBAL = "b_client_ban_list_global",
|
||||
B_CLIENT_BAN_TRIGGER_LIST = "b_client_ban_trigger_list",
|
||||
B_CLIENT_BAN_CREATE = "b_client_ban_create",
|
||||
B_CLIENT_BAN_CREATE_GLOBAL = "b_client_ban_create_global",
|
||||
B_CLIENT_BAN_NAME = "b_client_ban_name",
|
||||
B_CLIENT_BAN_IP = "b_client_ban_ip",
|
||||
B_CLIENT_BAN_HWID = "b_client_ban_hwid",
|
||||
B_CLIENT_BAN_EDIT = "b_client_ban_edit",
|
||||
B_CLIENT_BAN_EDIT_GLOBAL = "b_client_ban_edit_global",
|
||||
B_CLIENT_BAN_DELETE_OWN = "b_client_ban_delete_own",
|
||||
B_CLIENT_BAN_DELETE = "b_client_ban_delete",
|
||||
B_CLIENT_BAN_DELETE_OWN_GLOBAL = "b_client_ban_delete_own_global",
|
||||
B_CLIENT_BAN_DELETE_GLOBAL = "b_client_ban_delete_global",
|
||||
I_CLIENT_BAN_MAX_BANTIME = "i_client_ban_max_bantime",
|
||||
I_CLIENT_PRIVATE_TEXTMESSAGE_POWER = "i_client_private_textmessage_power",
|
||||
I_CLIENT_NEEDED_PRIVATE_TEXTMESSAGE_POWER = "i_client_needed_private_textmessage_power",
|
||||
B_CLIENT_EVEN_TEXTMESSAGE_SEND = "b_client_even_textmessage_send",
|
||||
B_CLIENT_SERVER_TEXTMESSAGE_SEND = "b_client_server_textmessage_send",
|
||||
B_CLIENT_CHANNEL_TEXTMESSAGE_SEND = "b_client_channel_textmessage_send",
|
||||
B_CLIENT_OFFLINE_TEXTMESSAGE_SEND = "b_client_offline_textmessage_send",
|
||||
I_CLIENT_TALK_POWER = "i_client_talk_power",
|
||||
I_CLIENT_NEEDED_TALK_POWER = "i_client_needed_talk_power",
|
||||
I_CLIENT_POKE_POWER = "i_client_poke_power",
|
||||
I_CLIENT_NEEDED_POKE_POWER = "i_client_needed_poke_power",
|
||||
B_CLIENT_SET_FLAG_TALKER = "b_client_set_flag_talker",
|
||||
I_CLIENT_WHISPER_POWER = "i_client_whisper_power",
|
||||
I_CLIENT_NEEDED_WHISPER_POWER = "i_client_needed_whisper_power",
|
||||
B_CLIENT_MODIFY_DESCRIPTION = "b_client_modify_description",
|
||||
B_CLIENT_MODIFY_OWN_DESCRIPTION = "b_client_modify_own_description",
|
||||
B_CLIENT_USE_BBCODE_ANY = "b_client_use_bbcode_any",
|
||||
B_CLIENT_USE_BBCODE_URL = "b_client_use_bbcode_url",
|
||||
B_CLIENT_USE_BBCODE_IMAGE = "b_client_use_bbcode_image",
|
||||
B_CLIENT_MODIFY_DBPROPERTIES = "b_client_modify_dbproperties",
|
||||
B_CLIENT_DELETE_DBPROPERTIES = "b_client_delete_dbproperties",
|
||||
B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN = "b_client_create_modify_serverquery_login",
|
||||
B_CLIENT_QUERY_CREATE = "b_client_query_create",
|
||||
B_CLIENT_QUERY_LIST = "b_client_query_list",
|
||||
B_CLIENT_QUERY_LIST_OWN = "b_client_query_list_own",
|
||||
B_CLIENT_QUERY_RENAME = "b_client_query_rename",
|
||||
B_CLIENT_QUERY_RENAME_OWN = "b_client_query_rename_own",
|
||||
B_CLIENT_QUERY_CHANGE_PASSWORD = "b_client_query_change_password",
|
||||
B_CLIENT_QUERY_CHANGE_OWN_PASSWORD = "b_client_query_change_own_password",
|
||||
B_CLIENT_QUERY_CHANGE_PASSWORD_GLOBAL = "b_client_query_change_password_global",
|
||||
B_CLIENT_QUERY_DELETE = "b_client_query_delete",
|
||||
B_CLIENT_QUERY_DELETE_OWN = "b_client_query_delete_own",
|
||||
B_FT_IGNORE_PASSWORD = "b_ft_ignore_password",
|
||||
B_FT_TRANSFER_LIST = "b_ft_transfer_list",
|
||||
I_FT_FILE_UPLOAD_POWER = "i_ft_file_upload_power",
|
||||
I_FT_NEEDED_FILE_UPLOAD_POWER = "i_ft_needed_file_upload_power",
|
||||
I_FT_FILE_DOWNLOAD_POWER = "i_ft_file_download_power",
|
||||
I_FT_NEEDED_FILE_DOWNLOAD_POWER = "i_ft_needed_file_download_power",
|
||||
I_FT_FILE_DELETE_POWER = "i_ft_file_delete_power",
|
||||
I_FT_NEEDED_FILE_DELETE_POWER = "i_ft_needed_file_delete_power",
|
||||
I_FT_FILE_RENAME_POWER = "i_ft_file_rename_power",
|
||||
I_FT_NEEDED_FILE_RENAME_POWER = "i_ft_needed_file_rename_power",
|
||||
I_FT_FILE_BROWSE_POWER = "i_ft_file_browse_power",
|
||||
I_FT_NEEDED_FILE_BROWSE_POWER = "i_ft_needed_file_browse_power",
|
||||
I_FT_DIRECTORY_CREATE_POWER = "i_ft_directory_create_power",
|
||||
I_FT_NEEDED_DIRECTORY_CREATE_POWER = "i_ft_needed_directory_create_power",
|
||||
I_FT_QUOTA_MB_DOWNLOAD_PER_CLIENT = "i_ft_quota_mb_download_per_client",
|
||||
I_FT_QUOTA_MB_UPLOAD_PER_CLIENT = "i_ft_quota_mb_upload_per_client"
|
||||
}
|
||||
|
||||
class PermissionInfo {
|
||||
export class PermissionInfo {
|
||||
name: string;
|
||||
id: number;
|
||||
description: string;
|
||||
|
@ -363,21 +18,21 @@ class PermissionInfo {
|
|||
}
|
||||
}
|
||||
|
||||
class PermissionGroup {
|
||||
export class PermissionGroup {
|
||||
begin: number;
|
||||
end: number;
|
||||
deep: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
class GroupedPermissions {
|
||||
export class GroupedPermissions {
|
||||
group: PermissionGroup;
|
||||
permissions: PermissionInfo[];
|
||||
children: GroupedPermissions[];
|
||||
parent: GroupedPermissions;
|
||||
}
|
||||
|
||||
class PermissionValue {
|
||||
export class PermissionValue {
|
||||
readonly type: PermissionInfo;
|
||||
value: number;
|
||||
flag_skip: boolean;
|
||||
|
@ -411,13 +66,12 @@ class PermissionValue {
|
|||
}
|
||||
}
|
||||
|
||||
class NeededPermissionValue extends PermissionValue {
|
||||
export class NeededPermissionValue extends PermissionValue {
|
||||
constructor(type, value) {
|
||||
super(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
namespace permissions {
|
||||
export type PermissionRequestKeys = {
|
||||
client_id?: number;
|
||||
channel_id?: number;
|
||||
|
@ -471,15 +125,15 @@ namespace permissions {
|
|||
group_id: number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type RequestLists =
|
||||
export type RequestLists =
|
||||
"requests_channel_permissions" |
|
||||
"requests_client_permissions" |
|
||||
"requests_client_channel_permissions" |
|
||||
"requests_playlist_permissions" |
|
||||
"requests_playlist_client_permissions";
|
||||
class PermissionManager extends connection.AbstractCommandHandler {
|
||||
|
||||
export class PermissionManager extends AbstractCommandHandler {
|
||||
readonly handle: ConnectionHandler;
|
||||
|
||||
permissionList: PermissionInfo[] = [];
|
||||
|
@ -488,11 +142,11 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
|
||||
needed_permission_change_listener: {[permission: string]:(() => any)[]} = {};
|
||||
|
||||
requests_channel_permissions: permissions.PermissionRequest[] = [];
|
||||
requests_client_permissions: permissions.PermissionRequest[] = [];
|
||||
requests_client_channel_permissions: permissions.PermissionRequest[] = [];
|
||||
requests_playlist_permissions: permissions.PermissionRequest[] = [];
|
||||
requests_playlist_client_permissions: permissions.PermissionRequest[] = [];
|
||||
requests_channel_permissions: PermissionRequest[] = [];
|
||||
requests_client_permissions: PermissionRequest[] = [];
|
||||
requests_client_channel_permissions: PermissionRequest[] = [];
|
||||
requests_playlist_permissions: PermissionRequest[] = [];
|
||||
requests_playlist_client_permissions: PermissionRequest[] = [];
|
||||
|
||||
requests_permfind: {
|
||||
timeout_id: number,
|
||||
|
@ -603,7 +257,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
this._cacheNeededPermissions = undefined;
|
||||
}
|
||||
|
||||
handle_command(command: connection.ServerCommand): boolean {
|
||||
handle_command(command: ServerCommand): boolean {
|
||||
switch (command.command) {
|
||||
case "notifyclientneededpermissions":
|
||||
this.onNeededPermissions(command.arguments);
|
||||
|
@ -775,7 +429,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
}, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions));
|
||||
}
|
||||
|
||||
private execute_channel_permission_request(request: permissions.PermissionRequestKeys) {
|
||||
private execute_channel_permission_request(request: PermissionRequestKeys) {
|
||||
this.handle.serverConnection.send_command("channelpermlist", {"cid": request.channel_id}).catch(error => {
|
||||
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT)
|
||||
this.fullfill_permission_request("requests_channel_permissions", request, "success", []);
|
||||
|
@ -785,7 +439,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
}
|
||||
|
||||
requestChannelPermissions(channelId: number) : Promise<PermissionValue[]> {
|
||||
const keys: permissions.PermissionRequestKeys = {
|
||||
const keys: PermissionRequestKeys = {
|
||||
channel_id: channelId
|
||||
};
|
||||
return this.execute_permission_request("requests_channel_permissions", keys, this.execute_channel_permission_request.bind(this));
|
||||
|
@ -799,7 +453,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
}, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions));
|
||||
}
|
||||
|
||||
private execute_client_permission_request(request: permissions.PermissionRequestKeys) {
|
||||
private execute_client_permission_request(request: PermissionRequestKeys) {
|
||||
this.handle.serverConnection.send_command("clientpermlist", {cldbid: request.client_id}).catch(error => {
|
||||
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT)
|
||||
this.fullfill_permission_request("requests_client_permissions", request, "success", []);
|
||||
|
@ -809,7 +463,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
}
|
||||
|
||||
requestClientPermissions(client_id: number) : Promise<PermissionValue[]> {
|
||||
const keys: permissions.PermissionRequestKeys = {
|
||||
const keys: PermissionRequestKeys = {
|
||||
client_id: client_id
|
||||
};
|
||||
return this.execute_permission_request("requests_client_permissions", keys, this.execute_client_permission_request.bind(this));
|
||||
|
@ -826,7 +480,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
}, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions));
|
||||
}
|
||||
|
||||
private execute_client_channel_permission_request(request: permissions.PermissionRequestKeys) {
|
||||
private execute_client_channel_permission_request(request: PermissionRequestKeys) {
|
||||
this.handle.serverConnection.send_command("channelclientpermlist", {cldbid: request.client_id, cid: request.channel_id})
|
||||
.catch(error => {
|
||||
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT)
|
||||
|
@ -837,7 +491,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
}
|
||||
|
||||
requestClientChannelPermissions(client_id: number, channel_id: number) : Promise<PermissionValue[]> {
|
||||
const keys: permissions.PermissionRequestKeys = {
|
||||
const keys: PermissionRequestKeys = {
|
||||
client_id: client_id
|
||||
};
|
||||
return this.execute_permission_request("requests_client_channel_permissions", keys, this.execute_client_channel_permission_request.bind(this));
|
||||
|
@ -852,7 +506,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
}, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions));
|
||||
}
|
||||
|
||||
private execute_playlist_permission_request(request: permissions.PermissionRequestKeys) {
|
||||
private execute_playlist_permission_request(request: PermissionRequestKeys) {
|
||||
this.handle.serverConnection.send_command("playlistpermlist", {playlist_id: request.playlist_id})
|
||||
.catch(error => {
|
||||
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT)
|
||||
|
@ -863,7 +517,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
}
|
||||
|
||||
requestPlaylistPermissions(playlist_id: number) : Promise<PermissionValue[]> {
|
||||
const keys: permissions.PermissionRequestKeys = {
|
||||
const keys: PermissionRequestKeys = {
|
||||
playlist_id: playlist_id
|
||||
};
|
||||
return this.execute_permission_request("requests_playlist_permissions", keys, this.execute_playlist_permission_request.bind(this));
|
||||
|
@ -880,7 +534,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
}, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions));
|
||||
}
|
||||
|
||||
private execute_playlist_client_permission_request(request: permissions.PermissionRequestKeys) {
|
||||
private execute_playlist_client_permission_request(request: PermissionRequestKeys) {
|
||||
this.handle.serverConnection.send_command("playlistclientpermlist", {playlist_id: request.playlist_id, cldbid: request.client_id})
|
||||
.catch(error => {
|
||||
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT)
|
||||
|
@ -891,7 +545,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
}
|
||||
|
||||
requestPlaylistClientPermissions(playlist_id: number, client_database_id: number) : Promise<PermissionValue[]> {
|
||||
const keys: permissions.PermissionRequestKeys = {
|
||||
const keys: PermissionRequestKeys = {
|
||||
playlist_id: playlist_id,
|
||||
client_id: client_database_id
|
||||
};
|
||||
|
@ -907,8 +561,8 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
};
|
||||
|
||||
private execute_permission_request(list: RequestLists,
|
||||
criteria: permissions.PermissionRequestKeys,
|
||||
execute: (criteria: permissions.PermissionRequestKeys) => any) : Promise<PermissionValue[]> {
|
||||
criteria: PermissionRequestKeys,
|
||||
execute: (criteria: PermissionRequestKeys) => any) : Promise<PermissionValue[]> {
|
||||
for(const request of this[list])
|
||||
if(this.criteria_equal(request, criteria) && request.promise.time() + 1000 < Date.now())
|
||||
return request.promise;
|
||||
|
@ -922,7 +576,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
return result.promise;
|
||||
};
|
||||
|
||||
private fullfill_permission_request(list: RequestLists, criteria: permissions.PermissionRequestKeys, status: "success" | "error", result: any) {
|
||||
private fullfill_permission_request(list: RequestLists, criteria: PermissionRequestKeys, status: "success" | "error", result: any) {
|
||||
for(const request of this[list]) {
|
||||
if(this.criteria_equal(request, criteria)) {
|
||||
this[list].remove(request);
|
||||
|
@ -932,7 +586,7 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
}
|
||||
}
|
||||
|
||||
find_permission(...permissions: string[]) : Promise<permissions.find.Entry[]> {
|
||||
find_permission(...permissions: string[]) : Promise<find.Entry[]> {
|
||||
const permission_ids = [];
|
||||
for(const permission of permissions) {
|
||||
const info = this.resolveInfo(permission);
|
||||
|
@ -942,11 +596,11 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
}
|
||||
if(!permission_ids.length) return Promise.resolve([]);
|
||||
|
||||
return new Promise<permissions.find.Entry[]>((resolve, reject) => {
|
||||
return new Promise<find.Entry[]>((resolve, reject) => {
|
||||
const single_handler = {
|
||||
command: "notifypermfind",
|
||||
function: command => {
|
||||
const result: permissions.find.Entry[] = [];
|
||||
const result: find.Entry[] = [];
|
||||
for(const entry of command.arguments) {
|
||||
const perm_id = parseInt(entry["p"]);
|
||||
if(permission_ids.indexOf(perm_id) === -1) return; /* not our permfind result */
|
||||
|
@ -960,32 +614,32 @@ class PermissionManager extends connection.AbstractCommandHandler {
|
|||
data = {
|
||||
type: "server_group",
|
||||
group_id: parseInt(entry["id1"]),
|
||||
} as permissions.find.ServerGroup;
|
||||
} as find.ServerGroup;
|
||||
break;
|
||||
case 1:
|
||||
data = {
|
||||
type: "client",
|
||||
client_id: parseInt(entry["id2"]),
|
||||
} as permissions.find.Client;
|
||||
} as find.Client;
|
||||
break;
|
||||
case 2:
|
||||
data = {
|
||||
type: "channel",
|
||||
channel_id: parseInt(entry["id2"]),
|
||||
} as permissions.find.Channel;
|
||||
} as find.Channel;
|
||||
break;
|
||||
case 3:
|
||||
data = {
|
||||
type: "channel_group",
|
||||
group_id: parseInt(entry["id1"]),
|
||||
} as permissions.find.ChannelGroup;
|
||||
} as find.ChannelGroup;
|
||||
break;
|
||||
case 4:
|
||||
data = {
|
||||
type: "client_channel",
|
||||
client_id: parseInt(entry["id1"]),
|
||||
channel_id: parseInt(entry["id1"]),
|
||||
} as permissions.find.ClientChannel;
|
||||
} as find.ClientChannel;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
|
|
350
shared/js/permission/PermissionType.ts
Normal file
350
shared/js/permission/PermissionType.ts
Normal file
|
@ -0,0 +1,350 @@
|
|||
export enum PermissionType {
|
||||
B_SERVERINSTANCE_HELP_VIEW = "b_serverinstance_help_view",
|
||||
B_SERVERINSTANCE_VERSION_VIEW = "b_serverinstance_version_view",
|
||||
B_SERVERINSTANCE_INFO_VIEW = "b_serverinstance_info_view",
|
||||
B_SERVERINSTANCE_VIRTUALSERVER_LIST = "b_serverinstance_virtualserver_list",
|
||||
B_SERVERINSTANCE_BINDING_LIST = "b_serverinstance_binding_list",
|
||||
B_SERVERINSTANCE_PERMISSION_LIST = "b_serverinstance_permission_list",
|
||||
B_SERVERINSTANCE_PERMISSION_FIND = "b_serverinstance_permission_find",
|
||||
B_VIRTUALSERVER_CREATE = "b_virtualserver_create",
|
||||
B_VIRTUALSERVER_DELETE = "b_virtualserver_delete",
|
||||
B_VIRTUALSERVER_START_ANY = "b_virtualserver_start_any",
|
||||
B_VIRTUALSERVER_STOP_ANY = "b_virtualserver_stop_any",
|
||||
B_VIRTUALSERVER_CHANGE_MACHINE_ID = "b_virtualserver_change_machine_id",
|
||||
B_VIRTUALSERVER_CHANGE_TEMPLATE = "b_virtualserver_change_template",
|
||||
B_SERVERQUERY_LOGIN = "b_serverquery_login",
|
||||
B_SERVERINSTANCE_TEXTMESSAGE_SEND = "b_serverinstance_textmessage_send",
|
||||
B_SERVERINSTANCE_LOG_VIEW = "b_serverinstance_log_view",
|
||||
B_SERVERINSTANCE_LOG_ADD = "b_serverinstance_log_add",
|
||||
B_SERVERINSTANCE_STOP = "b_serverinstance_stop",
|
||||
B_SERVERINSTANCE_MODIFY_SETTINGS = "b_serverinstance_modify_settings",
|
||||
B_SERVERINSTANCE_MODIFY_QUERYGROUP = "b_serverinstance_modify_querygroup",
|
||||
B_SERVERINSTANCE_MODIFY_TEMPLATES = "b_serverinstance_modify_templates",
|
||||
B_VIRTUALSERVER_SELECT = "b_virtualserver_select",
|
||||
B_VIRTUALSERVER_SELECT_GODMODE = "b_virtualserver_select_godmode",
|
||||
B_VIRTUALSERVER_INFO_VIEW = "b_virtualserver_info_view",
|
||||
B_VIRTUALSERVER_CONNECTIONINFO_VIEW = "b_virtualserver_connectioninfo_view",
|
||||
B_VIRTUALSERVER_CHANNEL_LIST = "b_virtualserver_channel_list",
|
||||
B_VIRTUALSERVER_CHANNEL_SEARCH = "b_virtualserver_channel_search",
|
||||
B_VIRTUALSERVER_CLIENT_LIST = "b_virtualserver_client_list",
|
||||
B_VIRTUALSERVER_CLIENT_SEARCH = "b_virtualserver_client_search",
|
||||
B_VIRTUALSERVER_CLIENT_DBLIST = "b_virtualserver_client_dblist",
|
||||
B_VIRTUALSERVER_CLIENT_DBSEARCH = "b_virtualserver_client_dbsearch",
|
||||
B_VIRTUALSERVER_CLIENT_DBINFO = "b_virtualserver_client_dbinfo",
|
||||
B_VIRTUALSERVER_PERMISSION_FIND = "b_virtualserver_permission_find",
|
||||
B_VIRTUALSERVER_CUSTOM_SEARCH = "b_virtualserver_custom_search",
|
||||
B_VIRTUALSERVER_START = "b_virtualserver_start",
|
||||
B_VIRTUALSERVER_STOP = "b_virtualserver_stop",
|
||||
B_VIRTUALSERVER_TOKEN_LIST = "b_virtualserver_token_list",
|
||||
B_VIRTUALSERVER_TOKEN_ADD = "b_virtualserver_token_add",
|
||||
B_VIRTUALSERVER_TOKEN_USE = "b_virtualserver_token_use",
|
||||
B_VIRTUALSERVER_TOKEN_DELETE = "b_virtualserver_token_delete",
|
||||
B_VIRTUALSERVER_LOG_VIEW = "b_virtualserver_log_view",
|
||||
B_VIRTUALSERVER_LOG_ADD = "b_virtualserver_log_add",
|
||||
B_VIRTUALSERVER_JOIN_IGNORE_PASSWORD = "b_virtualserver_join_ignore_password",
|
||||
B_VIRTUALSERVER_NOTIFY_REGISTER = "b_virtualserver_notify_register",
|
||||
B_VIRTUALSERVER_NOTIFY_UNREGISTER = "b_virtualserver_notify_unregister",
|
||||
B_VIRTUALSERVER_SNAPSHOT_CREATE = "b_virtualserver_snapshot_create",
|
||||
B_VIRTUALSERVER_SNAPSHOT_DEPLOY = "b_virtualserver_snapshot_deploy",
|
||||
B_VIRTUALSERVER_PERMISSION_RESET = "b_virtualserver_permission_reset",
|
||||
B_VIRTUALSERVER_MODIFY_NAME = "b_virtualserver_modify_name",
|
||||
B_VIRTUALSERVER_MODIFY_WELCOMEMESSAGE = "b_virtualserver_modify_welcomemessage",
|
||||
B_VIRTUALSERVER_MODIFY_MAXCLIENTS = "b_virtualserver_modify_maxclients",
|
||||
B_VIRTUALSERVER_MODIFY_RESERVED_SLOTS = "b_virtualserver_modify_reserved_slots",
|
||||
B_VIRTUALSERVER_MODIFY_PASSWORD = "b_virtualserver_modify_password",
|
||||
B_VIRTUALSERVER_MODIFY_DEFAULT_SERVERGROUP = "b_virtualserver_modify_default_servergroup",
|
||||
B_VIRTUALSERVER_MODIFY_DEFAULT_MUSICGROUP = "b_virtualserver_modify_default_musicgroup",
|
||||
B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELGROUP = "b_virtualserver_modify_default_channelgroup",
|
||||
B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELADMINGROUP = "b_virtualserver_modify_default_channeladmingroup",
|
||||
B_VIRTUALSERVER_MODIFY_CHANNEL_FORCED_SILENCE = "b_virtualserver_modify_channel_forced_silence",
|
||||
B_VIRTUALSERVER_MODIFY_COMPLAIN = "b_virtualserver_modify_complain",
|
||||
B_VIRTUALSERVER_MODIFY_ANTIFLOOD = "b_virtualserver_modify_antiflood",
|
||||
B_VIRTUALSERVER_MODIFY_FT_SETTINGS = "b_virtualserver_modify_ft_settings",
|
||||
B_VIRTUALSERVER_MODIFY_FT_QUOTAS = "b_virtualserver_modify_ft_quotas",
|
||||
B_VIRTUALSERVER_MODIFY_HOSTMESSAGE = "b_virtualserver_modify_hostmessage",
|
||||
B_VIRTUALSERVER_MODIFY_HOSTBANNER = "b_virtualserver_modify_hostbanner",
|
||||
B_VIRTUALSERVER_MODIFY_HOSTBUTTON = "b_virtualserver_modify_hostbutton",
|
||||
B_VIRTUALSERVER_MODIFY_PORT = "b_virtualserver_modify_port",
|
||||
B_VIRTUALSERVER_MODIFY_HOST = "b_virtualserver_modify_host",
|
||||
B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES = "b_virtualserver_modify_default_messages",
|
||||
B_VIRTUALSERVER_MODIFY_AUTOSTART = "b_virtualserver_modify_autostart",
|
||||
B_VIRTUALSERVER_MODIFY_NEEDED_IDENTITY_SECURITY_LEVEL = "b_virtualserver_modify_needed_identity_security_level",
|
||||
B_VIRTUALSERVER_MODIFY_PRIORITY_SPEAKER_DIMM_MODIFICATOR = "b_virtualserver_modify_priority_speaker_dimm_modificator",
|
||||
B_VIRTUALSERVER_MODIFY_LOG_SETTINGS = "b_virtualserver_modify_log_settings",
|
||||
B_VIRTUALSERVER_MODIFY_MIN_CLIENT_VERSION = "b_virtualserver_modify_min_client_version",
|
||||
B_VIRTUALSERVER_MODIFY_ICON_ID = "b_virtualserver_modify_icon_id",
|
||||
B_VIRTUALSERVER_MODIFY_WEBLIST = "b_virtualserver_modify_weblist",
|
||||
B_VIRTUALSERVER_MODIFY_CODEC_ENCRYPTION_MODE = "b_virtualserver_modify_codec_encryption_mode",
|
||||
B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS = "b_virtualserver_modify_temporary_passwords",
|
||||
B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS_OWN = "b_virtualserver_modify_temporary_passwords_own",
|
||||
B_VIRTUALSERVER_MODIFY_CHANNEL_TEMP_DELETE_DELAY_DEFAULT = "b_virtualserver_modify_channel_temp_delete_delay_default",
|
||||
B_VIRTUALSERVER_MODIFY_MUSIC_BOT_LIMIT = "b_virtualserver_modify_music_bot_limit",
|
||||
B_VIRTUALSERVER_MODIFY_COUNTRY_CODE = "b_virtualserver_modify_country_code",
|
||||
I_CHANNEL_MIN_DEPTH = "i_channel_min_depth",
|
||||
I_CHANNEL_MAX_DEPTH = "i_channel_max_depth",
|
||||
B_CHANNEL_GROUP_INHERITANCE_END = "b_channel_group_inheritance_end",
|
||||
I_CHANNEL_PERMISSION_MODIFY_POWER = "i_channel_permission_modify_power",
|
||||
I_CHANNEL_NEEDED_PERMISSION_MODIFY_POWER = "i_channel_needed_permission_modify_power",
|
||||
B_CHANNEL_INFO_VIEW = "b_channel_info_view",
|
||||
B_CHANNEL_CREATE_CHILD = "b_channel_create_child",
|
||||
B_CHANNEL_CREATE_PERMANENT = "b_channel_create_permanent",
|
||||
B_CHANNEL_CREATE_SEMI_PERMANENT = "b_channel_create_semi_permanent",
|
||||
B_CHANNEL_CREATE_TEMPORARY = "b_channel_create_temporary",
|
||||
B_CHANNEL_CREATE_PRIVATE = "b_channel_create_private",
|
||||
B_CHANNEL_CREATE_WITH_TOPIC = "b_channel_create_with_topic",
|
||||
B_CHANNEL_CREATE_WITH_DESCRIPTION = "b_channel_create_with_description",
|
||||
B_CHANNEL_CREATE_WITH_PASSWORD = "b_channel_create_with_password",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX8 = "b_channel_create_modify_with_codec_speex8",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX16 = "b_channel_create_modify_with_codec_speex16",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX32 = "b_channel_create_modify_with_codec_speex32",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_CELTMONO48 = "b_channel_create_modify_with_codec_celtmono48",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE = "b_channel_create_modify_with_codec_opusvoice",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC = "b_channel_create_modify_with_codec_opusmusic",
|
||||
I_CHANNEL_CREATE_MODIFY_WITH_CODEC_MAXQUALITY = "i_channel_create_modify_with_codec_maxquality",
|
||||
I_CHANNEL_CREATE_MODIFY_WITH_CODEC_LATENCY_FACTOR_MIN = "i_channel_create_modify_with_codec_latency_factor_min",
|
||||
I_CHANNEL_CREATE_MODIFY_CONVERSATION_HISTORY_LENGTH = "i_channel_create_modify_conversation_history_length",
|
||||
B_CHANNEL_CREATE_MODIFY_CONVERSATION_HISTORY_UNLIMITED = "b_channel_create_modify_conversation_history_unlimited",
|
||||
B_CHANNEL_CREATE_MODIFY_CONVERSATION_PRIVATE = "b_channel_create_modify_conversation_private",
|
||||
B_CHANNEL_CREATE_WITH_MAXCLIENTS = "b_channel_create_with_maxclients",
|
||||
B_CHANNEL_CREATE_WITH_MAXFAMILYCLIENTS = "b_channel_create_with_maxfamilyclients",
|
||||
B_CHANNEL_CREATE_WITH_SORTORDER = "b_channel_create_with_sortorder",
|
||||
B_CHANNEL_CREATE_WITH_DEFAULT = "b_channel_create_with_default",
|
||||
B_CHANNEL_CREATE_WITH_NEEDED_TALK_POWER = "b_channel_create_with_needed_talk_power",
|
||||
B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD = "b_channel_create_modify_with_force_password",
|
||||
I_CHANNEL_CREATE_MODIFY_WITH_TEMP_DELETE_DELAY = "i_channel_create_modify_with_temp_delete_delay",
|
||||
B_CHANNEL_MODIFY_PARENT = "b_channel_modify_parent",
|
||||
B_CHANNEL_MODIFY_MAKE_DEFAULT = "b_channel_modify_make_default",
|
||||
B_CHANNEL_MODIFY_MAKE_PERMANENT = "b_channel_modify_make_permanent",
|
||||
B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT = "b_channel_modify_make_semi_permanent",
|
||||
B_CHANNEL_MODIFY_MAKE_TEMPORARY = "b_channel_modify_make_temporary",
|
||||
B_CHANNEL_MODIFY_NAME = "b_channel_modify_name",
|
||||
B_CHANNEL_MODIFY_TOPIC = "b_channel_modify_topic",
|
||||
B_CHANNEL_MODIFY_DESCRIPTION = "b_channel_modify_description",
|
||||
B_CHANNEL_MODIFY_PASSWORD = "b_channel_modify_password",
|
||||
B_CHANNEL_MODIFY_CODEC = "b_channel_modify_codec",
|
||||
B_CHANNEL_MODIFY_CODEC_QUALITY = "b_channel_modify_codec_quality",
|
||||
B_CHANNEL_MODIFY_CODEC_LATENCY_FACTOR = "b_channel_modify_codec_latency_factor",
|
||||
B_CHANNEL_MODIFY_MAXCLIENTS = "b_channel_modify_maxclients",
|
||||
B_CHANNEL_MODIFY_MAXFAMILYCLIENTS = "b_channel_modify_maxfamilyclients",
|
||||
B_CHANNEL_MODIFY_SORTORDER = "b_channel_modify_sortorder",
|
||||
B_CHANNEL_MODIFY_NEEDED_TALK_POWER = "b_channel_modify_needed_talk_power",
|
||||
I_CHANNEL_MODIFY_POWER = "i_channel_modify_power",
|
||||
I_CHANNEL_NEEDED_MODIFY_POWER = "i_channel_needed_modify_power",
|
||||
B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED = "b_channel_modify_make_codec_encrypted",
|
||||
B_CHANNEL_MODIFY_TEMP_DELETE_DELAY = "b_channel_modify_temp_delete_delay",
|
||||
B_CHANNEL_DELETE_PERMANENT = "b_channel_delete_permanent",
|
||||
B_CHANNEL_DELETE_SEMI_PERMANENT = "b_channel_delete_semi_permanent",
|
||||
B_CHANNEL_DELETE_TEMPORARY = "b_channel_delete_temporary",
|
||||
B_CHANNEL_DELETE_FLAG_FORCE = "b_channel_delete_flag_force",
|
||||
I_CHANNEL_DELETE_POWER = "i_channel_delete_power",
|
||||
B_CHANNEL_CONVERSATION_MESSAGE_DELETE = "b_channel_conversation_message_delete",
|
||||
I_CHANNEL_NEEDED_DELETE_POWER = "i_channel_needed_delete_power",
|
||||
B_CHANNEL_JOIN_PERMANENT = "b_channel_join_permanent",
|
||||
B_CHANNEL_JOIN_SEMI_PERMANENT = "b_channel_join_semi_permanent",
|
||||
B_CHANNEL_JOIN_TEMPORARY = "b_channel_join_temporary",
|
||||
B_CHANNEL_JOIN_IGNORE_PASSWORD = "b_channel_join_ignore_password",
|
||||
B_CHANNEL_JOIN_IGNORE_MAXCLIENTS = "b_channel_join_ignore_maxclients",
|
||||
B_CHANNEL_IGNORE_VIEW_POWER = "b_channel_ignore_view_power",
|
||||
I_CHANNEL_JOIN_POWER = "i_channel_join_power",
|
||||
I_CHANNEL_NEEDED_JOIN_POWER = "i_channel_needed_join_power",
|
||||
B_CHANNEL_IGNORE_JOIN_POWER = "b_channel_ignore_join_power",
|
||||
B_CHANNEL_IGNORE_DESCRIPTION_VIEW_POWER = "b_channel_ignore_description_view_power",
|
||||
I_CHANNEL_VIEW_POWER = "i_channel_view_power",
|
||||
I_CHANNEL_NEEDED_VIEW_POWER = "i_channel_needed_view_power",
|
||||
I_CHANNEL_SUBSCRIBE_POWER = "i_channel_subscribe_power",
|
||||
I_CHANNEL_NEEDED_SUBSCRIBE_POWER = "i_channel_needed_subscribe_power",
|
||||
I_CHANNEL_DESCRIPTION_VIEW_POWER = "i_channel_description_view_power",
|
||||
I_CHANNEL_NEEDED_DESCRIPTION_VIEW_POWER = "i_channel_needed_description_view_power",
|
||||
I_ICON_ID = "i_icon_id",
|
||||
I_MAX_ICON_FILESIZE = "i_max_icon_filesize",
|
||||
I_MAX_PLAYLIST_SIZE = "i_max_playlist_size",
|
||||
I_MAX_PLAYLISTS = "i_max_playlists",
|
||||
B_ICON_MANAGE = "b_icon_manage",
|
||||
B_GROUP_IS_PERMANENT = "b_group_is_permanent",
|
||||
I_GROUP_AUTO_UPDATE_TYPE = "i_group_auto_update_type",
|
||||
I_GROUP_AUTO_UPDATE_MAX_VALUE = "i_group_auto_update_max_value",
|
||||
I_GROUP_SORT_ID = "i_group_sort_id",
|
||||
I_GROUP_SHOW_NAME_IN_TREE = "i_group_show_name_in_tree",
|
||||
B_VIRTUALSERVER_SERVERGROUP_CREATE = "b_virtualserver_servergroup_create",
|
||||
B_VIRTUALSERVER_SERVERGROUP_LIST = "b_virtualserver_servergroup_list",
|
||||
B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST = "b_virtualserver_servergroup_permission_list",
|
||||
B_VIRTUALSERVER_SERVERGROUP_CLIENT_LIST = "b_virtualserver_servergroup_client_list",
|
||||
B_VIRTUALSERVER_CHANNELGROUP_CREATE = "b_virtualserver_channelgroup_create",
|
||||
B_VIRTUALSERVER_CHANNELGROUP_LIST = "b_virtualserver_channelgroup_list",
|
||||
B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST = "b_virtualserver_channelgroup_permission_list",
|
||||
B_VIRTUALSERVER_CHANNELGROUP_CLIENT_LIST = "b_virtualserver_channelgroup_client_list",
|
||||
B_VIRTUALSERVER_CLIENT_PERMISSION_LIST = "b_virtualserver_client_permission_list",
|
||||
B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST = "b_virtualserver_channel_permission_list",
|
||||
B_VIRTUALSERVER_CHANNELCLIENT_PERMISSION_LIST = "b_virtualserver_channelclient_permission_list",
|
||||
B_VIRTUALSERVER_PLAYLIST_PERMISSION_LIST = "b_virtualserver_playlist_permission_list",
|
||||
I_SERVER_GROUP_MODIFY_POWER = "i_server_group_modify_power",
|
||||
I_SERVER_GROUP_NEEDED_MODIFY_POWER = "i_server_group_needed_modify_power",
|
||||
I_SERVER_GROUP_MEMBER_ADD_POWER = "i_server_group_member_add_power",
|
||||
I_SERVER_GROUP_SELF_ADD_POWER = "i_server_group_self_add_power",
|
||||
I_SERVER_GROUP_NEEDED_MEMBER_ADD_POWER = "i_server_group_needed_member_add_power",
|
||||
I_SERVER_GROUP_MEMBER_REMOVE_POWER = "i_server_group_member_remove_power",
|
||||
I_SERVER_GROUP_SELF_REMOVE_POWER = "i_server_group_self_remove_power",
|
||||
I_SERVER_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_server_group_needed_member_remove_power",
|
||||
I_CHANNEL_GROUP_MODIFY_POWER = "i_channel_group_modify_power",
|
||||
I_CHANNEL_GROUP_NEEDED_MODIFY_POWER = "i_channel_group_needed_modify_power",
|
||||
I_CHANNEL_GROUP_MEMBER_ADD_POWER = "i_channel_group_member_add_power",
|
||||
I_CHANNEL_GROUP_SELF_ADD_POWER = "i_channel_group_self_add_power",
|
||||
I_CHANNEL_GROUP_NEEDED_MEMBER_ADD_POWER = "i_channel_group_needed_member_add_power",
|
||||
I_CHANNEL_GROUP_MEMBER_REMOVE_POWER = "i_channel_group_member_remove_power",
|
||||
I_CHANNEL_GROUP_SELF_REMOVE_POWER = "i_channel_group_self_remove_power",
|
||||
I_CHANNEL_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_channel_group_needed_member_remove_power",
|
||||
I_GROUP_MEMBER_ADD_POWER = "i_group_member_add_power",
|
||||
I_GROUP_NEEDED_MEMBER_ADD_POWER = "i_group_needed_member_add_power",
|
||||
I_GROUP_MEMBER_REMOVE_POWER = "i_group_member_remove_power",
|
||||
I_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_group_needed_member_remove_power",
|
||||
I_GROUP_MODIFY_POWER = "i_group_modify_power",
|
||||
I_GROUP_NEEDED_MODIFY_POWER = "i_group_needed_modify_power",
|
||||
I_PERMISSION_MODIFY_POWER = "i_permission_modify_power",
|
||||
B_PERMISSION_MODIFY_POWER_IGNORE = "b_permission_modify_power_ignore",
|
||||
B_VIRTUALSERVER_SERVERGROUP_DELETE = "b_virtualserver_servergroup_delete",
|
||||
B_VIRTUALSERVER_CHANNELGROUP_DELETE = "b_virtualserver_channelgroup_delete",
|
||||
I_CLIENT_PERMISSION_MODIFY_POWER = "i_client_permission_modify_power",
|
||||
I_CLIENT_NEEDED_PERMISSION_MODIFY_POWER = "i_client_needed_permission_modify_power",
|
||||
I_CLIENT_MAX_CLONES_UID = "i_client_max_clones_uid",
|
||||
I_CLIENT_MAX_CLONES_IP = "i_client_max_clones_ip",
|
||||
I_CLIENT_MAX_CLONES_HWID = "i_client_max_clones_hwid",
|
||||
I_CLIENT_MAX_IDLETIME = "i_client_max_idletime",
|
||||
I_CLIENT_MAX_AVATAR_FILESIZE = "i_client_max_avatar_filesize",
|
||||
I_CLIENT_MAX_CHANNEL_SUBSCRIPTIONS = "i_client_max_channel_subscriptions",
|
||||
I_CLIENT_MAX_CHANNELS = "i_client_max_channels",
|
||||
I_CLIENT_MAX_TEMPORARY_CHANNELS = "i_client_max_temporary_channels",
|
||||
I_CLIENT_MAX_SEMI_CHANNELS = "i_client_max_semi_channels",
|
||||
I_CLIENT_MAX_PERMANENT_CHANNELS = "i_client_max_permanent_channels",
|
||||
B_CLIENT_USE_PRIORITY_SPEAKER = "b_client_use_priority_speaker",
|
||||
B_CLIENT_SKIP_CHANNELGROUP_PERMISSIONS = "b_client_skip_channelgroup_permissions",
|
||||
B_CLIENT_FORCE_PUSH_TO_TALK = "b_client_force_push_to_talk",
|
||||
B_CLIENT_IGNORE_BANS = "b_client_ignore_bans",
|
||||
B_CLIENT_IGNORE_VPN = "b_client_ignore_vpn",
|
||||
B_CLIENT_IGNORE_ANTIFLOOD = "b_client_ignore_antiflood",
|
||||
B_CLIENT_ENFORCE_VALID_HWID = "b_client_enforce_valid_hwid",
|
||||
B_CLIENT_ALLOW_INVALID_PACKET = "b_client_allow_invalid_packet",
|
||||
B_CLIENT_ALLOW_INVALID_BADGES = "b_client_allow_invalid_badges",
|
||||
B_CLIENT_ISSUE_CLIENT_QUERY_COMMAND = "b_client_issue_client_query_command",
|
||||
B_CLIENT_USE_RESERVED_SLOT = "b_client_use_reserved_slot",
|
||||
B_CLIENT_USE_CHANNEL_COMMANDER = "b_client_use_channel_commander",
|
||||
B_CLIENT_REQUEST_TALKER = "b_client_request_talker",
|
||||
B_CLIENT_AVATAR_DELETE_OTHER = "b_client_avatar_delete_other",
|
||||
B_CLIENT_IS_STICKY = "b_client_is_sticky",
|
||||
B_CLIENT_IGNORE_STICKY = "b_client_ignore_sticky",
|
||||
B_CLIENT_MUSIC_CREATE_PERMANENT = "b_client_music_create_permanent",
|
||||
B_CLIENT_MUSIC_CREATE_SEMI_PERMANENT = "b_client_music_create_semi_permanent",
|
||||
B_CLIENT_MUSIC_CREATE_TEMPORARY = "b_client_music_create_temporary",
|
||||
B_CLIENT_MUSIC_MODIFY_PERMANENT = "b_client_music_modify_permanent",
|
||||
B_CLIENT_MUSIC_MODIFY_SEMI_PERMANENT = "b_client_music_modify_semi_permanent",
|
||||
B_CLIENT_MUSIC_MODIFY_TEMPORARY = "b_client_music_modify_temporary",
|
||||
I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME = "i_client_music_create_modify_max_volume",
|
||||
I_CLIENT_MUSIC_LIMIT = "i_client_music_limit",
|
||||
I_CLIENT_MUSIC_NEEDED_DELETE_POWER = "i_client_music_needed_delete_power",
|
||||
I_CLIENT_MUSIC_DELETE_POWER = "i_client_music_delete_power",
|
||||
I_CLIENT_MUSIC_PLAY_POWER = "i_client_music_play_power",
|
||||
I_CLIENT_MUSIC_NEEDED_PLAY_POWER = "i_client_music_needed_play_power",
|
||||
I_CLIENT_MUSIC_MODIFY_POWER = "i_client_music_modify_power",
|
||||
I_CLIENT_MUSIC_NEEDED_MODIFY_POWER = "i_client_music_needed_modify_power",
|
||||
I_CLIENT_MUSIC_RENAME_POWER = "i_client_music_rename_power",
|
||||
I_CLIENT_MUSIC_NEEDED_RENAME_POWER = "i_client_music_needed_rename_power",
|
||||
B_PLAYLIST_CREATE = "b_playlist_create",
|
||||
I_PLAYLIST_VIEW_POWER = "i_playlist_view_power",
|
||||
I_PLAYLIST_NEEDED_VIEW_POWER = "i_playlist_needed_view_power",
|
||||
I_PLAYLIST_MODIFY_POWER = "i_playlist_modify_power",
|
||||
I_PLAYLIST_NEEDED_MODIFY_POWER = "i_playlist_needed_modify_power",
|
||||
I_PLAYLIST_PERMISSION_MODIFY_POWER = "i_playlist_permission_modify_power",
|
||||
I_PLAYLIST_NEEDED_PERMISSION_MODIFY_POWER = "i_playlist_needed_permission_modify_power",
|
||||
I_PLAYLIST_DELETE_POWER = "i_playlist_delete_power",
|
||||
I_PLAYLIST_NEEDED_DELETE_POWER = "i_playlist_needed_delete_power",
|
||||
I_PLAYLIST_SONG_ADD_POWER = "i_playlist_song_add_power",
|
||||
I_PLAYLIST_SONG_NEEDED_ADD_POWER = "i_playlist_song_needed_add_power",
|
||||
I_PLAYLIST_SONG_REMOVE_POWER = "i_playlist_song_remove_power",
|
||||
I_PLAYLIST_SONG_NEEDED_REMOVE_POWER = "i_playlist_song_needed_remove_power",
|
||||
B_CLIENT_INFO_VIEW = "b_client_info_view",
|
||||
B_CLIENT_PERMISSIONOVERVIEW_VIEW = "b_client_permissionoverview_view",
|
||||
B_CLIENT_PERMISSIONOVERVIEW_OWN = "b_client_permissionoverview_own",
|
||||
B_CLIENT_REMOTEADDRESS_VIEW = "b_client_remoteaddress_view",
|
||||
I_CLIENT_SERVERQUERY_VIEW_POWER = "i_client_serverquery_view_power",
|
||||
I_CLIENT_NEEDED_SERVERQUERY_VIEW_POWER = "i_client_needed_serverquery_view_power",
|
||||
B_CLIENT_CUSTOM_INFO_VIEW = "b_client_custom_info_view",
|
||||
B_CLIENT_MUSIC_CHANNEL_LIST = "b_client_music_channel_list",
|
||||
B_CLIENT_MUSIC_SERVER_LIST = "b_client_music_server_list",
|
||||
I_CLIENT_MUSIC_INFO = "i_client_music_info",
|
||||
I_CLIENT_MUSIC_NEEDED_INFO = "i_client_music_needed_info",
|
||||
I_CLIENT_KICK_FROM_SERVER_POWER = "i_client_kick_from_server_power",
|
||||
I_CLIENT_NEEDED_KICK_FROM_SERVER_POWER = "i_client_needed_kick_from_server_power",
|
||||
I_CLIENT_KICK_FROM_CHANNEL_POWER = "i_client_kick_from_channel_power",
|
||||
I_CLIENT_NEEDED_KICK_FROM_CHANNEL_POWER = "i_client_needed_kick_from_channel_power",
|
||||
I_CLIENT_BAN_POWER = "i_client_ban_power",
|
||||
I_CLIENT_NEEDED_BAN_POWER = "i_client_needed_ban_power",
|
||||
I_CLIENT_MOVE_POWER = "i_client_move_power",
|
||||
I_CLIENT_NEEDED_MOVE_POWER = "i_client_needed_move_power",
|
||||
I_CLIENT_COMPLAIN_POWER = "i_client_complain_power",
|
||||
I_CLIENT_NEEDED_COMPLAIN_POWER = "i_client_needed_complain_power",
|
||||
B_CLIENT_COMPLAIN_LIST = "b_client_complain_list",
|
||||
B_CLIENT_COMPLAIN_DELETE_OWN = "b_client_complain_delete_own",
|
||||
B_CLIENT_COMPLAIN_DELETE = "b_client_complain_delete",
|
||||
B_CLIENT_BAN_LIST = "b_client_ban_list",
|
||||
B_CLIENT_BAN_LIST_GLOBAL = "b_client_ban_list_global",
|
||||
B_CLIENT_BAN_TRIGGER_LIST = "b_client_ban_trigger_list",
|
||||
B_CLIENT_BAN_CREATE = "b_client_ban_create",
|
||||
B_CLIENT_BAN_CREATE_GLOBAL = "b_client_ban_create_global",
|
||||
B_CLIENT_BAN_NAME = "b_client_ban_name",
|
||||
B_CLIENT_BAN_IP = "b_client_ban_ip",
|
||||
B_CLIENT_BAN_HWID = "b_client_ban_hwid",
|
||||
B_CLIENT_BAN_EDIT = "b_client_ban_edit",
|
||||
B_CLIENT_BAN_EDIT_GLOBAL = "b_client_ban_edit_global",
|
||||
B_CLIENT_BAN_DELETE_OWN = "b_client_ban_delete_own",
|
||||
B_CLIENT_BAN_DELETE = "b_client_ban_delete",
|
||||
B_CLIENT_BAN_DELETE_OWN_GLOBAL = "b_client_ban_delete_own_global",
|
||||
B_CLIENT_BAN_DELETE_GLOBAL = "b_client_ban_delete_global",
|
||||
I_CLIENT_BAN_MAX_BANTIME = "i_client_ban_max_bantime",
|
||||
I_CLIENT_PRIVATE_TEXTMESSAGE_POWER = "i_client_private_textmessage_power",
|
||||
I_CLIENT_NEEDED_PRIVATE_TEXTMESSAGE_POWER = "i_client_needed_private_textmessage_power",
|
||||
B_CLIENT_EVEN_TEXTMESSAGE_SEND = "b_client_even_textmessage_send",
|
||||
B_CLIENT_SERVER_TEXTMESSAGE_SEND = "b_client_server_textmessage_send",
|
||||
B_CLIENT_CHANNEL_TEXTMESSAGE_SEND = "b_client_channel_textmessage_send",
|
||||
B_CLIENT_OFFLINE_TEXTMESSAGE_SEND = "b_client_offline_textmessage_send",
|
||||
I_CLIENT_TALK_POWER = "i_client_talk_power",
|
||||
I_CLIENT_NEEDED_TALK_POWER = "i_client_needed_talk_power",
|
||||
I_CLIENT_POKE_POWER = "i_client_poke_power",
|
||||
I_CLIENT_NEEDED_POKE_POWER = "i_client_needed_poke_power",
|
||||
B_CLIENT_SET_FLAG_TALKER = "b_client_set_flag_talker",
|
||||
I_CLIENT_WHISPER_POWER = "i_client_whisper_power",
|
||||
I_CLIENT_NEEDED_WHISPER_POWER = "i_client_needed_whisper_power",
|
||||
B_CLIENT_MODIFY_DESCRIPTION = "b_client_modify_description",
|
||||
B_CLIENT_MODIFY_OWN_DESCRIPTION = "b_client_modify_own_description",
|
||||
B_CLIENT_USE_BBCODE_ANY = "b_client_use_bbcode_any",
|
||||
B_CLIENT_USE_BBCODE_URL = "b_client_use_bbcode_url",
|
||||
B_CLIENT_USE_BBCODE_IMAGE = "b_client_use_bbcode_image",
|
||||
B_CLIENT_MODIFY_DBPROPERTIES = "b_client_modify_dbproperties",
|
||||
B_CLIENT_DELETE_DBPROPERTIES = "b_client_delete_dbproperties",
|
||||
B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN = "b_client_create_modify_serverquery_login",
|
||||
B_CLIENT_QUERY_CREATE = "b_client_query_create",
|
||||
B_CLIENT_QUERY_LIST = "b_client_query_list",
|
||||
B_CLIENT_QUERY_LIST_OWN = "b_client_query_list_own",
|
||||
B_CLIENT_QUERY_RENAME = "b_client_query_rename",
|
||||
B_CLIENT_QUERY_RENAME_OWN = "b_client_query_rename_own",
|
||||
B_CLIENT_QUERY_CHANGE_PASSWORD = "b_client_query_change_password",
|
||||
B_CLIENT_QUERY_CHANGE_OWN_PASSWORD = "b_client_query_change_own_password",
|
||||
B_CLIENT_QUERY_CHANGE_PASSWORD_GLOBAL = "b_client_query_change_password_global",
|
||||
B_CLIENT_QUERY_DELETE = "b_client_query_delete",
|
||||
B_CLIENT_QUERY_DELETE_OWN = "b_client_query_delete_own",
|
||||
B_FT_IGNORE_PASSWORD = "b_ft_ignore_password",
|
||||
B_FT_TRANSFER_LIST = "b_ft_transfer_list",
|
||||
I_FT_FILE_UPLOAD_POWER = "i_ft_file_upload_power",
|
||||
I_FT_NEEDED_FILE_UPLOAD_POWER = "i_ft_needed_file_upload_power",
|
||||
I_FT_FILE_DOWNLOAD_POWER = "i_ft_file_download_power",
|
||||
I_FT_NEEDED_FILE_DOWNLOAD_POWER = "i_ft_needed_file_download_power",
|
||||
I_FT_FILE_DELETE_POWER = "i_ft_file_delete_power",
|
||||
I_FT_NEEDED_FILE_DELETE_POWER = "i_ft_needed_file_delete_power",
|
||||
I_FT_FILE_RENAME_POWER = "i_ft_file_rename_power",
|
||||
I_FT_NEEDED_FILE_RENAME_POWER = "i_ft_needed_file_rename_power",
|
||||
I_FT_FILE_BROWSE_POWER = "i_ft_file_browse_power",
|
||||
I_FT_NEEDED_FILE_BROWSE_POWER = "i_ft_needed_file_browse_power",
|
||||
I_FT_DIRECTORY_CREATE_POWER = "i_ft_directory_create_power",
|
||||
I_FT_NEEDED_DIRECTORY_CREATE_POWER = "i_ft_needed_directory_create_power",
|
||||
I_FT_QUOTA_MB_DOWNLOAD_PER_CLIENT = "i_ft_quota_mb_download_per_client",
|
||||
I_FT_QUOTA_MB_UPLOAD_PER_CLIENT = "i_ft_quota_mb_upload_per_client"
|
||||
}
|
||||
export default PermissionType;
|
|
@ -1,4 +1,12 @@
|
|||
namespace profiles {
|
||||
import {decode_identity, IdentitifyType, Identity} from "tc-shared/profiles/Identity";
|
||||
import {guid} from "tc-shared/crypto/uid";
|
||||
import {TeaForumIdentity} from "tc-shared/profiles/identities/TeaForumIdentity";
|
||||
import {TeaSpeakIdentity} from "tc-shared/profiles/identities/TeamSpeakIdentity";
|
||||
import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase";
|
||||
import {HandshakeIdentityHandler} from "tc-shared/connection/HandshakeHandler";
|
||||
import {createErrorModal} from "tc-shared/ui/elements/Modal";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
|
||||
export class ConnectionProfile {
|
||||
id: string;
|
||||
|
||||
|
@ -7,7 +15,7 @@ namespace profiles {
|
|||
default_password: string;
|
||||
|
||||
selected_identity_type: string = "unset";
|
||||
identities: { [key: string]: identities.Identity } = {};
|
||||
identities: { [key: string]: Identity } = {};
|
||||
|
||||
constructor(id: string) {
|
||||
this.id = id;
|
||||
|
@ -22,31 +30,31 @@ namespace profiles {
|
|||
return name || "Another TeaSpeak user";
|
||||
}
|
||||
|
||||
selected_identity(current_type?: identities.IdentitifyType): identities.Identity {
|
||||
selected_identity(current_type?: IdentitifyType): Identity {
|
||||
if (!current_type)
|
||||
current_type = this.selected_type();
|
||||
|
||||
if (current_type === undefined)
|
||||
return undefined;
|
||||
|
||||
if (current_type == identities.IdentitifyType.TEAFORO) {
|
||||
return identities.static_forum_identity();
|
||||
} else if (current_type == identities.IdentitifyType.TEAMSPEAK || current_type == identities.IdentitifyType.NICKNAME) {
|
||||
return this.identities[identities.IdentitifyType[current_type].toLowerCase()];
|
||||
if (current_type == IdentitifyType.TEAFORO) {
|
||||
return TeaForumIdentity.identity();
|
||||
} else if (current_type == IdentitifyType.TEAMSPEAK || current_type == IdentitifyType.NICKNAME) {
|
||||
return this.identities[IdentitifyType[current_type].toLowerCase()];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
selected_type?(): identities.IdentitifyType {
|
||||
return this.selected_identity_type ? identities.IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined;
|
||||
selected_type?(): IdentitifyType {
|
||||
return this.selected_identity_type ? IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined;
|
||||
}
|
||||
|
||||
set_identity(type: identities.IdentitifyType, identity: identities.Identity) {
|
||||
this.identities[identities.IdentitifyType[type].toLowerCase()] = identity;
|
||||
set_identity(type: IdentitifyType, identity: Identity) {
|
||||
this.identities[IdentitifyType[type].toLowerCase()] = identity;
|
||||
}
|
||||
|
||||
spawn_identity_handshake_handler?(connection: connection.AbstractServerConnection): connection.HandshakeIdentityHandler {
|
||||
spawn_identity_handshake_handler?(connection: AbstractServerConnection): HandshakeIdentityHandler {
|
||||
const identity = this.selected_identity();
|
||||
if (!identity)
|
||||
return undefined;
|
||||
|
@ -72,9 +80,8 @@ namespace profiles {
|
|||
|
||||
valid(): boolean {
|
||||
const identity = this.selected_identity();
|
||||
if (!identity || !identity.valid()) return false;
|
||||
|
||||
return true;
|
||||
return !!identity && identity.valid();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,12 +97,12 @@ namespace profiles {
|
|||
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];
|
||||
for (const key of Object.keys(data.identity_data)) {
|
||||
const type = IdentitifyType[key.toUpperCase() as string];
|
||||
const _data = data.identity_data[key];
|
||||
if (type == undefined) continue;
|
||||
|
||||
const identity = await identities.decode_identity(type, _data);
|
||||
const identity = await decode_identity(type, _data);
|
||||
if (identity == undefined) continue;
|
||||
|
||||
result.identities[key.toLowerCase()] = identity;
|
||||
|
@ -122,7 +129,7 @@ namespace profiles {
|
|||
} 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();
|
||||
createErrorModal(tr("Profile data invalid"), formatMessage(tr("The profile data is invalid.{:br:}This might cause data loss."))).open();
|
||||
return {version: 0};
|
||||
}
|
||||
})();
|
||||
|
@ -153,14 +160,14 @@ namespace profiles {
|
|||
|
||||
/* generate default identity */
|
||||
try {
|
||||
const identity = await identities.TeaSpeakIdentity.generate_new();
|
||||
const identity = await 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];
|
||||
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();
|
||||
}
|
||||
|
@ -172,8 +179,8 @@ namespace profiles {
|
|||
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];
|
||||
profile.set_identity(IdentitifyType.TEAFORO, TeaForumIdentity.identity());
|
||||
profile.selected_identity_type = IdentitifyType[IdentitifyType.TEAFORO];
|
||||
}
|
||||
|
||||
save();
|
||||
|
@ -248,4 +255,3 @@ namespace profiles {
|
|||
export function delete_profile(profile: ConnectionProfile) {
|
||||
available_profiles.remove(profile);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
namespace profiles.identities {
|
||||
import {AbstractServerConnection, ServerCommand} from "tc-shared/connection/ConnectionBase";
|
||||
import {HandshakeIdentityHandler} from "tc-shared/connection/HandshakeHandler";
|
||||
import {AbstractCommandHandler} from "tc-shared/connection/AbstractCommandHandler";
|
||||
|
||||
export enum IdentitifyType {
|
||||
TEAFORO,
|
||||
TEAMSPEAK,
|
||||
|
@ -15,20 +18,24 @@ namespace profiles.identities {
|
|||
encode?() : string;
|
||||
decode(data: string) : Promise<void>;
|
||||
|
||||
spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler;
|
||||
spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler;
|
||||
}
|
||||
|
||||
/* avoid circular dependencies here */
|
||||
export async function decode_identity(type: IdentitifyType, data: string) : Promise<Identity> {
|
||||
let identity: Identity;
|
||||
switch (type) {
|
||||
case IdentitifyType.NICKNAME:
|
||||
identity = new NameIdentity();
|
||||
const nidentity = require("tc-shared/profiles/identities/NameIdentity");
|
||||
identity = new nidentity.NameIdentity();
|
||||
break;
|
||||
case IdentitifyType.TEAFORO:
|
||||
identity = new TeaForumIdentity(undefined);
|
||||
const fidentity = require("tc-shared/profiles/identities/TeaForumIdentity");
|
||||
identity = new fidentity.TeaForumIdentity(undefined);
|
||||
break;
|
||||
case IdentitifyType.TEAMSPEAK:
|
||||
identity = new TeaSpeakIdentity(undefined, undefined);
|
||||
const tidentity = require("tc-shared/profiles/identities/TeamSpeakIdentity");
|
||||
identity = new tidentity.TeaSpeakIdentity(undefined, undefined);
|
||||
break;
|
||||
}
|
||||
if(!identity)
|
||||
|
@ -49,28 +56,31 @@ namespace profiles.identities {
|
|||
let identity: Identity;
|
||||
switch (type) {
|
||||
case IdentitifyType.NICKNAME:
|
||||
identity = new NameIdentity();
|
||||
const nidentity = require("tc-shared/profiles/identities/NameIdentity");
|
||||
identity = new nidentity.NameIdentity();
|
||||
break;
|
||||
case IdentitifyType.TEAFORO:
|
||||
identity = new TeaForumIdentity(undefined);
|
||||
const fidentity = require("tc-shared/profiles/identities/TeaForumIdentity");
|
||||
identity = new fidentity.TeaForumIdentity(undefined);
|
||||
break;
|
||||
case IdentitifyType.TEAMSPEAK:
|
||||
identity = new TeaSpeakIdentity(undefined, undefined);
|
||||
const tidentity = require("tc-shared/profiles/identities/TeamSpeakIdentity");
|
||||
identity = new tidentity.TeaSpeakIdentity(undefined, undefined);
|
||||
break;
|
||||
}
|
||||
return identity;
|
||||
}
|
||||
|
||||
export class HandshakeCommandHandler<T extends AbstractHandshakeIdentityHandler> extends connection.AbstractCommandHandler {
|
||||
export class HandshakeCommandHandler<T extends AbstractHandshakeIdentityHandler> extends AbstractCommandHandler {
|
||||
readonly handle: T;
|
||||
|
||||
constructor(connection: connection.AbstractServerConnection, handle: T) {
|
||||
constructor(connection: AbstractServerConnection, handle: T) {
|
||||
super(connection);
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
|
||||
handle_command(command: connection.ServerCommand): boolean {
|
||||
handle_command(command: ServerCommand): boolean {
|
||||
if($.isFunction(this[command.command]))
|
||||
this[command.command](command.arguments);
|
||||
else if(command.command == "error") {
|
||||
|
@ -82,12 +92,12 @@ namespace profiles.identities {
|
|||
}
|
||||
}
|
||||
|
||||
export abstract class AbstractHandshakeIdentityHandler implements connection.HandshakeIdentityHandler {
|
||||
connection: connection.AbstractServerConnection;
|
||||
export abstract class AbstractHandshakeIdentityHandler implements HandshakeIdentityHandler {
|
||||
connection: AbstractServerConnection;
|
||||
|
||||
protected callbacks: ((success: boolean, message?: string) => any)[] = [];
|
||||
|
||||
protected constructor(connection: connection.AbstractServerConnection) {
|
||||
protected constructor(connection: AbstractServerConnection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
|
@ -107,4 +117,3 @@ namespace profiles.identities {
|
|||
callback(false, message);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,21 @@
|
|||
/// <reference path="../Identity.ts" />
|
||||
import {
|
||||
AbstractHandshakeIdentityHandler,
|
||||
HandshakeCommandHandler,
|
||||
IdentitifyType,
|
||||
Identity
|
||||
} from "tc-shared/profiles/Identity";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase";
|
||||
import {HandshakeIdentityHandler} from "tc-shared/connection/HandshakeHandler";
|
||||
|
||||
namespace profiles.identities {
|
||||
console.error(AbstractHandshakeIdentityHandler);
|
||||
class NameHandshakeHandler extends AbstractHandshakeIdentityHandler {
|
||||
readonly identity: NameIdentity;
|
||||
handler: HandshakeCommandHandler<NameHandshakeHandler>;
|
||||
|
||||
constructor(connection: connection.AbstractServerConnection, identity: profiles.identities.NameIdentity) {
|
||||
constructor(connection: AbstractServerConnection, identity: NameIdentity) {
|
||||
super(connection);
|
||||
this.identity = identity;
|
||||
|
||||
|
@ -81,8 +91,7 @@ namespace profiles.identities {
|
|||
});
|
||||
}
|
||||
|
||||
spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler {
|
||||
spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler {
|
||||
return new NameHandshakeHandler(connection, this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,21 @@
|
|||
/// <reference path="../Identity.ts" />
|
||||
import {
|
||||
AbstractHandshakeIdentityHandler,
|
||||
HandshakeCommandHandler,
|
||||
IdentitifyType,
|
||||
Identity
|
||||
} from "tc-shared/profiles/Identity";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase";
|
||||
import {HandshakeIdentityHandler} from "tc-shared/connection/HandshakeHandler";
|
||||
import * as forum from "./teaspeak-forum";
|
||||
|
||||
namespace profiles.identities {
|
||||
class TeaForumHandshakeHandler extends AbstractHandshakeIdentityHandler {
|
||||
readonly identity: TeaForumIdentity;
|
||||
handler: HandshakeCommandHandler<TeaForumHandshakeHandler>;
|
||||
|
||||
constructor(connection: connection.AbstractServerConnection, identity: profiles.identities.TeaForumIdentity) {
|
||||
constructor(connection: AbstractServerConnection, identity: TeaForumIdentity) {
|
||||
super(connection);
|
||||
this.identity = identity;
|
||||
this.handler = new HandshakeCommandHandler(connection, this);
|
||||
|
@ -62,7 +72,7 @@ namespace profiles.identities {
|
|||
this.identity_data = data;
|
||||
}
|
||||
|
||||
data() : forum.Data {
|
||||
data() {
|
||||
return this.identity_data;
|
||||
}
|
||||
|
||||
|
@ -80,7 +90,7 @@ namespace profiles.identities {
|
|||
});
|
||||
}
|
||||
|
||||
spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler {
|
||||
spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler {
|
||||
return new TeaForumHandshakeHandler(connection, this);
|
||||
}
|
||||
|
||||
|
@ -88,7 +98,7 @@ namespace profiles.identities {
|
|||
return this.identity_data ? this.identity_data.name() : undefined;
|
||||
}
|
||||
|
||||
type(): profiles.identities.IdentitifyType {
|
||||
type(): IdentitifyType {
|
||||
return IdentitifyType.TEAFORO;
|
||||
}
|
||||
|
||||
|
@ -96,6 +106,10 @@ namespace profiles.identities {
|
|||
//FIXME: Real UID!
|
||||
return "TeaForo#" + ((this.identity_data ? this.identity_data.name() : "Another TeaSpeak user"));
|
||||
}
|
||||
|
||||
public static identity() {
|
||||
return static_identity;
|
||||
}
|
||||
}
|
||||
|
||||
let static_identity: TeaForumIdentity;
|
||||
|
@ -119,4 +133,3 @@ namespace profiles.identities {
|
|||
export function static_forum_identity() : TeaForumIdentity | undefined {
|
||||
return static_identity;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,23 @@
|
|||
/// <reference path="../Identity.ts" />
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as asn1 from "tc-shared/crypto/asn1";
|
||||
import * as sha from "tc-shared/crypto/sha";
|
||||
|
||||
import {
|
||||
AbstractHandshakeIdentityHandler,
|
||||
HandshakeCommandHandler,
|
||||
IdentitifyType,
|
||||
Identity
|
||||
} from "tc-shared/profiles/Identity";
|
||||
import {settings} from "tc-shared/settings";
|
||||
import {arrayBufferBase64, base64_encode_ab, str2ab8} from "tc-shared/utils/buffers";
|
||||
import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {HandshakeIdentityHandler} from "tc-shared/connection/HandshakeHandler";
|
||||
|
||||
namespace profiles.identities {
|
||||
export namespace CryptoHelper {
|
||||
export function base64_url_encode(str){
|
||||
return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
|
||||
return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
}
|
||||
|
||||
export function base64_url_decode(str: string, pad?: boolean){
|
||||
|
@ -192,11 +206,6 @@ namespace profiles.identities {
|
|||
k = k.substr(1);
|
||||
}
|
||||
|
||||
/*
|
||||
console.log("Key x: %s (%d)", btoa(x), x.length);
|
||||
console.log("Key y: %s (%d)", btoa(y), y.length);
|
||||
console.log("Key k: %s (%d)", btoa(k), k.length);
|
||||
*/
|
||||
return {
|
||||
crv: "P-256",
|
||||
d: base64_url_encode(btoa(k)),
|
||||
|
@ -214,7 +223,7 @@ namespace profiles.identities {
|
|||
identity: TeaSpeakIdentity;
|
||||
handler: HandshakeCommandHandler<TeaSpeakHandshakeHandler>;
|
||||
|
||||
constructor(connection: connection.AbstractServerConnection, identity: TeaSpeakIdentity) {
|
||||
constructor(connection: AbstractServerConnection, identity: TeaSpeakIdentity) {
|
||||
super(connection);
|
||||
this.identity = identity;
|
||||
this.handler = new HandshakeCommandHandler(connection, this);
|
||||
|
@ -802,7 +811,6 @@ namespace profiles.identities {
|
|||
}
|
||||
|
||||
this._initialized = true;
|
||||
//const public_key = await profiles.identities.CryptoHelper.export_ecc_key(key, true);
|
||||
}
|
||||
|
||||
async export_ts(ini?: boolean) : Promise<string> {
|
||||
|
@ -864,8 +872,7 @@ namespace profiles.identities {
|
|||
return base64_encode_ab(buffer.subarray(0, index));
|
||||
}
|
||||
|
||||
spawn_identity_handshake_handler(connection: connection.AbstractServerConnection): connection.HandshakeIdentityHandler {
|
||||
spawn_identity_handshake_handler(connection: AbstractServerConnection): HandshakeIdentityHandler {
|
||||
return new TeaSpeakHandshakeHandler(connection, this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,12 @@
|
|||
import {settings, Settings} from "tc-shared/settings";
|
||||
import * as loader from "tc-loader";
|
||||
import * as fidentity from "./TeaForumIdentity";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
grecaptcha: GReCaptcha;
|
||||
}
|
||||
}
|
||||
|
||||
interface GReCaptcha {
|
||||
render(container: string | HTMLElement, parameters: {
|
||||
|
@ -18,7 +24,6 @@ interface GReCaptcha {
|
|||
reset(widget_id?: string);
|
||||
}
|
||||
|
||||
namespace forum {
|
||||
export namespace gcaptcha {
|
||||
export async function initialize() {
|
||||
if(typeof(window.grecaptcha) === "undefined") {
|
||||
|
@ -46,7 +51,7 @@ namespace forum {
|
|||
if(script)
|
||||
script.onerror = undefined;
|
||||
delete window[callback_name];
|
||||
clearTimeout(timeout);
|
||||
timeout && clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,7 +211,7 @@ namespace forum {
|
|||
localStorage.setItem("teaspeak-forum-data", response["data"]);
|
||||
localStorage.setItem("teaspeak-forum-sign", response["sign"]);
|
||||
localStorage.setItem("teaspeak-forum-auth", response["auth-key"]);
|
||||
profiles.identities.update_forum();
|
||||
fidentity.update_forum();
|
||||
} catch(error) {
|
||||
console.error(tr("Failed to parse forum given data: %o"), error);
|
||||
return {
|
||||
|
@ -266,7 +271,7 @@ namespace forum {
|
|||
_data = new Data(_data.auth_key, response["data"], response["sign"]);
|
||||
localStorage.setItem("teaspeak-forum-data", response["data"]);
|
||||
localStorage.setItem("teaspeak-forum-sign", response["sign"]);
|
||||
profiles.identities.update_forum();
|
||||
fidentity.update_forum();
|
||||
} catch(error) {
|
||||
console.error(tr("Failed to parse forum given data: %o"), error);
|
||||
throw tr("failed to parse data");
|
||||
|
@ -320,7 +325,7 @@ namespace forum {
|
|||
localStorage.removeItem("teaspeak-forum-data");
|
||||
localStorage.removeItem("teaspeak-forum-sign");
|
||||
localStorage.removeItem("teaspeak-forum-auth");
|
||||
profiles.identities.update_forum();
|
||||
fidentity.update_forum();
|
||||
}
|
||||
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
|
@ -363,4 +368,3 @@ namespace forum {
|
|||
}
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
//Used by CertAccept popup
|
||||
|
||||
declare global {
|
||||
interface Array<T> {
|
||||
remove(elem?: T): boolean;
|
||||
last?(): T;
|
||||
|
@ -41,6 +42,69 @@ interface String {
|
|||
format(arguments: string[]): string;
|
||||
}
|
||||
|
||||
interface Twemoji {
|
||||
parse(message: string) : string;
|
||||
}
|
||||
let twemoji: Twemoji;
|
||||
|
||||
interface HighlightJS {
|
||||
listLanguages() : string[];
|
||||
getLanguage(name: string) : any | undefined;
|
||||
|
||||
highlight(language: string, text: string, ignore_illegals?: boolean) : HighlightJSResult;
|
||||
highlightAuto(text: string) : HighlightJSResult;
|
||||
}
|
||||
|
||||
interface HighlightJSResult {
|
||||
language: string;
|
||||
relevance: number;
|
||||
|
||||
value: string;
|
||||
second_best?: any;
|
||||
}
|
||||
|
||||
interface DOMPurify {
|
||||
sanitize(html: string, config?: {
|
||||
ADD_ATTR?: string[]
|
||||
ADD_TAGS?: string[];
|
||||
}) : string;
|
||||
}
|
||||
let DOMPurify: DOMPurify;
|
||||
|
||||
let remarkable: typeof window.remarkable;
|
||||
|
||||
class webkitAudioContext extends AudioContext {}
|
||||
class webkitOfflineAudioContext extends OfflineAudioContext {}
|
||||
|
||||
interface Window {
|
||||
readonly webkitAudioContext: typeof webkitAudioContext;
|
||||
readonly AudioContext: typeof webkitAudioContext;
|
||||
readonly OfflineAudioContext: typeof OfflineAudioContext;
|
||||
readonly webkitOfflineAudioContext: typeof webkitOfflineAudioContext;
|
||||
readonly RTCPeerConnection: typeof RTCPeerConnection;
|
||||
readonly Pointer_stringify: any;
|
||||
readonly jsrender: any;
|
||||
|
||||
twemoji: Twemoji;
|
||||
hljs: HighlightJS;
|
||||
remarkable: any;
|
||||
|
||||
require(id: string): any;
|
||||
}
|
||||
|
||||
interface Navigator {
|
||||
browserSpecs: {
|
||||
name: string,
|
||||
version: string
|
||||
};
|
||||
|
||||
mozGetUserMedia(constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback): void;
|
||||
webkitGetUserMedia(constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback): void;
|
||||
}
|
||||
}
|
||||
|
||||
export function initialize() { }
|
||||
|
||||
if(!JSON.map_to) {
|
||||
JSON.map_to = function <T>(object: T, json: any, variables?: string | string[], validator?: (map_field: string, map_value: string) => boolean, variable_direction?: number): number {
|
||||
if (!validator) validator = (a, b) => true;
|
||||
|
@ -131,6 +195,7 @@ if(typeof ($) !== "undefined") {
|
|||
return $(document.createElement(tagName) as any);
|
||||
}
|
||||
}
|
||||
|
||||
if(!$.fn.renderTag) {
|
||||
$.fn.renderTag = function (this: JQuery, values?: any) : JQuery {
|
||||
let result;
|
||||
|
@ -184,7 +249,8 @@ if(typeof ($) !== "undefined") {
|
|||
const result = this.height();
|
||||
this.attr("style", original_style || "");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
if(!$.fn.visible_width)
|
||||
$.fn.visible_width = function (this: JQuery<HTMLElement>) {
|
||||
const original_style = this.attr("style");
|
||||
|
@ -197,7 +263,8 @@ if(typeof ($) !== "undefined") {
|
|||
const result = this.width();
|
||||
this.attr("style", original_style || "");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
if(!$.fn.firstParent)
|
||||
$.fn.firstParent = function (this: JQuery<HTMLElement>, selector: string) {
|
||||
if(this.is(selector))
|
||||
|
@ -232,30 +299,6 @@ function concatenate(resultConstructor, ...arrays) {
|
|||
return result;
|
||||
}
|
||||
|
||||
function formatDate(secs: number) : string {
|
||||
let years = Math.floor(secs / (60 * 60 * 24 * 365));
|
||||
let days = Math.floor(secs / (60 * 60 * 24)) % 365;
|
||||
let hours = Math.floor(secs / (60 * 60)) % 24;
|
||||
let minutes = Math.floor(secs / 60) % 60;
|
||||
let seconds = Math.floor(secs % 60);
|
||||
|
||||
let result = "";
|
||||
if(years > 0)
|
||||
result += years + " " + tr("years") + " ";
|
||||
if(years > 0 || days > 0)
|
||||
result += days + " " + tr("days") + " ";
|
||||
if(years > 0 || days > 0 || hours > 0)
|
||||
result += hours + " " + tr("hours") + " ";
|
||||
if(years > 0 || days > 0 || hours > 0 || minutes > 0)
|
||||
result += minutes + " " + tr("minutes") + " ";
|
||||
if(years > 0 || days > 0 || hours > 0 || minutes > 0 || seconds > 0)
|
||||
result += seconds + " " + tr("seconds") + " ";
|
||||
else
|
||||
result = tr("now") + " ";
|
||||
|
||||
return result.substr(0, result.length - 1);
|
||||
}
|
||||
|
||||
function calculate_width(text: string) : number {
|
||||
let element = $.spawn("div");
|
||||
element.text(text)
|
||||
|
@ -266,63 +309,3 @@ function calculate_width(text: string) : number {
|
|||
element.detach();
|
||||
return size;
|
||||
}
|
||||
|
||||
interface Twemoji {
|
||||
parse(message: string) : string;
|
||||
}
|
||||
declare let twemoji: Twemoji;
|
||||
|
||||
interface HighlightJS {
|
||||
listLanguages() : string[];
|
||||
getLanguage(name: string) : any | undefined;
|
||||
|
||||
highlight(language: string, text: string, ignore_illegals?: boolean) : HighlightJSResult;
|
||||
highlightAuto(text: string) : HighlightJSResult;
|
||||
}
|
||||
|
||||
interface HighlightJSResult {
|
||||
language: string;
|
||||
relevance: number;
|
||||
|
||||
value: string;
|
||||
second_best?: any;
|
||||
}
|
||||
|
||||
interface DOMPurify {
|
||||
sanitize(html: string, config?: {
|
||||
ADD_ATTR?: string[]
|
||||
ADD_TAGS?: string[];
|
||||
}) : string;
|
||||
}
|
||||
declare let DOMPurify: DOMPurify;
|
||||
|
||||
declare let remarkable: typeof window.remarkable;
|
||||
|
||||
declare class webkitAudioContext extends AudioContext {}
|
||||
declare class webkitOfflineAudioContext extends OfflineAudioContext {}
|
||||
|
||||
interface Window {
|
||||
readonly webkitAudioContext: typeof webkitAudioContext;
|
||||
readonly AudioContext: typeof webkitAudioContext;
|
||||
readonly OfflineAudioContext: typeof OfflineAudioContext;
|
||||
readonly webkitOfflineAudioContext: typeof webkitOfflineAudioContext;
|
||||
readonly RTCPeerConnection: typeof RTCPeerConnection;
|
||||
readonly Pointer_stringify: any;
|
||||
readonly jsrender: any;
|
||||
|
||||
twemoji: Twemoji;
|
||||
hljs: HighlightJS;
|
||||
remarkable: any;
|
||||
|
||||
require(id: string): any;
|
||||
}
|
||||
|
||||
interface Navigator {
|
||||
browserSpecs: {
|
||||
name: string,
|
||||
version: string
|
||||
};
|
||||
|
||||
mozGetUserMedia(constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback): void;
|
||||
webkitGetUserMedia(constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback): void;
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
/// <reference path="ui/elements/modal.ts" />
|
||||
//Used by CertAccept popup
|
||||
|
||||
import {createErrorModal} from "tc-shared/ui/elements/Modal";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as loader from "tc-loader";
|
||||
import * as log from "tc-shared/log";
|
||||
|
||||
if(typeof(customElements) !== "undefined") {
|
||||
try {
|
||||
class X_Properties extends HTMLElement {}
|
||||
|
@ -9,12 +13,12 @@ if(typeof(customElements) !== "undefined") {
|
|||
customElements.define('x-properties', X_Properties, { extends: 'div' });
|
||||
customElements.define('x-property', X_Property, { extends: 'div' });
|
||||
} catch(error) {
|
||||
console.warn("failed to define costum elements");
|
||||
console.warn("failed to define costume elements");
|
||||
}
|
||||
}
|
||||
|
||||
/* T = value type */
|
||||
interface SettingsKey<T> {
|
||||
export interface SettingsKey<T> {
|
||||
key: string;
|
||||
|
||||
fallback_keys?: string | string[];
|
||||
|
@ -25,7 +29,7 @@ interface SettingsKey<T> {
|
|||
require_restart?: boolean;
|
||||
}
|
||||
|
||||
class SettingsBase {
|
||||
export class SettingsBase {
|
||||
protected static readonly UPDATE_DIRECT: boolean = true;
|
||||
|
||||
protected static transformStO?<T>(input?: string, _default?: T, default_type?: string) : T {
|
||||
|
@ -77,7 +81,7 @@ class SettingsBase {
|
|||
}
|
||||
}
|
||||
|
||||
class StaticSettings extends SettingsBase {
|
||||
export class StaticSettings extends SettingsBase {
|
||||
private static _instance: StaticSettings;
|
||||
static get instance() : StaticSettings {
|
||||
if(!this._instance)
|
||||
|
@ -139,7 +143,7 @@ class StaticSettings extends SettingsBase {
|
|||
}
|
||||
}
|
||||
|
||||
class Settings extends StaticSettings {
|
||||
export class Settings extends StaticSettings {
|
||||
static readonly KEY_USER_IS_NEW: SettingsKey<boolean> = {
|
||||
key: 'user_is_new_user',
|
||||
default_value: true
|
||||
|
@ -433,7 +437,7 @@ class Settings extends StaticSettings {
|
|||
}
|
||||
}
|
||||
|
||||
class ServerSettings extends SettingsBase {
|
||||
export class ServerSettings extends SettingsBase {
|
||||
private cacheServer = {};
|
||||
private _server_unique_id: string;
|
||||
private _server_save_worker: NodeJS.Timer;
|
||||
|
@ -511,4 +515,4 @@ class ServerSettings extends SettingsBase {
|
|||
}
|
||||
}
|
||||
|
||||
let settings: Settings;
|
||||
export let settings: Settings;
|
|
@ -1,4 +1,10 @@
|
|||
enum Sound {
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {settings} from "tc-shared/settings";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import * as sbackend from "tc-backend/audio/sounds";
|
||||
|
||||
export enum Sound {
|
||||
SOUND_TEST = "sound.test",
|
||||
SOUND_EGG = "sound.egg",
|
||||
|
||||
|
@ -61,7 +67,6 @@ enum Sound {
|
|||
GROUP_CHANNEL_CHANGED_SELF = "group.channel.changed.self"
|
||||
}
|
||||
|
||||
namespace sound {
|
||||
export interface SoundHandle {
|
||||
key: string;
|
||||
filename: string;
|
||||
|
@ -136,21 +141,6 @@ namespace sound {
|
|||
ignore_muted = flag;
|
||||
}
|
||||
|
||||
export function reinitialisize_audio() {
|
||||
const context = audio.player.context();
|
||||
const destination = audio.player.destination();
|
||||
|
||||
if(master_mixed)
|
||||
master_mixed.disconnect();
|
||||
|
||||
master_mixed = context.createGain();
|
||||
if(master_mixed.gain.setValueAtTime)
|
||||
master_mixed.gain.setValueAtTime(master_volume, 0);
|
||||
else
|
||||
master_mixed.gain.value = master_volume;
|
||||
master_mixed.connect(destination);
|
||||
}
|
||||
|
||||
export function save() {
|
||||
if(volume_require_save) {
|
||||
volume_require_save = false;
|
||||
|
@ -158,7 +148,7 @@ namespace sound {
|
|||
const data: any = {};
|
||||
data.version = 1;
|
||||
|
||||
for(const key in Sound) {
|
||||
for(const key of Object.keys(Sound)) {
|
||||
if(typeof(speech_volume[Sound[key]]) !== "undefined")
|
||||
data[Sound[key]] = speech_volume[Sound[key]];
|
||||
}
|
||||
|
@ -183,7 +173,7 @@ namespace sound {
|
|||
/* volumes */
|
||||
{
|
||||
const data = JSON.parse(settings.static_global("sound_volume", "{}"));
|
||||
for(const sound_key in Sound) {
|
||||
for(const sound_key of Object.keys(Sound)) {
|
||||
if(typeof(data[Sound[sound_key]]) !== "undefined")
|
||||
speech_volume[Sound[sound_key]] = data[Sound[sound_key]];
|
||||
}
|
||||
|
@ -197,7 +187,6 @@ namespace sound {
|
|||
register_sound("message.send", "effects/message_send.wav");
|
||||
|
||||
manager = new SoundManager(undefined);
|
||||
audio.player.on_ready(reinitialisize_audio);
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
$.ajax({
|
||||
url: "audio/speech/mapping.json",
|
||||
|
@ -254,25 +243,19 @@ namespace sound {
|
|||
if(volume == 0 || master_volume == 0)
|
||||
return;
|
||||
|
||||
if(this._handle && !options.ignore_muted && !sound.ignore_output_muted() && this._handle.client_status.output_muted)
|
||||
if(this._handle && !options.ignore_muted && !ignore_output_muted() && this._handle.client_status.output_muted)
|
||||
return;
|
||||
|
||||
const context = audio.player.context();
|
||||
if(!context) {
|
||||
log.warn(LogCategory.AUDIO, tr("Tried to replay a sound without an audio context (Sound: %o). Dropping playback"), _sound);
|
||||
return;
|
||||
}
|
||||
|
||||
sound.resolve_sound(_sound).then(handle => {
|
||||
resolve_sound(_sound).then(handle => {
|
||||
if(!handle) return;
|
||||
|
||||
if(!options.ignore_overlap && (this._playing_sounds[handle.filename] > 0) && !sound.overlap_activated()) {
|
||||
if(!options.ignore_overlap && (this._playing_sounds[handle.filename] > 0) && !overlap_activated()) {
|
||||
log.info(LogCategory.AUDIO, tr("Dropping requested playback for sound %s because it would overlap."), _sound);
|
||||
return;
|
||||
}
|
||||
|
||||
this._playing_sounds[handle.filename] = (this._playing_sounds[handle.filename] || 0) + 1;
|
||||
audio.sounds.play_sound({
|
||||
sbackend.play_sound({
|
||||
path: "audio/" + handle.filename,
|
||||
volume: volume * master_volume
|
||||
}).then(() => {
|
||||
|
@ -286,10 +269,9 @@ namespace sound {
|
|||
this._playing_sounds[handle.filename]--;
|
||||
});
|
||||
}).catch(error => {
|
||||
log.warn(LogCategory.AUDIO, tr("Failed to replay sound %o because it could not be resolved: %o"), sound, error);
|
||||
log.warn(LogCategory.AUDIO, tr("Failed to replay sound %o because it could not be resolved: %o"), _sound, error);
|
||||
if(options.callback)
|
||||
options.callback(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
namespace stats {
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as log from "tc-shared/log";
|
||||
|
||||
const LOG_PREFIX = "[Statistics] ";
|
||||
|
||||
export enum CloseCodes {
|
||||
enum CloseCodes {
|
||||
UNSET = 3000,
|
||||
RECONNECT = 3001,
|
||||
INTERNAL_ERROR = 3002,
|
||||
|
@ -240,4 +242,3 @@ namespace stats {
|
|||
handler["notifyusercount"] = handle_notify_user_count;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,26 @@
|
|||
/// <reference path="view.ts" />
|
||||
/// <reference path="../utils/helpers.ts" />
|
||||
import {ChannelTree} from "tc-shared/ui/view";
|
||||
import {ClientEntry} from "tc-shared/ui/client";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory, LogType} from "tc-shared/log";
|
||||
import PermissionType from "tc-shared/permission/PermissionType";
|
||||
import {settings, Settings} from "tc-shared/settings";
|
||||
import * as contextmenu from "tc-shared/ui/elements/ContextMenu";
|
||||
import {Sound} from "tc-shared/sound/Sounds";
|
||||
import {createErrorModal, createInfoModal, createInputModal} from "tc-shared/ui/elements/Modal";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import * as htmltags from "./htmltags";
|
||||
import {hashPassword} from "tc-shared/utils/helpers";
|
||||
import * as server_log from "tc-shared/ui/frames/server_log";
|
||||
import {openChannelInfo} from "tc-shared/ui/modal/ModalChannelInfo";
|
||||
import {createChannelModal} from "tc-shared/ui/modal/ModalCreateChannel";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
|
||||
enum ChannelType {
|
||||
export enum ChannelType {
|
||||
PERMANENT,
|
||||
SEMI_PERMANENT,
|
||||
TEMPORARY
|
||||
}
|
||||
namespace ChannelType {
|
||||
export namespace ChannelType {
|
||||
export function normalize(mode: ChannelType) {
|
||||
let value: string = ChannelType[mode];
|
||||
value = value.toLowerCase();
|
||||
|
@ -14,13 +28,13 @@ namespace ChannelType {
|
|||
}
|
||||
}
|
||||
|
||||
enum ChannelSubscribeMode {
|
||||
export enum ChannelSubscribeMode {
|
||||
SUBSCRIBED,
|
||||
UNSUBSCRIBED,
|
||||
INHERITED
|
||||
}
|
||||
|
||||
class ChannelProperties {
|
||||
export class ChannelProperties {
|
||||
channel_order: number = 0;
|
||||
channel_name: string = "";
|
||||
channel_name_phonetic: string = "";
|
||||
|
@ -55,7 +69,7 @@ class ChannelProperties {
|
|||
channel_conversation_history_length: number = -1;
|
||||
}
|
||||
|
||||
class ChannelEntry {
|
||||
export class ChannelEntry {
|
||||
channelTree: ChannelTree;
|
||||
channelId: number;
|
||||
parent?: ChannelEntry;
|
||||
|
@ -525,7 +539,7 @@ class ChannelEntry {
|
|||
name: tr("Show channel info"),
|
||||
callback: () => {
|
||||
trigger_close = false;
|
||||
Modals.openChannelInfo(this);
|
||||
openChannelInfo(this);
|
||||
},
|
||||
icon_class: "client-about"
|
||||
},
|
||||
|
@ -565,7 +579,7 @@ class ChannelEntry {
|
|||
name: tr("Edit channel"),
|
||||
invalidPermission: !channelModify,
|
||||
callback: () => {
|
||||
Modals.createChannelModal(this.channelTree.client, this, undefined, this.channelTree.client.permissions, (changes?, permissions?) => {
|
||||
createChannelModal(this.channelTree.client, this, undefined, this.channelTree.client.permissions, (changes?, permissions?) => {
|
||||
if(changes) {
|
||||
changes["cid"] = this.channelId;
|
||||
this.channelTree.client.serverConnection.send_command("channeledit", changes);
|
||||
|
@ -617,7 +631,7 @@ class ChannelEntry {
|
|||
error = error.extra_message || error.message;
|
||||
}
|
||||
|
||||
createErrorModal(tr("Failed to create bot"), MessageHelper.formatMessage(tr("Failed to create the music bot:<br>{0}"), error)).open();
|
||||
createErrorModal(tr("Failed to create bot"), formatMessage(tr("Failed to create the music bot:<br>{0}"), error)).open();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -834,7 +848,7 @@ class ChannelEntry {
|
|||
!this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_JOIN_IGNORE_PASSWORD).granted(1)) {
|
||||
createInputModal(tr("Channel password"), tr("Channel password:"), () => true, text => {
|
||||
if(typeof(text) == typeof(true)) return;
|
||||
helpers.hashPassword(text as string).then(result => {
|
||||
hashPassword(text as string).then(result => {
|
||||
this._cachedPassword = result;
|
||||
this.joinChannel();
|
||||
this.updateChannelTypeIcon();
|
||||
|
@ -923,7 +937,7 @@ class ChannelEntry {
|
|||
this._tag_channel.find(".marker-text-unread").toggleClass("hidden", !flag);
|
||||
}
|
||||
|
||||
log_data() : log.server.base.Channel {
|
||||
log_data() : server_log.base.Channel {
|
||||
return {
|
||||
channel_name: this.channelName(),
|
||||
channel_id: this.channelId
|
||||
|
|
|
@ -1,8 +1,34 @@
|
|||
/// <reference path="channel.ts" />
|
||||
/// <reference path="modal/ModalChangeVolume.ts" />
|
||||
/// <reference path="client_move.ts" />
|
||||
import * as contextmenu from "tc-shared/ui/elements/ContextMenu";
|
||||
import {channel_tree, Registry} from "tc-shared/events";
|
||||
import {ChannelTree} from "tc-shared/ui/view";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory, LogType} from "tc-shared/log";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import {KeyCode, SpecialKey} from "tc-shared/PPTListener";
|
||||
import {Sound} from "tc-shared/sound/Sounds";
|
||||
import {Group, GroupManager, GroupTarget, GroupType} from "tc-shared/permission/GroupManager";
|
||||
import PermissionType from "tc-shared/permission/PermissionType";
|
||||
import {createErrorModal, createInputModal} from "tc-shared/ui/elements/Modal";
|
||||
import * as htmltags from "tc-shared/ui/htmltags";
|
||||
import * as server_log from "tc-shared/ui/frames/server_log";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {ChannelEntry} from "tc-shared/ui/channel";
|
||||
import {ConnectionHandler, ViewReasonId} from "tc-shared/ConnectionHandler";
|
||||
import {voice} from "tc-shared/connection/ConnectionBase";
|
||||
import VoiceClient = voice.VoiceClient;
|
||||
import {spawnPermissionEdit} from "tc-shared/ui/modal/permission/ModalPermissionEdit";
|
||||
import {createServerGroupAssignmentModal} from "tc-shared/ui/modal/ModalGroupAssignment";
|
||||
import {openClientInfo} from "tc-shared/ui/modal/ModalClientInfo";
|
||||
import {spawnBanClient} from "tc-shared/ui/modal/ModalBanClient";
|
||||
import {spawnChangeVolume} from "tc-shared/ui/modal/ModalChangeVolume";
|
||||
import {spawnChangeLatency} from "tc-shared/ui/modal/ModalChangeLatency";
|
||||
import {spawnPlaylistEdit} from "tc-shared/ui/modal/ModalPlaylistEdit";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import * as ppt from "tc-backend/ppt";
|
||||
import * as hex from "tc-shared/crypto/hex";
|
||||
|
||||
enum ClientType {
|
||||
export enum ClientType {
|
||||
CLIENT_VOICE,
|
||||
CLIENT_QUERY,
|
||||
CLIENT_INTERNAL,
|
||||
|
@ -11,7 +37,7 @@ enum ClientType {
|
|||
CLIENT_UNDEFINED
|
||||
}
|
||||
|
||||
class ClientProperties {
|
||||
export class ClientProperties {
|
||||
client_type: ClientType = ClientType.CLIENT_VOICE; //TeamSpeaks type
|
||||
client_type_exact: ClientType = ClientType.CLIENT_VOICE;
|
||||
|
||||
|
@ -57,7 +83,7 @@ class ClientProperties {
|
|||
client_is_priority_speaker: boolean = false;
|
||||
}
|
||||
|
||||
class ClientConnectionInfo {
|
||||
export class ClientConnectionInfo {
|
||||
connection_bandwidth_received_last_minute_control: number = -1;
|
||||
connection_bandwidth_received_last_minute_keepalive: number = -1;
|
||||
connection_bandwidth_received_last_minute_speech: number = -1;
|
||||
|
@ -109,8 +135,8 @@ class ClientConnectionInfo {
|
|||
connection_client_port: number = -1;
|
||||
}
|
||||
|
||||
class ClientEntry {
|
||||
readonly events: events.Registry<events.channel_tree.client>;
|
||||
export class ClientEntry {
|
||||
readonly events: Registry<channel_tree.client>;
|
||||
|
||||
protected _clientId: number;
|
||||
protected _channel: ChannelEntry;
|
||||
|
@ -121,7 +147,7 @@ class ClientEntry {
|
|||
protected _speaking: boolean;
|
||||
protected _listener_initialized: boolean;
|
||||
|
||||
protected _audio_handle: connection.voice.VoiceClient;
|
||||
protected _audio_handle: VoiceClient;
|
||||
protected _audio_volume: number;
|
||||
protected _audio_muted: boolean;
|
||||
|
||||
|
@ -136,7 +162,7 @@ class ClientEntry {
|
|||
channelTree: ChannelTree;
|
||||
|
||||
constructor(clientId: number, clientName, properties: ClientProperties = new ClientProperties()) {
|
||||
this.events = new events.Registry<events.channel_tree.client>();
|
||||
this.events = new Registry<channel_tree.client>();
|
||||
|
||||
this._properties = properties;
|
||||
this._properties.client_nickname = clientName;
|
||||
|
@ -181,7 +207,7 @@ class ClientEntry {
|
|||
this._channel = undefined;
|
||||
}
|
||||
|
||||
set_audio_handle(handle: connection.voice.VoiceClient) {
|
||||
set_audio_handle(handle: VoiceClient) {
|
||||
if(this._audio_handle === handle)
|
||||
return;
|
||||
|
||||
|
@ -200,7 +226,7 @@ class ClientEntry {
|
|||
handle.callback_stopped = () => this.speaking = false;
|
||||
}
|
||||
|
||||
get_audio_handle() : connection.voice.VoiceClient {
|
||||
get_audio_handle() : VoiceClient {
|
||||
return this._audio_handle;
|
||||
}
|
||||
|
||||
|
@ -285,7 +311,7 @@ class ClientEntry {
|
|||
|
||||
let clients = this.channelTree.currently_selected as (ClientEntry | ClientEntry[]);
|
||||
|
||||
if(ppt.key_pressed(ppt.SpecialKey.SHIFT)) {
|
||||
if(ppt.key_pressed(SpecialKey.SHIFT)) {
|
||||
if(clients != this && !($.isArray(clients) && clients.indexOf(this) != -1))
|
||||
clients = $.isArray(clients) ? [...clients, this] : [clients, this];
|
||||
} else {
|
||||
|
@ -418,20 +444,20 @@ class ClientEntry {
|
|||
type: contextmenu.MenuEntryType.ENTRY,
|
||||
icon_class: "client-permission_client",
|
||||
name: tr("Client permissions"),
|
||||
callback: () => Modals.spawnPermissionEdit(this.channelTree.client, "clp", {unique_id: this.clientUid()}).open()
|
||||
callback: () => spawnPermissionEdit(this.channelTree.client, "clp", {unique_id: this.clientUid()}).open()
|
||||
},
|
||||
{
|
||||
type: contextmenu.MenuEntryType.ENTRY,
|
||||
icon_class: "client-permission_client",
|
||||
name: tr("Client channel permissions"),
|
||||
callback: () => Modals.spawnPermissionEdit(this.channelTree.client, "clchp", {unique_id: this.clientUid(), channel_id: this._channel ? this._channel.channelId : undefined }).open()
|
||||
callback: () => spawnPermissionEdit(this.channelTree.client, "clchp", {unique_id: this.clientUid(), channel_id: this._channel ? this._channel.channelId : undefined }).open()
|
||||
}
|
||||
]
|
||||
}];
|
||||
}
|
||||
|
||||
open_assignment_modal() {
|
||||
Modals.createServerGroupAssignmentModal(this, (groups, flag) => {
|
||||
createServerGroupAssignmentModal(this, (groups, flag) => {
|
||||
if(groups.length == 0) return Promise.resolve(true);
|
||||
|
||||
if(groups.length == 1) {
|
||||
|
@ -490,7 +516,7 @@ class ClientEntry {
|
|||
type: contextmenu.MenuEntryType.ENTRY,
|
||||
icon_class: "client-about",
|
||||
name: tr("Show client info"),
|
||||
callback: () => Modals.openClientInfo(this)
|
||||
callback: () => openClientInfo(this)
|
||||
},
|
||||
contextmenu.Entry.HR(),
|
||||
{
|
||||
|
@ -582,7 +608,7 @@ class ClientEntry {
|
|||
name: tr("Ban client"),
|
||||
invalidPermission: !this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1),
|
||||
callback: () => {
|
||||
Modals.spawnBanClient(this.channelTree.client, [{
|
||||
spawnBanClient(this.channelTree.client, [{
|
||||
name: this.properties.client_nickname,
|
||||
unique_id: this.properties.client_unique_identifier
|
||||
}], (data) => {
|
||||
|
@ -622,7 +648,7 @@ class ClientEntry {
|
|||
icon_class: "client-volume",
|
||||
name: tr("Change Volume"),
|
||||
callback: () => {
|
||||
Modals.spawnChangeVolume(this, true, this._audio_volume, undefined, volume => {
|
||||
spawnChangeVolume(this, true, this._audio_volume, undefined, volume => {
|
||||
this._audio_volume = volume;
|
||||
this.channelTree.client.settings.changeServer("volume_client_" + this.clientUid(), volume);
|
||||
if(this._audio_handle)
|
||||
|
@ -635,7 +661,7 @@ class ClientEntry {
|
|||
type: contextmenu.MenuEntryType.ENTRY,
|
||||
name: tr("Change playback latency"),
|
||||
callback: () => {
|
||||
Modals.spawnChangeLatency(this, this._audio_handle.latency_settings(), () => {
|
||||
spawnChangeLatency(this, this._audio_handle.latency_settings(), () => {
|
||||
this._audio_handle.reset_latency_settings();
|
||||
return this._audio_handle.latency_settings();
|
||||
}, settings => this._audio_handle.latency_settings(settings), this._audio_handle.support_flush ? () => {
|
||||
|
@ -843,7 +869,7 @@ class ClientEntry {
|
|||
if(variable.key == "client_nickname") {
|
||||
if(variable.value !== old_value && typeof(old_value) === "string") {
|
||||
if(!(this instanceof LocalClientEntry)) { /* own changes will be logged somewhere else */
|
||||
this.channelTree.client.log.log(log.server.Type.CLIENT_NICKNAME_CHANGED, {
|
||||
this.channelTree.client.log.log(server_log.Type.CLIENT_NICKNAME_CHANGED, {
|
||||
own_client: false,
|
||||
client: this.log_data(),
|
||||
new_name: variable.value,
|
||||
|
@ -1082,7 +1108,7 @@ class ClientEntry {
|
|||
this.tag.css('padding-left', (5 + (index + 2) * 16) + "px");
|
||||
}
|
||||
|
||||
log_data() : log.server.base.Client {
|
||||
log_data() : server_log.base.Client {
|
||||
return {
|
||||
client_unique_id: this.properties.client_unique_identifier,
|
||||
client_name: this.clientNickName(),
|
||||
|
@ -1123,7 +1149,7 @@ class ClientEntry {
|
|||
}
|
||||
}
|
||||
|
||||
class LocalClientEntry extends ClientEntry {
|
||||
export class LocalClientEntry extends ClientEntry {
|
||||
handle: ConnectionHandler;
|
||||
|
||||
private renaming: boolean;
|
||||
|
@ -1216,14 +1242,14 @@ class LocalClientEntry extends ClientEntry {
|
|||
const old_name = this.clientNickName();
|
||||
this.handle.serverConnection.command_helper.updateClient("client_nickname", text).then((e) => {
|
||||
settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, text);
|
||||
this.channelTree.client.log.log(log.server.Type.CLIENT_NICKNAME_CHANGED, {
|
||||
this.channelTree.client.log.log(server_log.Type.CLIENT_NICKNAME_CHANGED, {
|
||||
client: this.log_data(),
|
||||
old_name: old_name,
|
||||
new_name: text,
|
||||
own_client: true
|
||||
});
|
||||
}).catch((e: CommandResult) => {
|
||||
this.channelTree.client.log.log(log.server.Type.CLIENT_NICKNAME_CHANGE_FAILED, {
|
||||
this.channelTree.client.log.log(server_log.Type.CLIENT_NICKNAME_CHANGE_FAILED, {
|
||||
reason: e.extra_message
|
||||
});
|
||||
this.openRename();
|
||||
|
@ -1232,7 +1258,7 @@ class LocalClientEntry extends ClientEntry {
|
|||
}
|
||||
}
|
||||
|
||||
class MusicClientProperties extends ClientProperties {
|
||||
export class MusicClientProperties extends ClientProperties {
|
||||
player_state: number = 0;
|
||||
player_volume: number = 0;
|
||||
|
||||
|
@ -1264,7 +1290,7 @@ class MusicClientProperties extends ClientProperties {
|
|||
}
|
||||
*/
|
||||
|
||||
class SongInfo {
|
||||
export class SongInfo {
|
||||
song_id: number = 0;
|
||||
song_url: string = "";
|
||||
song_invoker: number = 0;
|
||||
|
@ -1277,7 +1303,7 @@ class SongInfo {
|
|||
song_length: number = 0;
|
||||
}
|
||||
|
||||
class MusicClientPlayerInfo extends SongInfo {
|
||||
export class MusicClientPlayerInfo extends SongInfo {
|
||||
bot_id: number = 0;
|
||||
player_state: number = 0;
|
||||
|
||||
|
@ -1290,7 +1316,7 @@ class MusicClientPlayerInfo extends SongInfo {
|
|||
player_description: string = "";
|
||||
}
|
||||
|
||||
class MusicClientEntry extends ClientEntry {
|
||||
export class MusicClientEntry extends ClientEntry {
|
||||
private _info_promise: Promise<MusicClientPlayerInfo>;
|
||||
private _info_promise_age: number = 0;
|
||||
private _info_promise_resolve: any;
|
||||
|
@ -1366,7 +1392,7 @@ class MusicClientEntry extends ClientEntry {
|
|||
this.channelTree.client.serverConnection.command_helper.request_playlist_list().then(lists => {
|
||||
for(const entry of lists) {
|
||||
if(entry.playlist_id == this.properties.client_playlist_id) {
|
||||
Modals.spawnPlaylistEdit(this.channelTree.client, entry);
|
||||
spawnPlaylistEdit(this.channelTree.client, entry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1435,7 +1461,7 @@ class MusicClientEntry extends ClientEntry {
|
|||
icon_class: "client-volume",
|
||||
name: tr("Change local volume"),
|
||||
callback: () => {
|
||||
Modals.spawnChangeVolume(this, true, this._audio_handle.get_volume(), undefined, volume => {
|
||||
spawnChangeVolume(this, true, this._audio_handle.get_volume(), undefined, volume => {
|
||||
this.channelTree.client.settings.changeServer("volume_client_" + this.clientUid(), volume);
|
||||
this._audio_handle.set_volume(volume);
|
||||
});
|
||||
|
@ -1450,7 +1476,7 @@ class MusicClientEntry extends ClientEntry {
|
|||
if(max_volume < 0)
|
||||
max_volume = 100;
|
||||
|
||||
Modals.spawnChangeVolume(this, false, this.properties.player_volume, max_volume / 100, value => {
|
||||
spawnChangeVolume(this, false, this.properties.player_volume, max_volume / 100, value => {
|
||||
if(typeof(value) !== "number")
|
||||
return;
|
||||
|
||||
|
@ -1467,7 +1493,7 @@ class MusicClientEntry extends ClientEntry {
|
|||
type: contextmenu.MenuEntryType.ENTRY,
|
||||
name: tr("Change playback latency"),
|
||||
callback: () => {
|
||||
Modals.spawnChangeLatency(this, this._audio_handle.latency_settings(), () => {
|
||||
spawnChangeLatency(this, this._audio_handle.latency_settings(), () => {
|
||||
this._audio_handle.reset_latency_settings();
|
||||
return this._audio_handle.latency_settings();
|
||||
}, settings => this._audio_handle.latency_settings(settings), this._audio_handle.support_flush ? () => {
|
||||
|
@ -1482,8 +1508,8 @@ class MusicClientEntry extends ClientEntry {
|
|||
icon_class: "client-delete",
|
||||
disabled: false,
|
||||
callback: () => {
|
||||
const tag = $.spawn("div").append(MessageHelper.formatMessage(tr("Do you really want to delete {0}"), this.createChatTag(false)));
|
||||
Modals.spawnYesNo(tr("Are you sure?"), $.spawn("div").append(tag), result => {
|
||||
const tag = $.spawn("div").append(formatMessage(tr("Do you really want to delete {0}"), this.createChatTag(false)));
|
||||
spawnYesNo(tr("Are you sure?"), $.spawn("div").append(tag), result => {
|
||||
if(result) {
|
||||
this.channelTree.client.serverConnection.send_command("musicbotdelete", {
|
||||
bot_id: this.properties.client_database_id
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
/// <reference path="client.ts" />
|
||||
import {ChannelTree} from "tc-shared/ui/view";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {ClientEntry} from "tc-shared/ui/client";
|
||||
import {ChannelEntry} from "tc-shared/ui/channel";
|
||||
|
||||
class ClientMover {
|
||||
export class ClientMover {
|
||||
static readonly listener_root = $(document);
|
||||
static readonly move_element = $("#mouse-move");
|
||||
readonly channel_tree: ChannelTree;
|
||||
|
|
|
@ -1,6 +1,16 @@
|
|||
import {settings} from "tc-shared/settings";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as log from "tc-shared/log";
|
||||
|
||||
declare global {
|
||||
interface JQuery<TElement = HTMLElement> {
|
||||
dividerfy() : this;
|
||||
}
|
||||
}
|
||||
|
||||
export function initialize() {
|
||||
|
||||
}
|
||||
|
||||
if(!$.fn.dividerfy) {
|
||||
$.fn.dividerfy = function<T extends HTMLElement>(this: JQuery<T>) : JQuery<T> {
|
||||
|
@ -58,8 +68,8 @@ if(!$.fn.dividerfy) {
|
|||
Math.max(previous_offset.top + previous_element.height(), next_offset.top + next_element.height());
|
||||
*/
|
||||
|
||||
let previous = 0;
|
||||
let next = 0;
|
||||
let previous;
|
||||
let next;
|
||||
if(current < min) {
|
||||
previous = 0;
|
||||
next = 1;
|
||||
|
@ -89,7 +99,7 @@ if(!$.fn.dividerfy) {
|
|||
}));
|
||||
};
|
||||
|
||||
const listener_up = (event: MouseEvent) => {
|
||||
const listener_up = () => {
|
||||
document.removeEventListener('mousemove', listener_move);
|
||||
document.removeEventListener('touchmove', listener_move);
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
namespace contextmenu {
|
||||
export interface MenuEntry {
|
||||
callback?: () => void;
|
||||
type: MenuEntryType;
|
||||
|
@ -74,9 +73,8 @@ namespace contextmenu {
|
|||
provider = _provider;
|
||||
provider.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider {
|
||||
class HTMLContextMenuProvider implements ContextMenuProvider {
|
||||
private _global_click_listener: (event) => any;
|
||||
private _context_menu: JQuery;
|
||||
private _close_callbacks: (() => any)[] = [];
|
||||
|
@ -118,10 +116,10 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider {
|
|||
}
|
||||
}
|
||||
|
||||
private generate_tag(entry: contextmenu.MenuEntry) : JQuery {
|
||||
if(entry.type == contextmenu.MenuEntryType.HR) {
|
||||
private generate_tag(entry: MenuEntry) : JQuery {
|
||||
if(entry.type == MenuEntryType.HR) {
|
||||
return $.spawn("hr");
|
||||
} else if(entry.type == contextmenu.MenuEntryType.ENTRY) {
|
||||
} else if(entry.type == MenuEntryType.ENTRY) {
|
||||
let icon = entry.icon_class;
|
||||
if(!icon || icon.length == 0) icon = "icon_empty";
|
||||
else icon = "icon " + icon;
|
||||
|
@ -141,7 +139,7 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider {
|
|||
});
|
||||
}
|
||||
return tag;
|
||||
} else if(entry.type == contextmenu.MenuEntryType.CHECKBOX) {
|
||||
} else if(entry.type == MenuEntryType.CHECKBOX) {
|
||||
let checkbox = $.spawn("label").addClass("ccheckbox");
|
||||
$.spawn("input").attr("type", "checkbox").prop("checked", !!entry.checkbox_checked).appendTo(checkbox);
|
||||
$.spawn("span").addClass("checkmark").appendTo(checkbox);
|
||||
|
@ -161,7 +159,7 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider {
|
|||
});
|
||||
}
|
||||
return tag;
|
||||
} else if(entry.type == contextmenu.MenuEntryType.SUB_MENU) {
|
||||
} else if(entry.type == MenuEntryType.SUB_MENU) {
|
||||
let icon = entry.icon_class;
|
||||
if(!icon || icon.length == 0) icon = "icon_empty";
|
||||
else icon = "icon " + icon;
|
||||
|
@ -187,7 +185,7 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider {
|
|||
return $.spawn("div").text("undefined");
|
||||
}
|
||||
|
||||
spawn_context_menu(x: number, y: number, ...entries: contextmenu.MenuEntry[]) {
|
||||
spawn_context_menu(x: number, y: number, ...entries: MenuEntry[]) {
|
||||
this._visible = true;
|
||||
|
||||
let menu_tag = this._context_menu || (this._context_menu = $(".context-menu"));
|
||||
|
@ -200,7 +198,7 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider {
|
|||
if(typeof(entry.visible) === 'boolean' && !entry.visible)
|
||||
continue;
|
||||
|
||||
if(entry.type == contextmenu.MenuEntryType.CLOSE) {
|
||||
if(entry.type == MenuEntryType.CLOSE) {
|
||||
if(entry.callback)
|
||||
this._close_callbacks.push(entry.callback);
|
||||
} else
|
||||
|
@ -225,5 +223,4 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
contextmenu.set_provider(new HTMLContextMenuProvider());
|
||||
set_provider(new HTMLContextMenuProvider());
|
|
@ -1,13 +1,13 @@
|
|||
/// <reference path="../../PPTListener.ts" />
|
||||
import {KeyCode} from "tc-shared/PPTListener";
|
||||
|
||||
enum ElementType {
|
||||
export enum ElementType {
|
||||
HEADER,
|
||||
BODY,
|
||||
FOOTER
|
||||
}
|
||||
|
||||
type BodyCreator = (() => JQuery | JQuery[] | string) | string | JQuery | JQuery[];
|
||||
const ModalFunctions = {
|
||||
export type BodyCreator = (() => JQuery | JQuery[] | string) | string | JQuery | JQuery[];
|
||||
export const ModalFunctions = {
|
||||
divify: function (val: JQuery) {
|
||||
if(val.length > 1)
|
||||
return $.spawn("div").append(val);
|
||||
|
@ -54,7 +54,7 @@ const ModalFunctions = {
|
|||
}
|
||||
};
|
||||
|
||||
class ModalProperties {
|
||||
export class ModalProperties {
|
||||
template?: string;
|
||||
header: BodyCreator = () => "HEADER";
|
||||
body: BodyCreator = () => "BODY";
|
||||
|
@ -184,7 +184,7 @@ let _global_modal_count = 0;
|
|||
let _global_modal_last: HTMLElement;
|
||||
let _global_modal_last_time: number;
|
||||
|
||||
class Modal {
|
||||
export class Modal {
|
||||
private _htmlTag: JQuery;
|
||||
properties: ModalProperties;
|
||||
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));
|
||||
}
|
||||
|
||||
class InputModalProperties extends ModalProperties {
|
||||
export class InputModalProperties extends ModalProperties {
|
||||
maxLength?: number;
|
||||
|
||||
field_title?: string;
|
||||
|
@ -310,7 +310,7 @@ class InputModalProperties extends ModalProperties {
|
|||
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.template_properties || (props.template_properties = {});
|
||||
props.template_properties.field_title = props.field_title;
|
||||
|
@ -370,7 +370,7 @@ function createInputModal(headMessage: BodyCreator, question: BodyCreator, valid
|
|||
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.template_properties || (props.template_properties = {})).header_class = "modal-header-error";
|
||||
|
||||
|
@ -382,7 +382,7 @@ function createErrorModal(header: BodyCreator, message: BodyCreator, props: Moda
|
|||
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.template_properties || (props.template_properties = {})).header_class = "modal-header-info";
|
||||
|
||||
|
@ -394,52 +394,6 @@ function createInfoModal(header: BodyCreator, message: BodyCreator, props: 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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
433
shared/js/ui/elements/NetGraph.ts
Normal file
433
shared/js/ui/elements/NetGraph.ts
Normal file
|
@ -0,0 +1,433 @@
|
|||
export type Entry = {
|
||||
timestamp: number;
|
||||
|
||||
upload?: number;
|
||||
download?: number;
|
||||
|
||||
highlight?: boolean;
|
||||
}
|
||||
|
||||
export type Style = {
|
||||
background_color: string;
|
||||
|
||||
separator_color: string;
|
||||
separator_count: number;
|
||||
separator_width: number;
|
||||
|
||||
upload: {
|
||||
fill: string;
|
||||
stroke: string;
|
||||
strike_width: number;
|
||||
},
|
||||
download: {
|
||||
fill: string;
|
||||
stroke: string;
|
||||
strike_width: number;
|
||||
}
|
||||
}
|
||||
|
||||
export type TimeSpan = {
|
||||
origin: {
|
||||
begin: number;
|
||||
end: number;
|
||||
time: number;
|
||||
},
|
||||
target: {
|
||||
begin: number;
|
||||
end: number;
|
||||
time: number;
|
||||
}
|
||||
}
|
||||
|
||||
/* Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves
|
||||
*
|
||||
* Assuming A was the last point in the line plotted and B is the new point,
|
||||
* we draw a curve with control points P and Q as below.
|
||||
*
|
||||
* A---P
|
||||
* |
|
||||
* |
|
||||
* |
|
||||
* Q---B
|
||||
*
|
||||
* Importantly, A and P are at the same y coordinate, as are B and Q. This is
|
||||
* so adjacent curves appear to flow as one.
|
||||
*/
|
||||
export class Graph {
|
||||
private static _loops: (() => any)[] = [];
|
||||
|
||||
readonly canvas: HTMLCanvasElement;
|
||||
public style: Style = {
|
||||
background_color: "#28292b",
|
||||
//background_color: "red",
|
||||
|
||||
separator_color: "#283036",
|
||||
//separator_color: 'blue',
|
||||
separator_count: 10,
|
||||
separator_width: 1,
|
||||
|
||||
|
||||
upload: {
|
||||
fill: "#2d3f4d",
|
||||
stroke: "#336e9f",
|
||||
strike_width: 2,
|
||||
},
|
||||
|
||||
download: {
|
||||
fill: "#532c26",
|
||||
stroke: "#a9321c",
|
||||
strike_width: 2,
|
||||
}
|
||||
};
|
||||
|
||||
private _canvas_context: CanvasRenderingContext2D;
|
||||
private _entries: Entry[] = [];
|
||||
private _entry_max = {
|
||||
upload: 1,
|
||||
download: 1,
|
||||
};
|
||||
private _max_space = 1.12;
|
||||
private _max_gap = 5;
|
||||
private _listener_mouse_move;
|
||||
private _listener_mouse_out;
|
||||
private _animate_loop;
|
||||
|
||||
_time_span: TimeSpan = {
|
||||
origin: {
|
||||
begin: 0,
|
||||
end: 1,
|
||||
time: 0
|
||||
},
|
||||
target: {
|
||||
begin: 0,
|
||||
end: 1,
|
||||
time: 1
|
||||
}
|
||||
};
|
||||
|
||||
private _detailed_shown = false;
|
||||
callback_detailed_info: (upload: number, download: number, timestamp: number, event: MouseEvent) => any;
|
||||
callback_detailed_hide: () => any;
|
||||
|
||||
constructor(canvas: HTMLCanvasElement) {
|
||||
this.canvas = canvas;
|
||||
this._animate_loop = () => this.draw();
|
||||
this.recalculate_cache(); /* initialize cache */
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this._canvas_context = this.canvas.getContext("2d");
|
||||
|
||||
Graph._loops.push(this._animate_loop);
|
||||
if(Graph._loops.length == 1) {
|
||||
const static_loop = () => {
|
||||
Graph._loops.forEach(l => l());
|
||||
if(Graph._loops.length > 0)
|
||||
requestAnimationFrame(static_loop);
|
||||
else
|
||||
console.log("STATIC terminate!");
|
||||
};
|
||||
static_loop();
|
||||
}
|
||||
|
||||
this.canvas.onmousemove = this.on_mouse_move.bind(this);
|
||||
this.canvas.onmouseleave = this.on_mouse_leave.bind(this);
|
||||
}
|
||||
|
||||
terminate() {
|
||||
Graph._loops.remove(this._animate_loop);
|
||||
}
|
||||
|
||||
max_gap_size(value?: number) : number { return typeof(value) === "number" ? (this._max_gap = value) : this._max_gap; }
|
||||
|
||||
private recalculate_cache(time_span?: boolean) {
|
||||
this._entries = this._entries.sort((a, b) => a.timestamp - b.timestamp);
|
||||
this._entry_max = {
|
||||
download: 1,
|
||||
upload: 1
|
||||
};
|
||||
if(time_span) {
|
||||
this._time_span = {
|
||||
origin: {
|
||||
begin: 0,
|
||||
end: 0,
|
||||
time: 0
|
||||
},
|
||||
target: {
|
||||
begin: this._entries.length > 0 ? this._entries[0].timestamp : 0,
|
||||
end: this._entries.length > 0 ? this._entries.last().timestamp : 0,
|
||||
time: 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for(const entry of this._entries) {
|
||||
if(typeof(entry.upload) === "number")
|
||||
this._entry_max.upload = Math.max(this._entry_max.upload, entry.upload);
|
||||
|
||||
if(typeof(entry.download) === "number")
|
||||
this._entry_max.download = Math.max(this._entry_max.download, entry.download);
|
||||
}
|
||||
|
||||
this._entry_max.upload *= this._max_space;
|
||||
this._entry_max.download *= this._max_space;
|
||||
}
|
||||
|
||||
insert_entry(entry: Entry) {
|
||||
if(this._entries.length > 0 && entry.timestamp < this._entries.last().timestamp)
|
||||
throw "invalid timestamp";
|
||||
|
||||
this._entries.push(entry);
|
||||
|
||||
if(typeof(entry.upload) === "number")
|
||||
this._entry_max.upload = Math.max(this._entry_max.upload, entry.upload * this._max_space);
|
||||
|
||||
if(typeof(entry.download) === "number")
|
||||
this._entry_max.download = Math.max(this._entry_max.download, entry.download * this._max_space);
|
||||
}
|
||||
|
||||
insert_entries(entries: Entry[]) {
|
||||
this._entries.push(...entries);
|
||||
this.recalculate_cache();
|
||||
this.cleanup();
|
||||
}
|
||||
|
||||
resize() {
|
||||
this.canvas.style.height = "100%";
|
||||
this.canvas.style.width = "100%";
|
||||
const cstyle = getComputedStyle(this.canvas);
|
||||
|
||||
this.canvas.width = parseInt(cstyle.width);
|
||||
this.canvas.height = parseInt(cstyle.height);
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
const time = this.calculate_time_span();
|
||||
|
||||
let index = 0;
|
||||
for(;index < this._entries.length; index++) {
|
||||
if(this._entries[index].timestamp < time.begin)
|
||||
continue;
|
||||
|
||||
if(index == 0)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
/* keep the last entry as a reference point to the left */
|
||||
if(index > 1) {
|
||||
this._entries.splice(0, index - 1);
|
||||
this.recalculate_cache();
|
||||
}
|
||||
}
|
||||
|
||||
calculate_time_span() : { begin: number; end: number } {
|
||||
const time = Date.now();
|
||||
if(time >= this._time_span.target.time)
|
||||
return this._time_span.target;
|
||||
|
||||
if(time <= this._time_span.origin.time)
|
||||
return this._time_span.origin;
|
||||
|
||||
const ob = this._time_span.origin.begin;
|
||||
const oe = this._time_span.origin.end;
|
||||
const ot = this._time_span.origin.time;
|
||||
|
||||
const tb = this._time_span.target.begin;
|
||||
const te = this._time_span.target.end;
|
||||
const tt = this._time_span.target.time;
|
||||
|
||||
const offset = (time - ot) / (tt - ot);
|
||||
return {
|
||||
begin: ob + (tb - ob) * offset,
|
||||
end: oe + (te - oe) * offset,
|
||||
};
|
||||
}
|
||||
|
||||
draw() {
|
||||
let ctx = this._canvas_context;
|
||||
|
||||
const height = this.canvas.height;
|
||||
const width = this.canvas.width;
|
||||
|
||||
//console.log("Painting on %ox%o", height, width);
|
||||
|
||||
ctx.shadowBlur = 0;
|
||||
ctx.filter = "";
|
||||
ctx.lineCap = "square";
|
||||
|
||||
ctx.fillStyle = this.style.background_color;
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
/* first of all print the separators */
|
||||
{
|
||||
const sw = this.style.separator_width;
|
||||
const swh = this.style.separator_width / 2;
|
||||
|
||||
ctx.lineWidth = sw;
|
||||
ctx.strokeStyle = this.style.separator_color;
|
||||
|
||||
ctx.beginPath();
|
||||
/* horizontal */
|
||||
{
|
||||
const dw = width / this.style.separator_count;
|
||||
let dx = dw / 2;
|
||||
while(dx < width) {
|
||||
ctx.moveTo(Math.floor(dx - swh) + .5, .5);
|
||||
ctx.lineTo(Math.floor(dx - swh) + .5, Math.floor(height) + .5);
|
||||
dx += dw;
|
||||
}
|
||||
}
|
||||
|
||||
/* vertical */
|
||||
{
|
||||
const dh = height / 3; //tree lines (top, center, bottom)
|
||||
|
||||
let dy = dh / 2;
|
||||
while(dy < height) {
|
||||
ctx.moveTo(.5, Math.floor(dy - swh) + .5);
|
||||
ctx.lineTo(Math.floor(width) + .5, Math.floor(dy - swh) + .5);
|
||||
dy += dh;
|
||||
}
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
/* draw the lines */
|
||||
{
|
||||
const t = this.calculate_time_span();
|
||||
const tb = t.begin; /* time begin */
|
||||
const dt = t.end - t.begin; /* delta time */
|
||||
const dtw = width / dt; /* delta time width */
|
||||
|
||||
const draw_graph = (type: "upload" | "download", direction: number, max: number) => {
|
||||
const hy = Math.floor(height / 2); /* half y */
|
||||
const by = hy - direction * this.style[type].strike_width; /* the "base" line */
|
||||
|
||||
const marked_points: ({x: number, y: number})[] = [];
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, by);
|
||||
|
||||
let x, y, lx = 0, ly = by; /* last x, last y */
|
||||
|
||||
const floor = a => a; //Math.floor;
|
||||
for(const entry of this._entries) {
|
||||
x = floor((entry.timestamp - tb) * dtw);
|
||||
if(typeof entry[type] === "number")
|
||||
y = floor(hy - direction * Math.max(hy * (entry[type] / max), this.style[type].strike_width));
|
||||
else
|
||||
y = hy - direction * this.style[type].strike_width;
|
||||
|
||||
if(entry.timestamp < tb) {
|
||||
lx = x;
|
||||
ly = y;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(x - lx > this._max_gap && this._max_gap > 0) {
|
||||
ctx.lineTo(lx, by);
|
||||
ctx.lineTo(x, by);
|
||||
ctx.lineTo(x, y);
|
||||
|
||||
lx = x;
|
||||
ly = y;
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx.bezierCurveTo((x + lx) / 2, ly, (x + lx) / 2, y, x, y);
|
||||
if(entry.highlight)
|
||||
marked_points.push({x: x, y: y});
|
||||
|
||||
lx = x;
|
||||
ly = y;
|
||||
}
|
||||
|
||||
ctx.strokeStyle = this.style[type].stroke;
|
||||
ctx.lineWidth = this.style[type].strike_width;
|
||||
ctx.lineJoin = "miter";
|
||||
ctx.stroke();
|
||||
|
||||
//Close the path and fill
|
||||
ctx.lineTo(width, hy);
|
||||
ctx.lineTo(0, hy);
|
||||
|
||||
ctx.fillStyle = this.style[type].fill;
|
||||
ctx.fill();
|
||||
|
||||
ctx.closePath();
|
||||
|
||||
{
|
||||
ctx.beginPath();
|
||||
const radius = 3;
|
||||
for(const point of marked_points) {
|
||||
ctx.moveTo(point.x, point.y);
|
||||
ctx.ellipse(point.x, point.y, radius, radius, 0, 0, 2 * Math.PI, false);
|
||||
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
|
||||
ctx.closePath();
|
||||
}
|
||||
};
|
||||
|
||||
const shared_max = Math.max(this._entry_max.upload, this._entry_max.download);
|
||||
draw_graph("upload", 1, shared_max);
|
||||
draw_graph("download", -1, shared_max);
|
||||
}
|
||||
}
|
||||
|
||||
private on_mouse_move(event: MouseEvent) {
|
||||
const offset = event.offsetX;
|
||||
const max_offset = this.canvas.width;
|
||||
|
||||
if(offset < 0) return;
|
||||
if(offset > max_offset) return;
|
||||
|
||||
const time_span = this.calculate_time_span();
|
||||
const time = time_span.begin + (time_span.end - time_span.begin) * (offset / max_offset);
|
||||
let index = 0;
|
||||
for(;index < this._entries.length; index++) {
|
||||
if(this._entries[index].timestamp > time)
|
||||
break;
|
||||
}
|
||||
|
||||
const entry_before = this._entries[index - 1]; /* In JS negative array access is allowed and returns undefined */
|
||||
const entry_next = this._entries[index]; /* In JS negative array access is allowed and returns undefined */
|
||||
let entry: Entry;
|
||||
if(!entry_before || !entry_next) {
|
||||
entry = entry_before || entry_next;
|
||||
} else {
|
||||
const dn = entry_next.timestamp - time;
|
||||
const db = time - entry_before.timestamp;
|
||||
if(dn > db)
|
||||
entry = entry_before;
|
||||
else
|
||||
entry = entry_next;
|
||||
}
|
||||
|
||||
if(!entry) {
|
||||
this.on_mouse_leave(event);
|
||||
} else {
|
||||
this._entries.forEach(e => e.highlight = false);
|
||||
this._detailed_shown = true;
|
||||
entry.highlight = true;
|
||||
|
||||
if(this.callback_detailed_info)
|
||||
this.callback_detailed_info(entry.upload, entry.download, entry.timestamp, event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private on_mouse_leave(event: MouseEvent) {
|
||||
if(!this._detailed_shown) return;
|
||||
this._detailed_shown = false;
|
||||
|
||||
this._entries.forEach(e => e.highlight = false);
|
||||
if(this.callback_detailed_hide)
|
||||
this.callback_detailed_hide();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
interface SliderOptions {
|
||||
import * as tooltip from "tc-shared/ui/elements/Tooltip";
|
||||
|
||||
export interface SliderOptions {
|
||||
min_value?: number;
|
||||
max_value?: number;
|
||||
initial_value?: number;
|
||||
|
@ -8,11 +10,11 @@ interface SliderOptions {
|
|||
value_field?: JQuery | JQuery[];
|
||||
}
|
||||
|
||||
interface Slider {
|
||||
export interface Slider {
|
||||
value(value?: number) : number;
|
||||
}
|
||||
|
||||
function sliderfy(slider: JQuery, options?: SliderOptions) : Slider {
|
||||
export function sliderfy(slider: JQuery, options?: SliderOptions) : Slider {
|
||||
options = Object.assign( {
|
||||
initial_value: 0,
|
||||
min_value: 0,
|
||||
|
@ -30,7 +32,7 @@ function sliderfy(slider: JQuery, options?: SliderOptions) : Slider {
|
|||
throw "invalid step size";
|
||||
|
||||
|
||||
const tool = tooltip(slider); /* add the tooltip functionality */
|
||||
const tool = tooltip.initialize(slider); /* add the tooltip functionality */
|
||||
const filler = slider.find(".filler");
|
||||
const thumb = slider.find(".thumb");
|
||||
const tooltip_text = slider.find(".tooltip a");
|
|
@ -1,12 +1,11 @@
|
|||
/// <reference path="../../i18n/localize.ts" />
|
||||
|
||||
declare global {
|
||||
interface JQuery<TElement = HTMLElement> {
|
||||
asTabWidget(copy?: boolean) : JQuery<TElement>;
|
||||
tabify(copy?: boolean) : this;
|
||||
|
||||
changeElementType(type: string) : JQuery<TElement>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(typeof (customElements) !== "undefined") {
|
||||
try {
|
||||
|
@ -26,7 +25,7 @@ if(typeof (customElements) !== "undefined") {
|
|||
console.warn(tr("Could not defied tab customElements!"));
|
||||
}
|
||||
|
||||
var TabFunctions = {
|
||||
export const TabFunctions = {
|
||||
tabify(template: JQuery, copy: boolean = true) : JQuery {
|
||||
console.log("Tabify: copy=" + copy);
|
||||
console.log(template);
|
79
shared/js/ui/elements/Tooltip.ts
Normal file
79
shared/js/ui/elements/Tooltip.ts
Normal file
|
@ -0,0 +1,79 @@
|
|||
let _global_tooltip: JQuery;
|
||||
export type Handle = {
|
||||
show();
|
||||
is_shown();
|
||||
hide();
|
||||
update();
|
||||
}
|
||||
export function initialize(entry: JQuery, callbacks?: {
|
||||
on_show?(tag: JQuery),
|
||||
on_hide?(tag: JQuery)
|
||||
}) : Handle {
|
||||
if(!callbacks) callbacks = {};
|
||||
|
||||
let _show;
|
||||
let _hide;
|
||||
let _shown;
|
||||
let _update;
|
||||
|
||||
entry.find(".container-tooltip").each((index, _node) => {
|
||||
const node = $(_node) as JQuery;
|
||||
const node_content = node.find(".tooltip");
|
||||
|
||||
let _force_show = false, _flag_shown = false;
|
||||
|
||||
const mouseenter = (event?) => {
|
||||
const bounds = node[0].getBoundingClientRect();
|
||||
|
||||
if(!_global_tooltip) {
|
||||
_global_tooltip = $("#global-tooltip");
|
||||
}
|
||||
|
||||
_global_tooltip[0].style.left = (bounds.left + bounds.width / 2) + "px";
|
||||
_global_tooltip[0].style.top = bounds.top + "px";
|
||||
_global_tooltip[0].classList.add("shown");
|
||||
|
||||
_global_tooltip[0].innerHTML = node_content[0].innerHTML;
|
||||
callbacks.on_show && callbacks.on_show(_global_tooltip);
|
||||
_flag_shown = _flag_shown || !!event; /* if event is undefined then it has been triggered by hand */
|
||||
};
|
||||
|
||||
const mouseexit = () => {
|
||||
if(_global_tooltip) {
|
||||
if(!_force_show) {
|
||||
callbacks.on_hide && callbacks.on_hide(_global_tooltip);
|
||||
_global_tooltip[0].classList.remove("shown");
|
||||
}
|
||||
_flag_shown = false;
|
||||
}
|
||||
};
|
||||
|
||||
_node.addEventListener("mouseenter", mouseenter);
|
||||
|
||||
_node.addEventListener("mouseleave", mouseexit);
|
||||
|
||||
_show = () => {
|
||||
_force_show = true;
|
||||
mouseenter();
|
||||
};
|
||||
|
||||
_hide = () => {
|
||||
_force_show = false;
|
||||
if(!_flag_shown)
|
||||
mouseexit();
|
||||
};
|
||||
|
||||
_update = () => {
|
||||
if(_flag_shown || _force_show)
|
||||
mouseenter();
|
||||
};
|
||||
|
||||
_shown = () => _flag_shown || _force_show;
|
||||
});
|
||||
return {
|
||||
hide: _hide || (() => {}),
|
||||
show: _show || (() => {}),
|
||||
is_shown: _shown || (() => false),
|
||||
update: _update || (() => {})
|
||||
};
|
||||
}
|
|
@ -1,435 +0,0 @@
|
|||
namespace net.graph {
|
||||
export type Entry = {
|
||||
timestamp: number;
|
||||
|
||||
upload?: number;
|
||||
download?: number;
|
||||
|
||||
highlight?: boolean;
|
||||
}
|
||||
|
||||
export type Style = {
|
||||
background_color: string;
|
||||
|
||||
separator_color: string;
|
||||
separator_count: number;
|
||||
separator_width: number;
|
||||
|
||||
upload: {
|
||||
fill: string;
|
||||
stroke: string;
|
||||
strike_width: number;
|
||||
},
|
||||
download: {
|
||||
fill: string;
|
||||
stroke: string;
|
||||
strike_width: number;
|
||||
}
|
||||
}
|
||||
|
||||
export type TimeSpan = {
|
||||
origin: {
|
||||
begin: number;
|
||||
end: number;
|
||||
time: number;
|
||||
},
|
||||
target: {
|
||||
begin: number;
|
||||
end: number;
|
||||
time: number;
|
||||
}
|
||||
}
|
||||
|
||||
/* Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves
|
||||
*
|
||||
* Assuming A was the last point in the line plotted and B is the new point,
|
||||
* we draw a curve with control points P and Q as below.
|
||||
*
|
||||
* A---P
|
||||
* |
|
||||
* |
|
||||
* |
|
||||
* Q---B
|
||||
*
|
||||
* Importantly, A and P are at the same y coordinate, as are B and Q. This is
|
||||
* so adjacent curves appear to flow as one.
|
||||
*/
|
||||
export class Graph {
|
||||
private static _loops: (() => any)[] = [];
|
||||
|
||||
readonly canvas: HTMLCanvasElement;
|
||||
public style: Style = {
|
||||
background_color: "#28292b",
|
||||
//background_color: "red",
|
||||
|
||||
separator_color: "#283036",
|
||||
//separator_color: 'blue',
|
||||
separator_count: 10,
|
||||
separator_width: 1,
|
||||
|
||||
|
||||
upload: {
|
||||
fill: "#2d3f4d",
|
||||
stroke: "#336e9f",
|
||||
strike_width: 2,
|
||||
},
|
||||
|
||||
download: {
|
||||
fill: "#532c26",
|
||||
stroke: "#a9321c",
|
||||
strike_width: 2,
|
||||
}
|
||||
};
|
||||
|
||||
private _canvas_context: CanvasRenderingContext2D;
|
||||
private _entries: Entry[] = [];
|
||||
private _entry_max = {
|
||||
upload: 1,
|
||||
download: 1,
|
||||
};
|
||||
private _max_space = 1.12;
|
||||
private _max_gap = 5;
|
||||
private _listener_mouse_move;
|
||||
private _listener_mouse_out;
|
||||
private _animate_loop;
|
||||
|
||||
_time_span: TimeSpan = {
|
||||
origin: {
|
||||
begin: 0,
|
||||
end: 1,
|
||||
time: 0
|
||||
},
|
||||
target: {
|
||||
begin: 0,
|
||||
end: 1,
|
||||
time: 1
|
||||
}
|
||||
};
|
||||
|
||||
private _detailed_shown = false;
|
||||
callback_detailed_info: (upload: number, download: number, timestamp: number, event: MouseEvent) => any;
|
||||
callback_detailed_hide: () => any;
|
||||
|
||||
constructor(canvas: HTMLCanvasElement) {
|
||||
this.canvas = canvas;
|
||||
this._animate_loop = () => this.draw();
|
||||
this.recalculate_cache(); /* initialize cache */
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this._canvas_context = this.canvas.getContext("2d");
|
||||
|
||||
Graph._loops.push(this._animate_loop);
|
||||
if(Graph._loops.length == 1) {
|
||||
const static_loop = () => {
|
||||
Graph._loops.forEach(l => l());
|
||||
if(Graph._loops.length > 0)
|
||||
requestAnimationFrame(static_loop);
|
||||
else
|
||||
console.log("STATIC terminate!");
|
||||
};
|
||||
static_loop();
|
||||
}
|
||||
|
||||
this.canvas.onmousemove = this.on_mouse_move.bind(this);
|
||||
this.canvas.onmouseleave = this.on_mouse_leave.bind(this);
|
||||
}
|
||||
|
||||
terminate() {
|
||||
Graph._loops.remove(this._animate_loop);
|
||||
}
|
||||
|
||||
max_gap_size(value?: number) : number { return typeof(value) === "number" ? (this._max_gap = value) : this._max_gap; }
|
||||
|
||||
private recalculate_cache(time_span?: boolean) {
|
||||
this._entries = this._entries.sort((a, b) => a.timestamp - b.timestamp);
|
||||
this._entry_max = {
|
||||
download: 1,
|
||||
upload: 1
|
||||
};
|
||||
if(time_span) {
|
||||
this._time_span = {
|
||||
origin: {
|
||||
begin: 0,
|
||||
end: 0,
|
||||
time: 0
|
||||
},
|
||||
target: {
|
||||
begin: this._entries.length > 0 ? this._entries[0].timestamp : 0,
|
||||
end: this._entries.length > 0 ? this._entries.last().timestamp : 0,
|
||||
time: 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for(const entry of this._entries) {
|
||||
if(typeof(entry.upload) === "number")
|
||||
this._entry_max.upload = Math.max(this._entry_max.upload, entry.upload);
|
||||
|
||||
if(typeof(entry.download) === "number")
|
||||
this._entry_max.download = Math.max(this._entry_max.download, entry.download);
|
||||
}
|
||||
|
||||
this._entry_max.upload *= this._max_space;
|
||||
this._entry_max.download *= this._max_space;
|
||||
}
|
||||
|
||||
insert_entry(entry: Entry) {
|
||||
if(this._entries.length > 0 && entry.timestamp < this._entries.last().timestamp)
|
||||
throw "invalid timestamp";
|
||||
|
||||
this._entries.push(entry);
|
||||
|
||||
if(typeof(entry.upload) === "number")
|
||||
this._entry_max.upload = Math.max(this._entry_max.upload, entry.upload * this._max_space);
|
||||
|
||||
if(typeof(entry.download) === "number")
|
||||
this._entry_max.download = Math.max(this._entry_max.download, entry.download * this._max_space);
|
||||
}
|
||||
|
||||
insert_entries(entries: Entry[]) {
|
||||
this._entries.push(...entries);
|
||||
this.recalculate_cache();
|
||||
this.cleanup();
|
||||
}
|
||||
|
||||
resize() {
|
||||
this.canvas.style.height = "100%";
|
||||
this.canvas.style.width = "100%";
|
||||
const cstyle = getComputedStyle(this.canvas);
|
||||
|
||||
this.canvas.width = parseInt(cstyle.width);
|
||||
this.canvas.height = parseInt(cstyle.height);
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
const time = this.calculate_time_span();
|
||||
|
||||
let index = 0;
|
||||
for(;index < this._entries.length; index++) {
|
||||
if(this._entries[index].timestamp < time.begin)
|
||||
continue;
|
||||
|
||||
if(index == 0)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
/* keep the last entry as a reference point to the left */
|
||||
if(index > 1) {
|
||||
this._entries.splice(0, index - 1);
|
||||
this.recalculate_cache();
|
||||
}
|
||||
}
|
||||
|
||||
calculate_time_span() : { begin: number; end: number } {
|
||||
const time = Date.now();
|
||||
if(time >= this._time_span.target.time)
|
||||
return this._time_span.target;
|
||||
|
||||
if(time <= this._time_span.origin.time)
|
||||
return this._time_span.origin;
|
||||
|
||||
const ob = this._time_span.origin.begin;
|
||||
const oe = this._time_span.origin.end;
|
||||
const ot = this._time_span.origin.time;
|
||||
|
||||
const tb = this._time_span.target.begin;
|
||||
const te = this._time_span.target.end;
|
||||
const tt = this._time_span.target.time;
|
||||
|
||||
const offset = (time - ot) / (tt - ot);
|
||||
return {
|
||||
begin: ob + (tb - ob) * offset,
|
||||
end: oe + (te - oe) * offset,
|
||||
};
|
||||
}
|
||||
|
||||
draw() {
|
||||
let ctx = this._canvas_context;
|
||||
|
||||
const height = this.canvas.height;
|
||||
const width = this.canvas.width;
|
||||
|
||||
//console.log("Painting on %ox%o", height, width);
|
||||
|
||||
ctx.shadowBlur = 0;
|
||||
ctx.filter = "";
|
||||
ctx.lineCap = "square";
|
||||
|
||||
ctx.fillStyle = this.style.background_color;
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
/* first of all print the separators */
|
||||
{
|
||||
const sw = this.style.separator_width;
|
||||
const swh = this.style.separator_width / 2;
|
||||
|
||||
ctx.lineWidth = sw;
|
||||
ctx.strokeStyle = this.style.separator_color;
|
||||
|
||||
ctx.beginPath();
|
||||
/* horizontal */
|
||||
{
|
||||
const dw = width / this.style.separator_count;
|
||||
let dx = dw / 2;
|
||||
while(dx < width) {
|
||||
ctx.moveTo(Math.floor(dx - swh) + .5, .5);
|
||||
ctx.lineTo(Math.floor(dx - swh) + .5, Math.floor(height) + .5);
|
||||
dx += dw;
|
||||
}
|
||||
}
|
||||
|
||||
/* vertical */
|
||||
{
|
||||
const dh = height / 3; //tree lines (top, center, bottom)
|
||||
|
||||
let dy = dh / 2;
|
||||
while(dy < height) {
|
||||
ctx.moveTo(.5, Math.floor(dy - swh) + .5);
|
||||
ctx.lineTo(Math.floor(width) + .5, Math.floor(dy - swh) + .5);
|
||||
dy += dh;
|
||||
}
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
/* draw the lines */
|
||||
{
|
||||
const t = this.calculate_time_span();
|
||||
const tb = t.begin; /* time begin */
|
||||
const dt = t.end - t.begin; /* delta time */
|
||||
const dtw = width / dt; /* delta time width */
|
||||
|
||||
const draw_graph = (type: "upload" | "download", direction: number, max: number) => {
|
||||
const hy = Math.floor(height / 2); /* half y */
|
||||
const by = hy - direction * this.style[type].strike_width; /* the "base" line */
|
||||
|
||||
const marked_points: ({x: number, y: number})[] = [];
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, by);
|
||||
|
||||
let x, y, lx = 0, ly = by; /* last x, last y */
|
||||
|
||||
const floor = a => a; //Math.floor;
|
||||
for(const entry of this._entries) {
|
||||
x = floor((entry.timestamp - tb) * dtw);
|
||||
if(typeof entry[type] === "number")
|
||||
y = floor(hy - direction * Math.max(hy * (entry[type] / max), this.style[type].strike_width));
|
||||
else
|
||||
y = hy - direction * this.style[type].strike_width;
|
||||
|
||||
if(entry.timestamp < tb) {
|
||||
lx = x;
|
||||
ly = y;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(x - lx > this._max_gap && this._max_gap > 0) {
|
||||
ctx.lineTo(lx, by);
|
||||
ctx.lineTo(x, by);
|
||||
ctx.lineTo(x, y);
|
||||
|
||||
lx = x;
|
||||
ly = y;
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx.bezierCurveTo((x + lx) / 2, ly, (x + lx) / 2, y, x, y);
|
||||
if(entry.highlight)
|
||||
marked_points.push({x: x, y: y});
|
||||
|
||||
lx = x;
|
||||
ly = y;
|
||||
}
|
||||
|
||||
ctx.strokeStyle = this.style[type].stroke;
|
||||
ctx.lineWidth = this.style[type].strike_width;
|
||||
ctx.lineJoin = "miter";
|
||||
ctx.stroke();
|
||||
|
||||
//Close the path and fill
|
||||
ctx.lineTo(width, hy);
|
||||
ctx.lineTo(0, hy);
|
||||
|
||||
ctx.fillStyle = this.style[type].fill;
|
||||
ctx.fill();
|
||||
|
||||
ctx.closePath();
|
||||
|
||||
{
|
||||
ctx.beginPath();
|
||||
const radius = 3;
|
||||
for(const point of marked_points) {
|
||||
ctx.moveTo(point.x, point.y);
|
||||
ctx.ellipse(point.x, point.y, radius, radius, 0, 0, 2 * Math.PI, false);
|
||||
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
|
||||
ctx.closePath();
|
||||
}
|
||||
};
|
||||
|
||||
const shared_max = Math.max(this._entry_max.upload, this._entry_max.download);
|
||||
draw_graph("upload", 1, shared_max);
|
||||
draw_graph("download", -1, shared_max);
|
||||
}
|
||||
}
|
||||
|
||||
private on_mouse_move(event: MouseEvent) {
|
||||
const offset = event.offsetX;
|
||||
const max_offset = this.canvas.width;
|
||||
|
||||
if(offset < 0) return;
|
||||
if(offset > max_offset) return;
|
||||
|
||||
const time_span = this.calculate_time_span();
|
||||
const time = time_span.begin + (time_span.end - time_span.begin) * (offset / max_offset);
|
||||
let index = 0;
|
||||
for(;index < this._entries.length; index++) {
|
||||
if(this._entries[index].timestamp > time)
|
||||
break;
|
||||
}
|
||||
|
||||
const entry_before = this._entries[index - 1]; /* In JS negative array access is allowed and returns undefined */
|
||||
const entry_next = this._entries[index]; /* In JS negative array access is allowed and returns undefined */
|
||||
let entry: Entry;
|
||||
if(!entry_before || !entry_next) {
|
||||
entry = entry_before || entry_next;
|
||||
} else {
|
||||
const dn = entry_next.timestamp - time;
|
||||
const db = time - entry_before.timestamp;
|
||||
if(dn > db)
|
||||
entry = entry_before;
|
||||
else
|
||||
entry = entry_next;
|
||||
}
|
||||
|
||||
if(!entry) {
|
||||
this.on_mouse_leave(event);
|
||||
} else {
|
||||
this._entries.forEach(e => e.highlight = false);
|
||||
this._detailed_shown = true;
|
||||
entry.highlight = true;
|
||||
|
||||
if(this.callback_detailed_info)
|
||||
this.callback_detailed_info(entry.upload, entry.download, entry.timestamp, event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private on_mouse_leave(event: MouseEvent) {
|
||||
if(!this._detailed_shown) return;
|
||||
this._detailed_shown = false;
|
||||
|
||||
this._entries.forEach(e => e.highlight = false);
|
||||
if(this.callback_detailed_hide)
|
||||
this.callback_detailed_hide();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
function tooltip(entry: JQuery) {
|
||||
return tooltip.initialize(entry);
|
||||
}
|
||||
|
||||
namespace tooltip {
|
||||
let _global_tooltip: JQuery;
|
||||
export type Handle = {
|
||||
show();
|
||||
is_shown();
|
||||
hide();
|
||||
update();
|
||||
}
|
||||
export function initialize(entry: JQuery, callbacks?: {
|
||||
on_show?(tag: JQuery),
|
||||
on_hide?(tag: JQuery)
|
||||
}) : Handle {
|
||||
if(!callbacks) callbacks = {};
|
||||
|
||||
let _show;
|
||||
let _hide;
|
||||
let _shown;
|
||||
let _update;
|
||||
|
||||
entry.find(".container-tooltip").each((index, _node) => {
|
||||
const node = $(_node) as JQuery;
|
||||
const node_content = node.find(".tooltip");
|
||||
|
||||
let _force_show = false, _flag_shown = false;
|
||||
|
||||
const mouseenter = (event?) => {
|
||||
const bounds = node[0].getBoundingClientRect();
|
||||
|
||||
if(!_global_tooltip) {
|
||||
_global_tooltip = $("#global-tooltip");
|
||||
}
|
||||
|
||||
_global_tooltip[0].style.left = (bounds.left + bounds.width / 2) + "px";
|
||||
_global_tooltip[0].style.top = bounds.top + "px";
|
||||
_global_tooltip[0].classList.add("shown");
|
||||
|
||||
_global_tooltip[0].innerHTML = node_content[0].innerHTML;
|
||||
callbacks.on_show && callbacks.on_show(_global_tooltip);
|
||||
_flag_shown = _flag_shown || !!event; /* if event is undefined then it has been triggered by hand */
|
||||
};
|
||||
|
||||
const mouseexit = () => {
|
||||
if(_global_tooltip) {
|
||||
if(!_force_show) {
|
||||
callbacks.on_hide && callbacks.on_hide(_global_tooltip);
|
||||
_global_tooltip[0].classList.remove("shown");
|
||||
}
|
||||
_flag_shown = false;
|
||||
}
|
||||
};
|
||||
|
||||
_node.addEventListener("mouseenter", mouseenter);
|
||||
|
||||
_node.addEventListener("mouseleave", mouseexit);
|
||||
|
||||
_show = () => {
|
||||
_force_show = true;
|
||||
mouseenter();
|
||||
};
|
||||
|
||||
_hide = () => {
|
||||
_force_show = false;
|
||||
if(!_flag_shown)
|
||||
mouseexit();
|
||||
};
|
||||
|
||||
_update = () => {
|
||||
if(_flag_shown || _force_show)
|
||||
mouseenter();
|
||||
};
|
||||
|
||||
_shown = () => _flag_shown || _force_show;
|
||||
});
|
||||
return {
|
||||
hide: _hide || (() => {}),
|
||||
show: _show || (() => {}),
|
||||
is_shown: _shown || (() => false),
|
||||
update: _update || (() => {})
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,24 +1,40 @@
|
|||
/// <reference path="../../ConnectionHandler.ts" />
|
||||
/// <reference path="../modal/ModalSettings.ts" />
|
||||
/// <reference path="../modal/ModalBanList.ts" />
|
||||
/*
|
||||
client_output_hardware Value: '1'
|
||||
client_output_muted Value: '0'
|
||||
client_outputonly_muted Value: '0'
|
||||
import {ConnectionHandler, DisconnectReason} from "tc-shared/ConnectionHandler";
|
||||
import {createErrorModal, createInfoModal, createInputModal} from "tc-shared/ui/elements/Modal";
|
||||
import {manager, Sound} from "tc-shared/sound/Sounds";
|
||||
import {default_recorder} from "tc-shared/voice/RecorderProfile";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import {spawnSettingsModal} from "tc-shared/ui/modal/ModalSettings";
|
||||
import {spawnConnectModal} from "tc-shared/ui/modal/ModalConnect";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import PermissionType from "tc-shared/permission/PermissionType";
|
||||
import {spawnPermissionEdit} from "tc-shared/ui/modal/permission/ModalPermissionEdit";
|
||||
import {openBanList} from "tc-shared/ui/modal/ModalBanList";
|
||||
import {
|
||||
add_current_server,
|
||||
Bookmark,
|
||||
bookmarks,
|
||||
BookmarkType,
|
||||
boorkmak_connect,
|
||||
DirectoryBookmark
|
||||
} from "tc-shared/bookmarks";
|
||||
import {IconManager} from "tc-shared/FileManager";
|
||||
import {spawnBookmarkModal} from "tc-shared/ui/modal/ModalBookmarks";
|
||||
import {spawnQueryCreate} from "tc-shared/ui/modal/ModalQuery";
|
||||
import {spawnQueryManage} from "tc-shared/ui/modal/ModalQueryManage";
|
||||
import {spawnPlaylistManage} from "tc-shared/ui/modal/ModalPlaylistList";
|
||||
import * as contextmenu from "tc-shared/ui/elements/ContextMenu";
|
||||
import {server_connections} from "tc-shared/ui/frames/connection_handlers";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import * as slog from "tc-shared/ui/frames/server_log";
|
||||
import * as top_menu from "./MenuBar";
|
||||
|
||||
client_input_hardware Value: '1'
|
||||
client_input_muted Value: '0'
|
||||
export let control_bar: ControlBar; /* global variable to access the control bar */
|
||||
export function set_control_bar(bar: ControlBar) { control_bar = bar; }
|
||||
|
||||
client_away Value: '0'
|
||||
client_away_message Value: ''
|
||||
*/
|
||||
|
||||
let control_bar: ControlBar; /* global variable to access the control bar */
|
||||
|
||||
type MicrophoneState = "disabled" | "muted" | "enabled";
|
||||
type HeadphoneState = "muted" | "enabled";
|
||||
type AwayState = "away-global" | "away" | "online";
|
||||
class ControlBar {
|
||||
export type MicrophoneState = "disabled" | "muted" | "enabled";
|
||||
export type HeadphoneState = "muted" | "enabled";
|
||||
export type AwayState = "away-global" | "away" | "online";
|
||||
export class ControlBar {
|
||||
private _button_away_active: AwayState;
|
||||
private _button_microphone: MicrophoneState;
|
||||
private _button_speakers: HeadphoneState;
|
||||
|
@ -363,10 +379,10 @@ class ControlBar {
|
|||
private on_toggle_microphone() {
|
||||
if(this._button_microphone === "disabled" || this._button_microphone === "muted") {
|
||||
this.button_microphone = "enabled";
|
||||
sound.manager.play(Sound.MICROPHONE_ACTIVATED);
|
||||
manager.play(Sound.MICROPHONE_ACTIVATED);
|
||||
} else {
|
||||
this.button_microphone = "muted";
|
||||
sound.manager.play(Sound.MICROPHONE_MUTED);
|
||||
manager.play(Sound.MICROPHONE_MUTED);
|
||||
}
|
||||
|
||||
if(this.connection_handler) {
|
||||
|
@ -384,10 +400,10 @@ class ControlBar {
|
|||
private on_toggle_sound() {
|
||||
if(this._button_speakers === "muted") {
|
||||
this.button_speaker = "enabled";
|
||||
sound.manager.play(Sound.SOUND_ACTIVATED);
|
||||
manager.play(Sound.SOUND_ACTIVATED);
|
||||
} else {
|
||||
this.button_speaker = "muted";
|
||||
sound.manager.play(Sound.SOUND_MUTED);
|
||||
manager.play(Sound.SOUND_MUTED);
|
||||
}
|
||||
|
||||
if(this.connection_handler) {
|
||||
|
@ -421,20 +437,20 @@ class ControlBar {
|
|||
}
|
||||
|
||||
private on_open_settings() {
|
||||
Modals.spawnSettingsModal();
|
||||
spawnSettingsModal();
|
||||
}
|
||||
|
||||
private on_open_connect() {
|
||||
if(this.connection_handler)
|
||||
this.connection_handler.cancel_reconnect(true);
|
||||
Modals.spawnConnectModal({}, {
|
||||
spawnConnectModal({}, {
|
||||
url: "ts.TeaSpeak.de",
|
||||
enforce: false
|
||||
});
|
||||
}
|
||||
|
||||
private on_open_connect_new_tab() {
|
||||
Modals.spawnConnectModal({
|
||||
spawnConnectModal({
|
||||
default_connect_new_tab: true
|
||||
}, {
|
||||
url: "ts.TeaSpeak.de",
|
||||
|
@ -470,7 +486,7 @@ class ControlBar {
|
|||
this.connection_handler.handleDisconnect(DisconnectReason.REQUESTED); //TODO message?
|
||||
this.update_connection_state();
|
||||
this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED);
|
||||
this.connection_handler.log.log(log.server.Type.DISCONNECTED, {});
|
||||
this.connection_handler.log.log(slog.Type.DISCONNECTED, {});
|
||||
}
|
||||
|
||||
private on_token_use() {
|
||||
|
@ -483,7 +499,7 @@ class ControlBar {
|
|||
createInfoModal(tr("Use token"), tr("Toke successfully used!")).open();
|
||||
}).catch(error => {
|
||||
//TODO tr
|
||||
createErrorModal(tr("Use token"), MessageHelper.formatMessage(tr("Failed to use token: {}"), error instanceof CommandResult ? error.message : error)).open();
|
||||
createErrorModal(tr("Use token"), formatMessage(tr("Failed to use token: {}"), error instanceof CommandResult ? error.message : error)).open();
|
||||
});
|
||||
}).open();
|
||||
}
|
||||
|
@ -497,7 +513,7 @@ class ControlBar {
|
|||
button.addClass("activated");
|
||||
setTimeout(() => {
|
||||
if(this.connection_handler)
|
||||
Modals.spawnPermissionEdit(this.connection_handler).open();
|
||||
spawnPermissionEdit(this.connection_handler).open();
|
||||
else
|
||||
createErrorModal(tr("You have to be connected"), tr("You have to be connected!")).open();
|
||||
button.removeClass("activated");
|
||||
|
@ -508,7 +524,7 @@ class ControlBar {
|
|||
if(!this.connection_handler.serverConnection) return;
|
||||
|
||||
if(this.connection_handler.permissions.neededPermission(PermissionType.B_CLIENT_BAN_LIST).granted(1)) {
|
||||
Modals.openBanList(this.connection_handler);
|
||||
openBanList(this.connection_handler);
|
||||
} else {
|
||||
createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to view the ban list")).open();
|
||||
this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
|
||||
|
@ -516,7 +532,7 @@ class ControlBar {
|
|||
}
|
||||
|
||||
private on_bookmark_server_add() {
|
||||
bookmarks.add_current_server();
|
||||
add_current_server();
|
||||
}
|
||||
|
||||
update_bookmark_status() {
|
||||
|
@ -530,13 +546,13 @@ class ControlBar {
|
|||
let tag_bookmark = this.htmlTag.find(".btn_bookmark > .dropdown");
|
||||
tag_bookmark.find(".bookmark, .directory").remove();
|
||||
|
||||
const build_entry = (bookmark: bookmarks.DirectoryBookmark | bookmarks.Bookmark) => {
|
||||
if(bookmark.type == bookmarks.BookmarkType.ENTRY) {
|
||||
const mark = <bookmarks.Bookmark>bookmark;
|
||||
const build_entry = (bookmark: DirectoryBookmark | Bookmark) => {
|
||||
if(bookmark.type == BookmarkType.ENTRY) {
|
||||
const mark = <Bookmark>bookmark;
|
||||
|
||||
const bookmark_connect = (new_tab: boolean) => {
|
||||
this.htmlTag.find(".btn_bookmark").find(".dropdown").removeClass("displayed"); //FIXME Not working
|
||||
bookmarks.boorkmak_connect(mark, new_tab);
|
||||
boorkmak_connect(mark, new_tab);
|
||||
};
|
||||
|
||||
return $.spawn("div")
|
||||
|
@ -580,7 +596,7 @@ class ControlBar {
|
|||
})
|
||||
)
|
||||
} else {
|
||||
const mark = <bookmarks.DirectoryBookmark>bookmark;
|
||||
const mark = <DirectoryBookmark>bookmark;
|
||||
const container = $.spawn("div").addClass("sub-menu dropdown");
|
||||
|
||||
const result = $.spawn("div")
|
||||
|
@ -609,19 +625,19 @@ class ControlBar {
|
|||
}
|
||||
};
|
||||
|
||||
for(const bookmark of bookmarks.bookmarks().content) {
|
||||
for(const bookmark of bookmarks().content) {
|
||||
const entry = build_entry(bookmark);
|
||||
tag_bookmark.append(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private on_bookmark_manage() {
|
||||
Modals.spawnBookmarkModal();
|
||||
spawnBookmarkModal();
|
||||
}
|
||||
|
||||
private on_open_query_create() {
|
||||
if(this.connection_handler.permissions.neededPermission(PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN).granted(1)) {
|
||||
Modals.spawnQueryCreate(this.connection_handler);
|
||||
spawnQueryCreate(this.connection_handler);
|
||||
} else {
|
||||
createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to create a server query login")).open();
|
||||
this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
|
||||
|
@ -630,7 +646,7 @@ class ControlBar {
|
|||
|
||||
private on_open_query_manage() {
|
||||
if(this.connection_handler && this.connection_handler.connected) {
|
||||
Modals.spawnQueryManage(this.connection_handler);
|
||||
spawnQueryManage(this.connection_handler);
|
||||
} else {
|
||||
createErrorModal(tr("You have to be connected"), tr("You have to be connected!")).open();
|
||||
}
|
||||
|
@ -638,7 +654,7 @@ class ControlBar {
|
|||
|
||||
private on_open_playlist_manage() {
|
||||
if(this.connection_handler && this.connection_handler.connected) {
|
||||
Modals.spawnPlaylistManage(this.connection_handler);
|
||||
spawnPlaylistManage(this.connection_handler);
|
||||
} else {
|
||||
createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,31 @@
|
|||
namespace top_menu {
|
||||
import {Icon, IconManager} from "tc-shared/FileManager";
|
||||
import {spawnBookmarkModal} from "tc-shared/ui/modal/ModalBookmarks";
|
||||
import {
|
||||
add_current_server,
|
||||
Bookmark,
|
||||
bookmarks,
|
||||
BookmarkType,
|
||||
boorkmak_connect,
|
||||
DirectoryBookmark
|
||||
} from "tc-shared/bookmarks";
|
||||
import {ConnectionHandler, DisconnectReason} from "tc-shared/ConnectionHandler";
|
||||
import {Sound} from "tc-shared/sound/Sounds";
|
||||
import {spawnConnectModal} from "tc-shared/ui/modal/ModalConnect";
|
||||
import {spawnPermissionEdit} from "tc-shared/ui/modal/permission/ModalPermissionEdit";
|
||||
import {createErrorModal, createInfoModal, createInputModal} from "tc-shared/ui/elements/Modal";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import PermissionType from "tc-shared/permission/PermissionType";
|
||||
import {openBanList} from "tc-shared/ui/modal/ModalBanList";
|
||||
import {spawnQueryManage} from "tc-shared/ui/modal/ModalQueryManage";
|
||||
import {spawnQueryCreate} from "tc-shared/ui/modal/ModalQuery";
|
||||
import {spawnSettingsModal} from "tc-shared/ui/modal/ModalSettings";
|
||||
import {spawnAbout} from "tc-shared/ui/modal/ModalAbout";
|
||||
import {server_connections} from "tc-shared/ui/frames/connection_handlers";
|
||||
import {control_bar} from "tc-shared/ui/frames/ControlBar";
|
||||
import * as loader from "tc-loader";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import * as slog from "tc-shared/ui/frames/server_log";
|
||||
|
||||
export interface HRItem { }
|
||||
|
||||
export interface MenuItem {
|
||||
|
@ -47,7 +74,7 @@ namespace top_menu {
|
|||
export let native_actions: NativeActions;
|
||||
|
||||
namespace html {
|
||||
class HTMLHrItem implements top_menu.HRItem {
|
||||
class HTMLHrItem implements HRItem {
|
||||
readonly html_tag: JQuery;
|
||||
|
||||
constructor() {
|
||||
|
@ -55,7 +82,7 @@ namespace top_menu {
|
|||
}
|
||||
}
|
||||
|
||||
class HTMLMenuItem implements top_menu.MenuItem {
|
||||
class HTMLMenuItem implements MenuItem {
|
||||
readonly html_tag: JQuery;
|
||||
readonly _label_tag: JQuery;
|
||||
readonly _label_icon_tag: JQuery;
|
||||
|
@ -99,7 +126,7 @@ namespace top_menu {
|
|||
this.html_tag.append(this._submenu_tag);
|
||||
}
|
||||
|
||||
append_item(label: string): top_menu.MenuItem {
|
||||
append_item(label: string): MenuItem {
|
||||
const item = new HTMLMenuItem(label, "side");
|
||||
this._items.push(item);
|
||||
this._submenu_tag.append(item.html_tag);
|
||||
|
@ -114,7 +141,7 @@ namespace top_menu {
|
|||
return item;
|
||||
}
|
||||
|
||||
delete_item(item: top_menu.MenuItem | top_menu.HRItem) {
|
||||
delete_item(item: MenuItem | HRItem) {
|
||||
this._items.remove(item);
|
||||
(item as any).html_tag.detach();
|
||||
this.html_tag.toggleClass('sub-entries', this._items.length > 0);
|
||||
|
@ -128,7 +155,7 @@ namespace top_menu {
|
|||
return value;
|
||||
}
|
||||
|
||||
items(): (top_menu.MenuItem | top_menu.HRItem)[] {
|
||||
items(): (MenuItem | HRItem)[] {
|
||||
return this._items;
|
||||
}
|
||||
|
||||
|
@ -178,7 +205,7 @@ namespace top_menu {
|
|||
this.html_tag = $.spawn("div").addClass("top-menu-bar");
|
||||
}
|
||||
|
||||
append_item(label: string): top_menu.MenuItem {
|
||||
append_item(label: string): MenuItem {
|
||||
const item = new HTMLMenuItem(label, "down");
|
||||
this._items.push(item);
|
||||
|
||||
|
@ -209,7 +236,7 @@ namespace top_menu {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
items(): top_menu.MenuItem[] {
|
||||
items(): MenuItem[] {
|
||||
return this._items;
|
||||
}
|
||||
|
||||
|
@ -237,11 +264,11 @@ namespace top_menu {
|
|||
};
|
||||
_items_bookmark.manage = _items_bookmark.root.append_item(tr("Manage bookmarks"));
|
||||
_items_bookmark.manage.icon("client-bookmark_manager");
|
||||
_items_bookmark.manage.click(() => Modals.spawnBookmarkModal());
|
||||
_items_bookmark.manage.click(() => spawnBookmarkModal());
|
||||
|
||||
_items_bookmark.add_current = _items_bookmark.root.append_item(tr("Add current server to bookmarks"));
|
||||
_items_bookmark.add_current.icon('client-bookmark_add');
|
||||
_items_bookmark.add_current.click(() => bookmarks.add_current_server());
|
||||
_items_bookmark.add_current.click(() => add_current_server());
|
||||
_state_updater["bookmarks.ac"] = { item: _items_bookmark.add_current, conditions: [condition_connected]};
|
||||
}
|
||||
|
||||
|
@ -250,9 +277,9 @@ namespace top_menu {
|
|||
});
|
||||
_items_bookmark.root.append_hr();
|
||||
|
||||
const build_bookmark = (root: MenuItem, entry: bookmarks.DirectoryBookmark | bookmarks.Bookmark) => {
|
||||
if(entry.type == bookmarks.BookmarkType.DIRECTORY) {
|
||||
const directory = entry as bookmarks.DirectoryBookmark;
|
||||
const build_bookmark = (root: MenuItem, entry: DirectoryBookmark | Bookmark) => {
|
||||
if(entry.type == BookmarkType.DIRECTORY) {
|
||||
const directory = entry as DirectoryBookmark;
|
||||
const item = root.append_item(directory.display_name);
|
||||
item.icon('client-folder');
|
||||
for(const entry of directory.content)
|
||||
|
@ -260,14 +287,14 @@ namespace top_menu {
|
|||
if(directory.content.length == 0)
|
||||
item.disabled(true);
|
||||
} else {
|
||||
const bookmark = entry as bookmarks.Bookmark;
|
||||
const bookmark = entry as Bookmark;
|
||||
const item = root.append_item(bookmark.display_name);
|
||||
item.icon(IconManager.load_cached_icon(bookmark.last_icon_id || 0));
|
||||
item.click(() => bookmarks.boorkmak_connect(bookmark));
|
||||
item.click(() => boorkmak_connect(bookmark));
|
||||
}
|
||||
};
|
||||
|
||||
for(const entry of bookmarks.bookmarks().content)
|
||||
for(const entry of bookmarks().content)
|
||||
build_bookmark(_items_bookmark.root, entry);
|
||||
driver().flush_changes();
|
||||
}
|
||||
|
@ -302,16 +329,16 @@ namespace top_menu {
|
|||
}
|
||||
|
||||
export function initialize() {
|
||||
const driver = top_menu.driver();
|
||||
driver.initialize();
|
||||
const driver_ = driver();
|
||||
driver_.initialize();
|
||||
|
||||
/* build connection */
|
||||
let item: MenuItem;
|
||||
{
|
||||
const menu = driver.append_item(tr("Connection"));
|
||||
const menu = driver_.append_item(tr("Connection"));
|
||||
item = menu.append_item(tr("Connect to a server"));
|
||||
item.icon('client-connect');
|
||||
item.click(() => Modals.spawnConnectModal({}));
|
||||
item.click(() => spawnConnectModal({}));
|
||||
|
||||
const do_disconnect = (handlers: ConnectionHandler[]) => {
|
||||
for(const handler of handlers) {
|
||||
|
@ -319,7 +346,7 @@ namespace top_menu {
|
|||
handler.handleDisconnect(DisconnectReason.REQUESTED); //TODO message?
|
||||
server_connections.active_connection_handler().serverConnection.disconnect();
|
||||
handler.sound.play(Sound.CONNECTION_DISCONNECTED);
|
||||
handler.log.log(log.server.Type.DISCONNECTED, {});
|
||||
handler.log.log(slog.Type.DISCONNECTED, {});
|
||||
}
|
||||
control_bar.update_connection_state();
|
||||
update_state();
|
||||
|
@ -343,7 +370,7 @@ namespace top_menu {
|
|||
return true;
|
||||
}};
|
||||
|
||||
if(!app.is_web()) {
|
||||
if(loader.version().type !== "web") {
|
||||
menu.append_hr();
|
||||
|
||||
item = menu.append_item(tr("Quit"));
|
||||
|
@ -356,45 +383,45 @@ namespace top_menu {
|
|||
}
|
||||
|
||||
if(false) {
|
||||
const menu = driver.append_item(tr("Self"));
|
||||
const menu = driver_.append_item(tr("Self"));
|
||||
/* Microphone | Sound | Away */
|
||||
}
|
||||
|
||||
{
|
||||
const menu = driver.append_item(tr("Permissions"));
|
||||
const menu = driver_.append_item(tr("Permissions"));
|
||||
|
||||
item = menu.append_item(tr("Server Groups"));
|
||||
item.icon("client-permission_server_groups");
|
||||
item.click(() => {
|
||||
Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "sg").open();
|
||||
spawnPermissionEdit(server_connections.active_connection_handler(), "sg").open();
|
||||
});
|
||||
_state_updater["permission.sg"] = { item: item, conditions: [condition_connected]};
|
||||
|
||||
item = menu.append_item(tr("Client Permissions"));
|
||||
item.icon("client-permission_client");
|
||||
item.click(() => {
|
||||
Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "clp").open();
|
||||
spawnPermissionEdit(server_connections.active_connection_handler(), "clp").open();
|
||||
});
|
||||
_state_updater["permission.clp"] = { item: item, conditions: [condition_connected]};
|
||||
|
||||
item = menu.append_item(tr("Channel Client Permissions"));
|
||||
item.icon("client-permission_client");
|
||||
item.click(() => {
|
||||
Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "clchp").open();
|
||||
spawnPermissionEdit(server_connections.active_connection_handler(), "clchp").open();
|
||||
});
|
||||
_state_updater["permission.chclp"] = { item: item, conditions: [condition_connected]};
|
||||
|
||||
item = menu.append_item(tr("Channel Groups"));
|
||||
item.icon("client-permission_channel");
|
||||
item.click(() => {
|
||||
Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "cg").open();
|
||||
spawnPermissionEdit(server_connections.active_connection_handler(), "cg").open();
|
||||
});
|
||||
_state_updater["permission.cg"] = { item: item, conditions: [condition_connected]};
|
||||
|
||||
item = menu.append_item(tr("Channel Permissions"));
|
||||
item.icon("client-permission_channel");
|
||||
item.click(() => {
|
||||
Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "chp").open();
|
||||
spawnPermissionEdit(server_connections.active_connection_handler(), "chp").open();
|
||||
});
|
||||
_state_updater["permission.cp"] = { item: item, conditions: [condition_connected]};
|
||||
|
||||
|
@ -421,7 +448,7 @@ namespace top_menu {
|
|||
createInfoModal(tr("Use token"), tr("Toke successfully used!")).open();
|
||||
}).catch(error => {
|
||||
//TODO tr
|
||||
createErrorModal(tr("Use token"), MessageHelper.formatMessage(tr("Failed to use token: {}"), error instanceof CommandResult ? error.message : error)).open();
|
||||
createErrorModal(tr("Use token"), formatMessage(tr("Failed to use token: {}"), error instanceof CommandResult ? error.message : error)).open();
|
||||
});
|
||||
}).open();
|
||||
});
|
||||
|
@ -429,7 +456,7 @@ namespace top_menu {
|
|||
}
|
||||
|
||||
{
|
||||
const menu = driver.append_item(tr("Tools"));
|
||||
const menu = driver_.append_item(tr("Tools"));
|
||||
|
||||
/*
|
||||
item = menu.append_item(tr("Manage Playlists"));
|
||||
|
@ -451,7 +478,7 @@ namespace top_menu {
|
|||
const scon = server_connections.active_connection_handler();
|
||||
if(scon && scon.connected) {
|
||||
if(scon.permissions.neededPermission(PermissionType.B_CLIENT_BAN_LIST).granted(1)) {
|
||||
Modals.openBanList(scon);
|
||||
openBanList(scon);
|
||||
} else {
|
||||
createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to view the ban list")).open();
|
||||
scon.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
|
||||
|
@ -468,7 +495,7 @@ namespace top_menu {
|
|||
const scon = server_connections.active_connection_handler();
|
||||
if(scon && scon.connected) {
|
||||
if(scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_LIST).granted(1) || scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_LIST_OWN).granted(1)) {
|
||||
Modals.spawnQueryManage(scon);
|
||||
spawnQueryManage(scon);
|
||||
} else {
|
||||
createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to view the server query list")).open();
|
||||
scon.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
|
||||
|
@ -485,7 +512,7 @@ namespace top_menu {
|
|||
const scon = server_connections.active_connection_handler();
|
||||
if(scon && scon.connected) {
|
||||
if(scon.permissions.neededPermission(PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN).granted(1) || scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CREATE).granted(1)) {
|
||||
Modals.spawnQueryCreate(scon);
|
||||
spawnQueryCreate(scon);
|
||||
} else {
|
||||
createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to create a server query login")).open();
|
||||
scon.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
|
||||
|
@ -499,13 +526,13 @@ namespace top_menu {
|
|||
|
||||
item = menu.append_item(tr("Settings"));
|
||||
item.icon("client-settings");
|
||||
item.click(() => Modals.spawnSettingsModal());
|
||||
item.click(() => spawnSettingsModal());
|
||||
}
|
||||
|
||||
{
|
||||
const menu = driver.append_item(tr("Help"));
|
||||
const menu = driver_.append_item(tr("Help"));
|
||||
|
||||
if(!app.is_web()) {
|
||||
if(loader.version().type !== "web") {
|
||||
item = menu.append_item(tr("Check for updates"));
|
||||
item.click(() => native_actions.check_native_update());
|
||||
|
||||
|
@ -519,7 +546,7 @@ namespace top_menu {
|
|||
item = menu.append_item(tr("Visit TeaSpeak forum"));
|
||||
item.click(() => window.open('https://forum.teaspeak.de/', '_blank'));
|
||||
|
||||
if(!app.is_web() && typeof(native_actions.show_dev_tools) === "function" && native_actions.show_dev_tools()) {
|
||||
if(loader.version().type !== "web" && typeof(native_actions.show_dev_tools) === "function" && native_actions.show_dev_tools()) {
|
||||
menu.append_hr();
|
||||
item = menu.append_item(tr("Open developer tools"));
|
||||
item.click(() => native_actions.open_dev_tools());
|
||||
|
@ -529,13 +556,19 @@ namespace top_menu {
|
|||
}
|
||||
|
||||
menu.append_hr();
|
||||
item = menu.append_item(app.is_web() ? tr("About TeaWeb") : tr("About TeaClient"));
|
||||
item.click(() => Modals.spawnAbout())
|
||||
item = menu.append_item(loader.version().type === "web" ? tr("About TeaWeb") : tr("About TeaClient"));
|
||||
item.click(() => spawnAbout())
|
||||
}
|
||||
|
||||
update_state();
|
||||
}
|
||||
|
||||
/* default is HTML, the client will override this */
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
function: async () => {
|
||||
if(!driver())
|
||||
set_driver(html.HTMLMenuBarDriver.instance());
|
||||
}
|
||||
},
|
||||
priority: 100,
|
||||
name: "Menu bar init"
|
||||
});
|
|
@ -1,4 +1,10 @@
|
|||
enum ChatType {
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {settings, Settings} from "tc-shared/settings";
|
||||
import * as log from "tc-shared/log";
|
||||
import {bbcode} from "tc-shared/MessageFormatter";
|
||||
import * as loader from "tc-loader";
|
||||
|
||||
export enum ChatType {
|
||||
GENERAL,
|
||||
SERVER,
|
||||
CHANNEL,
|
||||
|
@ -6,7 +12,6 @@ enum ChatType {
|
|||
}
|
||||
|
||||
declare const xbbcode: any;
|
||||
namespace MessageHelper {
|
||||
export function htmlEscape(message: string) : string[] {
|
||||
const div = document.createElement('div');
|
||||
div.innerText = message;
|
||||
|
@ -92,7 +97,7 @@ namespace MessageHelper {
|
|||
|
||||
//TODO: Remove this (only legacy)
|
||||
export function bbcode_chat(message: string) : JQuery[] {
|
||||
return messages.formatter.bbcode.format(message, {
|
||||
return bbcode.format(message, {
|
||||
is_chat_message: true
|
||||
});
|
||||
}
|
||||
|
@ -243,8 +248,7 @@ namespace MessageHelper {
|
|||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "icon size init",
|
||||
function: async () => {
|
||||
MessageHelper.set_icon_size((settings.static_global(Settings.KEY_ICON_SIZE) / 100).toFixed(2) + "em");
|
||||
set_icon_size((settings.static_global(Settings.KEY_ICON_SIZE) / 100).toFixed(2) + "em");
|
||||
},
|
||||
priority: 10
|
||||
});
|
||||
}
|
|
@ -1,5 +1,15 @@
|
|||
/* the bar on the right with the chats (Channel & Client) */
|
||||
namespace chat {
|
||||
import {ClientEntry, MusicClientEntry} from "tc-shared/ui/client";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {ChannelEntry} from "tc-shared/ui/channel";
|
||||
import {ServerEntry} from "tc-shared/ui/server";
|
||||
import {openMusicManage} from "tc-shared/ui/modal/ModalMusicManage";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {PrivateConverations} from "tc-shared/ui/frames/side/private_conversations";
|
||||
import {ClientInfo} from "tc-shared/ui/frames/side/client_info";
|
||||
import {MusicInfo} from "tc-shared/ui/frames/side/music_info";
|
||||
import {ConversationManager} from "tc-shared/ui/frames/side/conversations";
|
||||
|
||||
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||
|
||||
|
@ -69,7 +79,7 @@ namespace chat {
|
|||
const bot = this.handle.music_info().current_bot();
|
||||
if(!bot) return;
|
||||
|
||||
Modals.openMusicManage(this.handle.handle, bot);
|
||||
openMusicManage(this.handle.handle, bot);
|
||||
});
|
||||
this._button_song_add = this._html_tag.find(".bot-add-song").on('click', event => {
|
||||
this.handle.music_info().events.fire("action_song_add");
|
||||
|
@ -157,7 +167,7 @@ namespace chat {
|
|||
|
||||
this.update_channel_limit(channel, html_limit_tag);
|
||||
} else if(channel_tree && current_channel_id > 0) {
|
||||
html_tag.append(MessageHelper.formatMessage(tr("Unknown channel id {}"), current_channel_id));
|
||||
html_tag.append(formatMessage(tr("Unknown channel id {}"), current_channel_id));
|
||||
} else if(channel_tree && current_channel_id == 0) {
|
||||
const server = this.handle.handle.channelTree.server;
|
||||
if(server.properties.virtualserver_icon_id != 0)
|
||||
|
@ -274,7 +284,7 @@ namespace chat {
|
|||
private _conversations: PrivateConverations;
|
||||
private _client_info: ClientInfo;
|
||||
private _music_info: MusicInfo;
|
||||
private _channel_conversations: channel.ConversationManager;
|
||||
private _channel_conversations: ConversationManager;
|
||||
|
||||
constructor(handle: ConnectionHandler) {
|
||||
this.handle = handle;
|
||||
|
@ -282,7 +292,7 @@ namespace chat {
|
|||
this._content_type = FrameContent.NONE;
|
||||
this._info_frame = new InfoFrame(this);
|
||||
this._conversations = new PrivateConverations(this);
|
||||
this._channel_conversations = new channel.ConversationManager(this);
|
||||
this._channel_conversations = new ConversationManager(this);
|
||||
this._client_info = new ClientInfo(this);
|
||||
this._music_info = new MusicInfo(this);
|
||||
|
||||
|
@ -335,7 +345,7 @@ namespace chat {
|
|||
return this._conversations;
|
||||
}
|
||||
|
||||
channel_conversations() : channel.ConversationManager {
|
||||
channel_conversations() : ConversationManager {
|
||||
return this._channel_conversations;
|
||||
}
|
||||
|
||||
|
@ -414,4 +424,3 @@ namespace chat {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,13 @@
|
|||
import {ConnectionHandler, DisconnectReason} from "tc-shared/ConnectionHandler";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import {control_bar} from "tc-shared/ui/frames/ControlBar";
|
||||
import * as top_menu from "./MenuBar";
|
||||
|
||||
let server_connections: ServerConnectionManager;
|
||||
|
||||
class ServerConnectionManager {
|
||||
export let server_connections: ServerConnectionManager;
|
||||
export function initialize(manager: ServerConnectionManager) {
|
||||
server_connections = manager;
|
||||
}
|
||||
export class ServerConnectionManager {
|
||||
private connection_handlers: ConnectionHandler[] = [];
|
||||
private active_handler: ConnectionHandler | undefined;
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
class Hostbanner {
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {settings, Settings} from "tc-shared/settings";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as log from "tc-shared/log";
|
||||
|
||||
export class Hostbanner {
|
||||
readonly html_tag: JQuery<HTMLElement>;
|
||||
readonly client: ConnectionHandler;
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
namespace image_preview {
|
||||
import * as loader from "tc-loader";
|
||||
|
||||
let preview_overlay: JQuery<HTMLDivElement>;
|
||||
let container_image: JQuery<HTMLDivElement>;
|
||||
let button_open_in_browser: JQuery;
|
||||
|
@ -78,4 +79,3 @@ namespace image_preview {
|
|||
});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
namespace log {
|
||||
export namespace server {
|
||||
import {tra} from "tc-shared/i18n/localize";
|
||||
import {PermissionInfo} from "tc-shared/permission/PermissionManager";
|
||||
import {ConnectionHandler, ViewReasonId} from "tc-shared/ConnectionHandler";
|
||||
import * as htmltags from "tc-shared/ui/htmltags";
|
||||
import {bbcode_chat, format_time, formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {formatDate} from "tc-shared/MessageFormatter";
|
||||
|
||||
export enum Type {
|
||||
CONNECTION_BEGIN = "connection_begin",
|
||||
CONNECTION_HOSTNAME_RESOLVE = "connection_hostname_resolve",
|
||||
|
@ -253,20 +258,19 @@ namespace log {
|
|||
}
|
||||
|
||||
export type MessageBuilderOptions = {};
|
||||
export type MessageBuilder<T extends keyof server.TypeInfo> = (data: TypeInfo[T], options: MessageBuilderOptions) => JQuery[] | undefined;
|
||||
export type MessageBuilder<T extends keyof TypeInfo> = (data: TypeInfo[T], options: MessageBuilderOptions) => JQuery[] | undefined;
|
||||
|
||||
export const MessageBuilders: {[key: string]: MessageBuilder<any>} = {
|
||||
"error_custom": (data: event.ErrorCustom, options) => {
|
||||
return [$.spawn("div").addClass("log-error").text(data.message)]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export class ServerLog {
|
||||
private readonly handle: ConnectionHandler;
|
||||
private history_length: number = 100;
|
||||
|
||||
private _log: server.LogMessage[] = [];
|
||||
private _log: LogMessage[] = [];
|
||||
private _html_tag: JQuery;
|
||||
private _log_container: JQuery;
|
||||
private auto_follow: boolean; /* automatic scroll to bottom */
|
||||
|
@ -290,7 +294,7 @@ namespace log {
|
|||
});
|
||||
}
|
||||
|
||||
log<T extends keyof server.TypeInfo>(type: T, data: server.TypeInfo[T]) {
|
||||
log<T extends keyof TypeInfo>(type: T, data: TypeInfo[T]) {
|
||||
const event = {
|
||||
data: data,
|
||||
timestamp: Date.now(),
|
||||
|
@ -318,7 +322,7 @@ namespace log {
|
|||
|
||||
private _scroll_task: number;
|
||||
|
||||
private append_log(message: server.LogMessage) {
|
||||
private append_log(message: LogMessage) {
|
||||
let container = $.spawn("div").addClass("log-message");
|
||||
|
||||
/* build timestamp */
|
||||
|
@ -333,9 +337,9 @@ namespace log {
|
|||
|
||||
/* build message data */
|
||||
{
|
||||
const builder = server.MessageBuilders[message.type];
|
||||
const builder = MessageBuilders[message.type];
|
||||
if(!builder) {
|
||||
MessageHelper.formatMessage(tr("missing log message builder {0}!"), message.type).forEach(e => e.addClass("log-error").appendTo(container));
|
||||
formatMessage(tr("missing log message builder {0}!"), message.type).forEach(e => e.addClass("log-error").appendTo(container));
|
||||
} else {
|
||||
const elements = builder(message.data, {});
|
||||
if(!elements || elements.length == 0)
|
||||
|
@ -365,12 +369,8 @@ namespace log {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* impl of the parsers */
|
||||
namespace log {
|
||||
export namespace server {
|
||||
namespace impl {
|
||||
const client_tag = (client: base.Client, braces?: boolean) => htmltags.generate_client_object({
|
||||
client_unique_id: client.client_unique_id,
|
||||
client_id: client.client_id,
|
||||
|
@ -385,23 +385,23 @@ namespace log {
|
|||
});
|
||||
|
||||
MessageBuilders["connection_begin"] = (data: event.ConnectBegin, options) => {
|
||||
return MessageHelper.formatMessage(tr("Connecting to {0}{1}"), data.address.server_hostname, data.address.server_port == 9987 ? "" : (":" + data.address.server_port));
|
||||
return formatMessage(tr("Connecting to {0}{1}"), data.address.server_hostname, data.address.server_port == 9987 ? "" : (":" + data.address.server_port));
|
||||
};
|
||||
|
||||
MessageBuilders["connection_hostname_resolve"] = (data: event.ConnectionHostnameResolve, options) => MessageHelper.formatMessage(tr("Resolving hostname"));
|
||||
MessageBuilders["connection_hostname_resolved"] = (data: event.ConnectionHostnameResolved, options) => MessageHelper.formatMessage(tr("Hostname resolved successfully to {0}:{1}"), data.address.server_hostname, data.address.server_port);
|
||||
MessageBuilders["connection_hostname_resolve_error"] = (data: event.ConnectionHostnameResolveError, options) => MessageHelper.formatMessage(tr("Failed to resolve hostname. Connecting to given hostname. Error: {0}"), data.message);
|
||||
MessageBuilders["connection_hostname_resolve"] = (data: event.ConnectionHostnameResolve, options) => formatMessage(tr("Resolving hostname"));
|
||||
MessageBuilders["connection_hostname_resolved"] = (data: event.ConnectionHostnameResolved, options) => formatMessage(tr("Hostname resolved successfully to {0}:{1}"), data.address.server_hostname, data.address.server_port);
|
||||
MessageBuilders["connection_hostname_resolve_error"] = (data: event.ConnectionHostnameResolveError, options) => formatMessage(tr("Failed to resolve hostname. Connecting to given hostname. Error: {0}"), data.message);
|
||||
|
||||
MessageBuilders["connection_login"] = (data: event.ConnectionLogin, options) => MessageHelper.formatMessage(tr("Logging in..."));
|
||||
MessageBuilders["connection_failed"] = (data: event.ConnectionFailed, options) => MessageHelper.formatMessage(tr("Connect failed."));
|
||||
MessageBuilders["connection_connected"] = (data: event.ConnectionConnected, options) => MessageHelper.formatMessage(tr("Connected as {0}"), client_tag(data.own_client, true));
|
||||
MessageBuilders["connection_login"] = (data: event.ConnectionLogin, options) => formatMessage(tr("Logging in..."));
|
||||
MessageBuilders["connection_failed"] = (data: event.ConnectionFailed, options) => formatMessage(tr("Connect failed."));
|
||||
MessageBuilders["connection_connected"] = (data: event.ConnectionConnected, options) => formatMessage(tr("Connected as {0}"), client_tag(data.own_client, true));
|
||||
|
||||
MessageBuilders["connection_voice_setup_failed"] = (data: event.ConnectionVoiceSetupFailed, options) => {
|
||||
return MessageHelper.formatMessage(tr("Failed to setup voice bridge: {0}. Allow reconnect: {1}"), data.reason, data.reconnect_delay > 0 ? tr("yes") : tr("no"));
|
||||
return formatMessage(tr("Failed to setup voice bridge: {0}. Allow reconnect: {1}"), data.reason, data.reconnect_delay > 0 ? tr("yes") : tr("no"));
|
||||
};
|
||||
|
||||
MessageBuilders["error_permission"] = (data: event.ErrorPermission, options) => {
|
||||
return MessageHelper.formatMessage(tr("Insufficient client permissions. Failed on permission {0}"), data.permission ? data.permission.name : "unknown").map(e => e.addClass("log-error"));
|
||||
return formatMessage(tr("Insufficient client permissions. Failed on permission {0}"), data.permission ? data.permission.name : "unknown").map(e => e.addClass("log-error"));
|
||||
};
|
||||
|
||||
MessageBuilders["client_view_enter"] = (data: event.ClientEnter, options) => {
|
||||
|
@ -410,20 +410,20 @@ namespace log {
|
|||
} if(data.reason == ViewReasonId.VREASON_USER_ACTION) {
|
||||
/* client appeared */
|
||||
if(data.channel_from) {
|
||||
return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your {2}") : tr("{0} appeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to));
|
||||
return formatMessage(data.own_channel ? tr("{0} appeared from {1} to your {2}") : tr("{0} appeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to));
|
||||
} else {
|
||||
return MessageHelper.formatMessage(data.own_channel ? tr("{0} connected to your channel {1}") : tr("{0} connected to channel {1}"), client_tag(data.client), channel_tag(data.channel_to));
|
||||
return formatMessage(data.own_channel ? tr("{0} connected to your channel {1}") : tr("{0} connected to channel {1}"), client_tag(data.client), channel_tag(data.channel_to));
|
||||
}
|
||||
} else if(data.reason == ViewReasonId.VREASON_MOVED) {
|
||||
if(data.channel_from) {
|
||||
return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, moved by {3}") : tr("{0} appeared from {1} to {2}, moved by {3}"),
|
||||
return formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, moved by {3}") : tr("{0} appeared from {1} to {2}, moved by {3}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_from),
|
||||
channel_tag(data.channel_to),
|
||||
client_tag(data.invoker)
|
||||
);
|
||||
} else {
|
||||
return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, moved by {2}") : tr("{0} appeared to {1}, moved by {2}"),
|
||||
return formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, moved by {2}") : tr("{0} appeared to {1}, moved by {2}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_to),
|
||||
client_tag(data.invoker)
|
||||
|
@ -431,7 +431,7 @@ namespace log {
|
|||
}
|
||||
} else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) {
|
||||
if(data.channel_from) {
|
||||
return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, kicked by {3}{4}") : tr("{0} appeared from {1} to {2}, kicked by {3}{4}"),
|
||||
return formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, kicked by {3}{4}") : tr("{0} appeared from {1} to {2}, kicked by {3}{4}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_from),
|
||||
channel_tag(data.channel_to),
|
||||
|
@ -439,7 +439,7 @@ namespace log {
|
|||
data.message ? (" (" + data.message + ")") : ""
|
||||
);
|
||||
} else {
|
||||
return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, kicked by {2}{3}") : tr("{0} appeared to {1}, kicked by {2}{3}"),
|
||||
return formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, kicked by {2}{3}") : tr("{0} appeared to {1}, kicked by {2}{3}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_to),
|
||||
client_tag(data.invoker),
|
||||
|
@ -452,20 +452,20 @@ namespace log {
|
|||
|
||||
MessageBuilders["client_view_move"] = (data: event.ClientMove, options) => {
|
||||
if(data.reason == ViewReasonId.VREASON_MOVED) {
|
||||
return MessageHelper.formatMessage(data.client_own ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"),
|
||||
return formatMessage(data.client_own ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_from),
|
||||
channel_tag(data.channel_to),
|
||||
client_tag(data.invoker)
|
||||
);
|
||||
} else if(data.reason == ViewReasonId.VREASON_USER_ACTION) {
|
||||
return MessageHelper.formatMessage(data.client_own ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"),
|
||||
return formatMessage(data.client_own ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_from),
|
||||
channel_tag(data.channel_to)
|
||||
);
|
||||
} else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) {
|
||||
return MessageHelper.formatMessage(data.client_own ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"),
|
||||
return formatMessage(data.client_own ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_from),
|
||||
channel_tag(data.channel_to),
|
||||
|
@ -478,13 +478,13 @@ namespace log {
|
|||
|
||||
MessageBuilders["client_view_leave"] = (data: event.ClientLeave, options) => {
|
||||
if(data.reason == ViewReasonId.VREASON_USER_ACTION) {
|
||||
return MessageHelper.formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}") : tr("{0} disappeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to));
|
||||
return formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}") : tr("{0} disappeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to));
|
||||
} else if(data.reason == ViewReasonId.VREASON_SERVER_LEFT) {
|
||||
return MessageHelper.formatMessage(tr("{0} left the server{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : "");
|
||||
return formatMessage(tr("{0} left the server{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : "");
|
||||
} else if(data.reason == ViewReasonId.VREASON_SERVER_KICK) {
|
||||
return MessageHelper.formatMessage(tr("{0} was kicked from the server by {1}.{2}"), client_tag(data.client), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : "");
|
||||
return formatMessage(tr("{0} was kicked from the server by {1}.{2}"), client_tag(data.client), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : "");
|
||||
} else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) {
|
||||
return MessageHelper.formatMessage(data.own_channel ? tr("{0} was kicked from your channel by {2}.{3}") : tr("{0} was kicked from channel {1} by {2}.{3}"),
|
||||
return formatMessage(data.own_channel ? tr("{0} was kicked from your channel by {2}.{3}") : tr("{0} was kicked from channel {1} by {2}.{3}"),
|
||||
client_tag(data.client),
|
||||
channel_tag(data.channel_from),
|
||||
client_tag(data.invoker),
|
||||
|
@ -494,34 +494,34 @@ namespace log {
|
|||
let duration = "permanently";
|
||||
if(data.ban_time)
|
||||
duration = "for " + formatDate(data.ban_time);
|
||||
return MessageHelper.formatMessage(tr("{0} was banned {1} by {2}.{3}"),
|
||||
return formatMessage(tr("{0} was banned {1} by {2}.{3}"),
|
||||
client_tag(data.client),
|
||||
duration,
|
||||
client_tag(data.invoker),
|
||||
data.message ? (" (" + data.message + ")") : ""
|
||||
);
|
||||
} else if(data.reason == ViewReasonId.VREASON_TIMEOUT) {
|
||||
return MessageHelper.formatMessage(tr("{0} timed out{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : "");
|
||||
return formatMessage(tr("{0} timed out{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : "");
|
||||
} else if(data.reason == ViewReasonId.VREASON_MOVED) {
|
||||
return MessageHelper.formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}, moved by {3}") : tr("{0} disappeared from {1} to {2}, moved by {3}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker));
|
||||
return formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}, moved by {3}") : tr("{0} disappeared from {1} to {2}, moved by {3}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker));
|
||||
}
|
||||
|
||||
return [$.spawn("div").addClass("log-error").text("Invalid view leave reason id (" + data.reason + ")")];
|
||||
};
|
||||
|
||||
MessageBuilders["server_welcome_message"] = (data: event.WelcomeMessage, options) => {
|
||||
return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]");
|
||||
return bbcode_chat("[color=green]" + data.message + "[/color]");
|
||||
};
|
||||
|
||||
MessageBuilders["server_host_message"] = (data: event.WelcomeMessage, options) => {
|
||||
return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]");
|
||||
return bbcode_chat("[color=green]" + data.message + "[/color]");
|
||||
};
|
||||
|
||||
MessageBuilders["client_nickname_changed"] = (data: event.ClientNicknameChanged, options) => {
|
||||
if(data.own_client) {
|
||||
return MessageHelper.formatMessage(tr("Nickname successfully changed."));
|
||||
return formatMessage(tr("Nickname successfully changed."));
|
||||
} else {
|
||||
return MessageHelper.formatMessage(tr("{0} changed his nickname from \"{1}\" to \"{2}\""), client_tag(data.client), data.old_name, data.new_name);
|
||||
return formatMessage(tr("{0} changed his nickname from \"{1}\" to \"{2}\""), client_tag(data.client), data.old_name, data.new_name);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -529,10 +529,10 @@ namespace log {
|
|||
return []; /* we do not show global messages within log */
|
||||
};
|
||||
|
||||
MessageBuilders["disconnected"] = () => MessageHelper.formatMessage(tr("Disconnected from server"));
|
||||
MessageBuilders["disconnected"] = () => formatMessage(tr("Disconnected from server"));
|
||||
|
||||
MessageBuilders["reconnect_scheduled"] = (data: event.ReconnectScheduled, options) => {
|
||||
return tra("Reconnecting in {0}.", MessageHelper.format_time(data.timeout, tr("now")))
|
||||
return tra("Reconnecting in {0}.", format_time(data.timeout, tr("now")))
|
||||
};
|
||||
|
||||
MessageBuilders["reconnect_canceled"] = (data: event.ReconnectCanceled, options) => {
|
||||
|
@ -546,7 +546,7 @@ namespace log {
|
|||
MessageBuilders["server_banned"] = (data: event.ServerBanned, options) => {
|
||||
let result: JQuery[];
|
||||
|
||||
const time = data.time == 0 ? tr("ever") : MessageHelper.format_time(data.time * 1000, tr("one second"));
|
||||
const time = data.time == 0 ? tr("ever") : format_time(data.time * 1000, tr("one second"));
|
||||
if(data.invoker.client_id > 0) {
|
||||
if(data.message)
|
||||
result = tra("You've been banned from the server by {0} for {1}. Reason: {2}", client_tag(data.invoker), time, data.message);
|
||||
|
@ -561,6 +561,3 @@ namespace log {
|
|||
|
||||
return result.map(e => e.addClass("log-error"));
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
namespace chat {
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import {helpers} from "tc-shared/ui/frames/side/chat_helper";
|
||||
|
||||
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||
|
||||
|
@ -264,4 +266,3 @@ namespace chat {
|
|||
this._html_input.focus();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,8 @@
|
|||
namespace chat {
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
|
||||
declare const xbbcode;
|
||||
export namespace helpers {
|
||||
//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;
|
||||
|
@ -420,4 +424,3 @@ test
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,12 @@
|
|||
namespace chat {
|
||||
import {GroupManager} from "tc-shared/permission/GroupManager";
|
||||
import {Frame, FrameContent} from "tc-shared/ui/frames/chat_frame";
|
||||
import {ClientEntry, LocalClientEntry} from "tc-shared/ui/client";
|
||||
import {openClientInfo} from "tc-shared/ui/modal/ModalClientInfo";
|
||||
import * as htmltags from "tc-shared/ui/htmltags";
|
||||
import * as image_preview from "../image_preview";
|
||||
import {format} from "tc-shared/ui/frames/side/chat_helper";
|
||||
import * as i18nc from "tc-shared/i18n/country";
|
||||
|
||||
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||
|
||||
|
@ -40,7 +48,7 @@ namespace chat {
|
|||
if(!this._current_client)
|
||||
return;
|
||||
|
||||
Modals.openClientInfo(this._current_client);
|
||||
openClientInfo(this._current_client);
|
||||
});
|
||||
this._html_tag.find('.container-avatar-edit').on('click', () => this.handle.handle.update_avatar());
|
||||
}
|
||||
|
@ -116,7 +124,7 @@ namespace chat {
|
|||
country.children().detach();
|
||||
const country_code = (client ? client.properties.client_country : undefined) || "xx";
|
||||
$.spawn("div").addClass("country flag-" + country_code.toLowerCase()).appendTo(country);
|
||||
$.spawn("a").text(i18n.country_name(country_code.toUpperCase())).appendTo(country);
|
||||
$.spawn("a").text(i18nc.country_name(country_code.toUpperCase())).appendTo(country);
|
||||
|
||||
|
||||
const version = this._html_tag.find(".client-version");
|
||||
|
@ -272,4 +280,3 @@ namespace chat {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,18 @@
|
|||
namespace chat {
|
||||
import {settings, Settings} from "tc-shared/settings";
|
||||
import {format} from "tc-shared/ui/frames/side/chat_helper";
|
||||
import {bbcode_chat, formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import PermissionType from "tc-shared/permission/PermissionType";
|
||||
import {ChatBox} from "tc-shared/ui/frames/side/chat_box";
|
||||
import {Frame, FrameContent} from "tc-shared/ui/frames/chat_frame";
|
||||
import {createErrorModal} from "tc-shared/ui/elements/Modal";
|
||||
import * as log from "tc-shared/log";
|
||||
import * as htmltags from "tc-shared/ui/htmltags";
|
||||
|
||||
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||
|
||||
export namespace channel {
|
||||
export type ViewEntry = {
|
||||
html_element: JQuery;
|
||||
update_timer?: number;
|
||||
|
@ -151,7 +161,7 @@ namespace chat {
|
|||
client_unique_id: data.sender_unique_id,
|
||||
client_id: 0
|
||||
}),
|
||||
message: MessageHelper.bbcode_chat(data.message),
|
||||
message: bbcode_chat(data.message),
|
||||
avatar: this.handle.handle.handle.fileManager.avatars.generate_chat_tag({database_id: data.sender_database_id}, data.sender_unique_id)
|
||||
});
|
||||
|
||||
|
@ -433,7 +443,7 @@ namespace chat {
|
|||
log.error(LogCategory.CHAT, tr("Failed to delete conversation message for conversation %o: %o"), this.channel_id, error);
|
||||
if(error instanceof CommandResult)
|
||||
error = error.extra_message || error.message;
|
||||
createErrorModal(tr("Failed to delete message"), MessageHelper.formatMessage(tr("Failed to delete conversation message{:br:}Error: {}"), error)).open();
|
||||
createErrorModal(tr("Failed to delete message"), formatMessage(tr("Failed to delete conversation message{:br:}Error: {}"), error)).open();
|
||||
});
|
||||
log.debug(LogCategory.CLIENT, tr("Deleting text message %o"), message);
|
||||
}
|
||||
|
@ -611,5 +621,3 @@ namespace chat {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,13 @@
|
|||
namespace chat {
|
||||
import PlayerState = connection.voice.PlayerState;
|
||||
import {Frame, FrameContent} from "tc-shared/ui/frames/chat_frame";
|
||||
import * as events from "tc-shared/events";
|
||||
import {MusicClientEntry, SongInfo} from "tc-shared/ui/client";
|
||||
import {voice} from "tc-shared/connection/ConnectionBase";
|
||||
import PlayerState = voice.PlayerState;
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {CommandResult, ErrorID, PlaylistSong} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {createErrorModal, createInputModal} from "tc-shared/ui/elements/Modal";
|
||||
import * as log from "tc-shared/log";
|
||||
import * as image_preview from "../image_preview";
|
||||
|
||||
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||
|
@ -846,4 +854,3 @@ namespace chat {
|
|||
return tag;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,14 @@
|
|||
/* the bar on the right with the chats (Channel & Client) */
|
||||
namespace chat {
|
||||
import {settings, Settings} from "tc-shared/settings";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {format, helpers} from "tc-shared/ui/frames/side/chat_helper";
|
||||
import {bbcode_chat} from "tc-shared/ui/frames/chat";
|
||||
import {Frame} from "tc-shared/ui/frames/chat_frame";
|
||||
import {ChatBox} from "tc-shared/ui/frames/side/chat_box";
|
||||
import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import * as log from "tc-shared/log";
|
||||
import * as htmltags from "tc-shared/ui/htmltags";
|
||||
|
||||
declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||
declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
|
||||
|
||||
|
@ -341,7 +350,7 @@ namespace chat {
|
|||
client_unique_id: message.sender_unique_id,
|
||||
client_id: message.sender_client_id
|
||||
}),
|
||||
message: MessageHelper.bbcode_chat(message.message),
|
||||
message: bbcode_chat(message.message),
|
||||
avatar: this.handle.handle.handle.fileManager.avatars.generate_chat_tag({id: message.sender_client_id}, message.sender_unique_id)
|
||||
});
|
||||
if(time.next_update > 0) {
|
||||
|
@ -792,7 +801,7 @@ namespace chat {
|
|||
}
|
||||
} else {
|
||||
conv.append_error(tr("Failed to send message. Lookup the console for more details"));
|
||||
log.error(LogCategory.CHAT, tr("Failed to send conversation message: %o", error));
|
||||
log.error(LogCategory.CHAT, tr("Failed to send conversation message: %o"), error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -893,4 +902,3 @@ namespace chat {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,10 @@
|
|||
namespace htmltags {
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {ChannelEntry} from "tc-shared/ui/channel";
|
||||
import {ClientEntry} from "tc-shared/ui/client";
|
||||
import {htmlEscape} from "tc-shared/ui/frames/chat";
|
||||
import {server_connections} from "tc-shared/ui/frames/connection_handlers";
|
||||
|
||||
let mouse_coordinates: {x: number, y: number} = {x: 0, y: 0};
|
||||
|
||||
function initialize() {
|
||||
|
@ -64,7 +70,7 @@ namespace htmltags {
|
|||
if(properties.add_braces)
|
||||
result = result + "\"";
|
||||
|
||||
result = result + MessageHelper.htmlEscape(properties.client_name || "undefined").join(" ");
|
||||
result = result + htmlEscape(properties.client_name || "undefined").join(" ");
|
||||
if(properties.add_braces)
|
||||
result = result + "\"";
|
||||
}
|
||||
|
@ -106,7 +112,7 @@ namespace htmltags {
|
|||
{
|
||||
if(properties.add_braces)
|
||||
result = result + "\"";
|
||||
result = result + MessageHelper.htmlEscape(properties.channel_display_name || properties.channel_name || "undefined").join(" ");
|
||||
result = result + htmlEscape(properties.channel_display_name || properties.channel_name || "undefined").join(" ");
|
||||
if(properties.add_braces)
|
||||
result = result + "\"";
|
||||
}
|
||||
|
@ -181,6 +187,7 @@ namespace htmltags {
|
|||
}
|
||||
}
|
||||
|
||||
declare const xbbcode;
|
||||
namespace bbcodes {
|
||||
/* the = because we sometimes get that */
|
||||
//const url_client_regex = /?client:\/\/(?<client_id>[0-9]+)\/(?<client_unique_id>[a-zA-Z0-9+=#]+)~(?<client_name>(?:[^%]|%[0-9A-Fa-f]{2})+)$/g;
|
||||
|
@ -223,31 +230,7 @@ namespace htmltags {
|
|||
}
|
||||
return origin_url.build_html_tag_close(layer);
|
||||
}
|
||||
})
|
||||
/*
|
||||
"img": {
|
||||
openTag: function(params,content) {
|
||||
let myUrl;
|
||||
|
||||
if (!params) {
|
||||
myUrl = content.replace(/<.*?>/g,"");
|
||||
} else {
|
||||
myUrl = params.substr(1);
|
||||
}
|
||||
|
||||
urlPattern.lastIndex = 0;
|
||||
if ( !urlPattern.test( myUrl ) ) {
|
||||
myUrl = "#";
|
||||
}
|
||||
|
||||
return '<a href="' + myUrl + '" target="_blank">';
|
||||
},
|
||||
closeTag: function(params,content) {
|
||||
return '</a>';
|
||||
}
|
||||
},
|
||||
*/
|
||||
});
|
||||
}
|
||||
initialize();
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
/// <reference path="../../ui/elements/modal.ts" />
|
||||
/// <reference path="../../ConnectionHandler.ts" />
|
||||
/// <reference path="../../proto.ts" />
|
||||
import {createModal} from "tc-shared/ui/elements/Modal";
|
||||
import * as loader from "tc-loader";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as log from "tc-shared/log";
|
||||
|
||||
namespace Modals {
|
||||
function format_date(date: number) {
|
||||
const d = new Date(date);
|
||||
|
||||
|
@ -27,9 +27,9 @@ namespace Modals {
|
|||
header: tr("About"),
|
||||
body: () => {
|
||||
let tag = $("#tmpl_about").renderTag({
|
||||
client: !app.is_web(),
|
||||
client: loader.version().type !== "web",
|
||||
|
||||
version_client: app.is_web() ? app_version || "in-dev" : "loading...",
|
||||
version_client: loader.version().type === "web" ? app_version || "in-dev" : "loading...",
|
||||
version_ui: app_version || "in-dev",
|
||||
|
||||
version_timestamp: !!app_version ? format_date(Date.now()) : "--"
|
||||
|
@ -43,7 +43,7 @@ namespace Modals {
|
|||
connectModal.htmlTag.find(".modal-body").addClass("modal-about");
|
||||
connectModal.open();
|
||||
|
||||
if(!app.is_web()) {
|
||||
if(loader.version().type !== "web") {
|
||||
(window as any).native.client_version().then(version => {
|
||||
connectModal.htmlTag.find(".version-client").text(version);
|
||||
}).catch(error => {
|
||||
|
@ -52,4 +52,3 @@ namespace Modals {
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
/// <reference path="../../ui/elements/modal.ts" />
|
||||
/// <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.
|
||||
import {createErrorModal, createModal} from "tc-shared/ui/elements/Modal";
|
||||
import {tra} from "tc-shared/i18n/localize";
|
||||
import {arrayBufferBase64} from "tc-shared/utils/buffers";
|
||||
|
||||
export function spawnAvatarUpload(callback_data: (data: ArrayBuffer | undefined | null) => any) {
|
||||
const modal = createModal({
|
||||
header: tr("Avatar Upload"),
|
||||
|
@ -71,4 +70,3 @@ namespace Modals {
|
|||
modal.close_listener.push(() => !_data_submitted && callback_data(undefined));
|
||||
modal.open();
|
||||
}
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
/// <reference path="../../ui/elements/modal.ts" />
|
||||
/// <reference path="../../ConnectionHandler.ts" />
|
||||
/// <reference path="../../proto.ts" />
|
||||
import {createErrorModal, createModal} from "tc-shared/ui/elements/Modal";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {base64_encode_ab} from "tc-shared/utils/buffers";
|
||||
import {media_image_type} from "tc-shared/FileManager";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import {ClientEntry} from "tc-shared/ui/client";
|
||||
import * as log from "tc-shared/log";
|
||||
|
||||
namespace Modals {
|
||||
const avatar_to_uid = (id: string) => {
|
||||
const buffer = new Uint8Array(id.length / 2);
|
||||
for(let index = 0; index < id.length; index += 2) {
|
||||
|
@ -20,6 +24,7 @@ namespace Modals {
|
|||
return (size / Math.pow(1024, exp)).toFixed(2) + 'KMGTPE'.charAt(exp - 1) + "iB";
|
||||
};
|
||||
|
||||
declare const moment;
|
||||
export function spawnAvatarList(client: ConnectionHandler) {
|
||||
const modal = createModal({
|
||||
header: tr("Avatars"),
|
||||
|
@ -159,4 +164,3 @@ namespace Modals {
|
|||
setTimeout(() => update_avatar_list(), 250);
|
||||
modal.open();
|
||||
}
|
||||
}
|
|
@ -2,7 +2,12 @@
|
|||
/// <reference path="../../ConnectionHandler.ts" />
|
||||
/// <reference path="../../proto.ts" />
|
||||
|
||||
namespace Modals {
|
||||
import PermissionType from "tc-shared/permission/PermissionType";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {createModal} from "tc-shared/ui/elements/Modal";
|
||||
import {duration_data} from "tc-shared/ui/modal/ModalBanList";
|
||||
import * as tooltip from "tc-shared/ui/elements/Tooltip";
|
||||
|
||||
export type BanEntry = {
|
||||
name?: string;
|
||||
unique_id: string;
|
||||
|
@ -165,7 +170,7 @@ namespace Modals {
|
|||
update_button_ok();
|
||||
}
|
||||
|
||||
tooltip(template);
|
||||
tooltip.initialize(template);
|
||||
return template.children();
|
||||
},
|
||||
footer: null,
|
||||
|
@ -177,4 +182,3 @@ namespace Modals {
|
|||
|
||||
modal.htmlTag.find(".modal-body").addClass("modal-ban-client");
|
||||
}
|
||||
}
|
|
@ -1,15 +1,20 @@
|
|||
/// <reference path="../../ConnectionHandler.ts" />
|
||||
/// <reference path="../../ui/elements/modal.ts" />
|
||||
/// <reference path="../../i18n/localize.ts" />
|
||||
/// <reference path="../../proto.ts" />
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {createErrorModal, createInfoModal, createModal, Modal} from "tc-shared/ui/elements/Modal";
|
||||
import {SingleCommandHandler} from "tc-shared/connection/ConnectionBase";
|
||||
import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import PermissionType from "tc-shared/permission/PermissionType";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as log from "tc-shared/log";
|
||||
import * as tooltip from "tc-shared/ui/elements/Tooltip";
|
||||
import * as htmltags from "tc-shared/ui/htmltags";
|
||||
import {format_time, formatMessage} from "tc-shared/ui/frames/chat";
|
||||
|
||||
namespace Modals {
|
||||
export function openBanList(client: ConnectionHandler) {
|
||||
let modal: Modal;
|
||||
|
||||
let _callback_bans;
|
||||
let _callback_triggers;
|
||||
const single_ban_handler: connection.SingleCommandHandler = {
|
||||
const single_ban_handler: SingleCommandHandler = {
|
||||
command: "notifybanlist",
|
||||
function: command => {
|
||||
const json = command.arguments;
|
||||
|
@ -41,7 +46,7 @@ namespace Modals {
|
|||
return false; /* do not remove me */
|
||||
}
|
||||
};
|
||||
const single_trigger_handler: connection.SingleCommandHandler = {
|
||||
const single_trigger_handler: SingleCommandHandler = {
|
||||
command: "notifybantriggerlist",
|
||||
function: command => {
|
||||
//TODO: Test the server id in the response?
|
||||
|
@ -265,6 +270,7 @@ namespace Modals {
|
|||
},
|
||||
};
|
||||
|
||||
declare const moment;
|
||||
function generate_dom(controller: BanListController) : JQuery {
|
||||
const template = $("#tmpl_ban_list").renderTag();
|
||||
|
||||
|
@ -392,7 +398,7 @@ namespace Modals {
|
|||
log.error(LogCategory.CLIENT, tr("Failed to delete ban: %o"), error);
|
||||
if(error instanceof CommandResult)
|
||||
error = error.id === ErrorID.PERMISSION_ERROR ? "no permissions" : error.extra_message || error.message;
|
||||
createErrorModal(tr("Failed to delete ban"), MessageHelper.formatMessage(tr("Failed to delete ban. {:br:}Error: {}"), error)).open();
|
||||
createErrorModal(tr("Failed to delete ban"), formatMessage(tr("Failed to delete ban. {:br:}Error: {}"), error)).open();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -721,7 +727,7 @@ namespace Modals {
|
|||
if(index > 0) index--;
|
||||
input_duration_type.find("option[value='" + periods[index] + "']").prop("selected", true);
|
||||
input_duration_value.val(Math.ceil(duration / duration_data[periods[index]].scale));
|
||||
tooltip_duration_detailed.text($.spawn("div").append(...MessageHelper.formatMessage(tr("The ban lasts for exact {}."), MessageHelper.format_time(duration * 1000, "never"))).text());
|
||||
tooltip_duration_detailed.text($.spawn("div").append(...formatMessage(tr("The ban lasts for exact {}."), format_time(duration * 1000, "never"))).text());
|
||||
} else {
|
||||
tooltip_duration_detailed.text(tr("The ban is forever."));
|
||||
input_duration_value.attr("placeholder", tr("for ever")).val(null).prop('disabled', true);
|
||||
|
@ -783,7 +789,7 @@ namespace Modals {
|
|||
log.error(LogCategory.CLIENT, tr("Failed to edited ban: %o"), error);
|
||||
if(error instanceof CommandResult)
|
||||
error = error.id === ErrorID.PERMISSION_ERROR ? "no permissions" : error.extra_message || error.message;
|
||||
createErrorModal(tr("Failed to edited ban"), MessageHelper.formatMessage(tr("Failed to edited ban. {:br:}Error: {}"), error)).open();
|
||||
createErrorModal(tr("Failed to edited ban"), formatMessage(tr("Failed to edited ban. {:br:}Error: {}"), error)).open();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -851,7 +857,7 @@ namespace Modals {
|
|||
log.error(LogCategory.CLIENT, tr("Failed to add ban: %o"), error);
|
||||
if(error instanceof CommandResult)
|
||||
error = error.id === ErrorID.PERMISSION_ERROR ? "no permissions" : error.extra_message || error.message;
|
||||
createErrorModal(tr("Failed to add ban"), MessageHelper.formatMessage(tr("Failed to add ban. {:br:}Error: {}"), error)).open();
|
||||
createErrorModal(tr("Failed to add ban"), formatMessage(tr("Failed to add ban. {:br:}Error: {}"), error)).open();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -913,9 +919,6 @@ namespace Modals {
|
|||
update_edit_window(false);
|
||||
update_banlist();
|
||||
|
||||
tooltip(template);
|
||||
tooltip.initialize(template);
|
||||
return template.children();
|
||||
}
|
||||
}
|
||||
|
||||
//container-triggerlist
|
|
@ -1,15 +1,31 @@
|
|||
/// <reference path="../../ui/elements/modal.ts" />
|
||||
/// <reference path="../../ConnectionHandler.ts" />
|
||||
/// <reference path="../../proto.ts" />
|
||||
import {createInputModal, createModal, Modal} from "tc-shared/ui/elements/Modal";
|
||||
import {
|
||||
Bookmark,
|
||||
bookmarks,
|
||||
BookmarkType, boorkmak_connect, create_bookmark, create_bookmark_directory,
|
||||
delete_bookmark,
|
||||
DirectoryBookmark,
|
||||
save_bookmark
|
||||
} from "tc-shared/bookmarks";
|
||||
import {connection_log, Regex} from "tc-shared/ui/modal/ModalConnect";
|
||||
import {IconManager} from "tc-shared/FileManager";
|
||||
import {profiles} from "tc-shared/profiles/ConnectionProfile";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as log from "tc-shared/log";
|
||||
import * as i18nc from "tc-shared/i18n/country";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {control_bar} from "tc-shared/ui/frames/ControlBar";
|
||||
import * as top_menu from "../frames/MenuBar";
|
||||
|
||||
namespace Modals {
|
||||
export function spawnBookmarkModal() {
|
||||
let modal: Modal;
|
||||
modal = createModal({
|
||||
header: tr("Manage bookmarks"),
|
||||
body: () => {
|
||||
let template = $("#tmpl_manage_bookmarks").renderTag({ });
|
||||
let selected_bookmark: bookmarks.Bookmark | bookmarks.DirectoryBookmark | undefined;
|
||||
let selected_bookmark: Bookmark | DirectoryBookmark | undefined;
|
||||
|
||||
const button_delete = template.find(".button-delete");
|
||||
const button_add_folder = template.find(".button-add-folder");
|
||||
|
@ -35,31 +51,31 @@ namespace Modals {
|
|||
|
||||
const update_buttons = () => {
|
||||
button_delete.prop("disabled", !selected_bookmark);
|
||||
button_connect.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY);
|
||||
button_connect_tab.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY);
|
||||
button_connect.prop("disabled", !selected_bookmark || selected_bookmark.type !== BookmarkType.ENTRY);
|
||||
button_connect_tab.prop("disabled", !selected_bookmark || selected_bookmark.type !== BookmarkType.ENTRY);
|
||||
};
|
||||
|
||||
const update_connect_info = () => {
|
||||
if(selected_bookmark && selected_bookmark.type === bookmarks.BookmarkType.ENTRY) {
|
||||
const entry = selected_bookmark as bookmarks.Bookmark;
|
||||
if(selected_bookmark && selected_bookmark.type === BookmarkType.ENTRY) {
|
||||
const entry = selected_bookmark as Bookmark;
|
||||
|
||||
const history = connection_log.history().find(e => e.address.hostname === entry.server_properties.server_address && e.address.port === entry.server_properties.server_port);
|
||||
if(history) {
|
||||
label_server_name.text(history.name);
|
||||
label_server_region.empty().append(
|
||||
$.spawn("div").addClass("country flag-" + history.country.toLowerCase()),
|
||||
$.spawn("div").text(i18n.country_name(history.country, tr("Global")))
|
||||
$.spawn("div").text(i18nc.country_name(history.country, tr("Global")))
|
||||
);
|
||||
label_client_count.text(history.clients_online + "/" + history.clients_total);
|
||||
label_connection_count.empty().append(
|
||||
...MessageHelper.formatMessage(tr("You've connected {} times"), $.spawn("div").addClass("connect-count").text(history.total_connection))
|
||||
...formatMessage(tr("You've connected {} times"), $.spawn("div").addClass("connect-count").text(history.total_connection))
|
||||
);
|
||||
} else {
|
||||
label_server_name.text(tr("Unknown"));
|
||||
label_server_region.empty().text(tr("Unknown"));
|
||||
label_client_count.text(tr("Unknown"));
|
||||
label_connection_count.empty().append(
|
||||
...MessageHelper.formatMessage(tr("You {} connected to that server address"), $.spawn("div").addClass("connect-never").text("never"))
|
||||
...formatMessage(tr("You {} connected to that server address"), $.spawn("div").addClass("connect-never").text("never"))
|
||||
);
|
||||
}
|
||||
label_last_ping.text(tr("Average ping isn't yet supported"));
|
||||
|
@ -74,17 +90,17 @@ namespace Modals {
|
|||
|
||||
const update_selected = () => {
|
||||
input_bookmark_name.prop("disabled", !selected_bookmark);
|
||||
input_connect_profile.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY);
|
||||
input_server_address.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY);
|
||||
input_server_password.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY);
|
||||
input_connect_profile.prop("disabled", !selected_bookmark || selected_bookmark.type !== BookmarkType.ENTRY);
|
||||
input_server_address.prop("disabled", !selected_bookmark || selected_bookmark.type !== BookmarkType.ENTRY);
|
||||
input_server_password.prop("disabled", !selected_bookmark || selected_bookmark.type !== BookmarkType.ENTRY);
|
||||
|
||||
if(selected_bookmark) {
|
||||
input_bookmark_name.val(selected_bookmark.display_name);
|
||||
label_bookmark_name.text(selected_bookmark.display_name);
|
||||
}
|
||||
|
||||
if(selected_bookmark && selected_bookmark.type === bookmarks.BookmarkType.ENTRY) {
|
||||
const entry = selected_bookmark as bookmarks.Bookmark;
|
||||
if(selected_bookmark && selected_bookmark.type === BookmarkType.ENTRY) {
|
||||
const entry = selected_bookmark as Bookmark;
|
||||
|
||||
const address = entry.server_properties.server_address + (entry.server_properties.server_port == 9987 ? "" : (" " + entry.server_properties.server_port));
|
||||
label_server_address.text(address);
|
||||
|
@ -113,9 +129,9 @@ namespace Modals {
|
|||
update_selected();
|
||||
|
||||
const hide_links: boolean[] = [];
|
||||
const build_entry = (entry: bookmarks.Bookmark | bookmarks.DirectoryBookmark, sibling_data: {first: boolean; last: boolean;}, index: number) => {
|
||||
const build_entry = (entry: Bookmark | DirectoryBookmark, sibling_data: {first: boolean; last: boolean;}, index: number) => {
|
||||
let container = $.spawn("div")
|
||||
.addClass(entry.type === bookmarks.BookmarkType.ENTRY ? "bookmark" : "directory")
|
||||
.addClass(entry.type === BookmarkType.ENTRY ? "bookmark" : "directory")
|
||||
.addClass(index > 0 ? "linked" : "")
|
||||
.addClass(sibling_data.first ? "link-start" : "");
|
||||
for (let i = 0; i < index; i++) {
|
||||
|
@ -127,8 +143,8 @@ namespace Modals {
|
|||
);
|
||||
}
|
||||
|
||||
if (entry.type === bookmarks.BookmarkType.ENTRY) {
|
||||
const bookmark = entry as bookmarks.Bookmark;
|
||||
if (entry.type === BookmarkType.ENTRY) {
|
||||
const bookmark = entry as Bookmark;
|
||||
container.append(
|
||||
bookmark.last_icon_id ?
|
||||
IconManager.generate_tag(IconManager.load_cached_icon(bookmark.last_icon_id || 0), {animate: false}) :
|
||||
|
@ -160,14 +176,14 @@ namespace Modals {
|
|||
|
||||
hide_links.push(sibling_data.last);
|
||||
let cindex = 0;
|
||||
const children = (entry as bookmarks.DirectoryBookmark).content || [];
|
||||
const children = (entry as DirectoryBookmark).content || [];
|
||||
for (const child of children)
|
||||
build_entry(child, {first: cindex++ == 0, last: cindex == children.length}, index + 1);
|
||||
hide_links.pop();
|
||||
};
|
||||
|
||||
let cindex = 0;
|
||||
const children = bookmarks.bookmarks().content;
|
||||
const children = bookmarks().content;
|
||||
for (const bookmark of children)
|
||||
build_entry(bookmark, {first: cindex++ == 0, last: cindex == children.length}, 0);
|
||||
};
|
||||
|
@ -180,7 +196,7 @@ namespace Modals {
|
|||
.text("")
|
||||
.css("display", "none")
|
||||
);
|
||||
for(const profile of profiles.profiles()) {
|
||||
for(const profile of profiles()) {
|
||||
input_connect_profile.append(
|
||||
$.spawn("option")
|
||||
.attr("value", profile.id)
|
||||
|
@ -194,17 +210,17 @@ namespace Modals {
|
|||
button_delete.on('click', event => {
|
||||
if(!selected_bookmark) return;
|
||||
|
||||
if(selected_bookmark.type === bookmarks.BookmarkType.DIRECTORY && (selected_bookmark as bookmarks.DirectoryBookmark).content.length > 0) {
|
||||
Modals.spawnYesNo(tr("Are you sure"), tr("Do you really want to delete this non empty directory?"), answer => {
|
||||
if(selected_bookmark.type === BookmarkType.DIRECTORY && (selected_bookmark as DirectoryBookmark).content.length > 0) {
|
||||
spawnYesNo(tr("Are you sure"), tr("Do you really want to delete this non empty directory?"), answer => {
|
||||
if(answer) {
|
||||
bookmarks.delete_bookmark(selected_bookmark);
|
||||
bookmarks.save_bookmark(selected_bookmark);
|
||||
delete_bookmark(selected_bookmark);
|
||||
save_bookmark(selected_bookmark);
|
||||
update_bookmark_list(undefined);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
bookmarks.delete_bookmark(selected_bookmark);
|
||||
bookmarks.save_bookmark(selected_bookmark);
|
||||
delete_bookmark(selected_bookmark);
|
||||
save_bookmark(selected_bookmark);
|
||||
update_bookmark_list(undefined);
|
||||
}
|
||||
});
|
||||
|
@ -214,15 +230,15 @@ namespace Modals {
|
|||
return true;
|
||||
}, result => {
|
||||
if(result) {
|
||||
const mark = bookmarks.create_bookmark_directory(
|
||||
const mark = create_bookmark_directory(
|
||||
selected_bookmark ?
|
||||
selected_bookmark.type === bookmarks.BookmarkType.DIRECTORY ?
|
||||
selected_bookmark as bookmarks.DirectoryBookmark :
|
||||
selected_bookmark.type === BookmarkType.DIRECTORY ?
|
||||
selected_bookmark as DirectoryBookmark :
|
||||
selected_bookmark.parent :
|
||||
bookmarks.bookmarks(),
|
||||
bookmarks(),
|
||||
result as string
|
||||
);
|
||||
bookmarks.save_bookmark(mark);
|
||||
save_bookmark(mark);
|
||||
update_bookmark_list(mark.unique_id);
|
||||
}
|
||||
}).open();
|
||||
|
@ -233,30 +249,30 @@ namespace Modals {
|
|||
return true;
|
||||
}, result => {
|
||||
if(result) {
|
||||
const mark = bookmarks.create_bookmark(result as string,
|
||||
const mark = create_bookmark(result as string,
|
||||
selected_bookmark ?
|
||||
selected_bookmark.type === bookmarks.BookmarkType.DIRECTORY ?
|
||||
selected_bookmark as bookmarks.DirectoryBookmark :
|
||||
selected_bookmark.type === BookmarkType.DIRECTORY ?
|
||||
selected_bookmark as DirectoryBookmark :
|
||||
selected_bookmark.parent :
|
||||
bookmarks.bookmarks(), {
|
||||
bookmarks(), {
|
||||
server_password: "",
|
||||
server_port: 9987,
|
||||
server_address: "",
|
||||
server_password_hash: ""
|
||||
}, "");
|
||||
bookmarks.save_bookmark(mark);
|
||||
save_bookmark(mark);
|
||||
update_bookmark_list(mark.unique_id);
|
||||
}
|
||||
}).open();
|
||||
});
|
||||
|
||||
button_connect_tab.on('click', event => {
|
||||
bookmarks.boorkmak_connect(selected_bookmark as bookmarks.Bookmark, true);
|
||||
boorkmak_connect(selected_bookmark as Bookmark, true);
|
||||
modal.close();
|
||||
}).toggle(!settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION));
|
||||
|
||||
button_connect.on('click', event => {
|
||||
bookmarks.boorkmak_connect(selected_bookmark as bookmarks.Bookmark, false);
|
||||
boorkmak_connect(selected_bookmark as Bookmark, false);
|
||||
modal.close();
|
||||
});
|
||||
}
|
||||
|
@ -280,7 +296,7 @@ namespace Modals {
|
|||
input_server_address.firstParent(".input-boxed").toggleClass("is-invalid", !valid);
|
||||
|
||||
if(valid) {
|
||||
const entry = selected_bookmark as bookmarks.Bookmark;
|
||||
const entry = selected_bookmark as Bookmark;
|
||||
let _v6_end = address.indexOf(']');
|
||||
let idx = address.lastIndexOf(':');
|
||||
if(idx != -1 && idx > _v6_end) {
|
||||
|
@ -298,9 +314,9 @@ namespace Modals {
|
|||
|
||||
input_connect_profile.on('change', event => {
|
||||
const id = input_connect_profile.val() as string;
|
||||
const profile = profiles.profiles().find(e => e.id === id);
|
||||
const profile = profiles().find(e => e.id === id);
|
||||
if(profile) {
|
||||
(selected_bookmark as bookmarks.Bookmark).connect_profile = id;
|
||||
(selected_bookmark as Bookmark).connect_profile = id;
|
||||
} else {
|
||||
log.warn(LogCategory.GENERAL, tr("Failed to change connect profile for profile %s to %s"), selected_bookmark.unique_id, id);
|
||||
}
|
||||
|
@ -365,4 +381,3 @@ namespace Modals {
|
|||
|
||||
modal.open();
|
||||
}
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
/// <reference path="../../ui/elements/modal.ts" />
|
||||
/// <reference path="../../ConnectionHandler.ts" />
|
||||
/// <reference path="../../proto.ts" />
|
||||
import {createModal, Modal} from "tc-shared/ui/elements/Modal";
|
||||
import {ClientEntry} from "tc-shared/ui/client";
|
||||
import {voice} from "tc-shared/connection/ConnectionBase";
|
||||
import LatencySettings = voice.LatencySettings;
|
||||
import {Slider, sliderfy} from "tc-shared/ui/elements/Slider";
|
||||
import * as htmltags from "tc-shared/ui/htmltags";
|
||||
|
||||
namespace Modals {
|
||||
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: LatencySettings, reset: () => LatencySettings, apply: (settings: LatencySettings) => any, callback_flush?: () => any) {
|
||||
if(modal) modal.close();
|
||||
|
||||
const begin = Object.assign({}, current);
|
||||
|
@ -111,4 +113,3 @@ namespace Modals {
|
|||
modal.open();
|
||||
modal.htmlTag.find(".modal-body").addClass("modal-latency");
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
/// <reference path="../../ui/elements/modal.ts" />
|
||||
/// <reference path="../../ConnectionHandler.ts" />
|
||||
/// <reference path="../../proto.ts" />
|
||||
|
||||
namespace Modals {
|
||||
//TODO: Use the max limit!
|
||||
|
||||
import {sliderfy} from "tc-shared/ui/elements/Slider";
|
||||
import {createModal, Modal} from "tc-shared/ui/elements/Modal";
|
||||
import {ClientEntry} from "tc-shared/ui/client";
|
||||
import * as htmltags from "tc-shared/ui/htmltags";
|
||||
|
||||
let modal: Modal;
|
||||
export function spawnChangeVolume(client: ClientEntry, local: boolean, current: number, max: number | undefined, callback: (number) => void) {
|
||||
if(modal) modal.close();
|
||||
|
@ -74,4 +74,3 @@ namespace Modals {
|
|||
modal.open();
|
||||
modal.htmlTag.find(".modal-body").addClass("modal-volume");
|
||||
}
|
||||
}
|
|
@ -1,4 +1,9 @@
|
|||
namespace Modals {
|
||||
import {createInfoModal, createModal, Modal} from "tc-shared/ui/elements/Modal";
|
||||
import {ChannelEntry} from "tc-shared/ui/channel";
|
||||
import {copy_to_clipboard} from "tc-shared/utils/helpers";
|
||||
import * as tooltip from "tc-shared/ui/elements/Tooltip";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
|
||||
export function openChannelInfo(channel: ChannelEntry) {
|
||||
let modal: Modal;
|
||||
|
||||
|
@ -22,7 +27,7 @@ namespace Modals {
|
|||
button_update.on('click', event => update_values(modal.htmlTag));
|
||||
|
||||
update_values(template);
|
||||
tooltip(template);
|
||||
tooltip.initialize(template);
|
||||
return template.children();
|
||||
},
|
||||
footer: null,
|
||||
|
@ -33,6 +38,7 @@ namespace Modals {
|
|||
modal.open();
|
||||
}
|
||||
|
||||
declare const xbbcode;
|
||||
function apply_channel_description(container: JQuery, channel: ChannelEntry) {
|
||||
const container_value = container.find(".value");
|
||||
const container_no_value = container.find(".no-value");
|
||||
|
@ -85,7 +91,7 @@ namespace Modals {
|
|||
else if(channel.properties.channel_conversation_history_length == 0)
|
||||
tag.text(tr("Public; Permanent message saving"));
|
||||
else
|
||||
tag.append(MessageHelper.formatMessage(tr("Public; Saving last {} messages"), channel.properties.channel_conversation_history_length));
|
||||
tag.append(formatMessage(tr("Public; Saving last {} messages"), channel.properties.channel_conversation_history_length));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,4 +155,3 @@ namespace Modals {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,11 @@
|
|||
namespace Modals {
|
||||
import {ClientConnectionInfo, ClientEntry} from "tc-shared/ui/client";
|
||||
import PermissionType from "tc-shared/permission/PermissionType";
|
||||
import {createInfoModal, createModal, Modal} from "tc-shared/ui/elements/Modal";
|
||||
import {copy_to_clipboard} from "tc-shared/utils/helpers";
|
||||
import * as i18nc from "tc-shared/i18n/country";
|
||||
import * as tooltip from "tc-shared/ui/elements/Tooltip";
|
||||
import {format_number, network} from "tc-shared/ui/frames/chat";
|
||||
|
||||
type InfoUpdateCallback = (info: ClientConnectionInfo) => any;
|
||||
export function openClientInfo(client: ClientEntry) {
|
||||
let modal: Modal;
|
||||
|
@ -31,7 +38,7 @@ namespace Modals {
|
|||
apply_groups(client, template.find(".container-groups"), modal, update_callbacks);
|
||||
apply_packets(client, template.find(".container-packets"), modal, update_callbacks);
|
||||
|
||||
tooltip(template);
|
||||
tooltip.initialize(template);
|
||||
return template.children();
|
||||
},
|
||||
footer: null,
|
||||
|
@ -119,6 +126,7 @@ namespace Modals {
|
|||
}
|
||||
}
|
||||
|
||||
declare const moment;
|
||||
function apply_basic_info(client: ClientEntry, tag: JQuery, modal: Modal, callbacks: InfoUpdateCallback[]) {
|
||||
/* Unique ID */
|
||||
{
|
||||
|
@ -191,7 +199,7 @@ namespace Modals {
|
|||
const container = tag.find(".property-country");
|
||||
container.find(".value").empty().append(
|
||||
$.spawn("div").addClass("country flag-" + client.properties.client_country.toLowerCase()),
|
||||
$.spawn("a").text(i18n.country_name(client.properties.client_country, tr("Unknown")))
|
||||
$.spawn("a").text(i18nc.country_name(client.properties.client_country, tr("Unknown")))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -361,7 +369,7 @@ namespace Modals {
|
|||
if(packets == 0 && info.connection_packets_received_keepalive == -1)
|
||||
node_downstream.innerText = tr("Not calculated");
|
||||
else
|
||||
node_downstream.innerText = MessageHelper.format_number(packets, {unit: "Packets"});
|
||||
node_downstream.innerText = format_number(packets, {unit: "Packets"});
|
||||
});
|
||||
node_downstream.innerText = tr("loading...");
|
||||
}
|
||||
|
@ -375,7 +383,7 @@ namespace Modals {
|
|||
if(packets == 0 && info.connection_packets_sent_keepalive == -1)
|
||||
node_upstream.innerText = tr("Not calculated");
|
||||
else
|
||||
node_upstream.innerText = MessageHelper.format_number(packets, {unit: "Packets"});
|
||||
node_upstream.innerText = format_number(packets, {unit: "Packets"});
|
||||
});
|
||||
node_upstream.innerText = tr("loading...");
|
||||
}
|
||||
|
@ -396,7 +404,7 @@ namespace Modals {
|
|||
if(bytes == 0 && info.connection_bytes_received_keepalive == -1)
|
||||
node_downstream.innerText = tr("Not calculated");
|
||||
else
|
||||
node_downstream.innerText = MessageHelper.network.format_bytes(bytes);
|
||||
node_downstream.innerText = network.format_bytes(bytes);
|
||||
});
|
||||
node_downstream.innerText = tr("loading...");
|
||||
}
|
||||
|
@ -410,7 +418,7 @@ namespace Modals {
|
|||
if(bytes == 0 && info.connection_bytes_sent_keepalive == -1)
|
||||
node_upstream.innerText = tr("Not calculated");
|
||||
else
|
||||
node_upstream.innerText = MessageHelper.network.format_bytes(bytes);
|
||||
node_upstream.innerText = network.format_bytes(bytes);
|
||||
});
|
||||
node_upstream.innerText = tr("loading...");
|
||||
}
|
||||
|
@ -431,7 +439,7 @@ namespace Modals {
|
|||
if(bytes == 0 && info.connection_bandwidth_received_last_second_keepalive == -1)
|
||||
node_downstream.innerText = tr("Not calculated");
|
||||
else
|
||||
node_downstream.innerText = MessageHelper.network.format_bytes(bytes, {time: "s"});
|
||||
node_downstream.innerText = network.format_bytes(bytes, {time: "s"});
|
||||
});
|
||||
node_downstream.innerText = tr("loading...");
|
||||
}
|
||||
|
@ -445,7 +453,7 @@ namespace Modals {
|
|||
if(bytes == 0 && info.connection_bandwidth_sent_last_second_keepalive == -1)
|
||||
node_upstream.innerText = tr("Not calculated");
|
||||
else
|
||||
node_upstream.innerText = MessageHelper.network.format_bytes(bytes, {time: "s"});
|
||||
node_upstream.innerText = network.format_bytes(bytes, {time: "s"});
|
||||
});
|
||||
node_upstream.innerText = tr("loading...");
|
||||
}
|
||||
|
@ -466,7 +474,7 @@ namespace Modals {
|
|||
if(bytes == 0 && info.connection_bandwidth_received_last_minute_keepalive == -1)
|
||||
node_downstream.innerText = tr("Not calculated");
|
||||
else
|
||||
node_downstream.innerText = MessageHelper.network.format_bytes(bytes, {time: "s"});
|
||||
node_downstream.innerText = network.format_bytes(bytes, {time: "s"});
|
||||
});
|
||||
node_downstream.innerText = tr("loading...");
|
||||
}
|
||||
|
@ -480,7 +488,7 @@ namespace Modals {
|
|||
if(bytes == 0 && info.connection_bandwidth_sent_last_minute_keepalive == -1)
|
||||
node_upstream.innerText = tr("Not calculated");
|
||||
else
|
||||
node_upstream.innerText = MessageHelper.network.format_bytes(bytes, {time: "s"});
|
||||
node_upstream.innerText = network.format_bytes(bytes, {time: "s"});
|
||||
});
|
||||
node_upstream.innerText = tr("loading...");
|
||||
}
|
||||
|
@ -495,7 +503,7 @@ namespace Modals {
|
|||
if(node_downstream) {
|
||||
client.updateClientVariables().then(info => {
|
||||
//TODO: Test for own client info and if so then show the max quota (needed permission)
|
||||
node_downstream.innerText = MessageHelper.network.format_bytes(client.properties.client_month_bytes_downloaded, {exact: false});
|
||||
node_downstream.innerText = network.format_bytes(client.properties.client_month_bytes_downloaded, {exact: false});
|
||||
});
|
||||
node_downstream.innerText = tr("loading...");
|
||||
}
|
||||
|
@ -503,10 +511,9 @@ namespace Modals {
|
|||
if(node_upstream) {
|
||||
client.updateClientVariables().then(info => {
|
||||
//TODO: Test for own client info and if so then show the max quota (needed permission)
|
||||
node_upstream.innerText = MessageHelper.network.format_bytes(client.properties.client_month_bytes_uploaded, {exact: false});
|
||||
node_upstream.innerText = network.format_bytes(client.properties.client_month_bytes_uploaded, {exact: false});
|
||||
});
|
||||
node_upstream.innerText = tr("loading...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,17 @@
|
|||
/// <reference path="../../ui/elements/modal.ts" />
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import * as log from "tc-shared/log";
|
||||
import * as loader from "tc-loader";
|
||||
import {createModal} from "tc-shared/ui/elements/Modal";
|
||||
import {ConnectionProfile, default_profile, find_profile, profiles} from "tc-shared/profiles/ConnectionProfile";
|
||||
import {KeyCode} from "tc-shared/PPTListener";
|
||||
import {IconManager} from "tc-shared/FileManager";
|
||||
import * as i18nc from "tc-shared/i18n/country";
|
||||
import {spawnSettingsModal} from "tc-shared/ui/modal/ModalSettings";
|
||||
import {server_connections} from "tc-shared/ui/frames/connection_handlers";
|
||||
|
||||
//FIXME: Move this shit out of this file!
|
||||
namespace connection_log {
|
||||
export namespace connection_log {
|
||||
//TODO: Save password data
|
||||
export type ConnectionData = {
|
||||
name: string;
|
||||
|
@ -91,11 +101,11 @@ namespace connection_log {
|
|||
});
|
||||
}
|
||||
|
||||
namespace Modals {
|
||||
declare const native_client;
|
||||
export function spawnConnectModal(options: {
|
||||
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}) {
|
||||
let selected_profile: profiles.ConnectionProfile;
|
||||
}, defaultHost: { url: string, enforce: boolean} = { url: "ts.TeaSpeak.de", enforce: false}, connect_profile?: { profile: ConnectionProfile, enforce: boolean}) {
|
||||
let selected_profile: ConnectionProfile;
|
||||
|
||||
const random_id = (() => {
|
||||
const array = new Uint32Array(10);
|
||||
|
@ -180,7 +190,7 @@ namespace Modals {
|
|||
button_connect.trigger('click');
|
||||
});
|
||||
button_manage.on('click', event => {
|
||||
const modal = Modals.spawnSettingsModal("identity-profiles");
|
||||
const modal = spawnSettingsModal("identity-profiles");
|
||||
modal.close_listener.push(() => {
|
||||
input_profile.trigger('change');
|
||||
});
|
||||
|
@ -189,14 +199,14 @@ namespace Modals {
|
|||
|
||||
/* Connect Profiles */
|
||||
{
|
||||
for(const profile of profiles.profiles()) {
|
||||
for(const profile of profiles()) {
|
||||
input_profile.append(
|
||||
$.spawn("option").text(profile.profile_name).val(profile.id)
|
||||
);
|
||||
}
|
||||
|
||||
input_profile.on('change', event => {
|
||||
selected_profile = profiles.find_profile(input_profile.val() as string) || profiles.default_profile();
|
||||
selected_profile = find_profile(input_profile.val() as string) || default_profile();
|
||||
{
|
||||
settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, undefined);
|
||||
input_nickname
|
||||
|
@ -289,7 +299,7 @@ namespace Modals {
|
|||
).append(
|
||||
$.spawn("div").addClass("column country-name").append([
|
||||
$.spawn("div").addClass("country flag-" + entry.country.toLowerCase()),
|
||||
$.spawn("a").text(i18n.country_name(entry.country, tr("Global")))
|
||||
$.spawn("a").text(i18nc.country_name(entry.country, tr("Global")))
|
||||
])
|
||||
).append(
|
||||
$.spawn("div").addClass("column clients").text(entry.clients_online + "/" + entry.clients_total)
|
||||
|
@ -328,4 +338,3 @@ namespace Modals {
|
|||
IP_V6: /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/,
|
||||
IP: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/,
|
||||
};
|
||||
}
|
|
@ -1,6 +1,16 @@
|
|||
/// <reference path="../../ui/elements/modal.ts" />
|
||||
import PermissionType from "tc-shared/permission/PermissionType";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {ChannelEntry, ChannelProperties} from "tc-shared/ui/channel";
|
||||
import {PermissionManager, PermissionValue} from "tc-shared/permission/PermissionManager";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {createModal} from "tc-shared/ui/elements/Modal";
|
||||
import * as log from "tc-shared/log";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import * as tooltip from "tc-shared/ui/elements/Tooltip";
|
||||
import {spawnIconSelect} from "tc-shared/ui/modal/ModalIconSelect";
|
||||
import {hashPassword} from "tc-shared/utils/helpers";
|
||||
import {sliderfy} from "tc-shared/ui/elements/Slider";
|
||||
|
||||
namespace Modals {
|
||||
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
|
||||
const modal = createModal({
|
||||
|
@ -92,7 +102,7 @@ namespace Modals {
|
|||
callback(properties, updated); //First may create the channel
|
||||
});
|
||||
|
||||
tooltip(modal.htmlTag);
|
||||
tooltip.initialize(modal.htmlTag);
|
||||
modal.htmlTag.find(".button_cancel").click(() => {
|
||||
modal.close();
|
||||
callback();
|
||||
|
@ -121,7 +131,7 @@ namespace Modals {
|
|||
}
|
||||
|
||||
tag.find(".button-select-icon").on('click', event => {
|
||||
Modals.spawnIconSelect(connection, id => {
|
||||
spawnIconSelect(connection, id => {
|
||||
const icon_node = tag.find(".icon-preview");
|
||||
icon_node.children().remove();
|
||||
icon_node.append(connection.fileManager.icons.generateTag(id));
|
||||
|
@ -145,7 +155,7 @@ namespace Modals {
|
|||
tag.find(".channel_password").change(function (this: HTMLInputElement) {
|
||||
properties.channel_flag_password = this.value.length != 0;
|
||||
if(properties.channel_flag_password)
|
||||
helpers.hashPassword(this.value).then(pass => properties.channel_password = pass);
|
||||
hashPassword(this.value).then(pass => properties.channel_password = pass);
|
||||
|
||||
channel_password.removeClass("input_error");
|
||||
if(!properties.channel_flag_password)
|
||||
|
@ -708,4 +718,3 @@ namespace Modals {
|
|||
}).prop("disabled", !permission).parent(".input-boxed").toggleClass("disabled", !permission);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue