Implemented a load animation
parent
42ed493db0
commit
2506923178
|
@ -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
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%">
|
||||||
|
|
39
js/load.ts
39
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"*/],
|
||||||
|
@ -254,4 +261,34 @@ 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…
Reference in New Issue