This commit is contained in:
WolverinDEV 2019-08-31 18:31:01 +02:00
parent 3b3430db5e
commit 63683eadba
27 changed files with 1322 additions and 578 deletions

View file

@ -316,12 +316,50 @@
]
];
$CERTACCEPT_FILE_LIST = [
[ /* html files */
"type" => "html",
"search-pattern" => "/^([a-zA-Z]+)\.(html|php|json)$/",
"build-target" => "dev|rel",
"path" => "./popup/certaccept/",
"local-path" => "./shared/popup/certaccept/html/"
],
[ /* javascript loader (debug) */
"type" => "js",
"search-pattern" => "/.*\.js$/",
"build-target" => "dev",
"path" => "./popup/certaccept/loader/",
"local-path" => "./shared/loader/"
],
[ /* javascript loader for releases */
"type" => "js",
"search-pattern" => "/.*loader_certaccept.min.js$/",
"build-target" => "rel",
"path" => "./popup/certaccept/loader/",
"local-path" => "./shared/generated/"
],
[ /* javascript for debug */
"type" => "js",
"search-pattern" => "/^.*\.js$/",
"build-target" => "dev",
"path" => "./popup/certaccept/js/",
"local-path" => "./shared/js/"
],
];
$APP_FILE_LIST = array_merge(
$APP_FILE_LIST_SHARED_SOURCE,
$APP_FILE_LIST_SHARED_VENDORS,
$APP_FILE_LIST_CLIENT_SOURCE,
$APP_FILE_LIST_WEB_SOURCE,
$APP_FILE_LIST_WEB_TEASPEAK
$APP_FILE_LIST_WEB_TEASPEAK,
$CERTACCEPT_FILE_LIST
);
function systemify_path($path) {
@ -472,8 +510,8 @@
$file = new AppFile;
$f_info = pathinfo($f_entry);
$file->target_path = systemify_path($entry["path"]) . DIRECTORY_SEPARATOR . $f_info["dirname"] . DIRECTORY_SEPARATOR;
$file->local_path = getcwd() . DIRECTORY_SEPARATOR . systemify_path($entry["local-path"]) . DIRECTORY_SEPARATOR . $f_info["dirname"] . DIRECTORY_SEPARATOR;
$file->target_path = realpath(systemify_path($entry["path"]) . DIRECTORY_SEPARATOR . $f_info["dirname"] . DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$file->local_path = realpath(getcwd() . DIRECTORY_SEPARATOR . systemify_path($entry["local-path"]) . DIRECTORY_SEPARATOR . $f_info["dirname"] . DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$file->name = $f_info["basename"];
$file->type = $entry["type"];
@ -481,7 +519,7 @@
if(strlen($file->hash) > 0) {
foreach ($result as $e)
if($e->hash == $file->hash) goto ignore;
if($e->hash == $file->hash && $e->target_path == $file->target_path) goto ignore;
}
array_push($result, $file);
ignore:

View file

@ -1,162 +1,5 @@
.modal .modal-bookmarks {
padding: 5px;
display: flex;
flex-direction: column;
justify-content: stretch;
.bookmark-list {
flex-grow: 1;
flex-shrink: 1;
min-height: 75px;
display: flex;
flex-direction: column;
justify-content: stretch;
.list {
display: flex;
flex-direction: column;
justify-content: start;
overflow-y: auto;
.entry {
flex-grow: 1;
flex-shrink: 1;
> .name {
cursor: pointer;
}
&.bookmark {
&.selected {
background-color: #0000FF77;
}
}
&.directory {
&.selected {
> .name {
background-color: #0000FF77;
}
}
> .name {
border: 0 solid gray;
border-bottom: 1px solid #ad9d9d33;
}
.members {
margin-left: 15px;
}
}
}
}
}
.buttons {
flex-grow: 0;
flex-shrink: 0;
display: flex;
justify-content: space-between;
flex-direction: row;
margin-top: 5px;
text-align: right;
button {
margin-left: 5px;
}
.button-large {
display: block;
}
.button-small {
display: none;
}
}
@media (max-width: 1000px) {
.buttons {
.button-large {
display: none;
}
.button-small {
display: block;
}
}
}
.group_box {
flex-shrink: 1;
.header {
flex-grow: 0;
flex-shrink: 0;
}
&.gb-settings {
overflow-y: auto;
}
&.gb-list {
min-height: 100px; /* 25px header + 75px body */
}
}
.bookmark-setting {
.group_box {
margin-top: 5px;
}
.property {
display: flex;
flex-direction: row;
justify-content: stretch;
&:not(:first-of-type) {
margin-top: 5px;
}
input, select, .default-channel-container {
flex-grow: 1;
flex-shrink: 1;
}
.default-channel-container {
display: flex;
flex-direction: row;
justify-content: stretch;
button {
margin-left: 5px;
max-width: 120px;
}
}
.key {
width: 160px;
flex-grow: 0;
flex-shrink: 0;
}
}
.container-default-channel-select {
display: flex;
flex-direction: row;
justify-content: stretch;
.container-default-channel {
flex-grow: 1;
flex-shrink: 1;
}
}
}
}
@import "properties";
@import "mixin";
.modal .modal-bookmark-create {
.property {
@ -189,3 +32,436 @@
margin-bottom: 5px;
}
}
.modal-body.modal-bookmarks {
padding: 0!important;
display: flex;
flex-direction: row!important;
justify-content: stretch!important;
min-width: 30em!important;
height: 60em;
width: 80em;
.container-tooltip {
flex-shrink: 0;
flex-grow: 0;
position: relative;
width: 1.6em;
margin-left: .5em;
font-size: .9em;
display: flex;
flex-direction: column;
justify-content: center;
img {
height: 1em;
width: 1em;
align-self: center;
font-size: 1.2em;
}
.tooltip {
display: none;
}
}
.input-boxed {
height: 2em;
}
.left {
min-width: 12em;
width: 30%;
flex-grow: 1;
flex-shrink: 1;
padding: .5em;
background-color: #212125;
display: flex;
flex-direction: column;
justify-content: stretch;
.title {
flex-shrink: 0;
flex-grow: 0;
text-align: center;
font-size: 1.5em;
color: #557edc;
text-transform: uppercase;
}
.container-bookmarks {
flex-shrink: 1;
flex-grow: 1;
min-height: 6em;
display: flex;
flex-direction: column;
justify-content: stretch;
overflow: auto;
@include chat-scrollbar-vertical();
@include chat-scrollbar-horizontal();
.bookmark, .directory {
flex-grow: 0;
flex-shrink: 0;
display: flex;
flex-direction: row;
justify-content: stretch;
border-radius: $border_radius_middle;
padding: .25em .5em;
cursor: pointer;
.icon-container {
flex-grow: 0;
flex-shrink: 0;
align-self: center;
margin-right: .5em;
}
.name {
flex-grow: 1;
flex-shrink: 1;
min-width: 5em;
align-self: center;
}
&:hover {
background-color: #2c2d2f;
}
&.selected {
background-color: #1a1a1b;
}
.link {
flex-grow: 0;
flex-shrink: 0;
position: relative;
width: 1.5em;
$line_width: 2px;
$color: hsla(0, 0%, 35%, 1);
&:not(.hidden) {
&:before {
content: "";
position: absolute;
height: 2.25em; /* connect with the previous one */
width: .75em;
left: .5em; /* icons have a width of 1em */
bottom: calc(.75em - #{$line_width / 2});
border-left: $line_width solid $color;
}
&.connected {
&:before {
border-bottom: $line_width solid $color;
border-bottom-left-radius: .3em;
}
}
}
}
}
.link-start {
.link.connected {
&:before {
height: 1.25em;
}
}
}
.directory {
.name {
//color: #557edc;
}
}
}
.buttons {
flex-shrink: 0;
flex-grow: 0;
display: flex;
flex-direction: row;
justify-content: space-between;
padding-top: .5em;
button {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&:not(:first-of-type) {
margin-left: .5em;
}
}
}
}
.right {
min-width: 25em;
width: 30%;
flex-grow: 1;
flex-shrink: 1;
background-color: #2f2f35;
display: flex;
flex-direction: column;
justify-content: flex-start;
.header {
flex-grow: 0;
flex-shrink: 0;
height: 10em;
background: url('../../../img/bookmark_background.png'), url('../../img/bookmark_background.png') no-repeat;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: .5em;
.container-name {
font-size: 2em;
color: #fcfcfc;
}
.container-address {
font-size: 1.5em;
color: #fcfcfc;
}
}
.container-settings {
flex-grow: 1;
flex-shrink: 1;
min-height: 10em;
padding: .5em;
display: flex;
flex-direction: column;
justify-content: flex-start;
.group {
padding: .5em;
border-radius: .2em;
border: 1px solid #1f2122;
background-color: #28292b;
display: flex;
flex-direction: column;
justify-content: flex-start;
> .row {
display: flex;
flex-direction: row;
justify-content: stretch;
.key {
flex-grow: 0;
flex-shrink: 1;
width: 15em;
min-width: 2em;
align-self: center;
color: #557edc;
text-transform: uppercase;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.value {
flex-grow: 1;
flex-shrink: 1;
min-width: 2em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&:not(:first-of-type) {
margin-top: 1em;
}
}
&:not(:first-of-type) {
margin-top: 1em;
}
&.info {
flex-direction: row;
}
.container-image {
flex-grow: 0;
flex-shrink: 100;
max-width: 15em;
max-height: 9em; /* minus one padding */
width: 15em;
display: flex;
flex-direction: column;
justify-content: center;
img {
object-fit: contain;
max-height: 100%;
max-width: 100%;
}
@include transition(.25s ease-in-out);
}
.container-properties {
flex-shrink: 1;
flex-grow: 1;
min-width: 23em;
display: flex;
flex-direction: column;
justify-content: flex-start;
height: inherit;
.row {
flex-grow: 0;
flex-shrink: 0;
height: 1.8em;
display: flex;
flex-direction: row;
justify-content: flex-start;
.key {
flex-shrink: 0;
flex-grow: 0;
color: #557edc;
text-transform: uppercase;
align-self: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 15em;
}
.value {
color: #d6d6d7;
align-self: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.server-region {
> div {
display: inline-block;
}
.country {
margin-right: .25em;
}
}
.connect-count, .connect-never {
display: inline-block;
color: #7a3131;
}
}
}
.container-network {
display: flex;
flex-direction: row;
justify-content: center;
.container-button {
margin-right: 1em;
flex-shrink: 1;
min-width: 5em;
display: flex;
flex-direction: column;
justify-content: flex-end;
button {
height: 2.5em;
width: 12em;
max-width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.right {
flex-grow: 1;
}
}
}
}
}
.buttons {
padding: .5em;
display: flex;
flex-direction: row;
justify-content: flex-end;
button {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&:not(:first-of-type) {
margin-left: .5em;
}
}
}
}
}

View file

@ -211,7 +211,7 @@
display: flex;
flex-direction: row;
justify-content: flex-end;
justify-content: space-between;
button {
min-width: 8em;

View file

@ -14,23 +14,31 @@
<meta charset="UTF-8">
<!-- App min width: 450px -->
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, min-zoom=1, max-zoom: 1, user-scalable=no">
<meta name="description" content="TeaSpeak Web Client, connect to any TeaSpeak server without installing anything." />
<meta name="description" content="The TeaSpeak Web client is a in the browser running client for the VoIP communication software TeaSpeak." />
<meta name="keywords" content="TeaSpeak, TeaWeb, TeaSpeak-Web,Web client TeaSpeak, веб клієнт TeaSpeak, TSDNS, багатомовність, мультимовність, теми, функціонал"/>
<meta name="og:description" content="The TeaSpeak Web client is a in the browser running client for the VoIP communication software TeaSpeak." />
<meta name="og:url" content="https://web.teaspeak.de/">
<meta name="og:image" content="https://www.whatsapp.com/img/whatsapp-promo.png">
<!-- TODO Needs some fix -->
<link rel="manifest" href="manifest.json">
<?php
<?php
if(!$WEB_CLIENT) {
echo "<title>TeaClient</title>";
echo "\t\t<title>TeaClient</title>" . PHP_EOL;
echo "\t\t<meta name='og:title' content='TeaClient'>" . PHP_EOL;
} else {
echo "<title>TeaSpeak-Web</title>";
echo '<link rel="icon" href="img/favicon/teacup.png" type="image/x-icon">';
echo "\t\t<title>TeaSpeak-Web</title>" . PHP_EOL;
echo "\t\t<meta name='og:title' content='TeaSpeak-Web'>" . PHP_EOL;
echo "\t\t<link rel='shortcut icon' href='img/favicon/teacup.png' type='image/x-icon'>" . PHP_EOL;
//<link rel="apple-touch-icon" sizes="194x194" href="/apple-touch-icon.png" type="image/png">
}
?>
?>
<!-- PHP generated properties -->
<x-properties id="properties">
<?php
<?php
function spawn_property($name, $value, $element_id = null)
{
if(isset($value))
@ -44,9 +52,12 @@
if ($version === false)
$version = "unknown";
spawn_property("version", $version, "app_version");
?>
?>
</x-properties>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="format-detection" content="telephone=no">
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-113151733-4"></script>
<script>
@ -79,9 +90,29 @@
.fulloverlay .container {
position: relative;
display: inline-block;
top: 30%;
max-width: unset!important; /* override bootstrap */
top: 20%;
}
#critical-load.shown {
display: block;
}
@media (max-height: 750px) {
#critical-load .container {
top: unset;
}
#critical-load {
font-size: .8rem;
flex-direction: column;
justify-content: center;
}
#critical-load.shown {
display: flex;
}
}
.no-js {
@ -92,10 +123,12 @@
<div id="style">
<link rel="stylesheet" href="css/loader/loader.css">
</div>
<meta name="app-loader-target" content="app">
<div id="scripts">
<script type="application/javascript" src="loader/loader_app.min.js" defer></script>
<script type="application/javascript" src="loader/loader_app.js" defer></script>
<script type="application/javascript" src="loader/loader.js" defer></script>
<script type="application/javascript" src="loader/loader.js?_<?php echo time() ?>" defer></script>
</div>
</head>
<body>
@ -134,13 +167,13 @@
<!-- Critical load error -->
<div class="fulloverlay" id="critical-load">
<div class="container">
<img src="img/loading_error_right.svg" height="192px">
<h1 class="error" style="color: red"></h1>
<h3 class="detail"></h3>
<img src="img/loading_error_right.svg" style="height: 12em">
<h1 class="error" style="color: red; margin-bottom: 0"></h1>
<h3 class="detail" style="margin-top: .5em"></h3>
</div>
</div>
<?php if($localhost && false) { ?>
<?php if($localhost && true) { ?>
<div id="spoiler-style" style="z-index: 1000000; position: absolute; display: block; background: white; right: 5px; left: 5px; top: 34px;">
<!-- <img src="https://www.chromatic-solutions.de/teaspeak/window/connect_opened.png"> -->
<!-- <img src="http://puu.sh/DZDgO/9149c0a1aa.png"> -->
@ -179,7 +212,7 @@
<!-- <img src="http://puu.sh/E6NXv/eb2f19c7c3.png"> -->
<!-- <img src="http://puu.sh/E9jT6/302912ae34.png"> -->
<img src="http://puu.sh/E9jYi/3003c58a2f.png">
<img src="http://puu.sh/Eb5w4/8d38fe5b8f.png">
</div>
<button class="toggle-spoiler-style" style="height: 30px; width: 100px; z-index: 100000000; position: absolute; bottom: 2px;">toggle style</button>
<script>
@ -190,19 +223,6 @@
});
}, 2500);
</script>
<?php } ?>
<div id="music-test"></div>
<div id="templates"></div>
<div id="sounds"></div>
<div id="mouse-move">
<div class="container">
</div>
</div>
<div id="global-tooltip">
<a></a>
</div>
<?php } ?>
</body>
<div id="top-menu-bar"></div>
</html>

View file

@ -1,6 +1,7 @@
{
"short_name": "TeaWeb",
"name": "TeaSpeak Web",
"description": "The TeaSpeak Web client is a in the browser running client for the VoIP communication software TeaSpeak.",
"icons": [
{
"src": "img/favicon/teacup.png",
@ -8,8 +9,8 @@
"sizes": "256x256"
}
],
"start_url": "/?",
"start_url": "./?",
"background_color": "#18BC9C",
"display": "standalone",
"display": "fullscreen",
"theme_color": "#18BC9C"
}

View file

@ -4374,143 +4374,167 @@
</script>
<script class="jsrender-template" id="tmpl_manage_bookmarks" type="text/html">
<div class="modal-bookmarks">
<div class="group_box gb-list">
<div class="header">{{tr "Bookmarks" /}}</div>
<div class="content bookmark-list">
<div class="list">
<div class="entry bookmark">
<div class="name">TeaSpeak official</div>
</div>
</div>
<div>
<div class="left">
<div class="title">{{tr "Your bookmarks" /}}</div>
<div class="container-bookmarks">
</div>
<div class="buttons">
<button class="btn btn-danger button-delete">{{tr "Delete" /}}</button>
<button class="btn btn-purple button-add-folder">{{tr "Add Folder" /}}</button>
<button class="btn btn-success button-add-bookmark">{{tr "Add Bookmark" /}}</button>
</div>
</div>
<div class="buttons">
<button class="button-large btn btn-raised btn-secondary button-create">{{tr "Create new
bookmark/directory" /}}
</button>
<button class="button-large btn btn-danger button-delete">{{tr "Delete selected bookmark/directory"
/}}
</button>
<button class="button-small btn btn-raised btn-secondary button-create">{{tr "Create" /}}</button>
<button class="button-small btn btn-danger button-delete">{{tr "Delete" /}}</button>
</div>
<hr>
<div class="group_box gb-settings">
<div class="header">{{tr "Bookmark settings" /}}</div>
<div class="content">
<div class="bookmark-setting bookmark-setting-bookmark">
<div class="form-group">
<label for="input-bookmark-name" class="bmd-label-static">{{tr "Bookmark name:"
/}}</label>
<input id="input-bookmark-name" class="form-control setting-bookmark-name">
</div>
<div class="form-group">
<label for="input-bookmark-profile" class="bmd-label-static">{{tr "Connect profile:"
/}}</label>
<select id="input-bookmark-profile"
class="form-control setting-bookmark-profile"></select>
</div>
<div class="group_box">
<div class="header">{{tr "Server Properties" /}}</div>
<div class="content">
<div class="form-group">
<label for="input-bookmark-server-host" class="bmd-label-static">{{tr "Server
address:" /}}</label>
<input id="input-bookmark-server-host" class="form-control setting-server-host">
</div>
<div class="form-group">
<label for="input-bookmark-server-port" class="bmd-label-static">{{tr "Server
port:" /}}</label>
<input type="number" min="1" max="65665" id="input-bookmark-server-port"
class="form-control setting-server-port">
</div>
<div class="form-group">
<!-- TODO generated id because of password field (id="bookmark_server_password_{{rnd '0~13377331'/}}") -->
<label for="input-bookmark-server-password" class="bmd-label-floating">{{tr
"Server password" /}}</label>
<input id="input-bookmark-server-password"
class="form-control setting-server-password" autocomplete="nope"
type="password">
<div class="container-seperator vertical" seperator-id="seperator-bookmarks"></div>
<div class="right">
<div class="header">
<div class="container-name">{{tr "Your bookmarks" /}}</div>
<div class="container-address"></div>
</div>
<div class="container-settings">
<div class="group">
<div class="row">
<div class="key">{{tr "Bookmark Name" /}}</div>
<div class="value">
<div class="input-boxed">
<input class="input-bookmark-name">
<!--
<div class="container-tooltip">
<img src="img/icon_tooltip.svg"/>
<div class="tooltip">
<a>{{tr "The displayed name of this bookmark." /}}</a>
</div>
</div>
-->
</div>
</div>
</div>
<!--
<div class="group_box">
<div class="header">{{tr "Connect Properties (Not yet supported)" /}}</div>
<div class="content">
<div class="form-group">
<label for="input-bookmark-username" class="bmd-label-floating">{{tr "Default Username" /}}</label>
<input id="input-bookmark-username" class="form-control setting-username" disabled>
</div>
<div class="form-row container-default-channel-select">
<div class="form-group container-default-channel">
<label for="input-bookmark-channel" class="bmd-label-floating">{{tr "Default Channel" /}}</label>
<input id="input-bookmark-channel" class="form-control setting-channel" disabled>
<div class="row">
<div class="key">{{tr "Connect Profile" /}}</div>
<div class="value">
<div class="input-boxed">
<select class="input-connect-profile">
</select>
<!--
<div class="container-tooltip">
<img src="img/icon_tooltip.svg"/>
<div class="tooltip">
<a>{{tr "The profile which you're connection with when you're using the bookmark." /}}</a>
</div>
</div>
<div class="form-group bmd-form-group">
<button class="btn btn-primary button-set-to-current" disabled>{{tr "Current Channel" /}}</button>
</div>
</div>
<div class="form-group">
<!- TODO generated id because of password field (id="bookmark_server_password_{{rnd '0~13377331'/}}") ->
<label for="input-bookmark-channel-password" class="bmd-label-floating">{{tr "Default channel password" /}}</label>
<input id="input-bookmark-channel-password" class="form-control setting-channel-password" type="password" disabled>
-->
</div>
</div>
</div>
-->
</div>
<div class="bookmark-setting bookmark-setting-directory">
<div class="form-group">
<label for="input-bookmark-dirname" class="bmd-label-static">{{tr "Directory name:"
/}}</label>
<input id="input-bookmark-dirname" class="form-control setting-bookmark-name">
<div class="group">
<div class="row">
<div class="key">{{tr "Server Address" /}}</div>
<div class="value">
<div class="input-boxed">
<input class="input-server-address">
<!--
<div class="container-tooltip">
<img src="img/icon_tooltip.svg"/>
<div class="tooltip">
<a>{{tr "The server address of the bookmark. The port is separated via a colon" /}}</a>
</div>
</div>
-->
</div>
</div>
</div>
<div class="row">
<div class="key">{{tr "Server Password" /}}</div>
<div class="value">
<div class="input-boxed">
<input type="password" class="input-server-password">
<!--
<div class="container-tooltip">
<img src="img/icon_tooltip.svg"/>
<div class="tooltip">
<a>{{tr "The server port of the bookmark" /}}</a>
</div>
</div>
-->
</div>
</div>
</div>
<div class="row">
<div class="key">{{tr "Default Channel" /}}</div>
<div class="value">
<div class="input-boxed">
<input class="input-default-channel" placeholder="{{tr 'Not yet implemented' /}}" disabled>
<!--
<div class="container-tooltip">
<img src="img/icon_tooltip.svg"/>
<div class="tooltip">
<a>{{tr "The server port of the bookmark" /}}</a>
</div>
</div>
-->
</div>
</div>
</div>
<div class="row">
<div class="key">{{tr "Channel Password" /}}</div>
<div class="value">
<div class="input-boxed">
<input type="password" class="input-default-channel-password" placeholder="{{tr 'Not yet implemented' /}}" disabled>
<!--
<div class="container-tooltip">
<img src="img/icon_tooltip.svg"/>
<div class="tooltip">
<a>{{tr "The server port of the bookmark" /}}</a>
</div>
</div>
-->
</div>
</div>
</div>
</div>
<div class="group info">
<div class="container-image">
<img src="img/serveredit_1.png">
</div>
<div class="container-properties">
<div class="row">
<a class="key">{{tr "Server name" /}}</a>
<div class="value server-name">
error: name
</div>
</div>
<div class="row">
<a class="key">{{tr "Server region" /}}</a>
<div class="value server-region">
error: region
</div>
</div>
<div class="row">
<a class="key">{{tr "Last ping" /}}</a>
<div class="value server-ping">
error: ping
</div>
</div>
<div class="row">
<a class="key">{{tr "Last client count" /}}</a>
<div class="value server-client-count">
error: last client
</div>
</div>
<div class="row">
<a class="key">{{tr "Your connection" /}}</a>
<div class="value server-connection-count">
error: connection count
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</script>
<script class="jsrender-template" id="tmpl_manage_bookmarks-list_entry" type="text/html">
{{if type == "bookmark" }}
<div class="entry bookmark">
<div class="name">{{>name}}</div>
</div>
{{else type == "directory" }}
<div class="entry directory">
<div class="name">{{>name}}</div>
<div class="members"></div>
</div>
{{/if}}
</script>
<script class="jsrender-template" id="tmpl_manage_bookmarks-create" type="text/html">
<div class="modal-bookmark-create">
<div class="form-group">
<label class="bmd-label-floating">{{tr "Bookmark type:" /}}</label>
<select class="form-control bookmark-type">
<option value="bookmark">Bookmark</option>
<option value="directory">Directory</option>
</select>
</div>
<div class="form-group">
<label class="bmd-label-floating">{{tr "Parent directory:" /}}</label>
<select class="form-control bookmark-parent">
<option bookmark-uuid=""></option>
</select>
</div>
<div class="form-group">
<label class="bmd-label-floating">{{tr "Bookmark name" /}}</label>
<input class="form-control bookmark-name">
</div>
<div class="buttons">
<button class="btn btn-success button-create">Create</button>
<div class="buttons">
<button class="btn btn-success button-connect-tab">{{tr "Connect in a new tab" /}}</button>
<button class="btn btn-success button-connect">{{tr "Connect" /}}</button>
</div>
</div>
</div>
</script>
@ -5175,6 +5199,7 @@
</div>
</div>
<div class="container-buttons">
<button class="btn btn-success button-update">{{tr "Refresh" /}}</button>
<button class="btn btn-danger button-close">{{tr "Close" /}}</button>
</div>
</div>

View file

@ -300,7 +300,7 @@ class ConnectionHandler {
private initialize_server_settings() {
let update_control = false;
this.settings.setServer(this.channelTree.server);
this.settings.setServer(this.channelTree.server.properties.virtualserver_unique_identifier);
{
const flag_subscribe = this.settings.server(Settings.KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL, true);
if(this.client_status.channel_subscribe_all != flag_subscribe) {

View file

@ -54,8 +54,8 @@ namespace bookmarks {
export interface Bookmark {
type: /* BookmarkType.ENTRY */ BookmarkType;
/* readonly */ parent: DirectoryBookmark;
/* readonly directory: DirectoryBookmark; */
server_properties: ServerProperties;
display_name: string;
unique_id: string;
@ -72,6 +72,7 @@ namespace bookmarks {
export interface DirectoryBookmark {
type: /* BookmarkType.DIRECTORY */ BookmarkType;
readonly parent: DirectoryBookmark;
readonly content: (Bookmark | DirectoryBookmark)[];
unique_id: string;
@ -110,11 +111,25 @@ namespace bookmarks {
save_config();
}
const fix_parent = (parent: DirectoryBookmark, entry: Bookmark | DirectoryBookmark) => {
entry.parent = parent;
if(entry.type === BookmarkType.DIRECTORY)
for(const child of (entry as DirectoryBookmark).content)
fix_parent(entry as DirectoryBookmark, child);
};
for(const entry of _bookmark_config.root_bookmark.content)
fix_parent(_bookmark_config.root_bookmark, entry);
return _bookmark_config;
}
function save_config() {
localStorage.setItem("bookmarks", JSON.stringify(bookmark_config()));
localStorage.setItem("bookmarks", JSON.stringify(bookmark_config(), (key, value) => {
if(key === "parent")
return undefined;
return value;
}));
}
export function bookmarks() : DirectoryBookmark {
@ -172,7 +187,8 @@ namespace bookmarks {
nickname: nickname,
type: BookmarkType.ENTRY,
connect_profile: "default",
unique_id: guid()
unique_id: guid(),
parent: directory
} as Bookmark;
directory.content.push(bookmark);
@ -185,7 +201,8 @@ namespace bookmarks {
display_name: name,
content: [],
unique_id: guid()
unique_id: guid(),
parent: parent
} as DirectoryBookmark;
parent.content.push(bookmark);

View file

@ -73,23 +73,17 @@ namespace log {
}
const group_mode: GroupMode = GroupMode.PREFIX;
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
name: "log enabled initialisation",
function: async () => initialize(),
priority: 150
});
//Category Example: <url>?log.i18n.enabled=0
//Level Example A: <url>?log.level.trace.enabled=0
//Level Example B: <url>?log.level=0
export function initialize() {
export function initialize(default_level: LogType) {
for(const category of Object.keys(LogCategory).map(e => parseInt(e))) {
if(isNaN(category)) continue;
const category_name = LogCategory[category].toLowerCase();
enabled_mapping.set(category, settings.static_global<boolean>("log." + category_name.toLowerCase() + ".enabled", enabled_mapping.get(category)));
}
const base_level = settings.static_global<number>("log.level", app.type === app.Type.CLIENT_DEBUG || app.type === app.Type.WEB_DEBUG ? LogType.TRACE : LogType.INFO);
const base_level = settings.static_global<number>("log.level", default_level);
for(const level of Object.keys(LogType).map(e => parseInt(e))) {
if(isNaN(level)) continue;

View file

@ -9,8 +9,6 @@
/// <reference path="log.ts" />
/// <reference path="PPTListener.ts" />
let settings: Settings;
const js_render = window.jsrender || $;
const native_client = window.require !== undefined;
@ -117,7 +115,7 @@ function setup_jsrender() : boolean {
}
async function initialize() {
settings = new Settings();
Settings.initialize();
try {
await i18n.initialize();
@ -425,8 +423,9 @@ function main() {
// Modals.openServerInfo(connection.channelTree.server);
//Modals.createServerModal(connection.channelTree.server, properties => Promise.resolve());
}, 1000);
Modals.spawnSettingsModal("identity-profiles");
//Modals.spawnSettingsModal("identity-profiles");
//Modals.spawnKeySelect(console.log);
Modals.spawnBookmarkModal();
}
const task_teaweb_starter: loader.Task = {

View file

@ -293,9 +293,9 @@ class Settings extends StaticSettings {
key: "font_size"
};
static readonly FN_SERVER_CHANNEL_SUBSCRIBE_MODE: (channel: ChannelEntry) => SettingsKey<ChannelSubscribeMode> = channel => {
static readonly FN_SERVER_CHANNEL_SUBSCRIBE_MODE: (channel_id: number) => SettingsKey<number> = channel => {
return {
key: 'channel_subscribe_mode_' + channel.getChannelId()
key: 'channel_subscribe_mode_' + channel
}
};
@ -320,6 +320,10 @@ class Settings extends StaticSettings {
return result;
})();
static initialize() {
settings = new Settings();
}
private cacheGlobal = {};
private saveWorker: NodeJS.Timer;
private updated: boolean = false;
@ -372,7 +376,7 @@ class Settings extends StaticSettings {
class ServerSettings extends SettingsBase {
private cacheServer = {};
private currentServer: ServerEntry;
private _server_unique_id: string;
private _server_save_worker: NodeJS.Timer;
private _server_settings_updated: boolean = false;
private _destroyed = false;
@ -388,7 +392,7 @@ class ServerSettings extends SettingsBase {
destroy() {
this._destroyed = true;
this.currentServer = undefined;
this._server_unique_id = undefined;
this.cacheServer = undefined;
clearInterval(this._server_save_worker);
@ -413,18 +417,17 @@ class ServerSettings extends SettingsBase {
this.save();
}
setServer(server: ServerEntry) {
setServer(server_unique_id: string) {
if(this._destroyed) throw "destroyed";
if(this.currentServer) {
if(this._server_unique_id) {
this.save();
this.cacheServer = {};
this.currentServer = undefined;
this._server_unique_id = undefined;
}
this.currentServer = server;
this._server_unique_id = server_unique_id;
if(this.currentServer) {
let serverId = this.currentServer.properties.virtualserver_unique_identifier;
this.cacheServer = JSON.parse(localStorage.getItem("settings.server_" + serverId));
if(this._server_unique_id) {
this.cacheServer = JSON.parse(localStorage.getItem("settings.server_" + server_unique_id));
if(!this.cacheServer)
this.cacheServer = {};
}
@ -434,12 +437,13 @@ class ServerSettings extends SettingsBase {
if(this._destroyed) throw "destroyed";
this._server_settings_updated = false;
if(this.currentServer) {
let serverId = this.currentServer.properties.virtualserver_unique_identifier;
if(this._server_unique_id) {
let server = JSON.stringify(this.cacheServer);
localStorage.setItem("settings.server_" + serverId, server);
localStorage.setItem("settings.server_" + this._server_unique_id, server);
if(localStorage.save)
localStorage.save();
}
}
}
let settings: Settings;

View file

@ -910,7 +910,7 @@ class ChannelEntry {
}
get subscribe_mode() : ChannelSubscribeMode {
return typeof(this._subscribe_mode) !== 'undefined' ? this._subscribe_mode : (this._subscribe_mode = this.channelTree.client.settings.server(Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE(this), ChannelSubscribeMode.INHERITED));
return typeof(this._subscribe_mode) !== 'undefined' ? this._subscribe_mode : (this._subscribe_mode = this.channelTree.client.settings.server(Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE(this.channelId), ChannelSubscribeMode.INHERITED));
}
set subscribe_mode(mode: ChannelSubscribeMode) {
@ -918,7 +918,7 @@ class ChannelEntry {
return;
this._subscribe_mode = mode;
this.channelTree.client.settings.changeServer(Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE(this), mode);
this.channelTree.client.settings.changeServer(Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE(this.channelId), mode);
}
set flag_text_unread(flag: boolean) {

View file

@ -65,6 +65,7 @@ if(!$.fn.dividerfy) {
}
//console.log(min + " - " + max + " - " + current);
const property = vertical ? "width" : "height";
const previous_p = Math.ceil(previous * 100);
const next_p = Math.ceil(next * 100);

View file

@ -3,40 +3,6 @@
/// <reference path="../../proto.ts" />
namespace Modals {
function bookmark_tag(callback_select: (entry, tag) => any, bookmark: bookmarks.Bookmark | bookmarks.DirectoryBookmark) {
const tag = $("#tmpl_manage_bookmarks-list_entry").renderTag({
name: bookmark.display_name,
type: bookmark.type == bookmarks.BookmarkType.DIRECTORY ? "directory" : "bookmark"
});
tag.find(".name").on('click', () => {
callback_select(bookmark, tag);
tag.addClass("selected");
});
if(bookmark.type == bookmarks.BookmarkType.DIRECTORY) {
const casted = <bookmarks.DirectoryBookmark>bookmark;
for(const member of casted.content)
tag.find("> .members").append(bookmark_tag(callback_select, member));
}
return tag;
}
function parent_tag(select_tag: JQuery, prefix: string, bookmark: bookmarks.Bookmark | bookmarks.DirectoryBookmark) {
if(bookmark.type == bookmarks.BookmarkType.DIRECTORY) {
const casted = <bookmarks.DirectoryBookmark>bookmark;
select_tag.append(
$.spawn("option")
.val(casted.unique_id)
.text(prefix + casted.display_name)
);
for(const member of casted.content)
parent_tag(select_tag, prefix + " ", member);
}
}
export function spawnBookmarkModal() {
let modal: Modal;
modal = createModal({
@ -44,214 +10,302 @@ namespace Modals {
body: () => {
let template = $("#tmpl_manage_bookmarks").renderTag({ });
let selected_bookmark: bookmarks.Bookmark | bookmarks.DirectoryBookmark | undefined;
let update_name: () => any;
const update_bookmarks = () => { //list bookmarks
template.find(".list").empty();
const button_delete = template.find(".button-delete");
const button_add_folder = template.find(".button-add-folder");
const button_add_bookmark = template.find(".button-add-bookmark");
const callback_selected = (entry: bookmarks.Bookmark | bookmarks.DirectoryBookmark, tag: JQuery) => {
template.find(".selected").removeClass("selected");
if(selected_bookmark == entry) return;
const button_connect = template.find(".button-connect");
const button_connect_tab = template.find(".button-connect-tab");
selected_bookmark = entry;
update_name = () => tag.find("> .name").text(entry.display_name);
const label_bookmark_name = template.find(".header .container-name");
const label_server_address = template.find(".header .container-address");
template.find(".bookmark-setting").hide();
template.find(".setting-bookmark-name").val(selected_bookmark.display_name);
const input_bookmark_name = template.find(".input-bookmark-name");
const input_connect_profile = template.find(".input-connect-profile");
if(selected_bookmark.type == bookmarks.BookmarkType.ENTRY) {
template.find(".bookmark-setting-bookmark").show();
const input_server_address = template.find(".input-server-address");
const input_server_password = template.find(".input-server-password");
const casted = <bookmarks.Bookmark>selected_bookmark;
const profile = profiles.find_profile(casted.connect_profile) || profiles.default_profile();
template.find(".setting-bookmark-profile").val(profile.id);
const label_server_name = template.find(".server-name");
const label_server_region = template.find(".server-region");
const label_last_ping = template.find(".server-ping");
const label_client_count = template.find(".server-client-count");
const label_connection_count = template.find(".server-connection-count");
template.find(".setting-server-host").val(casted.server_properties.server_address);
template.find(".setting-server-port").val(casted.server_properties.server_port);
template.find(".setting-server-password").val(casted.server_properties.server_password_hash || casted.server_properties.server_password);
template.find(".setting-username").val(casted.nickname);
template.find(".setting-channel").val(casted.default_channel);
template.find(".setting-channel-password").val(casted.default_channel_password_hash || casted.default_channel_password);
} else {
template.find(".bookmark-setting-directory").show();
}
};
for(const bookmark of bookmarks.bookmarks().content) {
template.find(".list").append(bookmark_tag(callback_selected, bookmark));
}
console.log( template.find(".list").find(".bookmark, .directory"));
template.find(".list").find(".bookmark, .directory").eq(0).find("> .name").trigger('click');
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);
};
{ //General buttons
template.find(".button-create").on('click', event => {
let create_modal: Modal;
create_modal = createModal({
header: tr("Create a new entry"),
body: () => {
let template = $("#tmpl_manage_bookmarks-create").renderTag({ });
template = $.spawn("div").append(template);
const update_connect_info = () => {
if(selected_bookmark && selected_bookmark.type === bookmarks.BookmarkType.ENTRY) {
const entry = selected_bookmark as bookmarks.Bookmark;
for(const bookmark of bookmarks.bookmarks().content)
parent_tag(template.find(".bookmark-parent"), "", 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")))
);
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))
);
} 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"))
);
}
label_last_ping.text(tr("Average ping isn't yet supported"));
} else {
label_server_name.text("--");
label_server_region.text("--");
label_last_ping.text("--");
label_client_count.text("--");
label_connection_count.text("--");
}
};
if(selected_bookmark) {
const parent = selected_bookmark.type == bookmarks.BookmarkType.ENTRY ?
bookmarks.parent_bookmark(selected_bookmark as bookmarks.Bookmark) :
selected_bookmark;
if(parent)
template.find(".bookmark-parent").val(parent.unique_id);
}
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);
template.find(".bookmark-name").on('change, keyup', event => {
template.find(".button-create").prop("disabled", (<HTMLInputElement>event.target).value.length < 3);
});
if(selected_bookmark) {
input_bookmark_name.val(selected_bookmark.display_name);
label_bookmark_name.text(selected_bookmark.display_name);
}
template.find(".button-create").prop("disabled", true).on('click', event => {
const name = template.find(".bookmark-name").val() as string;
const parent_uuid = template.find(".bookmark-parent").val() as string;
if(selected_bookmark && selected_bookmark.type === bookmarks.BookmarkType.ENTRY) {
const entry = selected_bookmark as bookmarks.Bookmark;
const parent = bookmarks.find_bookmark(parent_uuid);
const address = entry.server_properties.server_address + (entry.server_properties.server_port == 9987 ? "" : (" " + entry.server_properties.server_port));
label_server_address.text(address);
input_server_address.val(address);
let bookmark;
if(template.find(".bookmark-type").val() == "directory") {
bookmark = bookmarks.create_bookmark_directory(parent as bookmarks.DirectoryBookmark || bookmarks.bookmarks(), name);
} else {
bookmark = bookmarks.create_bookmark(name, parent as bookmarks.DirectoryBookmark || bookmarks.bookmarks(), {
server_port: 9987,
server_address: "ts.teaspeak.de"
}, "Another TeaSpeak user");
}
bookmarks.save_bookmark(bookmark);
create_modal.close();
update_bookmarks();
});
let profile = input_connect_profile.find("option[value='" + entry.connect_profile + "']");
if(profile.length == 0)
profile = input_connect_profile.find("option[value=default]");
profile.prop("selected", true);
return template;
},
footer: 400
input_server_password.val(entry.server_properties.server_password_hash || entry.server_properties.server_password ? "WolverinDEV" : "");
} else {
input_server_password.val("");
input_server_address.val("");
input_connect_profile.find("option[value='no-value']").prop('selected', true);
label_server_address.text(" ");
}
update_connect_info();
};
const container_bookmarks = template.find(".container-bookmarks");
const update_bookmark_list = () => {
container_bookmarks.empty();
selected_bookmark = undefined;
update_selected();
const hide_links: boolean[] = [];
const build_entry = (entry: bookmarks.Bookmark | bookmarks.DirectoryBookmark, sibling_data: {first: boolean; last: boolean;}, index: number) => {
let container = $.spawn("div")
.addClass(entry.type === bookmarks.BookmarkType.ENTRY ? "bookmark" : "directory")
.addClass(index > 0 ? "linked" : "")
.addClass(sibling_data.first ? "link-start" : "");
for (let i = 0; i < index; i++) {
container.append(
$.spawn("div")
.addClass("link")
.addClass(i + 1 === index ? " connected" : "")
.addClass(hide_links[i + 1] ? "hidden" : "")
);
}
if (entry.type === bookmarks.BookmarkType.ENTRY) {
const bookmark = entry as bookmarks.Bookmark;
container.append(
bookmark.last_icon_id ?
IconManager.generate_tag(IconManager.load_cached_icon(bookmark.last_icon_id || 0), {animate: false}) :
$.spawn("div").addClass("icon-container icon_em")
);
} else {
container.append(
$.spawn("div").addClass("icon-container icon_em client-folder")
);
}
container.append(
$.spawn("div").addClass("name").text(entry.display_name)
);
container.appendTo(container_bookmarks);
container.on('click', event => {
if(selected_bookmark === entry)
return;
selected_bookmark = entry;
container_bookmarks.find(".selected").removeClass("selected");
container.addClass("selected");
update_buttons();
update_selected();
});
create_modal.open();
});
hide_links.push(sibling_data.last);
let cindex = 0;
const children = (entry as bookmarks.DirectoryBookmark).content || [];
for (const child of children)
build_entry(child, {first: cindex++ == 0, last: cindex == children.length}, index + 1);
hide_links.pop();
};
template.find(".button-delete").on('click', event => {
let cindex = 0;
const children = bookmarks.bookmarks().content;
for (const bookmark of children)
build_entry(bookmark, {first: cindex++ == 0, last: cindex == children.length}, 0);
};
/* generate profile list */
{
input_connect_profile.append(
$.spawn("option")
.attr("value", "no-value")
.text("")
.css("display", "none")
);
for(const profile of profiles.profiles()) {
input_connect_profile.append(
$.spawn("option")
.attr("value", profile.id)
.text(profile.profile_name)
);
}
}
/* buttons */
{
button_delete.on('click', event => {
if(!selected_bookmark) return;
spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this entry?"), result => {
if(result) {
bookmarks.delete_bookmark(selected_bookmark);
bookmarks.save_bookmark(selected_bookmark); /* save the deleted state */
update_bookmarks();
}
});
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(answer) {
bookmarks.delete_bookmark(selected_bookmark);
bookmarks.save_bookmark(selected_bookmark);
update_bookmark_list();
}
});
} else {
bookmarks.delete_bookmark(selected_bookmark);
bookmarks.save_bookmark(selected_bookmark);
update_bookmark_list();
}
});
/* bookmark listener */
{
template.find(".setting-bookmark-profile").on('change', event => {
if(!selected_bookmark || selected_bookmark.type != bookmarks.BookmarkType.ENTRY) return;
const casted = <bookmarks.Bookmark>selected_bookmark;
const element = <HTMLInputElement>event.target;
casted.connect_profile = element.value;
bookmarks.save_bookmark(selected_bookmark);
});
template.find(".setting-server-host").on('change', event => {
if(!selected_bookmark || selected_bookmark.type != bookmarks.BookmarkType.ENTRY) return;
const casted = <bookmarks.Bookmark>selected_bookmark;
const element = <HTMLInputElement>event.target;
casted.server_properties.server_address = element.value;
bookmarks.save_bookmark(selected_bookmark);
});
template.find(".setting-server-port").on('change', event => {
if(!selected_bookmark || selected_bookmark.type != bookmarks.BookmarkType.ENTRY) return;
const casted = <bookmarks.Bookmark>selected_bookmark;
const element = <HTMLInputElement>event.target;
casted.server_properties.server_port = parseInt(element.value);
bookmarks.save_bookmark(selected_bookmark);
});
template.find(".setting-server-password").on('change', event => {
if(!selected_bookmark || selected_bookmark.type != bookmarks.BookmarkType.ENTRY) return;
const casted = <bookmarks.Bookmark>selected_bookmark;
const element = <HTMLInputElement>event.target;
casted.server_properties.server_password = element.value;
bookmarks.save_bookmark(selected_bookmark);
});
template.find(".setting-username").on('change', event => {
if(!selected_bookmark || selected_bookmark.type != bookmarks.BookmarkType.ENTRY) return;
const casted = <bookmarks.Bookmark>selected_bookmark;
const element = <HTMLInputElement>event.target;
casted.nickname = element.value;
bookmarks.save_bookmark(selected_bookmark);
});
template.find(".setting-channel").on('change', event => {
if(!selected_bookmark || selected_bookmark.type != bookmarks.BookmarkType.ENTRY) return;
const casted = <bookmarks.Bookmark>selected_bookmark;
const element = <HTMLInputElement>event.target;
casted.default_channel = element.value;
bookmarks.save_bookmark(selected_bookmark);
});
template.find(".setting-channel-password").on('change', event => {
if(!selected_bookmark || selected_bookmark.type != bookmarks.BookmarkType.ENTRY) return;
const casted = <bookmarks.Bookmark>selected_bookmark;
const element = <HTMLInputElement>event.target;
casted.default_channel_password = element.value;
bookmarks.save_bookmark(selected_bookmark);
});
}
/* listener for both */
{
template.find(".setting-bookmark-name").on('change', event => {
if(!selected_bookmark) return;
const element = <HTMLInputElement>event.target;
if(element.value.length >= 3) {
selected_bookmark.display_name = element.value;
bookmarks.save_bookmark(selected_bookmark);
if(update_name)
update_name();
button_add_folder.on('click', event => {
createInputModal(tr("Enter a folder name"), tr("Enter the folder name"), text => {
return true;
}, result => {
if(result) {
const mark = bookmarks.create_bookmark_directory(
selected_bookmark ?
selected_bookmark.type === bookmarks.BookmarkType.DIRECTORY ?
selected_bookmark as bookmarks.DirectoryBookmark :
selected_bookmark.parent :
bookmarks.bookmarks(),
result as string
);
bookmarks.save_bookmark(mark);
update_bookmark_list();
}
});
}
}).open();
});
button_add_bookmark.on('click', event => {
createInputModal(tr("Enter a bookmark name"), tr("Enter the bookmark name"), text => {
return true;
}, result => {
if(result) {
const mark = bookmarks.create_bookmark(result as string,
selected_bookmark ?
selected_bookmark.type === bookmarks.BookmarkType.DIRECTORY ?
selected_bookmark as bookmarks.DirectoryBookmark :
selected_bookmark.parent :
bookmarks.bookmarks(), {
server_password: "",
server_port: 9987,
server_address: "",
server_password_hash: ""
}, "");
bookmarks.save_bookmark(mark);
update_bookmark_list();
}
}).open();
});
button_connect_tab.on('click', event => {
bookmarks.boorkmak_connect(selected_bookmark, true);
modal.close();
}).toggle(!settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION));
button_connect.on('click', event => {
bookmarks.boorkmak_connect(selected_bookmark, false);
modal.close();
});
}
/* connect profile initialisation */
/* inputs */
{
const list = template.find(".setting-bookmark-profile");
for(const profile of profiles.profiles()) {
const tag = $.spawn("option").val(profile.id).text(profile.profile_name);
if(profile.id == "default")
tag.css("font-weight", "bold");
input_bookmark_name.on('change keydown', event => {
const name = input_bookmark_name.val() as string;
const valid = name.length > 3;
input_bookmark_name.firstParent(".input-boxed").toggleClass("is-invalid", !valid);
list.append(tag);
}
if(event.type === "change" && valid) {
selected_bookmark.display_name = name;
label_bookmark_name.text(name);
}
});
input_server_address.on('change keydown', event => {
const address = input_server_address.val() as string;
const valid = !!address.match(Regex.IP_V4) || !!address.match(Regex.IP_V6) || !!address.match(Regex.DOMAIN);
input_server_address.firstParent(".input-boxed").toggleClass("is-invalid", !valid);
if(valid) {
const entry = selected_bookmark as bookmarks.Bookmark;
let _v6_end = address.indexOf(']');
let idx = address.lastIndexOf(':');
if(idx != -1 && idx > _v6_end) {
entry.server_properties.server_port = parseInt(address.substr(idx + 1));
entry.server_properties.server_address = address.substr(0, idx);
} else {
entry.server_properties.server_address = address;
entry.server_properties.server_port = 9987;
}
label_server_address.text(entry.server_properties.server_address + (entry.server_properties.server_port == 9987 ? "" : (" " + entry.server_properties.server_port)));
update_connect_info();
}
});
}
update_bookmarks();
update_bookmark_list();
update_buttons();
template.find(".button-close").on('click', event => modal.close());
return template;
return template.children();
},
footer: undefined,
width: 750
});
modal.htmlTag.dividerfy().find(".modal-body").addClass("modal-bookmarks");
modal.close_listener.push(() => {
control_bar.update_bookmarks();
top_menu.rebuild_bookmarks();

View file

@ -9,11 +9,32 @@ namespace Modals {
body: () => {
const template = $("#tmpl_server_info").renderTag();
apply_hostbanner(server, template.find(".container-top"));
apply_category_1(server, template, update_callbacks);
apply_category_2(server, template, update_callbacks);
apply_category_3(server, template, update_callbacks);
const children = template.children();
const top = template.find(".container-top");
const update_values = () => {
apply_hostbanner(server, top);
apply_category_1(server, children, update_callbacks);
apply_category_2(server, children, update_callbacks);
apply_category_3(server, children, update_callbacks);
};
const button_update = template.find(".button-update");
button_update.on('click', event => {
button_update.prop("disabled", true);
server.updateProperties().then(() => {
update_callbacks = [];
update_values();
}).catch(error => {
log.warn(LogCategory.CLIENT, tr("Failed to refresh server properties: %o"), error);
if(error instanceof CommandResult)
error = error.extra_message || error.message;
createErrorModal(tr("Refresh failed"), MessageHelper.formatMessage(tr("Failed to refresh server properties.{:br:}Error: {}"), error)).open();
}).then(() => {
button_update.prop("disabled", false);
});
});
update_values();
tooltip(template);
return template.children();
},

View file

@ -581,6 +581,58 @@ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
priority: 10
});
loader.register_task(loader.Stage.SETUP, {
name: "page setup",
function: async () => {
const body = document.body;
/* top menu */
{
const container = document.createElement("div");
container.setAttribute('id', "top-menu-bar");
body.append(container);
}
/* template containers */
{
const container = document.createElement("div");
container.setAttribute('id', "templates");
body.append(container);
}
/* sounds container */
{
const container = document.createElement("div");
container.setAttribute('id', "sounds");
body.append(container);
}
/* mouse move container */
{
const container = document.createElement("div");
container.setAttribute('id', "mouse-move");
const inner_container = document.createElement("div");
inner_container.classList.add("container");
container.append(inner_container);
body.append(container);
}
/* tooltip container */
{
const container = document.createElement("div");
container.setAttribute('id', "global-tooltip");
container.append(document.createElement("a"));
body.append(container);
}
},
priority: 10
});
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 ? LogType.TRACE : LogType.INFO),
priority: 150
});
window["Module"] = (window["Module"] || {}) as any;
/* TeaClient */
if(window.require) {

View file

@ -0,0 +1,30 @@
/// <reference path="loader.ts" />
/* register tasks */
loader.register_task(loader.Stage.INITIALIZING, {
name: "safari fix",
function: async () => {
/* safari remove "fix" */
if(Element.prototype.remove === undefined)
Object.defineProperty(Element.prototype, "remove", {
enumerable: false,
configurable: false,
writable: false,
value: function(){
this.parentElement.removeChild(this);
}
});
},
priority: 50
});
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 ? LogType.TRACE : LogType.INFO),
priority: 150
});
if(!loader.running()) {
/* we know that we want to load the app */
loader.execute_managed();
}

View file

@ -214,9 +214,7 @@ namespace loader {
}
}
}).catch(error => {
if(config.error) {
console.error("App loading failed: %o", error);
}
console.error("App loading failed: %o", error);
loader.critical_error("Failed to execute loader", "Lookup the console for more detail");
});
}
@ -489,6 +487,7 @@ namespace loader {
return;
}
_callback_critical_called = true;
if(_callback_critical_error) {
_callback_critical_error(message, detail);
return;
@ -507,7 +506,7 @@ namespace loader {
node_detail.innerHTML = detail;
}
tag.style.display = "block";
tag.classList.add("shown");
}
export function critical_error_handler(handler?: ErrorHandler, override?: boolean) : ErrorHandler {
@ -614,11 +613,20 @@ setTimeout(() => {
priority: 100,
function: async () => {
let loader_type;
/*
location.search.replace(/(?:^\?|&)([a-zA-Z_]+)=([.a-zA-Z0-9]+)(?=$|&)/g, (_, key: string, value: string) => {
if(key.toLowerCase() == "loader_target")
loader_type = value;
return "";
});
*/
for(const node of document.head.getElementsByTagName("meta")) {
if(node.name === "app-loader-target") {
loader_type = node.content;
if(loader_type)
break;
}
}
loader_type = loader_type || "app";
if(loader_type === "app") {
try {
@ -628,6 +636,14 @@ setTimeout(() => {
loader.critical_error("Failed to load main app script", error);
throw "app loader failed";
}
} else if(loader_type === "certaccept") {
try {
await loader.load_scripts(["loader/certaccept.js"]);
} catch (error) {
console.error("Failed to load cert accept script: %o", error);
loader.critical_error("Failed to load cert accept script", error);
throw "app loader failed";
}
} else {
loader.critical_error("Missing loader target: " + loader_type);
throw "Missing loader target: " + loader_type;

View file

@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Certificate callback</title>
<div id="style">
<link rel="stylesheet" href="css/loader/loader.css">
</div>
<meta name="app-loader-target" content="certaccept">
<div id="scripts">
<script type="application/javascript" src="loader/loader_certaccept.min.js" defer></script>
<script type="application/javascript" src="loader/loader_certaccept.js" defer></script>
<script type="application/javascript" src="loader/loader.js?_<?php echo time() ?>" defer></script>
</div>
<!-- required static style for the critical page and the enable javascript page -->
<style>
.fulloverlay {
z-index: 10000;
display: none;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: gray;
text-align: center;
}
.fulloverlay .container {
position: relative;
display: inline-block;
top: 20%;
}
#critical-load.shown {
display: block;
}
@media (max-height: 750px) {
#critical-load .container {
top: unset;
}
#critical-load {
font-size: .8rem;
flex-direction: column;
justify-content: center;
}
#critical-load.shown {
display: flex;
}
}
</style>
</head>
<body>
<!-- Loading screen -->
<div class="loader" id="loader-overlay">
<div class="half right"></div>
<div class="half left"></div>
<div class="bookshelf_wrapper">
<ul class="books_list">
<li class="book_item first"></li>
<li class="book_item second"></li>
<li class="book_item third"></li>
<li class="book_item fourth"></li>
<li class="book_item fifth"></li>
<li class="book_item sixth"></li>
</ul>
<div class="shelf"></div>
</div>
</div>
<!-- Critical load error -->
<div class="fulloverlay" id="critical-load">
<div class="container">
<img src="img/loading_error_right.svg" style="height: 12em">
<h1 class="error" style="color: red; margin-bottom: 0"></h1>
<h3 class="detail" style="margin-top: .5em"></h3>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,59 @@
function tr(text: string) { return text; }
const task_certificate_callback: loader.Task = {
name: "certificate accept tester",
function: async () => {
Settings.initialize();
const certificate_accept = settings.static_global(Settings.KEY_CERTIFICATE_CALLBACK, undefined);
if(!certificate_accept) {
loader.critical_error("Missing certificate callback data");
throw "missing data";
}
log.info(LogCategory.IPC, tr("Using this instance as certificate callback. ID: %s"), certificate_accept);
try {
try {
await bipc.get_handler().post_certificate_accpected(certificate_accept);
} catch(e) {} //FIXME remove!
log.info(LogCategory.IPC, tr("Other instance has acknowledged out work. Closing this window."));
const seconds_tag = $.spawn("a");
let seconds = 5;
let interval_id;
interval_id = setInterval(() => {
seconds--;
seconds_tag.text(seconds.toString());
if(seconds <= 0) {
clearTimeout(interval_id);
log.info(LogCategory.GENERAL, tr("Closing window"));
window.close();
return;
}
}, 1000);
const message =
"You've successfully accepted the certificate.{:br:}" +
"This page will close in {0} seconds.";
/*
createInfoModal(
tr("Certificate acccepted successfully"),
MessageHelper.formatMessage(tr(message), seconds_tag),
{
closeable: false,
footer: undefined
}
).open();
*/
//TODO!
return;
} catch(error) {
log.warn(LogCategory.IPC, tr("Failed to successfully post certificate accept status: %o"), error);
//TODO!
}
},
priority: 10
};

View file

@ -5,5 +5,5 @@
"exclude": [
"../js/workers/**/*.ts"
],
"target_file": "../declarations/exports.d.ts"
"target_file": "../declarations/exports_app.d.ts"
}

View file

@ -0,0 +1,7 @@
{
"source_files": [
"../loader/loader.ts",
"../loader/certaccept.ts"
],
"target_file": "../declarations/exports_loader_certaccept.d.ts"
}

View file

@ -1,9 +0,0 @@
{
"source_files": [
"../js/**/*.ts"
],
"exclude": [
"../js/workers/**/*.ts"
],
"target_file": "../declarations/exports_packed.d.ts"
}

View file

@ -0,0 +1,16 @@
/* general shared project config */
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"sourceMap": true,
"experimentalDecorators": true,
"plugins": [ ]
},
"include": [
"../types",
"../declarations/exports_loader_certaccept.d.ts",
"../popup/certaccept/js/**/*.ts",
"../js/BrowserIPC.ts"
]
}

View file

@ -5,6 +5,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"target": "es6",
"module": "none",
"outFile": "../generated/shared.js",
"plugins": [ /* ttypescript */

View file

@ -0,0 +1,18 @@
/* general shared project config */
{
"compilerOptions": {
"target": "es6",
"module": "none",
"outFile": "../generated/certaccept.js",
"plugins": [ ]
},
"include": [
"../types",
"../declarations/exports_loader_certaccept.d.ts",
"../popup/certaccept/js/**/*.ts",
"../js/BrowserIPC.ts",
"../js/settings.ts",
"../js/proto.ts",
"../js/log.ts"
]
}

View file

@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es6",
"module": "none",
"sourceMap": true,
"outFile": "../generated/loader_certaccept.js"
},
"include": [
"../types",
"../loader/loader.ts",
"../loader/certaccept.ts"
]
}