Implemented a load animation
This commit is contained in:
parent
42ed493db0
commit
2506923178
12 changed files with 601 additions and 129 deletions
300
css/loader.scss
Normal file
300
css/loader.scss
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
$thickness : 5px;
|
||||||
|
$duration : 2500;
|
||||||
|
$delay : $duration/6;
|
||||||
|
$background: #222222;
|
||||||
|
|
||||||
|
@mixin polka($size, $dot, $base, $accent){
|
||||||
|
background: $base;
|
||||||
|
background-image: radial-gradient($accent $dot, transparent 0);
|
||||||
|
background-size:$size $size;
|
||||||
|
background-position: 0 -2.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
z-index: 900;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader .half {
|
||||||
|
position: fixed;
|
||||||
|
background: #222222;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 50%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader .half.right {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.loader .half.left {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookshelf_wrapper {
|
||||||
|
position: relative;
|
||||||
|
top: 40%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.books_list {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 300px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book_item {
|
||||||
|
position: absolute;
|
||||||
|
top: -120px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
list-style: none;
|
||||||
|
width: 40px;
|
||||||
|
height: 120px;
|
||||||
|
opacity: 0;
|
||||||
|
background-color: #1e6cc7;
|
||||||
|
border: $thickness solid white;
|
||||||
|
transform-origin: bottom left;
|
||||||
|
transform: translateX(300px);
|
||||||
|
animation: travel #{$duration}ms linear infinite;
|
||||||
|
|
||||||
|
&.first {
|
||||||
|
top: -140px;
|
||||||
|
height: 140px;
|
||||||
|
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
content:'';
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: $thickness;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
top: initial;
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.second,
|
||||||
|
&.fifth {
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
content:'';
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: $thickness*3.5;
|
||||||
|
border-top: $thickness solid white;
|
||||||
|
border-bottom: $thickness solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
top: initial;
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.third {
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
content:'';
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 9px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: $thickness solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
top: initial;
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fourth {
|
||||||
|
top: -130px;
|
||||||
|
height: 130px;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
box-sizing: border-box;
|
||||||
|
content:'';
|
||||||
|
position: absolute;
|
||||||
|
top: 46px;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: $thickness*3.5;
|
||||||
|
border-top: $thickness solid white;
|
||||||
|
border-bottom: $thickness solid white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fifth {
|
||||||
|
top: -100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.sixth {
|
||||||
|
top: -140px;
|
||||||
|
height: 140px;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
box-sizing: border-box;
|
||||||
|
content:'';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 31px;
|
||||||
|
left: 0px;
|
||||||
|
width: 100%;
|
||||||
|
height: $thickness;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
content:'';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 9px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: $thickness solid white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
animation-delay: #{$delay*1}ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(3) {
|
||||||
|
animation-delay: #{$delay*2}ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(4) {
|
||||||
|
animation-delay: #{$delay*3}ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(5) {
|
||||||
|
animation-delay: #{$delay*4}ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(6) {
|
||||||
|
animation-delay: #{$delay*5}ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.shelf {
|
||||||
|
width: 300px;
|
||||||
|
height: $thickness;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: white;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
content:'';
|
||||||
|
position : absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
@include polka(10px, 30%, $background, rgba(255,255,255,0.5));
|
||||||
|
top: 200%;
|
||||||
|
left: 5%;
|
||||||
|
animation: move #{$duration/10}ms linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
top: 400%;
|
||||||
|
left: 7.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes move {
|
||||||
|
|
||||||
|
from {
|
||||||
|
background-position-x: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
background-position-x: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes travel {
|
||||||
|
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(300px) rotateZ(0deg) scaleY(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
6.5% {
|
||||||
|
transform: translateX(279.5px) rotateZ(0deg) scaleY(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
8.8% {
|
||||||
|
transform: translateX(273.6px) rotateZ(0deg) scaleY(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
10% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(270px) rotateZ(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
17.6% {
|
||||||
|
transform: translateX(247.2px) rotateZ(-30deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
45% {
|
||||||
|
transform: translateX(165px) rotateZ(-30deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
49.5% {
|
||||||
|
transform: translateX(151.5px) rotateZ(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
61.5% {
|
||||||
|
transform: translateX(115.5px) rotateZ(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
67% {
|
||||||
|
transform: translateX(99px) rotateZ(-60deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
76% {
|
||||||
|
transform: translateX(72px) rotateZ(-60deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
83.5% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(49.5px) rotateZ(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
90% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(0px) rotateZ(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -682,65 +682,95 @@ class PushToTalkVAD extends VoiceActivityDetector {
|
||||||
class CodecPoolEntry {
|
class CodecPoolEntry {
|
||||||
}
|
}
|
||||||
class CodecPool {
|
class CodecPool {
|
||||||
constructor(handle, index, creator) {
|
constructor(handle, index, name, creator) {
|
||||||
this.entries = [];
|
this.entries = [];
|
||||||
this.maxInstances = 2;
|
this.maxInstances = 2;
|
||||||
|
this._supported = true;
|
||||||
this.creator = creator;
|
this.creator = creator;
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
this.codecIndex = index;
|
this.codecIndex = index;
|
||||||
|
this.name = name;
|
||||||
}
|
}
|
||||||
initialize(cached) {
|
initialize(cached) {
|
||||||
for (let i = 0; i < cached; i++)
|
for (let i = 0; i < cached; i++)
|
||||||
this.ownCodec(i);
|
this.ownCodec(i + 1).then(codec => {
|
||||||
for (let i = 0; i < cached; i++)
|
console.log("Release again! (%o)", codec);
|
||||||
this.releaseCodec(i);
|
this.releaseCodec(i + 1);
|
||||||
|
}).catch(error => {
|
||||||
|
if (this._supported) {
|
||||||
|
createErrorModal("Could not load codec driver", "Could not load or initialize codec " + this.name + "<br>" +
|
||||||
|
"Error: <code>" + JSON.stringify(error) + "</code>").open();
|
||||||
|
}
|
||||||
|
this._supported = false;
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
supported() { return this.creator != undefined; }
|
supported() { return this.creator != undefined && this._supported; }
|
||||||
ownCodec(clientId, create = true) {
|
ownCodec(clientId, create = true) {
|
||||||
if (!this.creator)
|
return new Promise((resolve, reject) => {
|
||||||
return null;
|
if (!this.creator || !this._supported) {
|
||||||
let free = 0;
|
reject("unsupported codec!");
|
||||||
for (let index = 0; index < this.entries.length; index++) {
|
return;
|
||||||
if (this.entries[index].owner == clientId) {
|
|
||||||
this.entries[index].last_access = new Date().getTime();
|
|
||||||
return this.entries[index].instance;
|
|
||||||
}
|
}
|
||||||
else if (free == 0 && this.entries[index].owner == 0) {
|
let freeSlot = 0;
|
||||||
free = index;
|
for (let index = 0; index < this.entries.length; index++) {
|
||||||
|
if (this.entries[index].owner == clientId) {
|
||||||
|
this.entries[index].last_access = new Date().getTime();
|
||||||
|
if (this.entries[index].instance.initialized())
|
||||||
|
resolve(this.entries[index].instance);
|
||||||
|
else {
|
||||||
|
this.entries[index].instance.initialise().then((flag) => {
|
||||||
|
//TODO test success flag
|
||||||
|
console.error(flag);
|
||||||
|
this.ownCodec(clientId, false).then(resolve).catch(reject);
|
||||||
|
}).catch(error => {
|
||||||
|
console.error("Could not initialize codec!\nError: %o", error);
|
||||||
|
reject("Could not initialize codec!");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (freeSlot == 0 && this.entries[index].owner == 0) {
|
||||||
|
freeSlot = index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!create) {
|
||||||
if (!create)
|
resolve(undefined);
|
||||||
return null;
|
return;
|
||||||
if (free == 0) {
|
}
|
||||||
free = this.entries.length;
|
if (freeSlot == 0) {
|
||||||
let entry = new CodecPoolEntry();
|
freeSlot = this.entries.length;
|
||||||
entry.instance = this.creator();
|
let entry = new CodecPoolEntry();
|
||||||
entry.instance.initialise();
|
entry.instance = this.creator();
|
||||||
entry.instance.on_encoded_data = buffer => this.handle.sendVoicePacket(buffer, this.codecIndex);
|
entry.instance.on_encoded_data = buffer => this.handle.sendVoicePacket(buffer, this.codecIndex);
|
||||||
this.entries.push(entry);
|
this.entries.push(entry);
|
||||||
}
|
}
|
||||||
this.entries[free].owner = clientId;
|
this.entries[freeSlot].owner = clientId;
|
||||||
this.entries[free].last_access = new Date().getTime();
|
this.entries[freeSlot].last_access = new Date().getTime();
|
||||||
this.entries[free].instance.reset();
|
if (this.entries[freeSlot].instance.initialized())
|
||||||
return this.entries[free].instance;
|
this.entries[freeSlot].instance.reset();
|
||||||
|
else {
|
||||||
|
this.ownCodec(clientId, false).then(resolve).catch(reject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(this.entries[freeSlot].instance);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
releaseCodec(clientId) {
|
releaseCodec(clientId) {
|
||||||
for (let index = 0; index < this.entries.length; index++) {
|
for (let index = 0; index < this.entries.length; index++)
|
||||||
if (this.entries[index].owner == clientId)
|
if (this.entries[index].owner == clientId)
|
||||||
this.entries[index].owner = 0;
|
this.entries[index].owner = 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class VoiceConnection {
|
class VoiceConnection {
|
||||||
constructor(client) {
|
constructor(client) {
|
||||||
this.codecPool = [
|
this.codecPool = [
|
||||||
new CodecPool(this, 0, undefined),
|
new CodecPool(this, 0, "Spex A", undefined),
|
||||||
new CodecPool(this, 1, undefined),
|
new CodecPool(this, 1, "Spex B", undefined),
|
||||||
new CodecPool(this, 2, undefined),
|
new CodecPool(this, 2, "Spex C", undefined),
|
||||||
new CodecPool(this, 3, undefined),
|
new CodecPool(this, 3, "CELT Mono", undefined),
|
||||||
new CodecPool(this, 4, () => { return new CodecWrapper(CodecWorkerType.WORKER_OPUS, 1); }),
|
new CodecPool(this, 4, "Opus Voice", () => { return new CodecWrapper(CodecWorkerType.WORKER_OPUS, 1); }),
|
||||||
new CodecPool(this, 5, () => { return new CodecWrapper(CodecWorkerType.WORKER_OPUS, 2); }) //opus music
|
new CodecPool(this, 5, "Opus Music", () => { return new CodecWrapper(CodecWorkerType.WORKER_OPUS, 2); }) //opus music
|
||||||
//FIXME Why is it at index 5 currently only 1?
|
|
||||||
];
|
];
|
||||||
this.vpacketId = 0;
|
this.vpacketId = 0;
|
||||||
this.chunkVPacketId = 0;
|
this.chunkVPacketId = 0;
|
||||||
|
@ -854,10 +884,9 @@ class VoiceConnection {
|
||||||
codecPool.releaseCodec(clientId);
|
codecPool.releaseCodec(clientId);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let decoder = codecPool.ownCodec(clientId);
|
codecPool.ownCodec(clientId)
|
||||||
decoder.decodeSamples(client.getAudioController().codecCache(codec), encodedData).then(buffer => {
|
.then(decoder => decoder.decodeSamples(client.getAudioController().codecCache(codec), encodedData))
|
||||||
client.getAudioController().playBuffer(buffer);
|
.then(buffer => client.getAudioController().playBuffer(buffer)).catch(error => {
|
||||||
}).catch(error => {
|
|
||||||
console.error("Could not playback client's (" + clientId + ") audio (" + error + ")");
|
console.error("Could not playback client's (" + clientId + ") audio (" + error + ")");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -865,16 +894,17 @@ class VoiceConnection {
|
||||||
handleVoiceData(data, head) {
|
handleVoiceData(data, head) {
|
||||||
if (!this.voiceRecorder)
|
if (!this.voiceRecorder)
|
||||||
return;
|
return;
|
||||||
|
if (!this.client.connected)
|
||||||
|
return false;
|
||||||
|
if (this.client.controlBar.muteInput)
|
||||||
|
return;
|
||||||
if (head) {
|
if (head) {
|
||||||
this.chunkVPacketId = 0;
|
this.chunkVPacketId = 0;
|
||||||
this.client.getClient().speaking = true;
|
this.client.getClient().speaking = true;
|
||||||
}
|
}
|
||||||
let encoder = this.codecPool[4].ownCodec(this.client.getClientId());
|
//TODO Use channel codec!
|
||||||
if (!encoder) {
|
this.codecPool[4].ownCodec(this.client.getClientId())
|
||||||
console.error("Could not reserve encoder!");
|
.then(encoder => encoder.encodeSamples(this.client.getClient().getAudioController().codecCache(4), data));
|
||||||
return;
|
|
||||||
}
|
|
||||||
encoder.encodeSamples(this.client.getClient().getAudioController().codecCache(4), data); //TODO Use channel codec!
|
|
||||||
//this.client.getClient().getAudioController().play(data);
|
//this.client.getClient().getAudioController().play(data);
|
||||||
}
|
}
|
||||||
handleVoiceEnded() {
|
handleVoiceEnded() {
|
||||||
|
@ -982,7 +1012,7 @@ function spawnMenu(x, y, ...entries) {
|
||||||
var sha;
|
var sha;
|
||||||
(function (sha) {
|
(function (sha) {
|
||||||
function sha1(message) {
|
function sha1(message) {
|
||||||
let buffer = message instanceof ArrayBuffer ? message : new TextEncoder("utf-8").encode(message);
|
let buffer = message instanceof ArrayBuffer ? message : new TextEncoder().encode(message);
|
||||||
return crypto.subtle.digest("SHA-1", buffer);
|
return crypto.subtle.digest("SHA-1", buffer);
|
||||||
}
|
}
|
||||||
sha.sha1 = sha1;
|
sha.sha1 = sha1;
|
||||||
|
@ -2330,6 +2360,7 @@ class ServerConnection {
|
||||||
constructor(client) {
|
constructor(client) {
|
||||||
this._connectionState = ConnectionState.UNCONNECTED;
|
this._connectionState = ConnectionState.UNCONNECTED;
|
||||||
this._connectTimeoutHandler = undefined;
|
this._connectTimeoutHandler = undefined;
|
||||||
|
this._connected = false;
|
||||||
this.on_connect = () => {
|
this.on_connect = () => {
|
||||||
console.log("Socket connected");
|
console.log("Socket connected");
|
||||||
chat.serverChat().appendMessage("Logging in...");
|
chat.serverChat().appendMessage("Logging in...");
|
||||||
|
@ -2355,6 +2386,7 @@ class ServerConnection {
|
||||||
this._remotePort = port;
|
this._remotePort = port;
|
||||||
this._handshakeHandler = handshake;
|
this._handshakeHandler = handshake;
|
||||||
this._handshakeHandler.setConnection(this);
|
this._handshakeHandler.setConnection(this);
|
||||||
|
this._connected = false;
|
||||||
chat.serverChat().appendMessage("Connecting to " + host + ":" + port);
|
chat.serverChat().appendMessage("Connecting to " + host + ":" + port);
|
||||||
const self = this;
|
const self = this;
|
||||||
try {
|
try {
|
||||||
|
@ -2371,12 +2403,13 @@ class ServerConnection {
|
||||||
this._socket.onopen = () => {
|
this._socket.onopen = () => {
|
||||||
if (this._socket != sockCpy)
|
if (this._socket != sockCpy)
|
||||||
return;
|
return;
|
||||||
|
this._connected = true;
|
||||||
this.on_connect();
|
this.on_connect();
|
||||||
};
|
};
|
||||||
this._socket.onclose = event => {
|
this._socket.onclose = event => {
|
||||||
if (this._socket != sockCpy)
|
if (this._socket != sockCpy)
|
||||||
return;
|
return;
|
||||||
this._client.handleDisconnect(DisconnectReason.CONNECTION_CLOSED, {
|
this._client.handleDisconnect(this._connected ? DisconnectReason.CONNECTION_CLOSED : DisconnectReason.CONNECT_FAILURE, {
|
||||||
code: event.code,
|
code: event.code,
|
||||||
reason: event.reason,
|
reason: event.reason,
|
||||||
event: event
|
event: event
|
||||||
|
@ -2414,6 +2447,7 @@ class ServerConnection {
|
||||||
future.reject("Connection closed");
|
future.reject("Connection closed");
|
||||||
this._retListener = [];
|
this._retListener = [];
|
||||||
this._retCodeIdx = 0;
|
this._retCodeIdx = 0;
|
||||||
|
this._connected = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
handleWebSocketMessage(data) {
|
handleWebSocketMessage(data) {
|
||||||
|
@ -2973,20 +3007,22 @@ class Settings {
|
||||||
initializeStatic() {
|
initializeStatic() {
|
||||||
location.search.substr(1).split("&").forEach(part => {
|
location.search.substr(1).split("&").forEach(part => {
|
||||||
let item = part.split("=");
|
let item = part.split("=");
|
||||||
$.spawn("div")
|
$("<x-property></x-property>")
|
||||||
.attr("key", item[0])
|
.attr("key", item[0])
|
||||||
.attr("value", item[1])
|
.attr("value", item[1])
|
||||||
.appendTo(this._staticPropsTag);
|
.appendTo(this._staticPropsTag);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static transformStO(input, _default) {
|
static transformStO(input, _default) {
|
||||||
|
if (typeof input === "undefined")
|
||||||
|
return _default;
|
||||||
if (typeof _default === "string")
|
if (typeof _default === "string")
|
||||||
return input;
|
return input;
|
||||||
else if (typeof _default === "number")
|
else if (typeof _default === "number")
|
||||||
return parseInt(input);
|
return parseInt(input);
|
||||||
else if (typeof _default === "boolean")
|
else if (typeof _default === "boolean")
|
||||||
return (input == "1" || input == "true");
|
return (input == "1" || input == "true");
|
||||||
else if (typeof _default == "undefined")
|
else if (typeof _default === "undefined")
|
||||||
return input;
|
return input;
|
||||||
return JSON.parse(input);
|
return JSON.parse(input);
|
||||||
}
|
}
|
||||||
|
@ -3011,7 +3047,8 @@ class Settings {
|
||||||
}
|
}
|
||||||
static(key, _default) {
|
static(key, _default) {
|
||||||
let result = this._staticPropsTag.find("[key='" + key + "']");
|
let result = this._staticPropsTag.find("[key='" + key + "']");
|
||||||
return Settings.transformStO(result.length > 0 ? decodeURIComponent(result.attr("value")) : undefined, _default);
|
console.log("%d | %o", result.length, result);
|
||||||
|
return Settings.transformStO(result.length > 0 ? decodeURIComponent(result.last().attr("value")) : undefined, _default);
|
||||||
}
|
}
|
||||||
changeGlobal(key, value) {
|
changeGlobal(key, value) {
|
||||||
if (this.cacheGlobal[key] == value)
|
if (this.cacheGlobal[key] == value)
|
||||||
|
@ -4218,6 +4255,9 @@ class TSClient {
|
||||||
this.groups.requestGroups();
|
this.groups.requestGroups();
|
||||||
this.controlBar.updateProperties();
|
this.controlBar.updateProperties();
|
||||||
}
|
}
|
||||||
|
get connected() {
|
||||||
|
return !!this.serverConnection && this.serverConnection.connected;
|
||||||
|
}
|
||||||
handleDisconnect(type, data = {}) {
|
handleDisconnect(type, data = {}) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case DisconnectReason.REQUESTED:
|
case DisconnectReason.REQUESTED:
|
||||||
|
@ -4225,7 +4265,10 @@ class TSClient {
|
||||||
case DisconnectReason.CONNECT_FAILURE:
|
case DisconnectReason.CONNECT_FAILURE:
|
||||||
console.error("Could not connect to remote host! Exception");
|
console.error("Could not connect to remote host! Exception");
|
||||||
console.error(data);
|
console.error(data);
|
||||||
createErrorModal("Could not connect", "Could not connect to remote host (Connection refused)").open();
|
//TODO test for status 1006
|
||||||
|
createErrorModal("Could not connect", "Could not connect to remote host (Connection refused)<br>" +
|
||||||
|
"If you're shure that the remot host is up, than you may not allow unsigned certificates.<br>" +
|
||||||
|
"Click <a href='https://" + this.serverConnection._remoteHost + ":" + this.serverConnection._remotePort + "'>here</a> to accept the remote certificate").open();
|
||||||
break;
|
break;
|
||||||
case DisconnectReason.CONNECTION_CLOSED:
|
case DisconnectReason.CONNECTION_CLOSED:
|
||||||
console.error("Lost connection to remote server!");
|
console.error("Lost connection to remote server!");
|
||||||
|
@ -5308,7 +5351,7 @@ class BasicCodec {
|
||||||
this.samplesPerUnit = 960;
|
this.samplesPerUnit = 960;
|
||||||
this._audioContext = new OfflineAudioContext(1, 1024, 44100);
|
this._audioContext = new OfflineAudioContext(1, 1024, 44100);
|
||||||
this._codecSampleRate = codecSampleRate;
|
this._codecSampleRate = codecSampleRate;
|
||||||
this._decodeResampler = new AudioResampler();
|
this._decodeResampler = new AudioResampler(AudioController.globalContext.sampleRate);
|
||||||
this._encodeResampler = new AudioResampler(codecSampleRate);
|
this._encodeResampler = new AudioResampler(codecSampleRate);
|
||||||
}
|
}
|
||||||
encodeSamples(cache, pcm) {
|
encodeSamples(cache, pcm) {
|
||||||
|
@ -5328,9 +5371,13 @@ class BasicCodec {
|
||||||
if (buf.index == buf.buffer.length)
|
if (buf.index == buf.buffer.length)
|
||||||
cache._chunks.pop_front();
|
cache._chunks.pop_front();
|
||||||
}
|
}
|
||||||
|
let encodeBegin = new Date().getTime();
|
||||||
this.encode(buffer).then(result => {
|
this.encode(buffer).then(result => {
|
||||||
if (result instanceof Uint8Array)
|
if (result instanceof Uint8Array) {
|
||||||
|
if (new Date().getTime() - 20 > encodeBegin)
|
||||||
|
console.error("Required time: %d", new Date().getTime() - encodeBegin);
|
||||||
this.on_encoded_data(result);
|
this.on_encoded_data(result);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
console.error("[Codec][" + this.name() + "] Could not encode buffer. Result: " + result);
|
console.error("[Codec][" + this.name() + "] Could not encode buffer. Result: " + result);
|
||||||
});
|
});
|
||||||
|
@ -5352,6 +5399,7 @@ class CodecWrapper extends BasicCodec {
|
||||||
this._workerListener = [];
|
this._workerListener = [];
|
||||||
this._workerCallbackToken = "callback_token";
|
this._workerCallbackToken = "callback_token";
|
||||||
this._workerTokeIndex = 0;
|
this._workerTokeIndex = 0;
|
||||||
|
this._initialized = false;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.channelCount = channelCount;
|
this.channelCount = channelCount;
|
||||||
}
|
}
|
||||||
|
@ -5359,12 +5407,31 @@ class CodecWrapper extends BasicCodec {
|
||||||
return "Worker for " + CodecWorkerType[this.type] + " Channels " + this.channelCount;
|
return "Worker for " + CodecWorkerType[this.type] + " Channels " + this.channelCount;
|
||||||
}
|
}
|
||||||
initialise() {
|
initialise() {
|
||||||
this.spawnWorker();
|
if (this._initializePromise)
|
||||||
this.sendWorkerMessage({
|
return this._initializePromise;
|
||||||
command: "initialise",
|
return this._initializePromise = this.spawnWorker().then(() => new Promise((resolve, reject) => {
|
||||||
type: this.type,
|
const token = this.generateToken();
|
||||||
channelCount: this.channelCount
|
this.sendWorkerMessage({
|
||||||
});
|
command: "initialise",
|
||||||
|
type: this.type,
|
||||||
|
channelCount: this.channelCount,
|
||||||
|
token: token
|
||||||
|
});
|
||||||
|
this._workerListener.push({
|
||||||
|
token: token,
|
||||||
|
resolve: data => {
|
||||||
|
console.log("Init result: %o", data);
|
||||||
|
this._initialized = data["success"] == true;
|
||||||
|
if (data["success"] == true)
|
||||||
|
resolve();
|
||||||
|
else
|
||||||
|
reject(data.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
initialized() {
|
||||||
|
return this._initialized;
|
||||||
}
|
}
|
||||||
deinitialise() {
|
deinitialise() {
|
||||||
this.sendWorkerMessage({
|
this.sendWorkerMessage({
|
||||||
|
@ -5372,7 +5439,7 @@ class CodecWrapper extends BasicCodec {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
decode(data) {
|
decode(data) {
|
||||||
let token = this._workerTokeIndex++ + "_token";
|
let token = this.generateToken();
|
||||||
let result = new Promise((resolve, reject) => {
|
let result = new Promise((resolve, reject) => {
|
||||||
this._workerListener.push({
|
this._workerListener.push({
|
||||||
token: token,
|
token: token,
|
||||||
|
@ -5402,7 +5469,7 @@ class CodecWrapper extends BasicCodec {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
encode(data) {
|
encode(data) {
|
||||||
let token = this._workerTokeIndex++ + "_token";
|
let token = this.generateToken();
|
||||||
let result = new Promise((resolve, reject) => {
|
let result = new Promise((resolve, reject) => {
|
||||||
this._workerListener.push({
|
this._workerListener.push({
|
||||||
token: token,
|
token: token,
|
||||||
|
@ -5439,16 +5506,35 @@ class CodecWrapper extends BasicCodec {
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
generateToken() {
|
||||||
|
return this._workerTokeIndex++ + "_token";
|
||||||
|
}
|
||||||
sendWorkerMessage(message, transfare) {
|
sendWorkerMessage(message, transfare) {
|
||||||
|
//console.log("Send worker: %o", message);
|
||||||
this._worker.postMessage(JSON.stringify(message), transfare);
|
this._worker.postMessage(JSON.stringify(message), transfare);
|
||||||
}
|
}
|
||||||
onWorkerMessage(message) {
|
onWorkerMessage(message) {
|
||||||
|
//console.log("Worker message: %o", message);
|
||||||
if (!message["token"]) {
|
if (!message["token"]) {
|
||||||
console.error("Invalid worker token!");
|
console.error("Invalid worker token!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (message["token"] == this._workerCallbackToken) {
|
if (message["token"] == this._workerCallbackToken) {
|
||||||
console.log("Callback data!");
|
if (message["type"] == "loaded") {
|
||||||
|
console.log("[Codec] Got worker init response: Success: %o Message: %o", message["success"], message["message"]);
|
||||||
|
if (message["success"]) {
|
||||||
|
if (this._workerCallbackResolve)
|
||||||
|
this._workerCallbackResolve();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (this._workerCallbackReject)
|
||||||
|
this._workerCallbackReject(message["message"]);
|
||||||
|
}
|
||||||
|
this._workerCallbackReject = undefined;
|
||||||
|
this._workerCallbackResolve = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("Costume callback! (%o)", message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (let entry of this._workerListener) {
|
for (let entry of this._workerListener) {
|
||||||
|
@ -5461,8 +5547,12 @@ class CodecWrapper extends BasicCodec {
|
||||||
console.error("Could not find worker token entry! (" + message["token"] + ")");
|
console.error("Could not find worker token entry! (" + message["token"] + ")");
|
||||||
}
|
}
|
||||||
spawnWorker() {
|
spawnWorker() {
|
||||||
this._worker = new Worker("js/codec/CompiledCodecWorker.js");
|
return new Promise((resolve, reject) => {
|
||||||
this._worker.onmessage = event => this.onWorkerMessage(JSON.parse(event.data));
|
this._workerCallbackReject = reject;
|
||||||
|
this._workerCallbackResolve = resolve;
|
||||||
|
this._worker = new Worker(settings.static("worker_directory", "js/workers/") + "WorkerCodec.js");
|
||||||
|
this._worker.onmessage = event => this.onWorkerMessage(JSON.parse(event.data));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <reference path="chat.ts" />
|
/// <reference path="chat.ts" />
|
||||||
|
@ -5478,7 +5568,7 @@ let settings;
|
||||||
let globalClient;
|
let globalClient;
|
||||||
let chat;
|
let chat;
|
||||||
let forumIdentity;
|
let forumIdentity;
|
||||||
function invokeMain() {
|
function main() {
|
||||||
//localhost:63343/Web-Client/index.php?disableUnloadDialog=1&default_connect_type=forum&default_connect_url=localhost
|
//localhost:63343/Web-Client/index.php?disableUnloadDialog=1&default_connect_type=forum&default_connect_url=localhost
|
||||||
AudioController.initializeAudioController();
|
AudioController.initializeAudioController();
|
||||||
if (!TSIdentityHelper.setup()) {
|
if (!TSIdentityHelper.setup()) {
|
||||||
|
@ -5512,6 +5602,7 @@ function invokeMain() {
|
||||||
Modals.spawnConnectModal(settings.static("default_connect_url"));
|
Modals.spawnConnectModal(settings.static("default_connect_url"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
app.loadedListener.push(() => main());
|
||||||
/// <reference path="BasicCodec.ts"/>
|
/// <reference path="BasicCodec.ts"/>
|
||||||
class RawCodec extends BasicCodec {
|
class RawCodec extends BasicCodec {
|
||||||
constructor(codecSampleRate) {
|
constructor(codecSampleRate) {
|
||||||
|
@ -5524,6 +5615,10 @@ class RawCodec extends BasicCodec {
|
||||||
initialise() {
|
initialise() {
|
||||||
this.converterRaw = Module._malloc(this.bufferSize);
|
this.converterRaw = Module._malloc(this.bufferSize);
|
||||||
this.converter = new Uint8Array(Module.HEAPU8.buffer, this.converterRaw, this.bufferSize);
|
this.converter = new Uint8Array(Module.HEAPU8.buffer, this.converterRaw, this.bufferSize);
|
||||||
|
return new Promise(resolve => resolve());
|
||||||
|
}
|
||||||
|
initialized() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
deinitialise() { }
|
deinitialise() { }
|
||||||
decode(data) {
|
decode(data) {
|
||||||
|
@ -5648,10 +5743,11 @@ class AudioResampler {
|
||||||
throw "The target sample rate is outside the range [3000, 384000].";
|
throw "The target sample rate is outside the range [3000, 384000].";
|
||||||
}
|
}
|
||||||
resample(buffer) {
|
resample(buffer) {
|
||||||
|
//console.log("Encode from %i to %i", buffer.sampleRate, this.targetSampleRate);
|
||||||
if (buffer.sampleRate == this.targetSampleRate)
|
if (buffer.sampleRate == this.targetSampleRate)
|
||||||
return new Promise(resolve => resolve(buffer));
|
return new Promise(resolve => resolve(buffer));
|
||||||
let context;
|
let context;
|
||||||
context = new OfflineAudioContext(buffer.numberOfChannels, Math.floor(buffer.length * this.targetSampleRate / buffer.sampleRate), this.targetSampleRate);
|
context = new OfflineAudioContext(buffer.numberOfChannels, Math.ceil(buffer.length * this.targetSampleRate / buffer.sampleRate), this.targetSampleRate);
|
||||||
let source = context.createBufferSource();
|
let source = context.createBufferSource();
|
||||||
source.buffer = buffer;
|
source.buffer = buffer;
|
||||||
source.connect(context.destination);
|
source.connect(context.destination);
|
||||||
|
|
File diff suppressed because one or more lines are too long
2
generated/js/client.min.js
vendored
2
generated/js/client.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
18
index.php
18
index.php
|
@ -29,6 +29,7 @@
|
||||||
<link rel="stylesheet" href="css/ts/icons.css" type="text/css">
|
<link rel="stylesheet" href="css/ts/icons.css" type="text/css">
|
||||||
<link rel="stylesheet" href="css/general.css" type="text/css">
|
<link rel="stylesheet" href="css/general.css" type="text/css">
|
||||||
<link rel="stylesheet" href="css/modals.css" type="text/css">
|
<link rel="stylesheet" href="css/modals.css" type="text/css">
|
||||||
|
<link rel="stylesheet" href="css/loader.css" type="text/css">
|
||||||
|
|
||||||
<!-- PHP generated properies -->
|
<!-- PHP generated properies -->
|
||||||
<!-- localhost:63342/TeaSpeak-Web/index.php?_ijt=o48hmliefjoa8cer8v7mpl98pj&connect_default_host=192.168.43.141 -->
|
<!-- localhost:63342/TeaSpeak-Web/index.php?_ijt=o48hmliefjoa8cer8v7mpl98pj&connect_default_host=192.168.43.141 -->
|
||||||
|
@ -70,6 +71,23 @@
|
||||||
elements.item(0).remove();
|
elements.item(0).remove();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- Loading screen -->
|
||||||
|
<div class="loader">
|
||||||
|
<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 -->
|
<!-- Critical load error -->
|
||||||
<div style="display: none; position: fixed; top: 0px; bottom: 0px; left: 0px; right: 0px; background-color: gray; z-index: 1000; text-align: center;" id="critical-load">
|
<div style="display: none; position: fixed; top: 0px; bottom: 0px; left: 0px; right: 0px; background-color: gray; z-index: 1000; text-align: center;" id="critical-load">
|
||||||
<div style="position: relative; display: inline-block; top: 30%">
|
<div style="position: relative; display: inline-block; top: 30%">
|
||||||
|
|
37
js/load.ts
37
js/load.ts
|
@ -9,6 +9,7 @@ namespace app {
|
||||||
let applicationLoaded: boolean;
|
let applicationLoaded: boolean;
|
||||||
export let type: Type = Type.UNDEFINED;
|
export let type: Type = Type.UNDEFINED;
|
||||||
export let loadedListener: (() => any)[];
|
export let loadedListener: (() => any)[];
|
||||||
|
export const appLoaded = Date.now();
|
||||||
|
|
||||||
export function initialized() : boolean {
|
export function initialized() : boolean {
|
||||||
return moduleInitialized && applicationLoaded;
|
return moduleInitialized && applicationLoaded;
|
||||||
|
@ -59,6 +60,7 @@ namespace app {
|
||||||
if(typeof Module === "undefined")
|
if(typeof Module === "undefined")
|
||||||
this["Module"] = {};
|
this["Module"] = {};
|
||||||
app.initialize();
|
app.initialize();
|
||||||
|
app.loadedListener.push(fadeoutLoader);
|
||||||
|
|
||||||
|
|
||||||
function loadScripts(paths: (string | string[])[]) : {path: string, promise: Promise<Boolean>}[] {
|
function loadScripts(paths: (string | string[])[]) : {path: string, promise: Promise<Boolean>}[] {
|
||||||
|
@ -213,6 +215,7 @@ function displayCriticalError(message: string, closeable: boolean = true) {
|
||||||
|
|
||||||
tag.style.display = "block";
|
tag.style.display = "block";
|
||||||
}
|
}
|
||||||
|
fadeoutLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadTemplates() {
|
function loadTemplates() {
|
||||||
|
@ -240,6 +243,10 @@ function loadTemplates() {
|
||||||
|
|
||||||
//TODO release config!
|
//TODO release config!
|
||||||
function loadSide() {
|
function loadSide() {
|
||||||
|
if(typeof (WebAssembly) === "undefined" || typeof (WebAssembly.compile) === "undefined") {
|
||||||
|
displayCriticalError("You require WebAssembly for TeaSpeak-Web!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
//Load the general scripts and required scripts
|
//Load the general scripts and required scripts
|
||||||
awaitLoad(loadScripts([
|
awaitLoad(loadScripts([
|
||||||
["vendor/jquery/jquery.min.js", /*"https://code.jquery.com/jquery-latest.min.js"*/],
|
["vendor/jquery/jquery.min.js", /*"https://code.jquery.com/jquery-latest.min.js"*/],
|
||||||
|
@ -255,3 +262,33 @@ function loadSide() {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSide();
|
loadSide();
|
||||||
|
|
||||||
|
//FUN: loader_ignore_age=0&loader_default_duration=1500&loader_default_age=5000
|
||||||
|
function fadeoutLoader(duration = undefined, minAge = undefined, ignoreAge = undefined) {
|
||||||
|
let settingsDefined = typeof(StaticSettings) !== "undefined";
|
||||||
|
if(!duration) {
|
||||||
|
if(settingsDefined)
|
||||||
|
duration = StaticSettings.instance.static("loader_default_duration", 750);
|
||||||
|
else duration = 750;
|
||||||
|
}
|
||||||
|
if(!minAge) {
|
||||||
|
if(settingsDefined)
|
||||||
|
minAge = StaticSettings.instance.static("loader_default_age", 1750);
|
||||||
|
else minAge = 750;
|
||||||
|
}
|
||||||
|
if(!ignoreAge) {
|
||||||
|
if(settingsDefined)
|
||||||
|
ignoreAge = StaticSettings.instance.static("loader_ignore_age", false);
|
||||||
|
else ignoreAge = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let age = Date.now() - app.appLoaded;
|
||||||
|
if(age < minAge && !ignoreAge) {
|
||||||
|
setTimeout(() => fadeoutLoader(duration, 0, true), minAge - age);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$(".loader .bookshelf_wrapper").animate({top: 0, opacity: 0}, duration);
|
||||||
|
$(".loader .half").animate({width: 0}, duration, () => {
|
||||||
|
$(".loader").detach();
|
||||||
|
});
|
||||||
|
}
|
118
js/settings.ts
118
js/settings.ts
|
@ -8,30 +8,41 @@ if(typeof(customElements) !== "undefined") {
|
||||||
customElements.define('x-property', X_Property, { extends: 'div' });
|
customElements.define('x-property', X_Property, { extends: 'div' });
|
||||||
}
|
}
|
||||||
|
|
||||||
class Settings {
|
class StaticSettings {
|
||||||
static readonly KEY_DISABLE_CONTEXT_MENU = "disableContextMenu";
|
private static _instance: StaticSettings;
|
||||||
static readonly KEY_DISABLE_UNLOAD_DIALOG = "disableUnloadDialog";
|
static get instance() : StaticSettings {
|
||||||
|
if(!this._instance)
|
||||||
|
this._instance = new StaticSettings(true);
|
||||||
|
return this._instance;
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly UPDATE_DIRECT: boolean = true;
|
protected static transformStO?<T>(input?: string, _default?: T) : T {
|
||||||
private cacheGlobal = {};
|
if (typeof input === "undefined") return _default;
|
||||||
private cacheServer = {};
|
if (typeof _default === "string") return input as any;
|
||||||
private currentServer: ServerEntry;
|
else if (typeof _default === "number") return parseInt(input) as any;
|
||||||
private saveWorker: NodeJS.Timer;
|
else if (typeof _default === "boolean") return (input == "1" || input == "true") as any;
|
||||||
private updated: boolean = false;
|
else if (typeof _default === "undefined") return input as any;
|
||||||
private _staticPropsTag: JQuery;
|
return JSON.parse(input) as any;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
protected static transformOtS?<T>(input: T) : string {
|
||||||
this._staticPropsTag = $("#properties");
|
if (typeof input === "string") return input as string;
|
||||||
|
else if (typeof input === "number") return input.toString();
|
||||||
|
else if (typeof input === "boolean") return input ? "1" : "0";
|
||||||
|
else if (typeof input == "undefined") return undefined;
|
||||||
|
return JSON.stringify(input);
|
||||||
|
}
|
||||||
|
|
||||||
this.cacheGlobal = JSON.parse(localStorage.getItem("settings.global"));
|
protected _handle: StaticSettings;
|
||||||
if(!this.cacheGlobal) this.cacheGlobal = {};
|
protected _staticPropsTag: JQuery;
|
||||||
const _this = this;
|
|
||||||
this.saveWorker = setInterval(() => {
|
|
||||||
if(_this.updated)
|
|
||||||
_this.save();
|
|
||||||
}, 5 * 1000);
|
|
||||||
|
|
||||||
this.initializeStatic();
|
protected constructor(_reserved = undefined) {
|
||||||
|
if(_reserved && !StaticSettings._instance) {
|
||||||
|
this._staticPropsTag = $("#properties");
|
||||||
|
this.initializeStatic();
|
||||||
|
} else {
|
||||||
|
this._handle = StaticSettings.instance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeStatic() {
|
private initializeStatic() {
|
||||||
|
@ -44,45 +55,59 @@ class Settings {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static transformStO?<T>(input?: string, _default?: T) : T {
|
static?<T>(key: string, _default?: T) : T {
|
||||||
if (typeof input === "undefined") return _default;
|
if(this._handle) return this._handle.static<T>(key, _default);
|
||||||
if (typeof _default === "string") return input as any;
|
let result = this._staticPropsTag.find("[key='" + key + "']");
|
||||||
else if (typeof _default === "number") return parseInt(input) as any;
|
console.log("%d | %o", result.length, result);
|
||||||
else if (typeof _default === "boolean") return (input == "1" || input == "true") as any;
|
return StaticSettings.transformStO(result.length > 0 ? decodeURIComponent(result.last().attr("value")) : undefined, _default);
|
||||||
else if (typeof _default === "undefined") return input as any;
|
|
||||||
return JSON.parse(input) as any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static transformOtS?<T>(input: T) : string {
|
deleteStatic(key: string) {
|
||||||
if (typeof input === "string") return input as string;
|
if(this._handle) {
|
||||||
else if (typeof input === "number") return input.toString();
|
this._handle.deleteStatic(key);
|
||||||
else if (typeof input === "boolean") return input ? "1" : "0";
|
return;
|
||||||
else if (typeof input == "undefined") return undefined;
|
}
|
||||||
return JSON.stringify(input);
|
let result = this._staticPropsTag.find("[key='" + key + "']");
|
||||||
|
if(result.length != 0) result.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Settings extends StaticSettings {
|
||||||
|
static readonly KEY_DISABLE_CONTEXT_MENU = "disableContextMenu";
|
||||||
|
static readonly KEY_DISABLE_UNLOAD_DIALOG = "disableUnloadDialog";
|
||||||
|
|
||||||
|
private static readonly UPDATE_DIRECT: boolean = true;
|
||||||
|
private cacheGlobal = {};
|
||||||
|
private cacheServer = {};
|
||||||
|
private currentServer: ServerEntry;
|
||||||
|
private saveWorker: NodeJS.Timer;
|
||||||
|
private updated: boolean = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.cacheGlobal = JSON.parse(localStorage.getItem("settings.global"));
|
||||||
|
if(!this.cacheGlobal) this.cacheGlobal = {};
|
||||||
|
this.saveWorker = setInterval(() => {
|
||||||
|
if(this.updated)
|
||||||
|
this.save();
|
||||||
|
}, 5 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
global?<T>(key: string, _default?: T) : T {
|
global?<T>(key: string, _default?: T) : T {
|
||||||
let result = this.cacheGlobal[key];
|
let result = this.cacheGlobal[key];
|
||||||
return Settings.transformStO(result, _default);
|
return StaticSettings.transformStO(result, _default);
|
||||||
}
|
}
|
||||||
|
|
||||||
server?<T>(key: string, _default?: T) : T {
|
server?<T>(key: string, _default?: T) : T {
|
||||||
let result = this.cacheServer[key];
|
let result = this.cacheServer[key];
|
||||||
return Settings.transformStO(result, _default);
|
return StaticSettings.transformStO(result, _default);
|
||||||
}
|
}
|
||||||
|
|
||||||
static?<T>(key: string, _default?: T) : T {
|
|
||||||
let result = this._staticPropsTag.find("[key='" + key + "']");
|
|
||||||
console.log("%d | %o", result.length, result);
|
|
||||||
return Settings.transformStO(result.length > 0 ? decodeURIComponent(result.last().attr("value")) : undefined, _default);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
changeGlobal<T>(key: string, value?: T){
|
changeGlobal<T>(key: string, value?: T){
|
||||||
if(this.cacheGlobal[key] == value) return;
|
if(this.cacheGlobal[key] == value) return;
|
||||||
|
|
||||||
this.updated = true;
|
this.updated = true;
|
||||||
this.cacheGlobal[key] = Settings.transformOtS(value);
|
this.cacheGlobal[key] = StaticSettings.transformOtS(value);
|
||||||
|
|
||||||
if(Settings.UPDATE_DIRECT)
|
if(Settings.UPDATE_DIRECT)
|
||||||
this.save();
|
this.save();
|
||||||
|
@ -92,7 +117,7 @@ class Settings {
|
||||||
if(this.cacheServer[key] == value) return;
|
if(this.cacheServer[key] == value) return;
|
||||||
|
|
||||||
this.updated = true;
|
this.updated = true;
|
||||||
this.cacheServer[key] = Settings.transformOtS(value);
|
this.cacheServer[key] = StaticSettings.transformOtS(value);
|
||||||
|
|
||||||
if(Settings.UPDATE_DIRECT)
|
if(Settings.UPDATE_DIRECT)
|
||||||
this.save();
|
this.save();
|
||||||
|
@ -126,9 +151,4 @@ class Settings {
|
||||||
let global = JSON.stringify(this.cacheGlobal);
|
let global = JSON.stringify(this.cacheGlobal);
|
||||||
localStorage.setItem("settings.global", global);
|
localStorage.setItem("settings.global", global);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteStatic(key: string) {
|
|
||||||
let result = this._staticPropsTag.find("[key='" + key + "']");
|
|
||||||
if(result.length != 0) result.detach();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -80,6 +80,7 @@ class AudioController {
|
||||||
private _volume: number = 1;
|
private _volume: number = 1;
|
||||||
private _codecCache: CodecClientCache[] = [];
|
private _codecCache: CodecClientCache[] = [];
|
||||||
private _timeIndex: number = 0;
|
private _timeIndex: number = 0;
|
||||||
|
private _latencyBufferLength: number = 3;
|
||||||
allowBuffering: boolean = true;
|
allowBuffering: boolean = true;
|
||||||
|
|
||||||
//Events
|
//Events
|
||||||
|
@ -105,7 +106,7 @@ class AudioController {
|
||||||
if (buffer.sampleRate != this.speakerContext.sampleRate)
|
if (buffer.sampleRate != this.speakerContext.sampleRate)
|
||||||
console.warn("[AudioController] Source sample rate isn't equal to playback sample rate! (" + buffer.sampleRate + " | " + this.speakerContext.sampleRate + ")");
|
console.warn("[AudioController] Source sample rate isn't equal to playback sample rate! (" + buffer.sampleRate + " | " + this.speakerContext.sampleRate + ")");
|
||||||
|
|
||||||
this.applayVolume(buffer);
|
this.applyVolume(buffer);
|
||||||
this.audioCache.push(buffer);
|
this.audioCache.push(buffer);
|
||||||
if(this.playerState == PlayerState.STOPPED || this.playerState == PlayerState.STOPPING) {
|
if(this.playerState == PlayerState.STOPPED || this.playerState == PlayerState.STOPPING) {
|
||||||
console.log("[Audio] Starting new playback");
|
console.log("[Audio] Starting new playback");
|
||||||
|
@ -117,7 +118,7 @@ class AudioController {
|
||||||
switch (this.playerState) {
|
switch (this.playerState) {
|
||||||
case PlayerState.PREBUFFERING:
|
case PlayerState.PREBUFFERING:
|
||||||
case PlayerState.BUFFERING:
|
case PlayerState.BUFFERING:
|
||||||
if(this.audioCache.length < 3) {
|
if(this.audioCache.length <= this._latencyBufferLength) {
|
||||||
if(this.playerState == PlayerState.BUFFERING) {
|
if(this.playerState == PlayerState.BUFFERING) {
|
||||||
if(this.allowBuffering) break;
|
if(this.allowBuffering) break;
|
||||||
} else break;
|
} else break;
|
||||||
|
@ -192,10 +193,10 @@ class AudioController {
|
||||||
if(this._volume == val) return;
|
if(this._volume == val) return;
|
||||||
this._volume = val;
|
this._volume = val;
|
||||||
for(let buffer of this.audioCache)
|
for(let buffer of this.audioCache)
|
||||||
this.applayVolume(buffer);
|
this.applyVolume(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private applayVolume(buffer: AudioBuffer) {
|
private applyVolume(buffer: AudioBuffer) {
|
||||||
for(let channel = 0; channel < buffer.numberOfChannels; channel++) {
|
for(let channel = 0; channel < buffer.numberOfChannels; channel++) {
|
||||||
let data = buffer.getChannelData(channel);
|
let data = buffer.getChannelData(channel);
|
||||||
for(let sample = 0; sample < data.length; sample++) {
|
for(let sample = 0; sample < data.length; sample++) {
|
||||||
|
|
|
@ -52,7 +52,6 @@ class CodecPool {
|
||||||
else {
|
else {
|
||||||
this.entries[index].instance.initialise().then((flag) => {
|
this.entries[index].instance.initialise().then((flag) => {
|
||||||
//TODO test success flag
|
//TODO test success flag
|
||||||
console.error(flag);
|
|
||||||
this.ownCodec(clientId, false).then(resolve).catch(reject);
|
this.ownCodec(clientId, false).then(resolve).catch(reject);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error("Could not initialize codec!\nError: %o", error);
|
console.error("Could not initialize codec!\nError: %o", error);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"../node_modules",
|
"../node_modules",
|
||||||
"../js/codec/workers"
|
"../js/codec/workers",
|
||||||
|
"../js/workers"
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"../js/load.ts",
|
"../js/load.ts",
|
||||||
"../node_modules",
|
"../node_modules",
|
||||||
"../js/codec/workers"
|
"../js/workers"
|
||||||
],
|
],
|
||||||
"include": [
|
"include": [
|
||||||
"../js/**/*"
|
"../js/**/*"
|
||||||
|
|
Loading…
Add table
Reference in a new issue