Implemented a load animation

canary
WolverinDEV 2018-04-19 19:46:47 +02:00
parent 42ed493db0
commit 2506923178
12 changed files with 601 additions and 129 deletions

300
css/loader.scss Normal file
View 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);
}
}

View File

@ -682,65 +682,95 @@ class PushToTalkVAD extends VoiceActivityDetector {
class CodecPoolEntry {
}
class CodecPool {
constructor(handle, index, creator) {
constructor(handle, index, name, creator) {
this.entries = [];
this.maxInstances = 2;
this._supported = true;
this.creator = creator;
this.handle = handle;
this.codecIndex = index;
this.name = name;
}
initialize(cached) {
for (let i = 0; i < cached; i++)
this.ownCodec(i);
for (let i = 0; i < cached; i++)
this.releaseCodec(i);
this.ownCodec(i + 1).then(codec => {
console.log("Release again! (%o)", codec);
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) {
if (!this.creator)
return null;
let free = 0;
for (let index = 0; index < this.entries.length; index++) {
if (this.entries[index].owner == clientId) {
this.entries[index].last_access = new Date().getTime();
return this.entries[index].instance;
return new Promise((resolve, reject) => {
if (!this.creator || !this._supported) {
reject("unsupported codec!");
return;
}
else if (free == 0 && this.entries[index].owner == 0) {
free = index;
let freeSlot = 0;
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)
return null;
if (free == 0) {
free = this.entries.length;
let entry = new CodecPoolEntry();
entry.instance = this.creator();
entry.instance.initialise();
entry.instance.on_encoded_data = buffer => this.handle.sendVoicePacket(buffer, this.codecIndex);
this.entries.push(entry);
}
this.entries[free].owner = clientId;
this.entries[free].last_access = new Date().getTime();
this.entries[free].instance.reset();
return this.entries[free].instance;
if (!create) {
resolve(undefined);
return;
}
if (freeSlot == 0) {
freeSlot = this.entries.length;
let entry = new CodecPoolEntry();
entry.instance = this.creator();
entry.instance.on_encoded_data = buffer => this.handle.sendVoicePacket(buffer, this.codecIndex);
this.entries.push(entry);
}
this.entries[freeSlot].owner = clientId;
this.entries[freeSlot].last_access = new Date().getTime();
if (this.entries[freeSlot].instance.initialized())
this.entries[freeSlot].instance.reset();
else {
this.ownCodec(clientId, false).then(resolve).catch(reject);
return;
}
resolve(this.entries[freeSlot].instance);
});
}
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)
this.entries[index].owner = 0;
}
}
}
class VoiceConnection {
constructor(client) {
this.codecPool = [
new CodecPool(this, 0, undefined),
new CodecPool(this, 1, undefined),
new CodecPool(this, 2, undefined),
new CodecPool(this, 3, undefined),
new CodecPool(this, 4, () => { return new CodecWrapper(CodecWorkerType.WORKER_OPUS, 1); }),
new CodecPool(this, 5, () => { return new CodecWrapper(CodecWorkerType.WORKER_OPUS, 2); }) //opus music
//FIXME Why is it at index 5 currently only 1?
new CodecPool(this, 0, "Spex A", undefined),
new CodecPool(this, 1, "Spex B", undefined),
new CodecPool(this, 2, "Spex C", undefined),
new CodecPool(this, 3, "CELT Mono", undefined),
new CodecPool(this, 4, "Opus Voice", () => { return new CodecWrapper(CodecWorkerType.WORKER_OPUS, 1); }),
new CodecPool(this, 5, "Opus Music", () => { return new CodecWrapper(CodecWorkerType.WORKER_OPUS, 2); }) //opus music
];
this.vpacketId = 0;
this.chunkVPacketId = 0;
@ -854,10 +884,9 @@ class VoiceConnection {
codecPool.releaseCodec(clientId);
}
else {
let decoder = codecPool.ownCodec(clientId);
decoder.decodeSamples(client.getAudioController().codecCache(codec), encodedData).then(buffer => {
client.getAudioController().playBuffer(buffer);
}).catch(error => {
codecPool.ownCodec(clientId)
.then(decoder => decoder.decodeSamples(client.getAudioController().codecCache(codec), encodedData))
.then(buffer => client.getAudioController().playBuffer(buffer)).catch(error => {
console.error("Could not playback client's (" + clientId + ") audio (" + error + ")");
});
}
@ -865,16 +894,17 @@ class VoiceConnection {
handleVoiceData(data, head) {
if (!this.voiceRecorder)
return;
if (!this.client.connected)
return false;
if (this.client.controlBar.muteInput)
return;
if (head) {
this.chunkVPacketId = 0;
this.client.getClient().speaking = true;
}
let encoder = this.codecPool[4].ownCodec(this.client.getClientId());
if (!encoder) {
console.error("Could not reserve encoder!");
return;
}
encoder.encodeSamples(this.client.getClient().getAudioController().codecCache(4), data); //TODO Use channel codec!
//TODO Use channel codec!
this.codecPool[4].ownCodec(this.client.getClientId())
.then(encoder => encoder.encodeSamples(this.client.getClient().getAudioController().codecCache(4), data));
//this.client.getClient().getAudioController().play(data);
}
handleVoiceEnded() {
@ -982,7 +1012,7 @@ function spawnMenu(x, y, ...entries) {
var sha;
(function (sha) {
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);
}
sha.sha1 = sha1;
@ -2330,6 +2360,7 @@ class ServerConnection {
constructor(client) {
this._connectionState = ConnectionState.UNCONNECTED;
this._connectTimeoutHandler = undefined;
this._connected = false;
this.on_connect = () => {
console.log("Socket connected");
chat.serverChat().appendMessage("Logging in...");
@ -2355,6 +2386,7 @@ class ServerConnection {
this._remotePort = port;
this._handshakeHandler = handshake;
this._handshakeHandler.setConnection(this);
this._connected = false;
chat.serverChat().appendMessage("Connecting to " + host + ":" + port);
const self = this;
try {
@ -2371,12 +2403,13 @@ class ServerConnection {
this._socket.onopen = () => {
if (this._socket != sockCpy)
return;
this._connected = true;
this.on_connect();
};
this._socket.onclose = event => {
if (this._socket != sockCpy)
return;
this._client.handleDisconnect(DisconnectReason.CONNECTION_CLOSED, {
this._client.handleDisconnect(this._connected ? DisconnectReason.CONNECTION_CLOSED : DisconnectReason.CONNECT_FAILURE, {
code: event.code,
reason: event.reason,
event: event
@ -2414,6 +2447,7 @@ class ServerConnection {
future.reject("Connection closed");
this._retListener = [];
this._retCodeIdx = 0;
this._connected = false;
return true;
}
handleWebSocketMessage(data) {
@ -2973,20 +3007,22 @@ class Settings {
initializeStatic() {
location.search.substr(1).split("&").forEach(part => {
let item = part.split("=");
$.spawn("div")
$("<x-property></x-property>")
.attr("key", item[0])
.attr("value", item[1])
.appendTo(this._staticPropsTag);
});
}
static transformStO(input, _default) {
if (typeof input === "undefined")
return _default;
if (typeof _default === "string")
return input;
else if (typeof _default === "number")
return parseInt(input);
else if (typeof _default === "boolean")
return (input == "1" || input == "true");
else if (typeof _default == "undefined")
else if (typeof _default === "undefined")
return input;
return JSON.parse(input);
}
@ -3011,7 +3047,8 @@ class Settings {
}
static(key, _default) {
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) {
if (this.cacheGlobal[key] == value)
@ -4218,6 +4255,9 @@ class TSClient {
this.groups.requestGroups();
this.controlBar.updateProperties();
}
get connected() {
return !!this.serverConnection && this.serverConnection.connected;
}
handleDisconnect(type, data = {}) {
switch (type) {
case DisconnectReason.REQUESTED:
@ -4225,7 +4265,10 @@ class TSClient {
case DisconnectReason.CONNECT_FAILURE:
console.error("Could not connect to remote host! Exception");
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;
case DisconnectReason.CONNECTION_CLOSED:
console.error("Lost connection to remote server!");
@ -5308,7 +5351,7 @@ class BasicCodec {
this.samplesPerUnit = 960;
this._audioContext = new OfflineAudioContext(1, 1024, 44100);
this._codecSampleRate = codecSampleRate;
this._decodeResampler = new AudioResampler();
this._decodeResampler = new AudioResampler(AudioController.globalContext.sampleRate);
this._encodeResampler = new AudioResampler(codecSampleRate);
}
encodeSamples(cache, pcm) {
@ -5328,9 +5371,13 @@ class BasicCodec {
if (buf.index == buf.buffer.length)
cache._chunks.pop_front();
}
let encodeBegin = new Date().getTime();
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);
}
else
console.error("[Codec][" + this.name() + "] Could not encode buffer. Result: " + result);
});
@ -5352,6 +5399,7 @@ class CodecWrapper extends BasicCodec {
this._workerListener = [];
this._workerCallbackToken = "callback_token";
this._workerTokeIndex = 0;
this._initialized = false;
this.type = type;
this.channelCount = channelCount;
}
@ -5359,12 +5407,31 @@ class CodecWrapper extends BasicCodec {
return "Worker for " + CodecWorkerType[this.type] + " Channels " + this.channelCount;
}
initialise() {
this.spawnWorker();
this.sendWorkerMessage({
command: "initialise",
type: this.type,
channelCount: this.channelCount
});
if (this._initializePromise)
return this._initializePromise;
return this._initializePromise = this.spawnWorker().then(() => new Promise((resolve, reject) => {
const token = this.generateToken();
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() {
this.sendWorkerMessage({
@ -5372,7 +5439,7 @@ class CodecWrapper extends BasicCodec {
});
}
decode(data) {
let token = this._workerTokeIndex++ + "_token";
let token = this.generateToken();
let result = new Promise((resolve, reject) => {
this._workerListener.push({
token: token,
@ -5402,7 +5469,7 @@ class CodecWrapper extends BasicCodec {
return result;
}
encode(data) {
let token = this._workerTokeIndex++ + "_token";
let token = this.generateToken();
let result = new Promise((resolve, reject) => {
this._workerListener.push({
token: token,
@ -5439,16 +5506,35 @@ class CodecWrapper extends BasicCodec {
});
return true;
}
generateToken() {
return this._workerTokeIndex++ + "_token";
}
sendWorkerMessage(message, transfare) {
//console.log("Send worker: %o", message);
this._worker.postMessage(JSON.stringify(message), transfare);
}
onWorkerMessage(message) {
//console.log("Worker message: %o", message);
if (!message["token"]) {
console.error("Invalid worker token!");
return;
}
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;
}
for (let entry of this._workerListener) {
@ -5461,8 +5547,12 @@ class CodecWrapper extends BasicCodec {
console.error("Could not find worker token entry! (" + message["token"] + ")");
}
spawnWorker() {
this._worker = new Worker("js/codec/CompiledCodecWorker.js");
this._worker.onmessage = event => this.onWorkerMessage(JSON.parse(event.data));
return new Promise((resolve, reject) => {
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" />
@ -5478,7 +5568,7 @@ let settings;
let globalClient;
let chat;
let forumIdentity;
function invokeMain() {
function main() {
//localhost:63343/Web-Client/index.php?disableUnloadDialog=1&default_connect_type=forum&default_connect_url=localhost
AudioController.initializeAudioController();
if (!TSIdentityHelper.setup()) {
@ -5512,6 +5602,7 @@ function invokeMain() {
Modals.spawnConnectModal(settings.static("default_connect_url"));
}
}
app.loadedListener.push(() => main());
/// <reference path="BasicCodec.ts"/>
class RawCodec extends BasicCodec {
constructor(codecSampleRate) {
@ -5524,6 +5615,10 @@ class RawCodec extends BasicCodec {
initialise() {
this.converterRaw = Module._malloc(this.bufferSize);
this.converter = new Uint8Array(Module.HEAPU8.buffer, this.converterRaw, this.bufferSize);
return new Promise(resolve => resolve());
}
initialized() {
return true;
}
deinitialise() { }
decode(data) {
@ -5648,10 +5743,11 @@ class AudioResampler {
throw "The target sample rate is outside the range [3000, 384000].";
}
resample(buffer) {
//console.log("Encode from %i to %i", buffer.sampleRate, this.targetSampleRate);
if (buffer.sampleRate == this.targetSampleRate)
return new Promise(resolve => resolve(buffer));
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();
source.buffer = buffer;
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

View File

@ -29,6 +29,7 @@
<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/modals.css" type="text/css">
<link rel="stylesheet" href="css/loader.css" type="text/css">
<!-- PHP generated properies -->
<!-- localhost:63342/TeaSpeak-Web/index.php?_ijt=o48hmliefjoa8cer8v7mpl98pj&connect_default_host=192.168.43.141 -->
@ -70,6 +71,23 @@
elements.item(0).remove();
</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 -->
<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%">

View File

@ -9,6 +9,7 @@ namespace app {
let applicationLoaded: boolean;
export let type: Type = Type.UNDEFINED;
export let loadedListener: (() => any)[];
export const appLoaded = Date.now();
export function initialized() : boolean {
return moduleInitialized && applicationLoaded;
@ -59,6 +60,7 @@ namespace app {
if(typeof Module === "undefined")
this["Module"] = {};
app.initialize();
app.loadedListener.push(fadeoutLoader);
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";
}
fadeoutLoader();
}
function loadTemplates() {
@ -240,6 +243,10 @@ function loadTemplates() {
//TODO release config!
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
awaitLoad(loadScripts([
["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();
});
}

View File

@ -8,30 +8,41 @@ if(typeof(customElements) !== "undefined") {
customElements.define('x-property', X_Property, { extends: 'div' });
}
class Settings {
static readonly KEY_DISABLE_CONTEXT_MENU = "disableContextMenu";
static readonly KEY_DISABLE_UNLOAD_DIALOG = "disableUnloadDialog";
class StaticSettings {
private static _instance: StaticSettings;
static get instance() : StaticSettings {
if(!this._instance)
this._instance = new StaticSettings(true);
return this._instance;
}
private static readonly UPDATE_DIRECT: boolean = true;
private cacheGlobal = {};
private cacheServer = {};
private currentServer: ServerEntry;
private saveWorker: NodeJS.Timer;
private updated: boolean = false;
private _staticPropsTag: JQuery;
protected static transformStO?<T>(input?: string, _default?: T) : T {
if (typeof input === "undefined") return _default;
if (typeof _default === "string") return input as any;
else if (typeof _default === "number") return parseInt(input) as any;
else if (typeof _default === "boolean") return (input == "1" || input == "true") as any;
else if (typeof _default === "undefined") return input as any;
return JSON.parse(input) as any;
}
constructor() {
this._staticPropsTag = $("#properties");
protected static transformOtS?<T>(input: T) : string {
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"));
if(!this.cacheGlobal) this.cacheGlobal = {};
const _this = this;
this.saveWorker = setInterval(() => {
if(_this.updated)
_this.save();
}, 5 * 1000);
protected _handle: StaticSettings;
protected _staticPropsTag: JQuery;
this.initializeStatic();
protected constructor(_reserved = undefined) {
if(_reserved && !StaticSettings._instance) {
this._staticPropsTag = $("#properties");
this.initializeStatic();
} else {
this._handle = StaticSettings.instance;
}
}
private initializeStatic() {
@ -44,45 +55,59 @@ class Settings {
});
}
private static transformStO?<T>(input?: string, _default?: T) : T {
if (typeof input === "undefined") return _default;
if (typeof _default === "string") return input as any;
else if (typeof _default === "number") return parseInt(input) as any;
else if (typeof _default === "boolean") return (input == "1" || input == "true") as any;
else if (typeof _default === "undefined") return input as any;
return JSON.parse(input) as any;
static?<T>(key: string, _default?: T) : T {
if(this._handle) return this._handle.static<T>(key, _default);
let result = this._staticPropsTag.find("[key='" + key + "']");
console.log("%d | %o", result.length, result);
return StaticSettings.transformStO(result.length > 0 ? decodeURIComponent(result.last().attr("value")) : undefined, _default);
}
private static transformOtS?<T>(input: T) : string {
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);
deleteStatic(key: string) {
if(this._handle) {
this._handle.deleteStatic(key);
return;
}
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 {
let result = this.cacheGlobal[key];
return Settings.transformStO(result, _default);
return StaticSettings.transformStO(result, _default);
}
server?<T>(key: string, _default?: T) : T {
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){
if(this.cacheGlobal[key] == value) return;
this.updated = true;
this.cacheGlobal[key] = Settings.transformOtS(value);
this.cacheGlobal[key] = StaticSettings.transformOtS(value);
if(Settings.UPDATE_DIRECT)
this.save();
@ -92,7 +117,7 @@ class Settings {
if(this.cacheServer[key] == value) return;
this.updated = true;
this.cacheServer[key] = Settings.transformOtS(value);
this.cacheServer[key] = StaticSettings.transformOtS(value);
if(Settings.UPDATE_DIRECT)
this.save();
@ -126,9 +151,4 @@ class Settings {
let global = JSON.stringify(this.cacheGlobal);
localStorage.setItem("settings.global", global);
}
deleteStatic(key: string) {
let result = this._staticPropsTag.find("[key='" + key + "']");
if(result.length != 0) result.detach();
}
}

View File

@ -80,6 +80,7 @@ class AudioController {
private _volume: number = 1;
private _codecCache: CodecClientCache[] = [];
private _timeIndex: number = 0;
private _latencyBufferLength: number = 3;
allowBuffering: boolean = true;
//Events
@ -105,7 +106,7 @@ class AudioController {
if (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);
if(this.playerState == PlayerState.STOPPED || this.playerState == PlayerState.STOPPING) {
console.log("[Audio] Starting new playback");
@ -117,7 +118,7 @@ class AudioController {
switch (this.playerState) {
case PlayerState.PREBUFFERING:
case PlayerState.BUFFERING:
if(this.audioCache.length < 3) {
if(this.audioCache.length <= this._latencyBufferLength) {
if(this.playerState == PlayerState.BUFFERING) {
if(this.allowBuffering) break;
} else break;
@ -192,10 +193,10 @@ class AudioController {
if(this._volume == val) return;
this._volume = val;
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++) {
let data = buffer.getChannelData(channel);
for(let sample = 0; sample < data.length; sample++) {

View File

@ -52,7 +52,6 @@ class CodecPool {
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);

View File

@ -6,6 +6,7 @@
},
"exclude": [
"../node_modules",
"../js/codec/workers"
"../js/codec/workers",
"../js/workers"
]
}

View File

@ -6,7 +6,7 @@
"exclude": [
"../js/load.ts",
"../node_modules",
"../js/codec/workers"
"../js/workers"
],
"include": [
"../js/**/*"